You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by aa...@apache.org on 2007/06/13 17:15:42 UTC

svn commit: r546914 - in /cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src: main/java/org/apache/cayenne/access/jdbc/ test/java/org/apache/cayenne/access/ test/resources/dml/

Author: aadamchik
Date: Wed Jun 13 08:15:39 2007
New Revision: 546914

URL: http://svn.apache.org/viewvc?view=rev&rev=546914
Log:
CAY-805: SQLTemplate improvement: new #bindObjectEqual #bindObjectNotEqual directives
#bindObjectEqual - first cut

Added:
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/BindObjectEqualDirective.java
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/test/resources/dml/access.DataContextSQLTemplateTest.xml
Modified:
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessor.java
    cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/test/java/org/apache/cayenne/access/DataContextSQLTemplateTest.java

Added: cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/BindObjectEqualDirective.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/BindObjectEqualDirective.java?view=auto&rev=546914
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/BindObjectEqualDirective.java (added)
+++ cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/BindObjectEqualDirective.java Wed Jun 13 08:15:39 2007
@@ -0,0 +1,158 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+package org.apache.cayenne.access.jdbc;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.sql.Types;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+
+import org.apache.cayenne.ObjectId;
+import org.apache.cayenne.Persistent;
+import org.apache.cayenne.dba.TypesMapping;
+import org.apache.velocity.context.InternalContextAdapter;
+import org.apache.velocity.exception.MethodInvocationException;
+import org.apache.velocity.exception.ParseErrorException;
+import org.apache.velocity.exception.ResourceNotFoundException;
+import org.apache.velocity.runtime.parser.node.Node;
+
+/**
+ * A custom Velocity directive to create a set of SQL conditions to match an ObjectId of
+ * an object. Usage in Velocity template is "WHERE #bindObjectEqual($object)" or "WHERE
+ * #bindObjectEqual($object $columns $idValues)".
+ * 
+ * @since 3.0
+ * @author Andrus Adamchik
+ */
+public class BindObjectEqualDirective extends BindDirective {
+
+    public String getName() {
+        return "bindObjectEqual";
+    }
+
+    public boolean render(InternalContextAdapter context, Writer writer, Node node)
+            throws IOException, ResourceNotFoundException, ParseErrorException,
+            MethodInvocationException {
+
+        Object object = getChild(context, node, 0);
+        Map idMap = toIdMap(object);
+
+        Object sqlColumns = getChild(context, node, 1);
+        Object idColumns = getChild(context, node, 2);
+
+        if (idMap == null) {
+            // assume null object, and bind all null values
+
+            if (sqlColumns == null || idColumns == null) {
+                throw new ParseErrorException("Invalid parameters. "
+                        + "Either object has to be set "
+                        + "or sqlColumns and idColumns or both.");
+            }
+
+            idMap = Collections.EMPTY_MAP;
+        }
+        else if (sqlColumns == null || idColumns == null) {
+            // infer SQL columns from ID columns
+            sqlColumns = idMap.keySet().toArray();
+            idColumns = sqlColumns;
+        }
+
+        Object[] sqlColumnsArray = toArray(sqlColumns);
+        Object[] idColumnsArray = toArray(idColumns);
+
+        if (sqlColumnsArray.length != idColumnsArray.length) {
+            throw new ParseErrorException(
+                    "SQL columns and ID columns arrays have different sizes.");
+        }
+
+        for (int i = 0; i < sqlColumnsArray.length; i++) {
+
+            Object value = idMap.get(idColumnsArray[i]);
+
+            int jdbcType = (value != null) ? TypesMapping.getSqlTypeByJava(value
+                    .getClass()) : Types.INTEGER;
+
+            renderColumn(context, writer, sqlColumnsArray[i], i);
+            writer.write(' ');
+            render(context, writer, new ParameterBinding(value, jdbcType, -1));
+        }
+
+        return true;
+    }
+
+    protected Object[] toArray(Object columns) {
+        if (columns instanceof Collection) {
+            return ((Collection) columns).toArray();
+        }
+        else if (columns.getClass().isArray()) {
+            return (Object[]) columns;
+        }
+        else {
+            return new Object[] {
+                columns
+            };
+        }
+    }
+
+    protected Map toIdMap(Object object) throws ParseErrorException {
+        if (object instanceof Persistent) {
+            return ((Persistent) object).getObjectId().getIdSnapshot();
+        }
+        else if (object instanceof ObjectId) {
+            return ((ObjectId) object).getIdSnapshot();
+        }
+        else if (object != null) {
+            throw new ParseErrorException(
+                    "Invalid object parameter, expected Persistent or ObjectId or null: "
+                            + object);
+        }
+        else {
+            return null;
+        }
+    }
+
+    protected void renderColumn(
+            InternalContextAdapter context,
+            Writer writer,
+            Object columnName,
+            int columnIndex) throws IOException {
+
+        if (columnIndex > 0) {
+            writer.write(" AND ");
+        }
+
+        writer.write(columnName.toString());
+    }
+
+    protected void render(
+            InternalContextAdapter context,
+            Writer writer,
+            ParameterBinding binding) throws IOException {
+
+        if (binding.getValue() != null) {
+            bind(context, binding);
+            writer.write("= ?");
+        }
+        else {
+            writer.write("IS NULL");
+        }
+    }
+}

