http://git-wip-us.apache.org/repos/asf/cayenne/blob/c8709542/cayenne-server/src/main/java/org/apache/cayenne/velocity/ResultDirective.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/velocity/ResultDirective.java b/cayenne-server/src/main/java/org/apache/cayenne/velocity/ResultDirective.java new file mode 100644 index 0000000..8a3a5e4 --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/velocity/ResultDirective.java @@ -0,0 +1,206 @@ +/***************************************************************** + * 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.velocity; + +import java.io.IOException; +import java.io.Writer; +import java.math.BigDecimal; +import java.math.BigInteger; +import java.sql.Date; +import java.sql.Time; +import java.sql.Timestamp; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +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.directive.Directive; +import org.apache.velocity.runtime.parser.node.Node; +import org.apache.cayenne.access.jdbc.ColumnDescriptor; +import org.apache.cayenne.util.Util; + +/** + * A custom Velocity directive to describe a ResultSet column. There are the following + * possible invocation formats inside the template: + * + * <pre> + * #result(column_name) - e.g. #result('ARTIST_ID') + * #result(column_name java_type) - e.g. #result('ARTIST_ID' 'String') + * #result(column_name java_type column_alias) - e.g. #result('ARTIST_ID' 'String' 'ID') + * #result(column_name java_type column_alias data_row_key) - e.g. #result('ARTIST_ID' 'String' 'ID' 'toArtist.ID') + * </pre> + * + * <p> + * 'data_row_key' is needed if SQL 'column_alias' is not appropriate as a DataRow key on + * the Cayenne side. One common case when this happens is when a DataRow retrieved from a + * query is mapped using joint prefetch keys. In this case DataRow must use DB_PATH + * expressions for joint column keys, and their format is incompatible with most databases + * alias format. + * </p> + * <p> + * Most common Java types used in JDBC can be specified without a package. This includes + * all numeric types, primitives, String, SQL dates, BigDecimal and BigInteger. + * </p> + * + * @since 1.1 + */ +public class ResultDirective extends Directive { + + private static final Map<String, String> typesGuess; + + static { + // init default types + typesGuess = new HashMap<String, String>(); + + // primitives + typesGuess.put("long", Long.class.getName()); + typesGuess.put("double", Double.class.getName()); + typesGuess.put("byte", Byte.class.getName()); + typesGuess.put("boolean", Boolean.class.getName()); + typesGuess.put("float", Float.class.getName()); + typesGuess.put("short", Short.class.getName()); + typesGuess.put("int", Integer.class.getName()); + + // numeric + typesGuess.put("Long", Long.class.getName()); + typesGuess.put("Double", Double.class.getName()); + typesGuess.put("Byte", Byte.class.getName()); + typesGuess.put("Boolean", Boolean.class.getName()); + typesGuess.put("Float", Float.class.getName()); + typesGuess.put("Short", Short.class.getName()); + typesGuess.put("Integer", Integer.class.getName()); + + // other + typesGuess.put("String", String.class.getName()); + typesGuess.put("Date", Date.class.getName()); + typesGuess.put("Time", Time.class.getName()); + typesGuess.put("Timestamp", Timestamp.class.getName()); + typesGuess.put("BigDecimal", BigDecimal.class.getName()); + typesGuess.put("BigInteger", BigInteger.class.getName()); + } + + @Override + public String getName() { + return "result"; + } + + @Override + public int getType() { + return LINE; + } + + @Override + public boolean render(InternalContextAdapter context, Writer writer, Node node) + throws IOException, ResourceNotFoundException, ParseErrorException, + MethodInvocationException { + + String column = getChildAsString(context, node, 0); + if (column == null) { + throw new ParseErrorException("Column name expected at line " + + node.getLine() + + ", column " + + node.getColumn()); + } + + String alias = getChildAsString(context, node, 2); + String dataRowKey = getChildAsString(context, node, 3); + + // determine what we want to name this column in a resulting DataRow... + String label = (!Util.isEmptyString(dataRowKey)) ? dataRowKey : (!Util + .isEmptyString(alias)) ? alias : null; + + ColumnDescriptor columnDescriptor = new ColumnDescriptor(); + columnDescriptor.setName(column); + columnDescriptor.setDataRowKey(label); + + String type = getChildAsString(context, node, 1); + if (type != null) { + columnDescriptor.setJavaClass(guessType(type)); + } + + // TODO: andrus 6/27/2007 - this is an unofficial jdbcType parameter that is added + // temporarily pending CAY-813 implementation for the sake of EJBQL query... + Object jdbcType = getChild(context, node, 4); + if (jdbcType instanceof Number) { + columnDescriptor.setJdbcType(((Number) jdbcType).intValue()); + } + + writer.write(column); + + // append column alias if needed. + + // Note that if table aliases are used, this logic will result in SQL like + // "t0.ARTIST_NAME AS ARTIST_NAME". Doing extra regex matching to handle this + // won't probably buy us much. + if (!Util.isEmptyString(alias) && !alias.equals(column)) { + writer.write(" AS "); + writer.write(alias); + } + + bindResult(context, columnDescriptor); + return true; + } + + protected Object getChild(InternalContextAdapter context, Node node, int i) + throws MethodInvocationException { + return (i >= 0 && i < node.jjtGetNumChildren()) ? node.jjtGetChild(i).value( + context) : null; + } + + /** + * Returns a directive argument at a given index converted to String. + * + * @since 1.2 + */ + protected String getChildAsString(InternalContextAdapter context, Node node, int i) + throws MethodInvocationException { + Object value = getChild(context, node, i); + return (value != null) ? value.toString() : null; + } + + /** + * Converts "short" type notation to the fully qualified class name. Right now + * supports all major standard SQL types, including primitives. All other types are + * expected to be fully qualified, and are not converted. + */ + protected String guessType(String type) { + String guessed = typesGuess.get(type); + return guessed != null ? guessed : type; + } + + /** + * Adds value to the list of result columns in the context. + */ + protected void bindResult( + InternalContextAdapter context, + ColumnDescriptor columnDescriptor) { + + Collection<Object> resultColumns = (Collection<Object>) context + .getInternalUserContext() + .get(SQLTemplateProcessor.RESULT_COLUMNS_LIST_KEY); + + if (resultColumns != null) { + resultColumns.add(columnDescriptor); + } + } +}
http://git-wip-us.apache.org/repos/asf/cayenne/blob/c8709542/cayenne-server/src/main/java/org/apache/cayenne/velocity/SQLTemplateProcessor.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/velocity/SQLTemplateProcessor.java b/cayenne-server/src/main/java/org/apache/cayenne/velocity/SQLTemplateProcessor.java new file mode 100644 index 0000000..71974d3 --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/velocity/SQLTemplateProcessor.java @@ -0,0 +1,155 @@ +/***************************************************************** + * 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.velocity; + +import java.io.StringReader; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.cayenne.CayenneRuntimeException; +import org.apache.cayenne.access.jdbc.ColumnDescriptor; +import org.apache.cayenne.access.jdbc.ParameterBinding; +import org.apache.cayenne.access.jdbc.SQLStatement; +import org.apache.velocity.VelocityContext; +import org.apache.velocity.context.InternalContextAdapterImpl; +import org.apache.velocity.runtime.RuntimeConstants; +import org.apache.velocity.runtime.RuntimeInstance; +import org.apache.velocity.runtime.log.NullLogChute; +import org.apache.velocity.runtime.parser.ParseException; +import org.apache.velocity.runtime.parser.node.SimpleNode; + +/** + * Processor for SQL velocity templates. + * + * @see org.apache.cayenne.query.SQLTemplate + * @since 4.0 + */ +public class SQLTemplateProcessor { + + private static RuntimeInstance sharedRuntime; + + static final String BINDINGS_LIST_KEY = "bindings"; + static final String RESULT_COLUMNS_LIST_KEY = "resultColumns"; + static final String HELPER_KEY = "helper"; + + private static final SQLTemplateRenderingUtils sharedUtils = new SQLTemplateRenderingUtils(); + + RuntimeInstance velocityRuntime; + SQLTemplateRenderingUtils renderingUtils; + + static { + initVelocityRuntime(); + } + + private static void initVelocityRuntime() { + // init static velocity engine + sharedRuntime = new RuntimeInstance(); + + // set null logger + sharedRuntime.addProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, new NullLogChute()); + + sharedRuntime.addProperty(RuntimeConstants.RESOURCE_MANAGER_CLASS, SQLTemplateResourceManager.class.getName()); + 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", BindObjectNotEqualDirective.class.getName()); + sharedRuntime.addProperty("userdirective", ResultDirective.class.getName()); + sharedRuntime.addProperty("userdirective", ChainDirective.class.getName()); + sharedRuntime.addProperty("userdirective", ChunkDirective.class.getName()); + try { + sharedRuntime.init(); + } catch (Exception ex) { + throw new CayenneRuntimeException("Error setting up Velocity RuntimeInstance.", ex); + } + } + + public SQLTemplateProcessor() { + this.velocityRuntime = sharedRuntime; + this.renderingUtils = sharedUtils; + } + + public SQLTemplateProcessor(RuntimeInstance velocityRuntime, SQLTemplateRenderingUtils renderingUtils) { + this.velocityRuntime = velocityRuntime; + this.renderingUtils = renderingUtils; + } + + /** + * Builds and returns a SQLStatement based on SQL template and a set of + * parameters. During rendering, VelocityContext exposes the following as + * variables: all parameters in the map, {@link SQLTemplateRenderingUtils} + * as a "helper" variable and SQLStatement object as "statement" variable. + */ + public SQLStatement processTemplate(String template, Map<String, ?> parameters) throws Exception { + // have to make a copy of parameter map since we are gonna modify it.. + Map<String, Object> internalParameters = (parameters != null && !parameters.isEmpty()) ? new HashMap<String, Object>( + parameters) : new HashMap<String, Object>(5); + + List<ParameterBinding> bindings = new ArrayList<ParameterBinding>(); + List<ColumnDescriptor> results = new ArrayList<ColumnDescriptor>(); + internalParameters.put(BINDINGS_LIST_KEY, bindings); + internalParameters.put(RESULT_COLUMNS_LIST_KEY, results); + internalParameters.put(HELPER_KEY, renderingUtils); + + String sql = buildStatement(new VelocityContext(internalParameters), template); + + ParameterBinding[] bindingsArray = new ParameterBinding[bindings.size()]; + bindings.toArray(bindingsArray); + + ColumnDescriptor[] resultsArray = new ColumnDescriptor[results.size()]; + results.toArray(resultsArray); + + return new SQLStatement(sql, resultsArray, bindingsArray); + } + + String buildStatement(VelocityContext context, String template) throws Exception { + // Note: this method is a reworked version of + // org.apache.velocity.app.Velocity.evaluate(..) + // cleaned up to avoid using any Velocity singletons + + StringWriter out = new StringWriter(template.length()); + SimpleNode nodeTree = null; + + try { + nodeTree = velocityRuntime.parse(new StringReader(template), template); + } catch (ParseException pex) { + throw new CayenneRuntimeException("Error parsing template '" + template + "' : " + pex.getMessage()); + } + + if (nodeTree == null) { + throw new CayenneRuntimeException("Error parsing template " + template); + } + + // ... not sure what InternalContextAdapter is for... + InternalContextAdapterImpl ica = new InternalContextAdapterImpl(context); + ica.pushCurrentTemplateName(template); + + try { + nodeTree.init(ica, velocityRuntime); + nodeTree.render(ica, out); + return out.toString(); + } finally { + ica.popCurrentTemplateName(); + } + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/c8709542/cayenne-server/src/main/java/org/apache/cayenne/velocity/SQLTemplateRenderingUtils.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/velocity/SQLTemplateRenderingUtils.java b/cayenne-server/src/main/java/org/apache/cayenne/velocity/SQLTemplateRenderingUtils.java new file mode 100644 index 0000000..14b8646 --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/velocity/SQLTemplateRenderingUtils.java @@ -0,0 +1,37 @@ +/***************************************************************** + * 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.velocity; + +import org.apache.cayenne.exp.ExpressionFactory; + +/** + * Implements utility methods used inside Velocity templates when rendering + * SQLTemplates. + * + * @since 1.1 + */ +public class SQLTemplateRenderingUtils { + /** + * Returns the result of evaluation of expression with object. + */ + public Object cayenneExp(Object object, String expression) { + return ExpressionFactory.exp(expression).evaluate(object); + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/c8709542/cayenne-server/src/main/java/org/apache/cayenne/velocity/SQLTemplateResourceManager.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/main/java/org/apache/cayenne/velocity/SQLTemplateResourceManager.java b/cayenne-server/src/main/java/org/apache/cayenne/velocity/SQLTemplateResourceManager.java new file mode 100644 index 0000000..e80cd46 --- /dev/null +++ b/cayenne-server/src/main/java/org/apache/cayenne/velocity/SQLTemplateResourceManager.java @@ -0,0 +1,106 @@ +/***************************************************************** + * 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.velocity; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.util.Map; + +import org.apache.commons.collections.ExtendedProperties; +import org.apache.commons.collections.map.LRUMap; +import org.apache.velocity.Template; +import org.apache.velocity.exception.ParseErrorException; +import org.apache.velocity.exception.ResourceNotFoundException; +import org.apache.velocity.runtime.RuntimeServices; +import org.apache.velocity.runtime.resource.Resource; +import org.apache.velocity.runtime.resource.ResourceManager; +import org.apache.velocity.runtime.resource.loader.ResourceLoader; + +/** + * An implementation of the Velocity ResourceManager and ResourceLoader that + * creates templates from in-memory Strings. + * + * @since 1.1 + */ +// class must be public since it is instantiated by Velocity via reflection. +public class SQLTemplateResourceManager + extends ResourceLoader + implements ResourceManager { + + protected Map<String, Template> templateCache; + + public void initialize(RuntimeServices rs) throws Exception { + super.rsvc = rs; + this.templateCache = new LRUMap(100); + } + + public void clearCache() { + templateCache.clear(); + } + + /** + * Returns a Velocity Resource which is a Template for the given SQL. + */ + public Resource getResource(String resourceName, int resourceType, String encoding) + throws ResourceNotFoundException, ParseErrorException, Exception { + + synchronized (templateCache) { + Template resource = templateCache.get(resourceName); + + if (resource == null) { + resource = new Template(); + resource.setRuntimeServices(rsvc); + resource.setResourceLoader(this); + resource.setName(resourceName); + resource.setEncoding(encoding); + resource.process(); + + templateCache.put(resourceName, resource); + } + + return resource; + } + } + + public String getLoaderNameForResource(String resourceName) { + return getClass().getName(); + } + + @Override + public long getLastModified(Resource resource) { + return -1; + } + + @Override + public InputStream getResourceStream(String source) + throws ResourceNotFoundException { + return new ByteArrayInputStream(source.getBytes()); + } + + @Override + public void init(ExtendedProperties configuration) { + + } + + @Override + public boolean isSourceModified(Resource resource) { + return false; + } +} http://git-wip-us.apache.org/repos/asf/cayenne/blob/c8709542/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/BindDirectiveIT.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/BindDirectiveIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/BindDirectiveIT.java deleted file mode 100644 index e9beaa8..0000000 --- a/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/BindDirectiveIT.java +++ /dev/null @@ -1,267 +0,0 @@ -/***************************************************************** - * 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 org.apache.cayenne.DataRow; -import org.apache.cayenne.ObjectContext; -import org.apache.cayenne.access.DataNode; -import org.apache.cayenne.access.MockOperationObserver; -import org.apache.cayenne.access.jdbc.reader.RowReaderFactory; -import org.apache.cayenne.dba.JdbcAdapter; -import org.apache.cayenne.dba.oracle.OracleAdapter; -import org.apache.cayenne.di.Inject; -import org.apache.cayenne.log.JdbcEventLogger; -import org.apache.cayenne.query.CapsStrategy; -import org.apache.cayenne.query.SQLTemplate; -import org.apache.cayenne.query.SelectQuery; -import org.apache.cayenne.test.jdbc.DBHelper; -import org.apache.cayenne.testdo.testmap.Artist; -import org.apache.cayenne.unit.di.server.ServerCase; -import org.apache.cayenne.unit.di.server.ServerCaseDataSourceFactory; -import org.apache.cayenne.unit.di.server.UseServerRuntime; - -import java.sql.Connection; -import java.sql.Timestamp; -import java.util.Calendar; -import java.util.Collections; -import java.util.Date; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -import static org.mockito.Mockito.mock; - -/** - * Tests BindDirective for passed null parameters and for not passed parameters - */ -@UseServerRuntime(ServerCase.TESTMAP_PROJECT) -public class BindDirectiveIT extends ServerCase { - - @Inject - private ServerCaseDataSourceFactory dataSourceFactory; - - @Inject - private JdbcAdapter adapter; - - @Inject - private ObjectContext context; - - @Inject - private DBHelper dbHelper; - - @Inject - private JdbcEventLogger logger; - - @Override - protected void setUpAfterInjection() throws Exception { - dbHelper.deleteAll("PAINTING_INFO"); - dbHelper.deleteAll("PAINTING"); - dbHelper.deleteAll("ARTIST_EXHIBIT"); - dbHelper.deleteAll("ARTIST_GROUP"); - dbHelper.deleteAll("ARTIST"); - } - - public void testBindTimestamp() throws Exception { - Map<String, Object> parameters = new HashMap<String, Object>(); - parameters.put("id", new Integer(1)); - parameters.put("name", "ArtistWithDOB"); - Calendar cal = Calendar.getInstance(); - cal.clear(); - cal.set(2010, 2, 8); - parameters.put("dob", new Timestamp(cal.getTime().getTime())); - - // without JDBC usage - Map<String, ?> row = performInsertForParameters(parameters, false, 1); - assertEquals(parameters.get("name"), row.get("ARTIST_NAME")); - assertEquals(cal.getTime(), row.get("DATE_OF_BIRTH")); - assertNotNull(row.get("DATE_OF_BIRTH")); - assertEquals(Date.class, row.get("DATE_OF_BIRTH").getClass()); - } - - public void testBindSQLDate() throws Exception { - Map<String, Object> parameters = new HashMap<String, Object>(); - parameters.put("id", new Integer(1)); - parameters.put("name", "ArtistWithDOB"); - Calendar cal = Calendar.getInstance(); - cal.clear(); - cal.set(2010, 2, 8); - parameters.put("dob", new java.sql.Date(cal.getTime().getTime())); - - // without JDBC usage - Map<String, ?> row = performInsertForParameters(parameters, false, 1); - assertEquals(parameters.get("name"), row.get("ARTIST_NAME")); - assertEquals(parameters.get("dob"), row.get("DATE_OF_BIRTH")); - assertNotNull(row.get("DATE_OF_BIRTH")); - assertEquals(Date.class, row.get("DATE_OF_BIRTH").getClass()); - } - - public void testBindUtilDate() throws Exception { - Map<String, Object> parameters = new HashMap<String, Object>(); - parameters.put("id", new Integer(1)); - parameters.put("name", "ArtistWithDOB"); - Calendar cal = Calendar.getInstance(); - cal.clear(); - cal.set(2010, 2, 8); - parameters.put("dob", cal.getTime()); - - // without JDBC usage - Map<String, ?> row = performInsertForParameters(parameters, false, 1); - assertEquals(parameters.get("name"), row.get("ARTIST_NAME")); - assertEquals(parameters.get("dob"), row.get("DATE_OF_BIRTH")); - assertNotNull(row.get("DATE_OF_BIRTH")); - assertEquals(Date.class, row.get("DATE_OF_BIRTH").getClass()); - } - - public void testBindingForCollection() throws Exception { - - // insert 3 artists - for (int i = 1; i < 4; i++) { - Map<String, Object> parameters = new HashMap<String, Object>(); - parameters.put("id", new Long(i)); - parameters.put("name", "Artist" + i); - performInsertForParameters(parameters, true, i); - } - - // now select only with names: Artist1 and Artist3 - Set<String> artistNames = new HashSet<String>(); - artistNames.add("Artist1"); - artistNames.add("Artist3"); - String sql = "SELECT * FROM ARTIST WHERE ARTIST_NAME in (#bind($ARTISTNAMES))"; - SQLTemplate query = new SQLTemplate(Artist.class, sql); - - // customize for DB's that require trimming CHAR spaces - query.setTemplate(OracleAdapter.class.getName(), "SELECT * FROM ARTIST WHERE RTRIM(ARTIST_NAME) in (#bind($ARTISTNAMES))"); - - query.setColumnNamesCapitalization(CapsStrategy.UPPER); - query.setParameters(Collections.singletonMap("ARTISTNAMES", artistNames)); - List<DataRow> result = context.performQuery(query); - assertEquals(2, result.size()); - } - - public void testBindForPassedNullParam() throws Exception { - Map<String, Object> parameters = new HashMap<String, Object>(); - parameters.put("id", new Long(1)); - parameters.put("name", "ArtistWithoutDOB"); - // passing null in parameter - parameters.put("dob", null); - - // without JDBC usage - Map<String, ?> row = performInsertForParameters(parameters, false, 1); - assertEquals(parameters.get("id"), row.get("ARTIST_ID")); - assertEquals(parameters.get("name"), row.get("ARTIST_NAME")); - assertEquals(parameters.get("dob"), row.get("DATE_OF_BIRTH")); - assertNull(row.get("DATE_OF_BIRTH")); - } - - public void testBindWithJDBCForPassedNullParam() throws Exception { - Map<String, Object> parameters = new HashMap<String, Object>(); - parameters.put("id", new Long(1)); - parameters.put("name", "ArtistWithoutDOB"); - // passing null in parameter - parameters.put("dob", null); - - // use JDBC - Map<String, ?> row = performInsertForParameters(parameters, true, 1); - assertEquals(parameters.get("id"), row.get("ARTIST_ID")); - assertEquals(parameters.get("name"), row.get("ARTIST_NAME")); - assertEquals(parameters.get("dob"), row.get("DATE_OF_BIRTH")); - assertNull(row.get("DATE_OF_BIRTH")); - } - - public void testBindForNotPassedParam() throws Exception { - Map<String, Object> parameters = new HashMap<String, Object>(); - parameters.put("id", new Long(1)); - parameters.put("name", "ArtistWithoutDOB"); - // not passing parameter parameters.put("dob", not passed!); - - // without JDBC usage - Map<String, ?> row = performInsertForParameters(parameters, false, 1); - assertEquals(parameters.get("id"), row.get("ARTIST_ID")); - assertEquals(parameters.get("name"), row.get("ARTIST_NAME")); - // parameter should be passed as null - assertNull(row.get("DATE_OF_BIRTH")); - } - - public void testBindWithJDBCForNotPassedParam() throws Exception { - Map<String, Object> parameters = new HashMap<String, Object>(); - parameters.put("id", new Long(1)); - parameters.put("name", "ArtistWithoutDOB"); - // not passing parameter parameters.put("dob", not passed!); - - // use JDBC - Map<String, ?> row = performInsertForParameters(parameters, true, 1); - assertEquals(parameters.get("id"), row.get("ARTIST_ID")); - assertEquals(parameters.get("name"), row.get("ARTIST_NAME")); - // parameter should be passed as null - assertNull(row.get("DATE_OF_BIRTH")); - } - - /** - * Inserts row for given parameters - * - * @return inserted row - */ - private Map<String, ?> performInsertForParameters( - Map<String, Object> parameters, - boolean useJDBCType, - int expectedRowCount) throws Exception { - - String templateString; - if (useJDBCType) { - templateString = "INSERT INTO ARTIST (ARTIST_ID, ARTIST_NAME, DATE_OF_BIRTH) " - + "VALUES (#bind($id), #bind($name), #bind($dob 'DATE'))"; - } - else { - templateString = "INSERT INTO ARTIST (ARTIST_ID, ARTIST_NAME, DATE_OF_BIRTH) " - + "VALUES (#bind($id), #bind($name), #bind($dob))"; - } - SQLTemplate template = new SQLTemplate(Object.class, templateString); - - template.setParameters(parameters); - - DataNode node = new DataNode(); - node.setEntityResolver(context.getEntityResolver()); - node.setRowReaderFactory(mock(RowReaderFactory.class)); - node.setAdapter(adapter); - SQLTemplateAction action = new SQLTemplateAction(template, node); - - Connection c = dataSourceFactory.getSharedDataSource().getConnection(); - try { - MockOperationObserver observer = new MockOperationObserver(); - action.performAction(c, observer); - - int[] batches = observer.countsForQuery(template); - assertNotNull(batches); - assertEquals(1, batches.length); - assertEquals(1, batches[0]); - } - finally { - c.close(); - } - - SelectQuery query = new SelectQuery(Artist.class); - query.setFetchingDataRows(true); - - List<DataRow> data = context.performQuery(query); - assertEquals(expectedRowCount, data.size()); - return data.get(0); - } -} http://git-wip-us.apache.org/repos/asf/cayenne/blob/c8709542/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/MockupRuntimeServices.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/MockupRuntimeServices.java b/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/MockupRuntimeServices.java deleted file mode 100644 index b272dfa..0000000 --- a/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/MockupRuntimeServices.java +++ /dev/null @@ -1,42 +0,0 @@ -/***************************************************************** - * 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.Reader; - -import org.apache.velocity.runtime.RuntimeInstance; -import org.apache.velocity.runtime.parser.ParseException; -import org.apache.velocity.runtime.parser.node.SimpleNode; - -/** - */ -class MockupRuntimeServices extends RuntimeInstance { - - @Override - public SimpleNode parse(Reader reader, String templateName, boolean dumpNamespace) - throws ParseException { - return new SimpleNode(1); - } - - @Override - public SimpleNode parse(Reader reader, String templateName) throws ParseException { - return new SimpleNode(1); - } -} http://git-wip-us.apache.org/repos/asf/cayenne/blob/c8709542/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/ResultDirectiveIT.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/ResultDirectiveIT.java b/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/ResultDirectiveIT.java deleted file mode 100644 index 7b9bce4..0000000 --- a/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/ResultDirectiveIT.java +++ /dev/null @@ -1,187 +0,0 @@ -/***************************************************************** - * 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 org.apache.cayenne.DataRow; -import org.apache.cayenne.access.DataNode; -import org.apache.cayenne.access.MockOperationObserver; -import org.apache.cayenne.access.jdbc.reader.RowReaderFactory; -import org.apache.cayenne.configuration.server.ServerRuntime; -import org.apache.cayenne.dba.JdbcAdapter; -import org.apache.cayenne.di.Inject; -import org.apache.cayenne.query.CapsStrategy; -import org.apache.cayenne.query.SQLTemplate; -import org.apache.cayenne.query.SelectQuery; -import org.apache.cayenne.test.jdbc.DBHelper; -import org.apache.cayenne.testdo.testmap.Artist; -import org.apache.cayenne.unit.di.server.ServerCase; -import org.apache.cayenne.unit.di.server.UseServerRuntime; - -import java.sql.Connection; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -import static org.mockito.Mockito.mock; - -/** - * Test for Result directive to check if we could use ResultDitrective optionally. - */ -@UseServerRuntime(ServerCase.TESTMAP_PROJECT) -public class ResultDirectiveIT extends ServerCase { - - @Inject - private ServerRuntime runtime; - - @Inject - private DBHelper dbHelper; - - @Inject - private JdbcAdapter dbAdapter; - - @Override - protected void setUpAfterInjection() throws Exception { - dbHelper.deleteAll("PAINTING_INFO"); - dbHelper.deleteAll("PAINTING"); - dbHelper.deleteAll("PAINTING1"); - dbHelper.deleteAll("ARTIST_EXHIBIT"); - dbHelper.deleteAll("ARTIST_GROUP"); - dbHelper.deleteAll("ARTIST"); - dbHelper.deleteAll("EXHIBIT"); - dbHelper.deleteAll("GALLERY"); - } - - public void testWithoutResultDirective() throws Exception { - String sql = "SELECT ARTIST_ID, ARTIST_NAME FROM ARTIST"; - Map<String, Object> artist = insertArtist(); - Map<String, Object> selectResult = selectForQuery(sql); - - assertEquals(artist.get("ARTIST_ID"), selectResult.get("ARTIST_ID")); - assertEquals(artist.get("ARTIST_NAME"), selectResult.get("ARTIST_NAME")); - } - - public void testWithOnlyResultDirective() throws Exception { - String sql = "SELECT #result('ARTIST_ID' 'java.lang.Integer')," - + " #result('ARTIST_NAME' 'java.lang.String')" - + " FROM ARTIST"; - Map<String, Object> artist = insertArtist(); - Map<String, Object> selectResult = selectForQuery(sql); - - assertEquals(artist.get("ARTIST_ID"), selectResult.get("ARTIST_ID")); - assertEquals(artist.get("ARTIST_NAME"), selectResult - .get("ARTIST_NAME") - .toString() - .trim()); - } - - public void testWithMixedDirectiveUse1() throws Exception { - String sql = "SELECT ARTIST_ID," - + " #result('ARTIST_NAME' 'java.lang.String')" - + " FROM ARTIST"; - Map<String, Object> artist = insertArtist(); - Map<String, Object> selectResult = selectForQuery(sql); - - assertEquals(artist.get("ARTIST_ID"), selectResult.get("ARTIST_ID")); - assertEquals(artist.get("ARTIST_NAME"), selectResult - .get("ARTIST_NAME") - .toString() - .trim()); - } - - public void testWithMixedDirectiveUse2() throws Exception { - String sql = "SELECT #result('ARTIST_ID' 'java.lang.Integer')," - + " ARTIST_NAME " - + " FROM ARTIST"; - Map<String, Object> artist = insertArtist(); - Map<String, Object> selectResult = selectForQuery(sql); - - assertEquals(artist.get("ARTIST_ID"), selectResult.get("ARTIST_ID")); - assertEquals(artist.get("ARTIST_NAME"), selectResult.get("ARTIST_NAME")); - } - - private Map<String, Object> selectForQuery(String sql) { - SQLTemplate template = new SQLTemplate(Artist.class, sql); - template.setColumnNamesCapitalization(CapsStrategy.UPPER); - MockOperationObserver observer = new MockOperationObserver(); - runtime.getDataDomain().performQueries( - Collections.singletonList(template), - observer); - - List<Map<String, Object>> data = observer.rowsForQuery(template); - assertEquals(1, data.size()); - Map<String, Object> row = data.get(0); - return row; - } - - /** - * Inserts one Artist - * - * @return Inserted Artist as a DataRow - */ - private Map<String, Object> insertArtist() throws Exception { - Map<String, Object> parameters = new HashMap<String, Object>(); - parameters.put("id", new Integer(1)); - parameters.put("name", "ArtistToTestResult"); - String templateString = "INSERT INTO ARTIST (ARTIST_ID, ARTIST_NAME, DATE_OF_BIRTH) " - + "VALUES (#bind($id), #bind($name), #bind($dob))"; - - SQLTemplate template = new SQLTemplate(Object.class, templateString); - - template.setParameters(parameters); - - DataNode node = new DataNode(); - node.setEntityResolver(runtime.getDataDomain().getEntityResolver()); - node.setRowReaderFactory(mock(RowReaderFactory.class)); - node.setAdapter(dbAdapter); - - SQLTemplateAction action = new SQLTemplateAction(template, node); - - Connection c = runtime - .getDataDomain() - .getDataNodes() - .iterator() - .next() - .getDataSource() - .getConnection(); - try { - MockOperationObserver observer = new MockOperationObserver(); - action.performAction(c, observer); - - int[] batches = observer.countsForQuery(template); - assertNotNull(batches); - assertEquals(1, batches.length); - assertEquals(1, batches[0]); - } - finally { - c.close(); - } - - MockOperationObserver observer = new MockOperationObserver(); - SelectQuery query = new SelectQuery(Artist.class); - runtime - .getDataDomain() - .performQueries(Collections.singletonList(query), observer); - - List<?> data = observer.rowsForQuery(query); - assertEquals(1, data.size()); - DataRow row = (DataRow) data.get(0); - return row; - } -} http://git-wip-us.apache.org/repos/asf/cayenne/blob/c8709542/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessorChainTest.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessorChainTest.java b/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessorChainTest.java deleted file mode 100644 index 69e5bf4..0000000 --- a/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessorChainTest.java +++ /dev/null @@ -1,219 +0,0 @@ -/***************************************************************** - * 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 org.junit.Test; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import static org.junit.Assert.assertEquals; - -public class SQLTemplateProcessorChainTest { - - @Test - public void testProcessTemplateNoChunks() throws Exception { - // whatever is inside the chain, it should render as empty if there - // is no chunks... - - SQLStatement compiled = new SQLTemplateProcessor().processTemplate( - "#chain(' AND ') #end", - Collections.EMPTY_MAP); - - assertEquals("", compiled.getSql()); - - compiled = new SQLTemplateProcessor().processTemplate( - "#chain(' AND ') garbage #end", - Collections.EMPTY_MAP); - - assertEquals("", compiled.getSql()); - - compiled = new SQLTemplateProcessor().processTemplate( - "#chain(' AND ' 'PREFIX') #end", - Collections.EMPTY_MAP); - - assertEquals("", compiled.getSql()); - compiled = new SQLTemplateProcessor().processTemplate( - "#chain(' AND ' 'PREFIX') garbage #end", - Collections.EMPTY_MAP); - - assertEquals("", compiled.getSql()); - } - - @Test - public void testProcessTemplateFullChain() throws Exception { - String template = "#chain(' OR ')" - + "#chunk($a)$a#end" - + "#chunk($b)$b#end" - + "#chunk($c)$c#end" - + "#end"; - - Map map = new HashMap(); - map.put("a", "[A]"); - map.put("b", "[B]"); - map.put("c", "[C]"); - - SQLStatement compiled = new SQLTemplateProcessor().processTemplate(template, map); - assertEquals("[A] OR [B] OR [C]", compiled.getSql()); - } - - @Test - public void testProcessTemplateFullChainAndPrefix() throws Exception { - String template = "#chain(' OR ' 'WHERE ')" - + "#chunk($a)$a#end" - + "#chunk($b)$b#end" - + "#chunk($c)$c#end" - + "#end"; - - Map map = new HashMap(); - map.put("a", "[A]"); - map.put("b", "[B]"); - map.put("c", "[C]"); - - SQLStatement compiled = new SQLTemplateProcessor().processTemplate(template, map); - assertEquals("WHERE [A] OR [B] OR [C]", compiled.getSql()); - } - - @Test - public void testProcessTemplatePartialChainMiddle() throws Exception { - String template = "#chain(' OR ' 'WHERE ')" - + "#chunk($a)$a#end" - + "#chunk($b)$b#end" - + "#chunk($c)$c#end" - + "#end"; - - Map map = new HashMap(); - map.put("a", "[A]"); - map.put("c", "[C]"); - - SQLStatement compiled = new SQLTemplateProcessor().processTemplate(template, map); - assertEquals("WHERE [A] OR [C]", compiled.getSql()); - } - - @Test - public void testProcessTemplatePartialChainStart() throws Exception { - String template = "#chain(' OR ' 'WHERE ')" - + "#chunk($a)$a#end" - + "#chunk($b)$b#end" - + "#chunk($c)$c#end" - + "#end"; - - Map map = new HashMap(); - map.put("b", "[B]"); - map.put("c", "[C]"); - - SQLStatement compiled = new SQLTemplateProcessor().processTemplate(template, map); - assertEquals("WHERE [B] OR [C]", compiled.getSql()); - } - - @Test - public void testProcessTemplatePartialChainEnd() throws Exception { - String template = "#chain(' OR ' 'WHERE ')" - + "#chunk($a)$a#end" - + "#chunk($b)$b#end" - + "#chunk($c)$c#end" - + "#end"; - - Map map = new HashMap(); - map.put("a", "[A]"); - map.put("b", "[B]"); - - SQLStatement compiled = new SQLTemplateProcessor().processTemplate(template, map); - assertEquals("WHERE [A] OR [B]", compiled.getSql()); - } - - @Test - public void testProcessTemplateChainWithGarbage() throws Exception { - String template = "#chain(' OR ' 'WHERE ')" - + "#chunk($a)$a#end" - + " some other stuff" - + "#chunk($c)$c#end" - + "#end"; - - Map map = new HashMap(); - map.put("a", "[A]"); - map.put("c", "[C]"); - - SQLStatement compiled = new SQLTemplateProcessor().processTemplate(template, map); - assertEquals("WHERE [A] some other stuff OR [C]", compiled.getSql()); - } - - @Test - public void testProcessTemplateChainUnconditionalChunks() throws Exception { - String template = "#chain(' OR ' 'WHERE ')" - + "#chunk()C1#end" - + "#chunk()C2#end" - + "#chunk()C3#end" - + "#end"; - - SQLStatement compiled = new SQLTemplateProcessor().processTemplate( - template, - Collections.EMPTY_MAP); - assertEquals("WHERE C1 OR C2 OR C3", compiled.getSql()); - } - - @Test - public void testProcessTemplateEmptyChain() throws Exception { - String template = "#chain(' OR ' 'WHERE ')" - + "#chunk($a)$a#end" - + "#chunk($b)$b#end" - + "#chunk($c)$c#end" - + "#end"; - - SQLStatement compiled = new SQLTemplateProcessor().processTemplate( - template, - Collections.EMPTY_MAP); - assertEquals("", compiled.getSql()); - } - - @Test - public void testProcessTemplateWithFalseOrZero1() throws Exception { - String template = "#chain(' OR ' 'WHERE ')" - + "#chunk($a)[A]#end" - + "#chunk($b)[B]#end" - + "#chunk($c)$c#end" - + "#end"; - - Map map = new HashMap(); - map.put("a", false); - map.put("b", 0); - - SQLStatement compiled = new SQLTemplateProcessor().processTemplate(template, map); - assertEquals("WHERE [A] OR [B]", compiled.getSql()); - } - - @Test - public void testProcessTemplateWithFalseOrZero2() throws Exception { - String template = "#chain(' OR ' 'WHERE ')" - + "#chunk($a)$a#end" - + "#chunk($b)$b#end" - + "#chunk($c)$c#end" - + "#end"; - - Map map = new HashMap(); - map.put("a", false); - map.put("b", 0); - - SQLStatement compiled = new SQLTemplateProcessor().processTemplate(template, map); - assertEquals("WHERE false OR 0", compiled.getSql()); - } - -} http://git-wip-us.apache.org/repos/asf/cayenne/blob/c8709542/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessorSelectTest.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessorSelectTest.java b/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessorSelectTest.java deleted file mode 100644 index bc498c8..0000000 --- a/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessorSelectTest.java +++ /dev/null @@ -1,109 +0,0 @@ -/***************************************************************** - * 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 org.junit.Test; - -import java.util.Collections; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - -public class SQLTemplateProcessorSelectTest { - - @Test - public void testProcessTemplateUnchanged() throws Exception { - String sqlTemplate = "SELECT * FROM ME"; - - SQLStatement compiled = new SQLTemplateProcessor().processTemplate( - sqlTemplate, - Collections.EMPTY_MAP); - - assertEquals(sqlTemplate, compiled.getSql()); - assertEquals(0, compiled.getBindings().length); - assertEquals(0, compiled.getResultColumns().length); - } - - @Test - public void testProcessSelectTemplate1() throws Exception { - String sqlTemplate = "SELECT #result('A') FROM ME"; - - SQLStatement compiled = new SQLTemplateProcessor().processTemplate( - sqlTemplate, - Collections.EMPTY_MAP); - - assertEquals("SELECT A FROM ME", compiled.getSql()); - assertEquals(0, compiled.getBindings().length); - assertEquals(1, compiled.getResultColumns().length); - assertEquals("A", compiled.getResultColumns()[0].getName()); - assertNull(compiled.getResultColumns()[0].getJavaClass()); - } - - @Test - public void testProcessSelectTemplate2() throws Exception { - String sqlTemplate = "SELECT #result('A' 'String') FROM ME"; - - SQLStatement compiled = new SQLTemplateProcessor().processTemplate( - sqlTemplate, - Collections.EMPTY_MAP); - - assertEquals("SELECT A FROM ME", compiled.getSql()); - assertEquals(0, compiled.getBindings().length); - - assertEquals(1, compiled.getResultColumns().length); - assertEquals("A", compiled.getResultColumns()[0].getName()); - assertEquals("java.lang.String", compiled.getResultColumns()[0].getJavaClass()); - } - - @Test - public void testProcessSelectTemplate3() throws Exception { - String sqlTemplate = "SELECT #result('A' 'String' 'B') FROM ME"; - - SQLStatement compiled = new SQLTemplateProcessor().processTemplate( - sqlTemplate, - Collections.EMPTY_MAP); - - assertEquals("SELECT A AS B FROM ME", compiled.getSql()); - assertEquals(0, compiled.getBindings().length); - - assertEquals(1, compiled.getResultColumns().length); - ColumnDescriptor column = compiled.getResultColumns()[0]; - assertEquals("A", column.getName()); - assertEquals("B", column.getDataRowKey()); - assertEquals("java.lang.String", column.getJavaClass()); - } - - @Test - public void testProcessSelectTemplate4() throws Exception { - String sqlTemplate = "SELECT #result('A'), #result('B'), #result('C') FROM ME"; - - SQLStatement compiled = new SQLTemplateProcessor().processTemplate( - sqlTemplate, - Collections.EMPTY_MAP); - - assertEquals("SELECT A, B, C FROM ME", compiled.getSql()); - assertEquals(0, compiled.getBindings().length); - - assertEquals(3, compiled.getResultColumns().length); - assertEquals("A", compiled.getResultColumns()[0].getName()); - assertEquals("B", compiled.getResultColumns()[1].getName()); - assertEquals("C", compiled.getResultColumns()[2].getName()); - } -} http://git-wip-us.apache.org/repos/asf/cayenne/blob/c8709542/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessorTest.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessorTest.java b/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessorTest.java deleted file mode 100644 index a45c068..0000000 --- a/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/SQLTemplateProcessorTest.java +++ /dev/null @@ -1,260 +0,0 @@ -/***************************************************************** - * 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 org.apache.cayenne.CayenneDataObject; -import org.apache.cayenne.DataObject; -import org.apache.cayenne.ObjectId; -import org.junit.Test; - -import java.sql.Types; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; - -/** - */ -public class SQLTemplateProcessorTest { - - @Test - public void testProcessTemplateUnchanged1() throws Exception { - String sqlTemplate = "SELECT * FROM ME"; - - SQLStatement compiled = new SQLTemplateProcessor().processTemplate( - sqlTemplate, - Collections.EMPTY_MAP); - - assertEquals(sqlTemplate, compiled.getSql()); - assertEquals(0, compiled.getBindings().length); - } - - @Test - public void testProcessTemplateUnchanged2() throws Exception { - String sqlTemplate = "SELECT a.b as XYZ FROM $SYSTEM_TABLE"; - - SQLStatement compiled = new SQLTemplateProcessor().processTemplate( - sqlTemplate, - Collections.EMPTY_MAP); - - assertEquals(sqlTemplate, compiled.getSql()); - assertEquals(0, compiled.getBindings().length); - } - - @Test - public void testProcessTemplateSimpleDynamicContent() throws Exception { - String sqlTemplate = "SELECT * FROM ME WHERE $a"; - - Map map = Collections.singletonMap("a", "VALUE_OF_A"); - SQLStatement compiled = new SQLTemplateProcessor().processTemplate( - sqlTemplate, - map); - - assertEquals("SELECT * FROM ME WHERE VALUE_OF_A", compiled.getSql()); - - // bindings are not populated, since no "bind" macro is used. - assertEquals(0, compiled.getBindings().length); - } - - @Test - public void testProcessTemplateBind() throws Exception { - String sqlTemplate = "SELECT * FROM ME WHERE " - + "COLUMN1 = #bind($a 'VARCHAR') AND COLUMN2 = #bind($b 'INTEGER')"; - Map map = Collections.singletonMap("a", "VALUE_OF_A"); - SQLStatement compiled = new SQLTemplateProcessor().processTemplate( - sqlTemplate, - map); - - assertEquals("SELECT * FROM ME WHERE COLUMN1 = ? AND COLUMN2 = ?", compiled - .getSql()); - assertEquals(2, compiled.getBindings().length); - assertBindingValue("VALUE_OF_A", compiled.getBindings()[0]); - assertBindingValue(null, compiled.getBindings()[1]); - } - - @Test - public void testProcessTemplateBindGuessVarchar() throws Exception { - String sqlTemplate = "SELECT * FROM ME WHERE COLUMN1 = #bind($a)"; - Map map = Collections.singletonMap("a", "VALUE_OF_A"); - - SQLStatement compiled = new SQLTemplateProcessor().processTemplate( - sqlTemplate, - map); - - assertEquals(1, compiled.getBindings().length); - assertBindingType(Types.VARCHAR, compiled.getBindings()[0]); - } - - @Test - public void testProcessTemplateBindGuessInteger() throws Exception { - String sqlTemplate = "SELECT * FROM ME WHERE COLUMN1 = #bind($a)"; - Map map = Collections.singletonMap("a", new Integer(4)); - - SQLStatement compiled = new SQLTemplateProcessor().processTemplate( - sqlTemplate, - map); - - assertEquals(1, compiled.getBindings().length); - assertBindingType(Types.INTEGER, compiled.getBindings()[0]); - } - - @Test - public void testProcessTemplateBindEqual() throws Exception { - String sqlTemplate = "SELECT * FROM ME WHERE COLUMN #bindEqual($a 'VARCHAR')"; - - SQLStatement compiled = new SQLTemplateProcessor().processTemplate( - sqlTemplate, - Collections.EMPTY_MAP); - - assertEquals("SELECT * FROM ME WHERE COLUMN IS NULL", compiled.getSql()); - assertEquals(0, compiled.getBindings().length); - - Map map = Collections.singletonMap("a", "VALUE_OF_A"); - - compiled = new SQLTemplateProcessor().processTemplate(sqlTemplate, map); - - assertEquals("SELECT * FROM ME WHERE COLUMN = ?", compiled.getSql()); - assertEquals(1, compiled.getBindings().length); - assertBindingValue("VALUE_OF_A", compiled.getBindings()[0]); - } - - @Test - public void testProcessTemplateBindNotEqual() throws Exception { - String sqlTemplate = "SELECT * FROM ME WHERE COLUMN #bindNotEqual($a 'VARCHAR')"; - - SQLStatement compiled = new SQLTemplateProcessor().processTemplate( - sqlTemplate, - Collections.EMPTY_MAP); - - assertEquals("SELECT * FROM ME WHERE COLUMN IS NOT NULL", compiled.getSql()); - assertEquals(0, compiled.getBindings().length); - - Map map = Collections.singletonMap("a", "VALUE_OF_A"); - - compiled = new SQLTemplateProcessor().processTemplate(sqlTemplate, map); - - assertEquals("SELECT * FROM ME WHERE COLUMN <> ?", compiled.getSql()); - assertEquals(1, compiled.getBindings().length); - assertBindingValue("VALUE_OF_A", compiled.getBindings()[0]); - } - - @Test - public void testProcessTemplateID() throws Exception { - String sqlTemplate = "SELECT * FROM ME WHERE COLUMN1 = #bind($helper.cayenneExp($a, 'db:ID_COLUMN'))"; - - DataObject dataObject = new CayenneDataObject(); - dataObject.setObjectId(new ObjectId("T", "ID_COLUMN", 5)); - - Map map = Collections.singletonMap("a", dataObject); - - SQLStatement compiled = new SQLTemplateProcessor().processTemplate( - sqlTemplate, - map); - - assertEquals("SELECT * FROM ME WHERE COLUMN1 = ?", compiled.getSql()); - assertEquals(1, compiled.getBindings().length); - assertBindingValue(new Integer(5), compiled.getBindings()[0]); - } - - @Test - public void testProcessTemplateNotEqualID() throws Exception { - String sqlTemplate = "SELECT * FROM ME WHERE " - + "COLUMN1 #bindNotEqual($helper.cayenneExp($a, 'db:ID_COLUMN1')) " - + "AND COLUMN2 #bindNotEqual($helper.cayenneExp($a, 'db:ID_COLUMN2'))"; - - Map idMap = new HashMap(); - idMap.put("ID_COLUMN1", new Integer(3)); - idMap.put("ID_COLUMN2", "aaa"); - ObjectId id = new ObjectId("T", idMap); - DataObject dataObject = new CayenneDataObject(); - dataObject.setObjectId(id); - - Map map = Collections.singletonMap("a", dataObject); - - SQLStatement compiled = new SQLTemplateProcessor().processTemplate( - sqlTemplate, - map); - - assertEquals("SELECT * FROM ME WHERE COLUMN1 <> ? AND COLUMN2 <> ?", compiled - .getSql()); - assertEquals(2, compiled.getBindings().length); - assertBindingValue(new Integer(3), compiled.getBindings()[0]); - assertBindingValue("aaa", compiled.getBindings()[1]); - } - - @Test - public void testProcessTemplateConditions() throws Exception { - String sqlTemplate = "SELECT * FROM ME #if($a) WHERE COLUMN1 > #bind($a)#end"; - - Map map = Collections.singletonMap("a", "VALUE_OF_A"); - - SQLStatement compiled = new SQLTemplateProcessor().processTemplate( - sqlTemplate, - map); - - assertEquals("SELECT * FROM ME WHERE COLUMN1 > ?", compiled.getSql()); - assertEquals(1, compiled.getBindings().length); - assertBindingValue("VALUE_OF_A", compiled.getBindings()[0]); - - compiled = new SQLTemplateProcessor().processTemplate( - sqlTemplate, - Collections.EMPTY_MAP); - - assertEquals("SELECT * FROM ME ", compiled.getSql()); - assertEquals(0, compiled.getBindings().length); - } - - @Test - public void testProcessTemplateBindCollection() throws Exception { - String sqlTemplate = "SELECT * FROM ME WHERE COLUMN IN (#bind($list 'VARCHAR'))"; - - Map map = new HashMap(); - map.put("list", Arrays.asList("a", "b", "c")); - SQLStatement compiled = new SQLTemplateProcessor().processTemplate( - sqlTemplate, - map); - - assertEquals("SELECT * FROM ME WHERE COLUMN IN (?,?,?)", compiled.getSql()); - assertEquals(3, compiled.getBindings().length); - - compiled = new SQLTemplateProcessor().processTemplate(sqlTemplate, map); - assertBindingValue("a", compiled.getBindings()[0]); - assertBindingValue("b", compiled.getBindings()[1]); - assertBindingValue("c", compiled.getBindings()[2]); - } - - protected void assertBindingValue(Object expectedValue, Object binding) { - assertTrue("Not a binding!", binding instanceof ParameterBinding); - assertEquals(expectedValue, ((ParameterBinding) binding).getValue()); - } - - protected void assertBindingType(int expectedType, Object binding) { - assertTrue("Not a binding!", binding instanceof ParameterBinding); - assertEquals(expectedType, ((ParameterBinding) binding).getJdbcType()); - } - - protected void assertBindingPrecision(int expectedPrecision, Object binding) { - assertTrue("Not a binding!", binding instanceof ParameterBinding); - assertEquals(expectedPrecision, ((ParameterBinding) binding).getScale()); - } -} http://git-wip-us.apache.org/repos/asf/cayenne/blob/c8709542/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/SQLTemplateResourceManagerTest.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/SQLTemplateResourceManagerTest.java b/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/SQLTemplateResourceManagerTest.java deleted file mode 100644 index ad7bf61..0000000 --- a/cayenne-server/src/test/java/org/apache/cayenne/access/jdbc/SQLTemplateResourceManagerTest.java +++ /dev/null @@ -1,65 +0,0 @@ -/***************************************************************** - * 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 org.apache.velocity.Template; -import org.apache.velocity.runtime.RuntimeConstants; -import org.apache.velocity.runtime.resource.Resource; -import org.apache.velocity.runtime.resource.ResourceManager; -import org.junit.Test; - -import static org.junit.Assert.assertNotSame; -import static org.junit.Assert.assertSame; -import static org.junit.Assert.assertTrue; - -public class SQLTemplateResourceManagerTest { - - @Test - public void testFetResource() throws Exception { - SQLTemplateResourceManager rm = new SQLTemplateResourceManager(); - rm.initialize(new MockupRuntimeServices()); - - Resource resource = rm.getResource( - "abc", - ResourceManager.RESOURCE_TEMPLATE, - RuntimeConstants.ENCODING_DEFAULT); - - assertTrue(resource instanceof Template); - - // must be cached... - assertSame(resource, rm.getResource( - "abc", - ResourceManager.RESOURCE_TEMPLATE, - RuntimeConstants.ENCODING_DEFAULT)); - - // new resource must be different - assertNotSame(resource, rm.getResource( - "xyz", - ResourceManager.RESOURCE_TEMPLATE, - RuntimeConstants.ENCODING_DEFAULT)); - - // after clearing cache, resource must be refreshed - rm.clearCache(); - assertNotSame(resource, rm.getResource( - "abc", - ResourceManager.RESOURCE_TEMPLATE, - RuntimeConstants.ENCODING_DEFAULT)); - } -} http://git-wip-us.apache.org/repos/asf/cayenne/blob/c8709542/cayenne-server/src/test/java/org/apache/cayenne/velocity/BindDirectiveIT.java ---------------------------------------------------------------------- diff --git a/cayenne-server/src/test/java/org/apache/cayenne/velocity/BindDirectiveIT.java b/cayenne-server/src/test/java/org/apache/cayenne/velocity/BindDirectiveIT.java new file mode 100644 index 0000000..78e55e8 --- /dev/null +++ b/cayenne-server/src/test/java/org/apache/cayenne/velocity/BindDirectiveIT.java @@ -0,0 +1,268 @@ +/***************************************************************** + * 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.velocity; + +import org.apache.cayenne.DataRow; +import org.apache.cayenne.ObjectContext; +import org.apache.cayenne.access.DataNode; +import org.apache.cayenne.access.MockOperationObserver; +import org.apache.cayenne.access.jdbc.SQLTemplateAction; +import org.apache.cayenne.access.jdbc.reader.RowReaderFactory; +import org.apache.cayenne.dba.JdbcAdapter; +import org.apache.cayenne.dba.oracle.OracleAdapter; +import org.apache.cayenne.di.Inject; +import org.apache.cayenne.log.JdbcEventLogger; +import org.apache.cayenne.query.CapsStrategy; +import org.apache.cayenne.query.SQLTemplate; +import org.apache.cayenne.query.SelectQuery; +import org.apache.cayenne.test.jdbc.DBHelper; +import org.apache.cayenne.testdo.testmap.Artist; +import org.apache.cayenne.unit.di.server.ServerCase; +import org.apache.cayenne.unit.di.server.ServerCaseDataSourceFactory; +import org.apache.cayenne.unit.di.server.UseServerRuntime; + +import java.sql.Connection; +import java.sql.Timestamp; +import java.util.Calendar; +import java.util.Collections; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static org.mockito.Mockito.mock; + +/** + * Tests BindDirective for passed null parameters and for not passed parameters + */ +@UseServerRuntime(ServerCase.TESTMAP_PROJECT) +public class BindDirectiveIT extends ServerCase { + + @Inject + private ServerCaseDataSourceFactory dataSourceFactory; + + @Inject + private JdbcAdapter adapter; + + @Inject + private ObjectContext context; + + @Inject + private DBHelper dbHelper; + + @Inject + private JdbcEventLogger logger; + + @Override + protected void setUpAfterInjection() throws Exception { + dbHelper.deleteAll("PAINTING_INFO"); + dbHelper.deleteAll("PAINTING"); + dbHelper.deleteAll("ARTIST_EXHIBIT"); + dbHelper.deleteAll("ARTIST_GROUP"); + dbHelper.deleteAll("ARTIST"); + } + + public void testBindTimestamp() throws Exception { + Map<String, Object> parameters = new HashMap<String, Object>(); + parameters.put("id", new Integer(1)); + parameters.put("name", "ArtistWithDOB"); + Calendar cal = Calendar.getInstance(); + cal.clear(); + cal.set(2010, 2, 8); + parameters.put("dob", new Timestamp(cal.getTime().getTime())); + + // without JDBC usage + Map<String, ?> row = performInsertForParameters(parameters, false, 1); + assertEquals(parameters.get("name"), row.get("ARTIST_NAME")); + assertEquals(cal.getTime(), row.get("DATE_OF_BIRTH")); + assertNotNull(row.get("DATE_OF_BIRTH")); + assertEquals(Date.class, row.get("DATE_OF_BIRTH").getClass()); + } + + public void testBindSQLDate() throws Exception { + Map<String, Object> parameters = new HashMap<String, Object>(); + parameters.put("id", new Integer(1)); + parameters.put("name", "ArtistWithDOB"); + Calendar cal = Calendar.getInstance(); + cal.clear(); + cal.set(2010, 2, 8); + parameters.put("dob", new java.sql.Date(cal.getTime().getTime())); + + // without JDBC usage + Map<String, ?> row = performInsertForParameters(parameters, false, 1); + assertEquals(parameters.get("name"), row.get("ARTIST_NAME")); + assertEquals(parameters.get("dob"), row.get("DATE_OF_BIRTH")); + assertNotNull(row.get("DATE_OF_BIRTH")); + assertEquals(Date.class, row.get("DATE_OF_BIRTH").getClass()); + } + + public void testBindUtilDate() throws Exception { + Map<String, Object> parameters = new HashMap<String, Object>(); + parameters.put("id", new Integer(1)); + parameters.put("name", "ArtistWithDOB"); + Calendar cal = Calendar.getInstance(); + cal.clear(); + cal.set(2010, 2, 8); + parameters.put("dob", cal.getTime()); + + // without JDBC usage + Map<String, ?> row = performInsertForParameters(parameters, false, 1); + assertEquals(parameters.get("name"), row.get("ARTIST_NAME")); + assertEquals(parameters.get("dob"), row.get("DATE_OF_BIRTH")); + assertNotNull(row.get("DATE_OF_BIRTH")); + assertEquals(Date.class, row.get("DATE_OF_BIRTH").getClass()); + } + + public void testBindingForCollection() throws Exception { + + // insert 3 artists + for (int i = 1; i < 4; i++) { + Map<String, Object> parameters = new HashMap<String, Object>(); + parameters.put("id", new Long(i)); + parameters.put("name", "Artist" + i); + performInsertForParameters(parameters, true, i); + } + + // now select only with names: Artist1 and Artist3 + Set<String> artistNames = new HashSet<String>(); + artistNames.add("Artist1"); + artistNames.add("Artist3"); + String sql = "SELECT * FROM ARTIST WHERE ARTIST_NAME in (#bind($ARTISTNAMES))"; + SQLTemplate query = new SQLTemplate(Artist.class, sql); + + // customize for DB's that require trimming CHAR spaces + query.setTemplate(OracleAdapter.class.getName(), "SELECT * FROM ARTIST WHERE RTRIM(ARTIST_NAME) in (#bind($ARTISTNAMES))"); + + query.setColumnNamesCapitalization(CapsStrategy.UPPER); + query.setParams(Collections.singletonMap("ARTISTNAMES", artistNames)); + List<DataRow> result = context.performQuery(query); + assertEquals(2, result.size()); + } + + public void testBindForPassedNullParam() throws Exception { + Map<String, Object> parameters = new HashMap<String, Object>(); + parameters.put("id", new Long(1)); + parameters.put("name", "ArtistWithoutDOB"); + // passing null in parameter + parameters.put("dob", null); + + // without JDBC usage + Map<String, ?> row = performInsertForParameters(parameters, false, 1); + assertEquals(parameters.get("id"), row.get("ARTIST_ID")); + assertEquals(parameters.get("name"), row.get("ARTIST_NAME")); + assertEquals(parameters.get("dob"), row.get("DATE_OF_BIRTH")); + assertNull(row.get("DATE_OF_BIRTH")); + } + + public void testBindWithJDBCForPassedNullParam() throws Exception { + Map<String, Object> parameters = new HashMap<String, Object>(); + parameters.put("id", new Long(1)); + parameters.put("name", "ArtistWithoutDOB"); + // passing null in parameter + parameters.put("dob", null); + + // use JDBC + Map<String, ?> row = performInsertForParameters(parameters, true, 1); + assertEquals(parameters.get("id"), row.get("ARTIST_ID")); + assertEquals(parameters.get("name"), row.get("ARTIST_NAME")); + assertEquals(parameters.get("dob"), row.get("DATE_OF_BIRTH")); + assertNull(row.get("DATE_OF_BIRTH")); + } + + public void testBindForNotPassedParam() throws Exception { + Map<String, Object> parameters = new HashMap<String, Object>(); + parameters.put("id", new Long(1)); + parameters.put("name", "ArtistWithoutDOB"); + // not passing parameter parameters.put("dob", not passed!); + + // without JDBC usage + Map<String, ?> row = performInsertForParameters(parameters, false, 1); + assertEquals(parameters.get("id"), row.get("ARTIST_ID")); + assertEquals(parameters.get("name"), row.get("ARTIST_NAME")); + // parameter should be passed as null + assertNull(row.get("DATE_OF_BIRTH")); + } + + public void testBindWithJDBCForNotPassedParam() throws Exception { + Map<String, Object> parameters = new HashMap<String, Object>(); + parameters.put("id", new Long(1)); + parameters.put("name", "ArtistWithoutDOB"); + // not passing parameter parameters.put("dob", not passed!); + + // use JDBC + Map<String, ?> row = performInsertForParameters(parameters, true, 1); + assertEquals(parameters.get("id"), row.get("ARTIST_ID")); + assertEquals(parameters.get("name"), row.get("ARTIST_NAME")); + // parameter should be passed as null + assertNull(row.get("DATE_OF_BIRTH")); + } + + /** + * Inserts row for given parameters + * + * @return inserted row + */ + private Map<String, ?> performInsertForParameters( + Map<String, Object> parameters, + boolean useJDBCType, + int expectedRowCount) throws Exception { + + String templateString; + if (useJDBCType) { + templateString = "INSERT INTO ARTIST (ARTIST_ID, ARTIST_NAME, DATE_OF_BIRTH) " + + "VALUES (#bind($id), #bind($name), #bind($dob 'DATE'))"; + } + else { + templateString = "INSERT INTO ARTIST (ARTIST_ID, ARTIST_NAME, DATE_OF_BIRTH) " + + "VALUES (#bind($id), #bind($name), #bind($dob))"; + } + SQLTemplate template = new SQLTemplate(Object.class, templateString); + + template.setParams(parameters); + + DataNode node = new DataNode(); + node.setEntityResolver(context.getEntityResolver()); + node.setRowReaderFactory(mock(RowReaderFactory.class)); + node.setAdapter(adapter); + SQLTemplateAction action = new SQLTemplateAction(template, node); + + Connection c = dataSourceFactory.getSharedDataSource().getConnection(); + try { + MockOperationObserver observer = new MockOperationObserver(); + action.performAction(c, observer); + + int[] batches = observer.countsForQuery(template); + assertNotNull(batches); + assertEquals(1, batches.length); + assertEquals(1, batches[0]); + } + finally { + c.close(); + } + + SelectQuery query = new SelectQuery(Artist.class); + query.setFetchingDataRows(true); + + List<DataRow> data = context.performQuery(query); + assertEquals(expectedRowCount, data.size()); + return data.get(0); + } +}