Modified: cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessor.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessor.java?view=diff&rev=546914&r1=546913&r2=546914
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessor.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessor.java Wed Jun 13 08:15:39 2007
@@ -74,6 +74,7 @@
         sharedRuntime.addProperty("userdirective", BindDirective.class.getName());
         sharedRuntime.addProperty("userdirective", BindEqualDirective.class.getName());
         sharedRuntime.addProperty("userdirective", BindNotEqualDirective.class.getName());
+        sharedRuntime.addProperty("userdirective", BindObjectEqualDirective.class.getName());
         sharedRuntime.addProperty("userdirective", ResultDirective.class.getName());
         sharedRuntime.addProperty("userdirective", ChainDirective.class.getName());
         sharedRuntime.addProperty("userdirective", ChunkDirective.class.getName());

Modified: cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/test/java/org/apache/cayenne/access/DataContextSQLTemplateTest.java
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/test/java/org/apache/cayenne/access/DataContextSQLTemplateTest.java?view=diff&rev=546914&r1=546913&r2=546914
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/test/java/org/apache/cayenne/access/DataContextSQLTemplateTest.java (original)
+++ cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/test/java/org/apache/cayenne/access/DataContextSQLTemplateTest.java Wed Jun 13 08:15:39 2007
@@ -19,11 +19,15 @@
 
 package org.apache.cayenne.access;
 
+import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 
 import org.apache.art.Artist;
+import org.apache.art.Painting;
+import org.apache.cayenne.DataObjectUtils;
 import org.apache.cayenne.DataRow;
+import org.apache.cayenne.ObjectContext;
 import org.apache.cayenne.query.SQLTemplate;
 import org.apache.cayenne.unit.CayenneCase;
 
@@ -57,9 +61,9 @@
         DataRow row2 = (DataRow) rows.get(1);
         assertFalse(row2.containsKey("ARTIST_ID"));
         assertTrue(row2.containsKey("artist_id"));
-    
+
         query.setColumnNamesCapitalization(SQLTemplate.UPPERCASE_COLUMN_NAMES);
-        
+
         List rowsUpper = context.performQuery(query);
 
         DataRow row3 = (DataRow) rowsUpper.get(0);
@@ -108,6 +112,46 @@
 
         Artist artist2 = (Artist) objects.get(1);
         assertEquals("artist2", artist2.getArtistName());
+    }
+
+    public void testBindObjectEqualShort() throws Exception {
+        createTestData("prepare");
+
+        ObjectContext context = createDataContext();
+
+        Artist a = (Artist) DataObjectUtils.objectForPK(context, Artist.class, 33002);
+
+        String template = "SELECT * FROM PAINTING "
+                + "WHERE #bindObjectEqual($a) ORDER BY PAINTING_ID";
+        SQLTemplate query = new SQLTemplate(Painting.class, template);
+        query.setColumnNamesCapitalization(SQLTemplate.UPPERCASE_COLUMN_NAMES);
+        query.setParameters(Collections.singletonMap("a", a));
+
+        List objects = context.performQuery(query);
+        assertEquals(1, objects.size());
+
+        Painting p = (Painting) objects.get(0);
+        assertEquals(33002, DataObjectUtils.intPKForObject(p));
+    }
+
+    public void testBindObjectEqualFull() throws Exception {
+        createTestData("prepare");
+
+        ObjectContext context = createDataContext();
+
+        Artist a = (Artist) DataObjectUtils.objectForPK(context, Artist.class, 33002);
+
+        String template = "SELECT * FROM PAINTING t0"
+                + " WHERE #bindObjectEqual($a [ 't0.ARTIST_ID' ] [ 'ARTIST_ID' ] ) ORDER BY PAINTING_ID";
+        SQLTemplate query = new SQLTemplate(Painting.class, template);
+        query.setColumnNamesCapitalization(SQLTemplate.UPPERCASE_COLUMN_NAMES);
+        query.setParameters(Collections.singletonMap("a", a));
+
+        List objects = context.performQuery(query);
+        assertEquals(1, objects.size());
+
+        Painting p = (Painting) objects.get(0);
+        assertEquals(33002, DataObjectUtils.intPKForObject(p));
     }
 
     public void testFetchLimit() throws Exception {

Added: cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/test/resources/dml/access.DataContextSQLTemplateTest.xml
URL: http://svn.apache.org/viewvc/cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/test/resources/dml/access.DataContextSQLTemplateTest.xml?view=auto&rev=546914
==============================================================================
--- cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/test/resources/dml/access.DataContextSQLTemplateTest.xml (added)
+++ cayenne/main/trunk/framework/cayenne-jdk1.4-unpublished/src/test/resources/dml/access.DataContextSQLTemplateTest.xml Wed Jun 13 08:15:39 2007
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
+
+<beans default-lazy-init="true">	
+	<!-- ======================================= -->
+	<!-- Named Queries -->
+	<!-- ======================================= -->
+	
+	<!-- ARTIST -->
+	<bean id="A1" class="org.apache.cayenne.unit.util.UpdatingSQLTemplate">
+		<constructor-arg type="java.lang.Class"><value>org.apache.art.Artist</value></constructor-arg>
+		<constructor-arg><value>
+		insert into ARTIST (ARTIST_ID, ARTIST_NAME) values (33001, 'AA1')
+		</value></constructor-arg>
+	</bean>
+	
+	<bean id="A2" class="org.apache.cayenne.unit.util.UpdatingSQLTemplate">
+		<constructor-arg type="java.lang.Class"><value>org.apache.art.Artist</value></constructor-arg>
+		<constructor-arg><value>
+		insert into ARTIST (ARTIST_ID, ARTIST_NAME) values (33002, 'AA2')
+		</value></constructor-arg>
+	</bean>
+	
+	<bean id="A3" class="org.apache.cayenne.unit.util.UpdatingSQLTemplate">
+		<constructor-arg type="java.lang.Class"><value>org.apache.art.Artist</value></constructor-arg>
+		<constructor-arg><value>
+		insert into ARTIST (ARTIST_ID, ARTIST_NAME) values (33003, 'BB1')
+		</value></constructor-arg>
+	</bean>
+	
+	<bean id="A4" class="org.apache.cayenne.unit.util.UpdatingSQLTemplate">
+		<constructor-arg type="java.lang.Class"><value>org.apache.art.Artist</value></constructor-arg>
+		<constructor-arg><value>
+		insert into ARTIST (ARTIST_ID, ARTIST_NAME) values (33004, 'BB2')
+		</value></constructor-arg>
+	</bean>
+	
+	<bean id="P11" class="org.apache.cayenne.unit.util.UpdatingSQLTemplate">
+		<constructor-arg type="java.lang.Class"><value>org.apache.art.Painting</value></constructor-arg>
+		<constructor-arg><value>
+		INSERT INTO PAINTING (PAINTING_ID, PAINTING_TITLE, ARTIST_ID, ESTIMATED_PRICE) VALUES (33001, 'P1', 33001, 3000)
+		</value></constructor-arg>
+	</bean>
+	
+	<bean id="P12" class="org.apache.cayenne.unit.util.UpdatingSQLTemplate">
+		<constructor-arg type="java.lang.Class"><value>org.apache.art.Painting</value></constructor-arg>
+		<constructor-arg><value>
+		INSERT INTO PAINTING (PAINTING_ID, PAINTING_TITLE, ARTIST_ID, ESTIMATED_PRICE) VALUES (33002, 'P2', 33002, 5000)
+		</value></constructor-arg>
+	</bean>
+	
+
+	<!-- ======================================= -->
+	<!-- Data Sets -->
+	<!-- ======================================= -->	
+	
+	<bean id="prepare" class="java.util.ArrayList">
+		<constructor-arg>
+			<list>
+				<ref bean="A1"/>
+				<ref bean="A2"/>
+				<ref bean="A3"/>
+				<ref bean="A4"/>
+				<ref bean="P11"/>
+				<ref bean="P12"/>
+			</list>
+		</constructor-arg>
+	</bean>
+</beans>
\ No newline at end of file