This is an automated email from the ASF dual-hosted git repository. asf-gitbox-commits pushed a commit to branch past-M2 in repository https://gitbox.apache.org/repos/asf/cayenne.git
commit 0596f7e516ca69f8addfbf003c057d42416f8dfb Author: Andrus Adamchik <[email protected]> AuthorDate: Sun May 31 10:56:09 2026 -0400 Unifying ParameterBinding hierarchy into a single class and reducing its mutability --- .../CryptoBatchTranslatorFactoryDecorator.java | 8 +-- .../crypto/transformer/BindingsTransformer.java | 4 +- .../transformer/DefaultBindingsTransformer.java | 9 ++-- .../transformer/DefaultTransformerFactory.java | 6 +-- .../crypto/transformer/TransformerFactory.java | 4 +- .../merge/token/DefaultValueForNullProvider.java | 7 ++- .../cayenne/dbsync/merge/token/ValueForNullIT.java | 2 +- .../org/apache/cayenne/velocity/BindDirective.java | 35 ++++++++++-- .../cayenne/velocity/BindEqualDirective.java | 7 +-- .../cayenne/velocity/BindNotEqualDirective.java | 7 +-- .../cayenne/velocity/BindObjectEqualDirective.java | 9 ++-- .../velocity/BindObjectNotEqualDirective.java | 7 +-- .../velocity/VelocitySQLTemplateTranslator.java | 13 +++-- .../VelocitySQLTemplateTranslatorTest.java | 43 +++++++++------ .../VelocitySQLTemplateTranslator_ChainTest.java | 37 ++++++++----- .../VelocitySQLTemplateTranslator_SelectTest.java | 19 +++++-- .../apache/cayenne/access/jdbc/BatchAction.java | 10 ++-- .../cayenne/access/jdbc/SQLTemplateAction.java | 19 +------ .../apache/cayenne/access/jdbc/SelectAction.java | 6 +-- .../access/sqlbuilder/SQLGenerationContext.java | 4 +- .../access/sqlbuilder/sqltree/ValueNode.java | 29 ++++++---- .../access/translator/DbAttributeBinding.java | 49 ----------------- .../access/translator/ParameterBinding.java | 63 +++++++++++----------- .../translator/ProcedureParameterBinding.java | 49 ----------------- .../translator/batch/BaseBatchTranslator.java | 8 +-- .../access/translator/batch/BatchTranslator.java | 6 +-- .../translator/batch/BatchTranslatorContext.java | 6 +-- .../translator/batch/DeleteBatchTranslator.java | 4 +- .../translator/batch/InsertBatchTranslator.java | 4 +- .../batch/SoftDeleteBatchTranslator.java | 4 +- .../translator/batch/UpdateBatchTranslator.java | 4 +- .../batch/legacy/DefaultBatchTranslator.java | 12 ++--- .../translator/procedure/ProcedureTranslator.java | 12 ++--- .../access/translator/select/TranslatedSelect.java | 4 +- .../translator/select/TranslatorContext.java | 8 +-- .../access/translator/sqltemplate/Context.java | 27 +++++++++- .../sqltemplate/DefaultSQLTemplateTranslator.java | 9 ++-- .../sqltemplate/DefaultTemplateContextFactory.java | 5 +- .../sqltemplate/SQLTemplateTranslator.java | 13 ++--- .../sqltemplate/TemplateContextFactory.java | 14 +++-- .../translator/sqltemplate/directive/Bind.java | 6 +-- .../sqltemplate/directive/BindEqual.java | 6 +-- .../sqltemplate/directive/BindNotEqual.java | 6 +-- .../sqltemplate/directive/BindObjectEqual.java | 8 +-- .../sqltemplate/directive/BindObjectNotEqual.java | 6 +-- .../java/org/apache/cayenne/dba/AutoAdapter.java | 8 +++ .../java/org/apache/cayenne/dba/DbAdapter.java | 13 +++++ .../org/apache/cayenne/dba/db2/DB2Adapter.java | 10 ++-- .../org/apache/cayenne/dba/derby/DerbyAdapter.java | 11 ++-- .../org/apache/cayenne/dba/mysql/MySQLAdapter.java | 11 ++-- .../cayenne/dba/oracle/Oracle8LOBBatchAction.java | 30 +++++------ .../dba/oracle/Oracle8LOBBatchTranslator.java | 23 ++++---- .../oracle/Oracle8LOBInsertBatchTranslator.java | 18 ------- .../oracle/Oracle8LOBUpdateBatchTranslator.java | 27 ---------- .../cayenne/dba/postgres/PostgresAdapter.java | 11 ++-- .../cayenne/dba/sqlserver/SQLServerAdapter.java | 15 +++--- .../apache/cayenne/dba/sybase/SybaseAdapter.java | 24 ++++----- .../cayenne/log/CompactSlf4jJdbcEventLogger.java | 9 ++-- .../apache/cayenne/log/Slf4jJdbcEventLogger.java | 11 ++-- .../batch/legacy/DefaultBatchTranslatorIT.java | 26 ++++----- .../DefaultSQLTemplateTranslatorTest.java | 41 ++++++++------ .../sqltemplate/parser/SQLTemplateParserTest.java | 51 ++++++++++-------- .../log/CompactSlf4jJdbcEventLoggerTest.java | 15 +++--- 63 files changed, 467 insertions(+), 505 deletions(-) diff --git a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/batch/CryptoBatchTranslatorFactoryDecorator.java b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/batch/CryptoBatchTranslatorFactoryDecorator.java index 35f9ca66a..ddc021d6c 100644 --- a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/batch/CryptoBatchTranslatorFactoryDecorator.java +++ b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/batch/CryptoBatchTranslatorFactoryDecorator.java @@ -18,7 +18,7 @@ ****************************************************************/ package org.apache.cayenne.crypto.batch; -import org.apache.cayenne.access.translator.DbAttributeBinding; +import org.apache.cayenne.access.translator.ParameterBinding; import org.apache.cayenne.access.translator.batch.BatchTranslator; import org.apache.cayenne.access.translator.batch.BatchTranslatorFactory; import org.apache.cayenne.crypto.transformer.BindingsTransformer; @@ -65,16 +65,16 @@ public class CryptoBatchTranslatorFactoryDecorator implements BatchTranslatorFac } @Override - public DbAttributeBinding[] getBindings() { + public ParameterBinding[] getBindings() { return delegateTranslator.getBindings(); } @Override - public DbAttributeBinding[] updateBindings(BatchQueryRow row) { + public ParameterBinding[] updateBindings(BatchQueryRow row) { ensureEncryptorCompiled(); - DbAttributeBinding[] bindings = delegateTranslator.updateBindings(row); + ParameterBinding[] bindings = delegateTranslator.updateBindings(row); if (encryptor != null) { encryptor.transform(bindings); diff --git a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/BindingsTransformer.java b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/BindingsTransformer.java index 35a9e96aa..251d3766e 100644 --- a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/BindingsTransformer.java +++ b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/BindingsTransformer.java @@ -18,12 +18,12 @@ ****************************************************************/ package org.apache.cayenne.crypto.transformer; -import org.apache.cayenne.access.translator.DbAttributeBinding; +import org.apache.cayenne.access.translator.ParameterBinding; /** * @since 4.0 */ public interface BindingsTransformer { - void transform(DbAttributeBinding[] bindings); + void transform(ParameterBinding[] bindings); } diff --git a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/DefaultBindingsTransformer.java b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/DefaultBindingsTransformer.java index 96f0920b8..02d43bda3 100644 --- a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/DefaultBindingsTransformer.java +++ b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/DefaultBindingsTransformer.java @@ -18,7 +18,7 @@ ****************************************************************/ package org.apache.cayenne.crypto.transformer; -import org.apache.cayenne.access.translator.DbAttributeBinding; +import org.apache.cayenne.access.translator.ParameterBinding; import org.apache.cayenne.access.types.ExtendedType; import org.apache.cayenne.access.types.ExtendedTypeMap; import org.apache.cayenne.crypto.transformer.bytes.BytesEncryptor; @@ -45,20 +45,19 @@ public class DefaultBindingsTransformer implements BindingsTransformer { } @Override - public void transform(DbAttributeBinding[] bindings) { + public void transform(ParameterBinding[] bindings) { int len = positions.length; for (int i = 0; i < len; i++) { - DbAttributeBinding b = bindings[positions[i]]; + ParameterBinding b = bindings[positions[i]]; Object transformed = transformers[i].encrypt(encryptor, b.getValue()); - b.setValue(transformed); ExtendedType extendedType = transformed != null ? extendedTypeMap.getRegisteredType(transformed.getClass()) : extendedTypeMap.getDefaultType(); - b.setExtendedType(extendedType); + b.include(b.getStatementPosition(), transformed, extendedType); } } diff --git a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/DefaultTransformerFactory.java b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/DefaultTransformerFactory.java index 439bd4dc7..958cc013c 100644 --- a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/DefaultTransformerFactory.java +++ b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/DefaultTransformerFactory.java @@ -19,7 +19,7 @@ package org.apache.cayenne.crypto.transformer; import org.apache.cayenne.access.jdbc.ColumnDescriptor; -import org.apache.cayenne.access.translator.DbAttributeBinding; +import org.apache.cayenne.access.translator.ParameterBinding; import org.apache.cayenne.access.types.ExtendedTypeMap; import org.apache.cayenne.crypto.map.ColumnMapper; import org.apache.cayenne.crypto.transformer.bytes.BytesTransformerFactory; @@ -91,7 +91,7 @@ public class DefaultTransformerFactory implements TransformerFactory { } @Override - public BindingsTransformer encryptor(DbAttributeBinding[] bindings, ExtendedTypeMap extendedTypeMap) { + public BindingsTransformer encryptor(ParameterBinding[] bindings, ExtendedTypeMap extendedTypeMap) { int len = bindings.length; List<Integer> cryptoColumns = null; @@ -116,7 +116,7 @@ public class DefaultTransformerFactory implements TransformerFactory { for (int i = 0; i < dlen; i++) { int pos = cryptoColumns.get(i); - DbAttributeBinding b = bindings[pos]; + ParameterBinding b = bindings[pos]; positions[i] = pos; transformers[i] = transformerFactory.encryptor(b.getAttribute()); } diff --git a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/TransformerFactory.java b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/TransformerFactory.java index 96c9f9ec0..a2eade242 100644 --- a/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/TransformerFactory.java +++ b/cayenne-crypto/src/main/java/org/apache/cayenne/crypto/transformer/TransformerFactory.java @@ -19,7 +19,7 @@ package org.apache.cayenne.crypto.transformer; import org.apache.cayenne.access.jdbc.ColumnDescriptor; -import org.apache.cayenne.access.translator.DbAttributeBinding; +import org.apache.cayenne.access.translator.ParameterBinding; import org.apache.cayenne.access.types.ExtendedTypeMap; /** @@ -30,7 +30,7 @@ import org.apache.cayenne.access.types.ExtendedTypeMap; */ public interface TransformerFactory { - BindingsTransformer encryptor(DbAttributeBinding[] bindings, ExtendedTypeMap extendedTypeMap); + BindingsTransformer encryptor(ParameterBinding[] bindings, ExtendedTypeMap extendedTypeMap); MapTransformer decryptor(ColumnDescriptor[] columns, Object sampleRow); } diff --git a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/DefaultValueForNullProvider.java b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/DefaultValueForNullProvider.java index 63753c0c6..28d8564cd 100644 --- a/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/DefaultValueForNullProvider.java +++ b/cayenne-dbsync/src/main/java/org/apache/cayenne/dbsync/merge/token/DefaultValueForNullProvider.java @@ -33,8 +33,11 @@ public class DefaultValueForNullProvider implements ValueForNullProvider { private Map<String, ParameterBinding> values = new HashMap<>(); public void set(DbEntity entity, DbAttribute column, Object value, int type) { - values.put(createKey(entity, column), new ParameterBinding(value, type, column - .getAttributePrecision())); + // the binding is only ever read for its value (inlined into the UPDATE below), never bound to a + // statement, so the position is irrelevant here + ParameterBinding binding = new ParameterBinding(type, column.getAttributePrecision()) + .include(1, value, null); + values.put(createKey(entity, column), binding); } protected ParameterBinding get(DbEntity entity, DbAttribute column) { diff --git a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/token/ValueForNullIT.java b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/token/ValueForNullIT.java index 249b4f57e..f85815c94 100644 --- a/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/token/ValueForNullIT.java +++ b/cayenne-dbsync/src/test/java/org/apache/cayenne/dbsync/merge/token/ValueForNullIT.java @@ -105,7 +105,7 @@ public class ValueForNullIT extends MergeCase { int type = column.getType(); switch (type) { case Types.VARCHAR: - return new ParameterBinding(DEFAULT_VALUE_STRING, type, -1); + return new ParameterBinding(type, -1).include(1, DEFAULT_VALUE_STRING, null); default: throw new AssertionError("should not get here"); } diff --git a/cayenne-velocity/src/main/java/org/apache/cayenne/velocity/BindDirective.java b/cayenne-velocity/src/main/java/org/apache/cayenne/velocity/BindDirective.java index dbf8a4370..26f927328 100644 --- a/cayenne-velocity/src/main/java/org/apache/cayenne/velocity/BindDirective.java +++ b/cayenne-velocity/src/main/java/org/apache/cayenne/velocity/BindDirective.java @@ -25,6 +25,9 @@ import java.util.Collection; import java.util.Iterator; import org.apache.cayenne.access.translator.ParameterBinding; +import org.apache.cayenne.access.types.ExtendedType; +import org.apache.cayenne.access.types.ExtendedTypeMap; +import org.apache.cayenne.dba.DbAdapter; import org.apache.cayenne.dba.TypesMapping; import org.apache.cayenne.util.ConversionUtil; import org.apache.velocity.context.InternalContextAdapter; @@ -130,12 +133,26 @@ public class BindDirective extends Directive { + ") at line " + node.getLine() + ", column " + node.getColumn()); } - render(context, writer, new ParameterBinding(value, jdbcType, scale)); + render(context, writer, new ParameterBinding(preferredBindingType(context, jdbcType), scale), value); } - protected void render(InternalContextAdapter context, Writer writer, ParameterBinding binding) throws IOException { + /** + * Resolves the JDBC type the target adapter prefers for binding. + * + * @since 5.0 + */ + protected int preferredBindingType(InternalContextAdapter context, int jdbcType) { + return adapter(context).preferredBindingType(jdbcType); + } + + private static DbAdapter adapter(InternalContextAdapter context) { + return (DbAdapter) context.getInternalUserContext().get(VelocitySQLTemplateTranslator.ADAPTER_KEY); + } + + protected void render(InternalContextAdapter context, Writer writer, ParameterBinding binding, Object value) + throws IOException { - bind(context, binding); + bind(context, binding, value); writer.write('?'); } @@ -146,14 +163,24 @@ public class BindDirective extends Directive { /** * Adds value to the list of bindings in the context. */ - protected void bind(InternalContextAdapter context, ParameterBinding binding) { + protected void bind(InternalContextAdapter context, ParameterBinding binding, Object value) { @SuppressWarnings("unchecked") Collection<ParameterBinding> bindings = (Collection<ParameterBinding>) context.getInternalUserContext().get(VelocitySQLTemplateTranslator.BINDINGS_LIST_KEY); if (bindings != null) { + // a binding's statement position is its 1-based ordinal among the bound parameters; the + // ExtendedType is resolved from the value via the adapter + binding.include(bindings.size() + 1, value, extendedType(context, value)); bindings.add(binding); } } + + private ExtendedType<?> extendedType(InternalContextAdapter context, Object value) { + ExtendedTypeMap extendedTypes = adapter(context).getExtendedTypes(); + return value != null + ? extendedTypes.getRegisteredType(value.getClass()) + : extendedTypes.getDefaultType(); + } } diff --git a/cayenne-velocity/src/main/java/org/apache/cayenne/velocity/BindEqualDirective.java b/cayenne-velocity/src/main/java/org/apache/cayenne/velocity/BindEqualDirective.java index 2c3518691..af8916d47 100644 --- a/cayenne-velocity/src/main/java/org/apache/cayenne/velocity/BindEqualDirective.java +++ b/cayenne-velocity/src/main/java/org/apache/cayenne/velocity/BindEqualDirective.java @@ -43,11 +43,12 @@ public class BindEqualDirective extends BindDirective { protected void render( InternalContextAdapter context, Writer writer, - ParameterBinding binding) + ParameterBinding binding, + Object value) throws IOException { - if (binding.getValue() != null) { - bind(context, binding); + if (value != null) { + bind(context, binding, value); writer.write("= ?"); } else { diff --git a/cayenne-velocity/src/main/java/org/apache/cayenne/velocity/BindNotEqualDirective.java b/cayenne-velocity/src/main/java/org/apache/cayenne/velocity/BindNotEqualDirective.java index c95af7541..f25bf7992 100644 --- a/cayenne-velocity/src/main/java/org/apache/cayenne/velocity/BindNotEqualDirective.java +++ b/cayenne-velocity/src/main/java/org/apache/cayenne/velocity/BindNotEqualDirective.java @@ -43,10 +43,11 @@ public class BindNotEqualDirective extends BindDirective { protected void render( InternalContextAdapter context, Writer writer, - ParameterBinding binding) throws IOException { + ParameterBinding binding, + Object value) throws IOException { - if (binding.getValue() != null) { - bind(context, binding); + if (value != null) { + bind(context, binding, value); writer.write("<> ?"); } else { diff --git a/cayenne-velocity/src/main/java/org/apache/cayenne/velocity/BindObjectEqualDirective.java b/cayenne-velocity/src/main/java/org/apache/cayenne/velocity/BindObjectEqualDirective.java index 179f340a7..9662cb710 100644 --- a/cayenne-velocity/src/main/java/org/apache/cayenne/velocity/BindObjectEqualDirective.java +++ b/cayenne-velocity/src/main/java/org/apache/cayenne/velocity/BindObjectEqualDirective.java @@ -94,7 +94,7 @@ public class BindObjectEqualDirective extends BindDirective { renderColumn(context, writer, sqlColumnsArray[i], i); writer.write(' '); - render(context, writer, new ParameterBinding(value, jdbcType, -1)); + render(context, writer, new ParameterBinding(preferredBindingType(context, jdbcType), -1), value); } return true; @@ -151,10 +151,11 @@ public class BindObjectEqualDirective extends BindDirective { protected void render( InternalContextAdapter context, Writer writer, - ParameterBinding binding) throws IOException { + ParameterBinding binding, + Object value) throws IOException { - if (binding.getValue() != null) { - bind(context, binding); + if (value != null) { + bind(context, binding, value); writer.write("= ?"); } else { diff --git a/cayenne-velocity/src/main/java/org/apache/cayenne/velocity/BindObjectNotEqualDirective.java b/cayenne-velocity/src/main/java/org/apache/cayenne/velocity/BindObjectNotEqualDirective.java index faac03252..70afc50ff 100644 --- a/cayenne-velocity/src/main/java/org/apache/cayenne/velocity/BindObjectNotEqualDirective.java +++ b/cayenne-velocity/src/main/java/org/apache/cayenne/velocity/BindObjectNotEqualDirective.java @@ -57,10 +57,11 @@ public class BindObjectNotEqualDirective extends BindObjectEqualDirective { protected void render( InternalContextAdapter context, Writer writer, - ParameterBinding binding) throws IOException { + ParameterBinding binding, + Object value) throws IOException { - if (binding.getValue() != null) { - bind(context, binding); + if (value != null) { + bind(context, binding, value); writer.write("<> ?"); } else { diff --git a/cayenne-velocity/src/main/java/org/apache/cayenne/velocity/VelocitySQLTemplateTranslator.java b/cayenne-velocity/src/main/java/org/apache/cayenne/velocity/VelocitySQLTemplateTranslator.java index 704a0d6a0..d88bb39f2 100644 --- a/cayenne-velocity/src/main/java/org/apache/cayenne/velocity/VelocitySQLTemplateTranslator.java +++ b/cayenne-velocity/src/main/java/org/apache/cayenne/velocity/VelocitySQLTemplateTranslator.java @@ -24,6 +24,7 @@ import org.apache.cayenne.access.jdbc.ColumnDescriptor; import org.apache.cayenne.access.translator.sqltemplate.TranslatedSQL; import org.apache.cayenne.access.translator.sqltemplate.SQLTemplateTranslator; import org.apache.cayenne.access.translator.ParameterBinding; +import org.apache.cayenne.dba.DbAdapter; import org.apache.cayenne.exp.ExpressionException; import org.apache.cayenne.access.translator.sqltemplate.SQLTemplateRenderingUtils; import org.apache.velocity.Template; @@ -92,6 +93,7 @@ public class VelocitySQLTemplateTranslator implements SQLTemplateTranslator { static final String BINDINGS_LIST_KEY = "bindings"; static final String RESULT_COLUMNS_LIST_KEY = "resultColumns"; static final String HELPER_KEY = "helper"; + static final String ADAPTER_KEY = "adapter"; protected RuntimeInstance velocityRuntime; protected SQLTemplateRenderingUtils renderingUtils; @@ -123,17 +125,17 @@ public class VelocitySQLTemplateTranslator implements SQLTemplateTranslator { * as a "helper" variable and SQLStatement object as "statement" variable. */ @Override - public TranslatedSQL translate(String template, Map<String, ?> parameters) { + public TranslatedSQL translate(String template, Map<String, ?> parameters, DbAdapter adapter) { // have to make a copy of parameter map since we are gonna modify it.. Map<String, Object> internalParameters = (parameters != null && !parameters.isEmpty()) ? new HashMap<>( parameters) : new HashMap<String, Object>(5); SimpleNode parsedTemplate = parse(template); - return translate(template, parsedTemplate, internalParameters); + return translate(template, parsedTemplate, internalParameters, adapter); } @Override - public TranslatedSQL translate(String template, List<Object> positionalParameters) { + public TranslatedSQL translate(String template, List<Object> positionalParameters, DbAdapter adapter) { SimpleNode parsedTemplate = parse(template); @@ -143,15 +145,16 @@ public class VelocitySQLTemplateTranslator implements SQLTemplateTranslator { parsedTemplate.jjtAccept(visitor, null); visitor.onFinish(); - return translate(template, parsedTemplate, internalParameters); + return translate(template, parsedTemplate, internalParameters, adapter); } - TranslatedSQL translate(String template, SimpleNode parsedTemplate, Map<String, Object> parameters) { + TranslatedSQL translate(String template, SimpleNode parsedTemplate, Map<String, Object> parameters, DbAdapter adapter) { List<ParameterBinding> bindings = new ArrayList<>(); List<ColumnDescriptor> results = new ArrayList<>(); parameters.put(BINDINGS_LIST_KEY, bindings); parameters.put(RESULT_COLUMNS_LIST_KEY, results); parameters.put(HELPER_KEY, renderingUtils); + parameters.put(ADAPTER_KEY, adapter); String sql; try { diff --git a/cayenne-velocity/src/test/java/org/apache/cayenne/velocity/VelocitySQLTemplateTranslatorTest.java b/cayenne-velocity/src/test/java/org/apache/cayenne/velocity/VelocitySQLTemplateTranslatorTest.java index f3daaf396..ae96a6e18 100644 --- a/cayenne-velocity/src/test/java/org/apache/cayenne/velocity/VelocitySQLTemplateTranslatorTest.java +++ b/cayenne-velocity/src/test/java/org/apache/cayenne/velocity/VelocitySQLTemplateTranslatorTest.java @@ -21,6 +21,9 @@ package org.apache.cayenne.velocity; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import java.sql.Types; import java.util.Arrays; @@ -33,23 +36,29 @@ import org.apache.cayenne.ObjectId; import org.apache.cayenne.Persistent; import org.apache.cayenne.access.translator.sqltemplate.TranslatedSQL; import org.apache.cayenne.access.translator.ParameterBinding; +import org.apache.cayenne.access.types.ExtendedTypeMap; +import org.apache.cayenne.dba.DbAdapter; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class VelocitySQLTemplateTranslatorTest { private VelocitySQLTemplateTranslator processor; + private DbAdapter adapter; @BeforeEach public void before() { processor = new VelocitySQLTemplateTranslator(); + adapter = mock(DbAdapter.class); + when(adapter.preferredBindingType(anyInt())).thenAnswer(i -> i.getArgument(0)); + when(adapter.getExtendedTypes()).thenReturn(new ExtendedTypeMap()); } @Test public void processTemplateUnchanged1() throws Exception { String sqlTemplate = "SELECT * FROM ME"; - TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.<String, Object> emptyMap()); + TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.<String, Object> emptyMap(), adapter); assertEquals(sqlTemplate, compiled.sql()); assertEquals(0, compiled.bindings().length); @@ -59,7 +68,7 @@ public class VelocitySQLTemplateTranslatorTest { public void processTemplateUnchanged2() throws Exception { String sqlTemplate = "SELECT a.b as XYZ FROM $SYSTEM_TABLE"; - TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.<String, Object> emptyMap()); + TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.<String, Object> emptyMap(), adapter); assertEquals(sqlTemplate, compiled.sql()); assertEquals(0, compiled.bindings().length); @@ -70,7 +79,7 @@ public class VelocitySQLTemplateTranslatorTest { String sqlTemplate = "SELECT * FROM ME WHERE $a"; Map<String, Object> map = Collections.<String, Object> singletonMap("a", "VALUE_OF_A"); - TranslatedSQL compiled = processor.translate(sqlTemplate, map); + TranslatedSQL compiled = processor.translate(sqlTemplate, map, adapter); assertEquals("SELECT * FROM ME WHERE VALUE_OF_A", compiled.sql()); @@ -83,7 +92,7 @@ public class VelocitySQLTemplateTranslatorTest { String sqlTemplate = "SELECT * FROM ME WHERE " + "COLUMN1 = #bind($a 'VARCHAR') AND COLUMN2 = #bind($b 'INTEGER')"; Map<String, Object> map = Collections.<String, Object> singletonMap("a", "VALUE_OF_A"); - TranslatedSQL compiled = processor.translate(sqlTemplate, map); + TranslatedSQL compiled = processor.translate(sqlTemplate, map, adapter); assertEquals("SELECT * FROM ME WHERE COLUMN1 = ? AND COLUMN2 = ?", compiled.sql()); assertEquals(2, compiled.bindings().length); @@ -96,7 +105,7 @@ public class VelocitySQLTemplateTranslatorTest { String sqlTemplate = "SELECT * FROM ME WHERE COLUMN1 = #bind($a)"; Map<String, Object> map = Collections.<String, Object> singletonMap("a", "VALUE_OF_A"); - TranslatedSQL compiled = processor.translate(sqlTemplate, map); + TranslatedSQL compiled = processor.translate(sqlTemplate, map, adapter); assertEquals(1, compiled.bindings().length); assertBindingType(Types.VARCHAR, compiled.bindings()[0]); @@ -107,7 +116,7 @@ public class VelocitySQLTemplateTranslatorTest { String sqlTemplate = "SELECT * FROM ME WHERE COLUMN1 = #bind($a)"; Map<String, Object> map = Collections.<String, Object> singletonMap("a", 4); - TranslatedSQL compiled = processor.translate(sqlTemplate, map); + TranslatedSQL compiled = processor.translate(sqlTemplate, map, adapter); assertEquals(1, compiled.bindings().length); assertBindingType(Types.INTEGER, compiled.bindings()[0]); @@ -117,14 +126,14 @@ public class VelocitySQLTemplateTranslatorTest { public void processTemplateBindEqual() throws Exception { String sqlTemplate = "SELECT * FROM ME WHERE COLUMN #bindEqual($a 'VARCHAR')"; - TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.<String, Object> emptyMap()); + TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.<String, Object> emptyMap(), adapter); assertEquals("SELECT * FROM ME WHERE COLUMN IS NULL", compiled.sql()); assertEquals(0, compiled.bindings().length); Map<String, Object> map = Collections.<String, Object> singletonMap("a", "VALUE_OF_A"); - compiled = processor.translate(sqlTemplate, map); + compiled = processor.translate(sqlTemplate, map, adapter); assertEquals("SELECT * FROM ME WHERE COLUMN = ?", compiled.sql()); assertEquals(1, compiled.bindings().length); @@ -135,14 +144,14 @@ public class VelocitySQLTemplateTranslatorTest { public void processTemplateBindNotEqual() throws Exception { String sqlTemplate = "SELECT * FROM ME WHERE COLUMN #bindNotEqual($a 'VARCHAR')"; - TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.<String, Object> emptyMap()); + TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.<String, Object> emptyMap(), adapter); assertEquals("SELECT * FROM ME WHERE COLUMN IS NOT NULL", compiled.sql()); assertEquals(0, compiled.bindings().length); Map<String, Object> map = Collections.<String, Object> singletonMap("a", "VALUE_OF_A"); - compiled = processor.translate(sqlTemplate, map); + compiled = processor.translate(sqlTemplate, map, adapter); assertEquals("SELECT * FROM ME WHERE COLUMN <> ?", compiled.sql()); assertEquals(1, compiled.bindings().length); @@ -158,7 +167,7 @@ public class VelocitySQLTemplateTranslatorTest { Map<String, Object> map = Collections.<String, Object> singletonMap("a", persistent); - TranslatedSQL compiled = processor.translate(sqlTemplate, map); + TranslatedSQL compiled = processor.translate(sqlTemplate, map, adapter); assertEquals("SELECT * FROM ME WHERE COLUMN1 = ?", compiled.sql()); assertEquals(1, compiled.bindings().length); @@ -180,7 +189,7 @@ public class VelocitySQLTemplateTranslatorTest { Map<String, Object> map = Collections.<String, Object> singletonMap("a", persistent); - TranslatedSQL compiled = processor.translate(sqlTemplate, map); + TranslatedSQL compiled = processor.translate(sqlTemplate, map, adapter); assertEquals("SELECT * FROM ME WHERE COLUMN1 <> ? AND COLUMN2 <> ?", compiled.sql()); assertEquals(2, compiled.bindings().length); @@ -194,13 +203,13 @@ public class VelocitySQLTemplateTranslatorTest { Map<String, Object> map = Collections.<String, Object> singletonMap("a", "VALUE_OF_A"); - TranslatedSQL compiled = processor.translate(sqlTemplate, map); + TranslatedSQL compiled = processor.translate(sqlTemplate, map, adapter); assertEquals("SELECT * FROM ME WHERE COLUMN1 > ?", compiled.sql()); assertEquals(1, compiled.bindings().length); assertBindingValue("VALUE_OF_A", compiled.bindings()[0]); - compiled = processor.translate(sqlTemplate, Collections.<String, Object> emptyMap()); + compiled = processor.translate(sqlTemplate, Collections.<String, Object> emptyMap(), adapter); assertEquals("SELECT * FROM ME ", compiled.sql()); assertEquals(0, compiled.bindings().length); @@ -211,12 +220,12 @@ public class VelocitySQLTemplateTranslatorTest { String sqlTemplate = "SELECT * FROM ME WHERE COLUMN IN (#bind($list 'VARCHAR'))"; Map<String, Object> map = Collections.<String, Object> singletonMap("list", Arrays.asList("a", "b", "c")); - TranslatedSQL compiled = new VelocitySQLTemplateTranslator().translate(sqlTemplate, map); + TranslatedSQL compiled = new VelocitySQLTemplateTranslator().translate(sqlTemplate, map, adapter); assertEquals("SELECT * FROM ME WHERE COLUMN IN (?,?,?)", compiled.sql()); assertEquals(3, compiled.bindings().length); - compiled = processor.translate(sqlTemplate, map); + compiled = processor.translate(sqlTemplate, map, adapter); assertBindingValue("a", compiled.bindings()[0]); assertBindingValue("b", compiled.bindings()[1]); assertBindingValue("c", compiled.bindings()[2]); @@ -225,7 +234,7 @@ public class VelocitySQLTemplateTranslatorTest { @Test public void unknownDirective() throws Exception { String sqlTemplate = "SELECT #from(1) FROM a"; - TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.emptyMap()); + TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.emptyMap(), adapter); assertEquals("SELECT #from(1) FROM a", compiled.sql()); } diff --git a/cayenne-velocity/src/test/java/org/apache/cayenne/velocity/VelocitySQLTemplateTranslator_ChainTest.java b/cayenne-velocity/src/test/java/org/apache/cayenne/velocity/VelocitySQLTemplateTranslator_ChainTest.java index a8b707e64..2a8284ff8 100644 --- a/cayenne-velocity/src/test/java/org/apache/cayenne/velocity/VelocitySQLTemplateTranslator_ChainTest.java +++ b/cayenne-velocity/src/test/java/org/apache/cayenne/velocity/VelocitySQLTemplateTranslator_ChainTest.java @@ -20,22 +20,31 @@ package org.apache.cayenne.velocity; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import java.util.Collections; import java.util.HashMap; import java.util.Map; import org.apache.cayenne.access.translator.sqltemplate.TranslatedSQL; +import org.apache.cayenne.access.types.ExtendedTypeMap; +import org.apache.cayenne.dba.DbAdapter; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class VelocitySQLTemplateTranslator_ChainTest { private VelocitySQLTemplateTranslator processor; + private DbAdapter adapter; @BeforeEach public void before() { processor = new VelocitySQLTemplateTranslator(); + adapter = mock(DbAdapter.class); + when(adapter.preferredBindingType(anyInt())).thenAnswer(i -> i.getArgument(0)); + when(adapter.getExtendedTypes()).thenReturn(new ExtendedTypeMap()); } @Test @@ -44,17 +53,17 @@ public class VelocitySQLTemplateTranslator_ChainTest { // is no chunks... TranslatedSQL compiled = processor.translate("#chain(' AND ') #end", - Collections.<String, Object> emptyMap()); + Collections.<String, Object> emptyMap(), adapter); assertEquals("", compiled.sql()); - compiled = processor.translate("#chain(' AND ') garbage #end", Collections.<String, Object> emptyMap()); + compiled = processor.translate("#chain(' AND ') garbage #end", Collections.<String, Object> emptyMap(), adapter); assertEquals("", compiled.sql()); - compiled = processor.translate("#chain(' AND ' 'PREFIX') #end", Collections.<String, Object> emptyMap()); + compiled = processor.translate("#chain(' AND ' 'PREFIX') #end", Collections.<String, Object> emptyMap(), adapter); assertEquals("", compiled.sql()); compiled = processor.translate("#chain(' AND ' 'PREFIX') garbage #end", - Collections.<String, Object> emptyMap()); + Collections.<String, Object> emptyMap(), adapter); assertEquals("", compiled.sql()); } @@ -68,7 +77,7 @@ public class VelocitySQLTemplateTranslator_ChainTest { map.put("b", "[B]"); map.put("c", "[C]"); - TranslatedSQL compiled = processor.translate(template, map); + TranslatedSQL compiled = processor.translate(template, map, adapter); assertEquals("[A] OR [B] OR [C]", compiled.sql()); } @@ -82,7 +91,7 @@ public class VelocitySQLTemplateTranslator_ChainTest { map.put("b", "[B]"); map.put("c", "[C]"); - TranslatedSQL compiled = processor.translate(template, map); + TranslatedSQL compiled = processor.translate(template, map, adapter); assertEquals("WHERE [A] OR [B] OR [C]", compiled.sql()); } @@ -95,7 +104,7 @@ public class VelocitySQLTemplateTranslator_ChainTest { map.put("a", "[A]"); map.put("c", "[C]"); - TranslatedSQL compiled = processor.translate(template, map); + TranslatedSQL compiled = processor.translate(template, map, adapter); assertEquals("WHERE [A] OR [C]", compiled.sql()); } @@ -108,7 +117,7 @@ public class VelocitySQLTemplateTranslator_ChainTest { map.put("b", "[B]"); map.put("c", "[C]"); - TranslatedSQL compiled = processor.translate(template, map); + TranslatedSQL compiled = processor.translate(template, map, adapter); assertEquals("WHERE [B] OR [C]", compiled.sql()); } @@ -121,7 +130,7 @@ public class VelocitySQLTemplateTranslator_ChainTest { map.put("a", "[A]"); map.put("b", "[B]"); - TranslatedSQL compiled = processor.translate(template, map); + TranslatedSQL compiled = processor.translate(template, map, adapter); assertEquals("WHERE [A] OR [B]", compiled.sql()); } @@ -134,7 +143,7 @@ public class VelocitySQLTemplateTranslator_ChainTest { map.put("a", "[A]"); map.put("c", "[C]"); - TranslatedSQL compiled = processor.translate(template, map); + TranslatedSQL compiled = processor.translate(template, map, adapter); assertEquals("WHERE [A] some other stuff OR [C]", compiled.sql()); } @@ -142,7 +151,7 @@ public class VelocitySQLTemplateTranslator_ChainTest { public void processTemplateChainUnconditionalChunks() throws Exception { String template = "#chain(' OR ' 'WHERE ')" + "#chunk()C1#end" + "#chunk()C2#end" + "#chunk()C3#end" + "#end"; - TranslatedSQL compiled = processor.translate(template, Collections.<String, Object> emptyMap()); + TranslatedSQL compiled = processor.translate(template, Collections.<String, Object> emptyMap(), adapter); assertEquals("WHERE C1 OR C2 OR C3", compiled.sql()); } @@ -151,7 +160,7 @@ public class VelocitySQLTemplateTranslator_ChainTest { String template = "#chain(' OR ' 'WHERE ')" + "#chunk($a)$a#end" + "#chunk($b)$b#end" + "#chunk($c)$c#end" + "#end"; - TranslatedSQL compiled = processor.translate(template, Collections.<String, Object> emptyMap()); + TranslatedSQL compiled = processor.translate(template, Collections.<String, Object> emptyMap(), adapter); assertEquals("", compiled.sql()); } @@ -164,7 +173,7 @@ public class VelocitySQLTemplateTranslator_ChainTest { map.put("a", false); map.put("b", 0); - TranslatedSQL compiled = processor.translate(template, map); + TranslatedSQL compiled = processor.translate(template, map, adapter); assertEquals("WHERE [A] OR [B]", compiled.sql()); } @@ -177,7 +186,7 @@ public class VelocitySQLTemplateTranslator_ChainTest { map.put("a", false); map.put("b", 0); - TranslatedSQL compiled = processor.translate(template, map); + TranslatedSQL compiled = processor.translate(template, map, adapter); assertEquals("WHERE false OR 0", compiled.sql()); } diff --git a/cayenne-velocity/src/test/java/org/apache/cayenne/velocity/VelocitySQLTemplateTranslator_SelectTest.java b/cayenne-velocity/src/test/java/org/apache/cayenne/velocity/VelocitySQLTemplateTranslator_SelectTest.java index cc7369be1..f49d13b92 100644 --- a/cayenne-velocity/src/test/java/org/apache/cayenne/velocity/VelocitySQLTemplateTranslator_SelectTest.java +++ b/cayenne-velocity/src/test/java/org/apache/cayenne/velocity/VelocitySQLTemplateTranslator_SelectTest.java @@ -21,28 +21,37 @@ package org.apache.cayenne.velocity; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import java.util.Collections; import org.apache.cayenne.access.jdbc.ColumnDescriptor; import org.apache.cayenne.access.translator.sqltemplate.TranslatedSQL; +import org.apache.cayenne.access.types.ExtendedTypeMap; +import org.apache.cayenne.dba.DbAdapter; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; public class VelocitySQLTemplateTranslator_SelectTest { private VelocitySQLTemplateTranslator processor; + private DbAdapter adapter; @BeforeEach public void before() { processor = new VelocitySQLTemplateTranslator(); + adapter = mock(DbAdapter.class); + when(adapter.preferredBindingType(anyInt())).thenAnswer(i -> i.getArgument(0)); + when(adapter.getExtendedTypes()).thenReturn(new ExtendedTypeMap()); } @Test public void processTemplateUnchanged() throws Exception { String sqlTemplate = "SELECT * FROM ME"; - TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.<String, Object> emptyMap()); + TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.<String, Object> emptyMap(), adapter); assertEquals(sqlTemplate, compiled.sql()); assertEquals(0, compiled.bindings().length); @@ -53,7 +62,7 @@ public class VelocitySQLTemplateTranslator_SelectTest { public void processSelectTemplate1() throws Exception { String sqlTemplate = "SELECT #result('A') FROM ME"; - TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.<String, Object> emptyMap()); + TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.<String, Object> emptyMap(), adapter); assertEquals("SELECT A FROM ME", compiled.sql()); assertEquals(0, compiled.bindings().length); @@ -66,7 +75,7 @@ public class VelocitySQLTemplateTranslator_SelectTest { public void processSelectTemplate2() throws Exception { String sqlTemplate = "SELECT #result('A' 'String') FROM ME"; - TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.<String, Object> emptyMap()); + TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.<String, Object> emptyMap(), adapter); assertEquals("SELECT A FROM ME", compiled.sql()); assertEquals(0, compiled.bindings().length); @@ -80,7 +89,7 @@ public class VelocitySQLTemplateTranslator_SelectTest { public void processSelectTemplate3() throws Exception { String sqlTemplate = "SELECT #result('A' 'String' 'B') FROM ME"; - TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.<String, Object> emptyMap()); + TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.<String, Object> emptyMap(), adapter); assertEquals("SELECT A AS B FROM ME", compiled.sql()); assertEquals(0, compiled.bindings().length); @@ -96,7 +105,7 @@ public class VelocitySQLTemplateTranslator_SelectTest { public void processSelectTemplate4() throws Exception { String sqlTemplate = "SELECT #result('A'), #result('B'), #result('C') FROM ME"; - TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.<String, Object> emptyMap()); + TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.<String, Object> emptyMap(), adapter); assertEquals("SELECT A, B, C FROM ME", compiled.sql()); assertEquals(0, compiled.bindings().length); diff --git a/cayenne/src/main/java/org/apache/cayenne/access/jdbc/BatchAction.java b/cayenne/src/main/java/org/apache/cayenne/access/jdbc/BatchAction.java index d1043b41f..42d918132 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/jdbc/BatchAction.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/jdbc/BatchAction.java @@ -25,7 +25,7 @@ import org.apache.cayenne.access.DataNode; import org.apache.cayenne.access.OperationObserver; import org.apache.cayenne.access.OptimisticLockException; import org.apache.cayenne.access.jdbc.reader.RowReader; -import org.apache.cayenne.access.translator.DbAttributeBinding; +import org.apache.cayenne.access.translator.ParameterBinding; import org.apache.cayenne.access.translator.batch.BatchTranslator; import org.apache.cayenne.dba.DbAdapter; import org.apache.cayenne.log.JdbcEventLogger; @@ -55,10 +55,10 @@ public class BatchAction extends BaseSQLAction { protected BatchQuery query; protected RowDescriptor keyRowDescriptor; - private static void bind(DbAdapter adapter, PreparedStatement statement, DbAttributeBinding[] bindings) + private static void bind(DbAdapter adapter, PreparedStatement statement, ParameterBinding[] bindings) throws Exception { - for (DbAttributeBinding b : bindings) { + for (ParameterBinding b : bindings) { if (!b.isExcluded()) { adapter.bindParameter(statement, b); } @@ -130,7 +130,7 @@ public class BatchAction extends BaseSQLAction { try (PreparedStatement statement = prepareStatement(con, sql, adapter, generatesKeys)) { for (BatchQueryRow row : query.getRows()) { - DbAttributeBinding[] bindings = translator.updateBindings(row); + ParameterBinding[] bindings = translator.updateBindings(row); logger.logQueryParameters("batch bind", bindings); bind(adapter, statement, bindings); @@ -189,7 +189,7 @@ public class BatchAction extends BaseSQLAction { try (PreparedStatement statement = prepareStatement(connection, queryStr, adapter, generatesKeys)) { for (BatchQueryRow row : query.getRows()) { - DbAttributeBinding[] bindings = translator.updateBindings(row); + ParameterBinding[] bindings = translator.updateBindings(row); logger.logQueryParameters("bind", bindings); bind(adapter, statement, bindings); diff --git a/cayenne/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateAction.java b/cayenne/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateAction.java index 5976e9f67..c4bd62c6d 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateAction.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/jdbc/SQLTemplateAction.java @@ -26,7 +26,6 @@ import org.apache.cayenne.access.OperationObserver; import org.apache.cayenne.access.jdbc.reader.RowReader; import org.apache.cayenne.access.translator.ParameterBinding; import org.apache.cayenne.access.translator.sqltemplate.TranslatedSQL; -import org.apache.cayenne.access.types.ExtendedType; import org.apache.cayenne.access.types.ExtendedTypeMap; import org.apache.cayenne.dba.DbAdapter; import org.apache.cayenne.dba.TypesMapping; @@ -125,25 +124,12 @@ public class SQLTemplateAction implements SQLAction { callback.nextBatchCount(query, ints); } - private void bindExtendedTypes(ParameterBinding[] bindings) { - int i = 1; - for (ParameterBinding binding : bindings) { - Object value = binding.getValue(); - ExtendedType extendedType = value != null - ? getAdapter().getExtendedTypes().getRegisteredType(value.getClass()) - : getAdapter().getExtendedTypes().getDefaultType(); - binding.setExtendedType(extendedType); - binding.setStatementPosition(i++); - } - } - private void runWithPositionalParameters(Connection connection, OperationObserver callback, String template, Collection<Number> counts, boolean loggable) throws Exception { TranslatedSQL compiled = dataNode.getSqlTemplateTranslator().translate(template, - query.getPositionalParams()); + query.getPositionalParams(), getAdapter()); - bindExtendedTypes(compiled.bindings()); if (loggable) { dataNode.getJdbcEventLogger().logQuery(compiled.sql(), compiled.bindings()); } @@ -171,8 +157,7 @@ public class SQLTemplateAction implements SQLAction { for (int i = 0; i < batchSize; i++) { Map<String, ?> nextParameters = it.next(); - TranslatedSQL compiled = dataNode.getSqlTemplateTranslator().translate(template, nextParameters); - bindExtendedTypes(compiled.bindings()); + TranslatedSQL compiled = dataNode.getSqlTemplateTranslator().translate(template, nextParameters, getAdapter()); if (loggable) { dataNode.getJdbcEventLogger().logQuery(compiled.sql(), compiled.bindings()); } diff --git a/cayenne/src/main/java/org/apache/cayenne/access/jdbc/SelectAction.java b/cayenne/src/main/java/org/apache/cayenne/access/jdbc/SelectAction.java index 5094135f5..6318d1798 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/jdbc/SelectAction.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/jdbc/SelectAction.java @@ -23,7 +23,7 @@ import org.apache.cayenne.ResultIterator; import org.apache.cayenne.access.DataNode; import org.apache.cayenne.access.OperationObserver; import org.apache.cayenne.access.jdbc.reader.RowReader; -import org.apache.cayenne.access.translator.DbAttributeBinding; +import org.apache.cayenne.access.translator.ParameterBinding; import org.apache.cayenne.access.translator.select.TranslatedSelect; import org.apache.cayenne.dba.DbAdapter; import org.apache.cayenne.log.JdbcEventLogger; @@ -44,9 +44,9 @@ import java.util.List; */ public class SelectAction extends BaseSQLAction { - private static void bind(DbAdapter adapter, PreparedStatement statement, DbAttributeBinding[] bindings) throws Exception { + private static void bind(DbAdapter adapter, PreparedStatement statement, ParameterBinding[] bindings) throws Exception { - for (DbAttributeBinding b : bindings) { + for (ParameterBinding b : bindings) { if (b.isExcluded()) { continue; diff --git a/cayenne/src/main/java/org/apache/cayenne/access/sqlbuilder/SQLGenerationContext.java b/cayenne/src/main/java/org/apache/cayenne/access/sqlbuilder/SQLGenerationContext.java index 7e0b7597a..661f352c1 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/sqlbuilder/SQLGenerationContext.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/sqlbuilder/SQLGenerationContext.java @@ -21,7 +21,7 @@ package org.apache.cayenne.access.sqlbuilder; import java.util.Collection; -import org.apache.cayenne.access.translator.DbAttributeBinding; +import org.apache.cayenne.access.translator.ParameterBinding; import org.apache.cayenne.dba.DbAdapter; import org.apache.cayenne.dba.QuotingStrategy; import org.apache.cayenne.map.DbEntity; @@ -33,7 +33,7 @@ public interface SQLGenerationContext { DbAdapter getAdapter(); - Collection<DbAttributeBinding> getBindings(); + Collection<ParameterBinding> getBindings(); QuotingStrategy getQuotingStrategy(); diff --git a/cayenne/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/ValueNode.java b/cayenne/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/ValueNode.java index 220aadcf8..52cc75383 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/ValueNode.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/sqlbuilder/sqltree/ValueNode.java @@ -19,18 +19,19 @@ package org.apache.cayenne.access.sqlbuilder.sqltree; -import java.util.Objects; -import java.util.function.Supplier; - import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.ObjectId; import org.apache.cayenne.Persistent; -import org.apache.cayenne.access.sqlbuilder.SQLGenerationContext; -import org.apache.cayenne.access.translator.DbAttributeBinding; import org.apache.cayenne.access.sqlbuilder.QuotingAppendable; +import org.apache.cayenne.access.sqlbuilder.SQLGenerationContext; +import org.apache.cayenne.access.translator.ParameterBinding; import org.apache.cayenne.access.types.ExtendedType; +import org.apache.cayenne.dba.TypesMapping; import org.apache.cayenne.map.DbAttribute; +import java.util.Objects; +import java.util.function.Supplier; + /** * @since 4.2 */ @@ -143,10 +144,20 @@ public class ValueNode extends Node { SQLGenerationContext context = buffer.getContext(); // allow translation in out-of-context scope, to be able to use as a standalone SQL generator ExtendedType<?> extendedType = context.getAdapter().getExtendedTypes().getRegisteredType(value.getClass()); - DbAttributeBinding binding = new DbAttributeBinding(attribute); - binding.setStatementPosition(context.getBindings().size() + 1); - binding.setExtendedType(extendedType); - binding.setValue(value); + + // 'attribute' is only a type hint and may be absent (e.g. function arguments and other + // literals not bound to a column); fall back to deriving the JDBC type from the value + ParameterBinding binding; + if (attribute != null) { + binding = new ParameterBinding( + context.getAdapter().preferredBindingType(attribute.getType()), + attribute.getScale(), + attribute); + } else { + int jdbcType = context.getAdapter().preferredBindingType(TypesMapping.getSqlTypeByJava(value.getClass())); + binding = new ParameterBinding(jdbcType, -1); + } + binding.include(context.getBindings().size() + 1, value, extendedType); context.getBindings().add(binding); } diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/DbAttributeBinding.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/DbAttributeBinding.java deleted file mode 100644 index 0cf44365d..000000000 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/DbAttributeBinding.java +++ /dev/null @@ -1,49 +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 - * - * https://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.translator; - -import org.apache.cayenne.map.DbAttribute; - -/** - * Describes a PreparedStatement parameter binding mapped to a DbAttribute. - * - * @since 4.0 - */ -public class DbAttributeBinding extends ParameterBinding { - - private final DbAttribute attribute; - - public DbAttributeBinding(DbAttribute attribute) { - this.attribute = attribute; - } - - public DbAttribute getAttribute() { - return attribute; - } - - @Override - public Integer getJdbcType() { - return super.getJdbcType() != null ? super.getJdbcType() : attribute.getType(); - } - - @Override - public int getScale() { - return getAttribute().getScale(); - } -} diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/ParameterBinding.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/ParameterBinding.java index 1e6fc103b..410539961 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/ParameterBinding.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/ParameterBinding.java @@ -19,29 +19,42 @@ package org.apache.cayenne.access.translator; import org.apache.cayenne.access.types.ExtendedType; +import org.apache.cayenne.map.DbAttribute; /** - * Describes a PreparedStatement parameter generic binding. - * + * Describes a single PreparedStatement parameter binding. A binding carries immutable metadata + * (the optional source {@link DbAttribute}, the JDBC type and scale) established at creation time, + * plus a mutable per-iteration state (value, statement position, {@link ExtendedType}) updated via + * {@link #include(int, Object, ExtendedType)} / {@link #exclude()} as batch rows are processed. + * * @since 4.0 */ public class ParameterBinding { private static final int EXCLUDED_POSITION = -1; + private final DbAttribute attribute; + private final int jdbcType; + private final int scale; + private Object value; private int statementPosition; private ExtendedType<?> extendedType; - private Integer jdbcType; - private int scale; - public ParameterBinding(Object value, Integer jdbcType, int scale) { - this.value = value; - this.jdbcType = jdbcType; - this.scale = scale; + /** + * @since 5.0 + */ + public ParameterBinding(int jdbcType, int scale) { + this(jdbcType, scale, null); } - public ParameterBinding() { + /** + * @since 5.0 + */ + public ParameterBinding(int jdbcType, int scale, DbAttribute attribute) { + this.attribute = attribute; + this.jdbcType = jdbcType; + this.scale = scale; this.statementPosition = EXCLUDED_POSITION; } @@ -49,18 +62,10 @@ public class ParameterBinding { return value; } - public void setValue(Object value) { - this.value = value; - } - public int getStatementPosition() { return statementPosition; } - public void setStatementPosition(int statementPosition) { - this.statementPosition = statementPosition; - } - public boolean isExcluded() { return statementPosition == EXCLUDED_POSITION; } @@ -69,10 +74,6 @@ public class ParameterBinding { return extendedType; } - public void setExtendedType(ExtendedType<?> extendedType) { - this.extendedType = extendedType; - } - /** * Marks the binding object as excluded for the current iteration. */ @@ -83,28 +84,28 @@ public class ParameterBinding { } /** - * Sets the value of the binding and initializes statement position var, - * thus "including" this binding in the current iteration. + * Sets the value, statement position and {@link ExtendedType} of the binding, thus "including" it in the current + * iteration. Returns this binding for chaining. */ - public void include(int statementPosition, Object value, ExtendedType<?> extendedType) { + public ParameterBinding include(int statementPosition, Object value, ExtendedType<?> extendedType) { this.statementPosition = statementPosition; this.value = value; this.extendedType = extendedType; + return this; } - public Integer getJdbcType() { + public int getJdbcType() { return jdbcType; } - public void setJdbcType(Integer type) { - this.jdbcType = type; - } - public int getScale() { return scale; } - public void setScale(int scale) { - this.scale = scale; + /** + * @since 5.0 + */ + public DbAttribute getAttribute() { + return attribute; } } diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/ProcedureParameterBinding.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/ProcedureParameterBinding.java deleted file mode 100644 index ffca313cc..000000000 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/ProcedureParameterBinding.java +++ /dev/null @@ -1,49 +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 - * <p/> - * https://www.apache.org/licenses/LICENSE-2.0 - * <p/> - * 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.translator; - -import org.apache.cayenne.map.ProcedureParameter; - -/** - * Describes a PreparedStatement parameter binding mapped to a DbAttribute. - * - * @since 4.0 - */ -public class ProcedureParameterBinding extends ParameterBinding { - - private final ProcedureParameter parameter; - - public ProcedureParameterBinding(ProcedureParameter procedureParameter) { - this.parameter = procedureParameter; - } - - public ProcedureParameter getParameter() { - return parameter; - } - - @Override - public Integer getJdbcType() { - return parameter.getType(); - } - - @Override - public int getScale() { - return parameter.getPrecision(); - } -} diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/BaseBatchTranslator.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/BaseBatchTranslator.java index 92966ed01..d5ab3fa1b 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/BaseBatchTranslator.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/BaseBatchTranslator.java @@ -26,7 +26,7 @@ import org.apache.cayenne.access.sqlbuilder.NodeBuilder; import org.apache.cayenne.access.sqlbuilder.SQLBuilder; import org.apache.cayenne.access.sqlbuilder.SQLGenerationVisitor; import org.apache.cayenne.access.sqlbuilder.sqltree.Node; -import org.apache.cayenne.access.translator.DbAttributeBinding; +import org.apache.cayenne.access.translator.ParameterBinding; import org.apache.cayenne.access.translator.select.DefaultQuotingAppendable; import org.apache.cayenne.dba.DbAdapter; import org.apache.cayenne.map.DbAttribute; @@ -40,13 +40,13 @@ public abstract class BaseBatchTranslator<T extends BatchQuery> { protected final BatchTranslatorContext<T> context; - protected DbAttributeBinding[] bindings; + protected ParameterBinding[] bindings; public BaseBatchTranslator(T query, DbAdapter adapter) { this.context = new BatchTranslatorContext<>(query, adapter); } - public DbAttributeBinding[] getBindings() { + public ParameterBinding[] getBindings() { return bindings; } @@ -65,7 +65,7 @@ public abstract class BaseBatchTranslator<T extends BatchQuery> { SQLGenerationVisitor visitor = new SQLGenerationVisitor(new DefaultQuotingAppendable(context)); node.visit(visitor); - bindings = context.getBindings().toArray(new DbAttributeBinding[0]); + bindings = context.getBindings().toArray(new ParameterBinding[0]); return visitor.getSQLString(); } diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/BatchTranslator.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/BatchTranslator.java index 4be8497c4..73a75fdbc 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/BatchTranslator.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/BatchTranslator.java @@ -19,7 +19,7 @@ package org.apache.cayenne.access.translator.batch; -import org.apache.cayenne.access.translator.DbAttributeBinding; +import org.apache.cayenne.access.translator.ParameterBinding; import org.apache.cayenne.query.BatchQueryRow; /** @@ -37,7 +37,7 @@ public interface BatchTranslator { /** * Returns the widest possible array of bindings for this query. */ - DbAttributeBinding[] getBindings(); + ParameterBinding[] getBindings(); /** * Updates internal bindings to be used with a given row, returning updated @@ -47,5 +47,5 @@ public interface BatchTranslator { * parameter). Usually the returned array is actually the same object reused * for every iteration, only with changed object state. */ - DbAttributeBinding[] updateBindings(BatchQueryRow row); + ParameterBinding[] updateBindings(BatchQueryRow row); } diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/BatchTranslatorContext.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/BatchTranslatorContext.java index 17352dd56..341d3c8d7 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/BatchTranslatorContext.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/BatchTranslatorContext.java @@ -24,7 +24,7 @@ import java.util.Collection; import java.util.List; import org.apache.cayenne.access.sqlbuilder.SQLGenerationContext; -import org.apache.cayenne.access.translator.DbAttributeBinding; +import org.apache.cayenne.access.translator.ParameterBinding; import org.apache.cayenne.dba.DbAdapter; import org.apache.cayenne.dba.QuotingStrategy; import org.apache.cayenne.map.DbEntity; @@ -38,7 +38,7 @@ class BatchTranslatorContext<T extends BatchQuery> implements SQLGenerationConte private final T query; private final DbAdapter adapter; - private final List<DbAttributeBinding> bindings; + private final List<ParameterBinding> bindings; BatchTranslatorContext(T query, DbAdapter adapter) { this.query = query; @@ -52,7 +52,7 @@ class BatchTranslatorContext<T extends BatchQuery> implements SQLGenerationConte } @Override - public Collection<DbAttributeBinding> getBindings() { + public Collection<ParameterBinding> getBindings() { return bindings; } diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/DeleteBatchTranslator.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/DeleteBatchTranslator.java index 57675d154..78c87d6ea 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/DeleteBatchTranslator.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/DeleteBatchTranslator.java @@ -21,7 +21,7 @@ package org.apache.cayenne.access.translator.batch; import org.apache.cayenne.access.sqlbuilder.DeleteBuilder; import org.apache.cayenne.access.sqlbuilder.SQLBuilder; -import org.apache.cayenne.access.translator.DbAttributeBinding; +import org.apache.cayenne.access.translator.ParameterBinding; import org.apache.cayenne.access.types.ExtendedType; import org.apache.cayenne.dba.DbAdapter; import org.apache.cayenne.map.DbAttribute; @@ -51,7 +51,7 @@ public class DeleteBatchTranslator extends BaseBatchTranslator<DeleteBatchQuery> } @Override - public DbAttributeBinding[] updateBindings(BatchQueryRow row) { + public ParameterBinding[] updateBindings(BatchQueryRow row) { DeleteBatchQuery deleteBatch = context.getQuery(); for(int i=0, position=0; i<deleteBatch.getDbAttributes().size(); i++) { position = updateBinding(row.getValue(i), position); diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/InsertBatchTranslator.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/InsertBatchTranslator.java index 03e6e41a9..bddf13877 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/InsertBatchTranslator.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/InsertBatchTranslator.java @@ -21,7 +21,7 @@ package org.apache.cayenne.access.translator.batch; import org.apache.cayenne.access.sqlbuilder.InsertBuilder; import org.apache.cayenne.access.sqlbuilder.SQLBuilder; -import org.apache.cayenne.access.translator.DbAttributeBinding; +import org.apache.cayenne.access.translator.ParameterBinding; import org.apache.cayenne.access.types.ExtendedType; import org.apache.cayenne.dba.DbAdapter; import org.apache.cayenne.map.DbAttribute; @@ -58,7 +58,7 @@ public class InsertBatchTranslator extends BaseBatchTranslator<InsertBatchQuery> } @Override - public DbAttributeBinding[] updateBindings(BatchQueryRow row) { + public ParameterBinding[] updateBindings(BatchQueryRow row) { InsertBatchQuery query = context.getQuery(); int i=0; int j=0; diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/SoftDeleteBatchTranslator.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/SoftDeleteBatchTranslator.java index 43fad4d30..50e7824df 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/SoftDeleteBatchTranslator.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/SoftDeleteBatchTranslator.java @@ -21,7 +21,7 @@ package org.apache.cayenne.access.translator.batch; import org.apache.cayenne.access.sqlbuilder.SQLBuilder; import org.apache.cayenne.access.sqlbuilder.UpdateBuilder; -import org.apache.cayenne.access.translator.DbAttributeBinding; +import org.apache.cayenne.access.translator.ParameterBinding; import org.apache.cayenne.access.types.ExtendedType; import org.apache.cayenne.dba.DbAdapter; import org.apache.cayenne.dba.TypesMapping; @@ -63,7 +63,7 @@ public class SoftDeleteBatchTranslator extends DeleteBatchTranslator { } @Override - public DbAttributeBinding[] updateBindings(BatchQueryRow row) { + public ParameterBinding[] updateBindings(BatchQueryRow row) { DeleteBatchQuery deleteBatch = context.getQuery(); for(int i=0, position=1; i<deleteBatch.getDbAttributes().size(); i++) { diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/UpdateBatchTranslator.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/UpdateBatchTranslator.java index 6b973b5dd..a7c074fef 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/UpdateBatchTranslator.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/UpdateBatchTranslator.java @@ -21,7 +21,7 @@ package org.apache.cayenne.access.translator.batch; import org.apache.cayenne.access.sqlbuilder.SQLBuilder; import org.apache.cayenne.access.sqlbuilder.UpdateBuilder; -import org.apache.cayenne.access.translator.DbAttributeBinding; +import org.apache.cayenne.access.translator.ParameterBinding; import org.apache.cayenne.access.types.ExtendedType; import org.apache.cayenne.dba.DbAdapter; import org.apache.cayenne.map.DbAttribute; @@ -59,7 +59,7 @@ public class UpdateBatchTranslator extends BaseBatchTranslator<UpdateBatchQuery> } @Override - public DbAttributeBinding[] updateBindings(BatchQueryRow row) { + public ParameterBinding[] updateBindings(BatchQueryRow row) { UpdateBatchQuery updateBatch = context.getQuery(); int i = 0; diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/legacy/DefaultBatchTranslator.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/legacy/DefaultBatchTranslator.java index f5a07939d..aa8435253 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/legacy/DefaultBatchTranslator.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/batch/legacy/DefaultBatchTranslator.java @@ -20,7 +20,7 @@ package org.apache.cayenne.access.translator.batch.legacy; import java.sql.Types; -import org.apache.cayenne.access.translator.DbAttributeBinding; +import org.apache.cayenne.access.translator.ParameterBinding; import org.apache.cayenne.access.translator.batch.BatchTranslator; import org.apache.cayenne.dba.DbAdapter; import org.apache.cayenne.dba.QuotingStrategy; @@ -43,7 +43,7 @@ public abstract class DefaultBatchTranslator implements BatchTranslator { protected boolean translated; protected String sql; - protected DbAttributeBinding[] bindings; + protected ParameterBinding[] bindings; public DefaultBatchTranslator(BatchQuery query, DbAdapter adapter, String trimFunction) { this.query = query; @@ -70,22 +70,22 @@ public abstract class DefaultBatchTranslator implements BatchTranslator { } @Override - public DbAttributeBinding[] getBindings() { + public ParameterBinding[] getBindings() { ensureTranslated(); return bindings; } @Override - public DbAttributeBinding[] updateBindings(BatchQueryRow row) { + public ParameterBinding[] updateBindings(BatchQueryRow row) { ensureTranslated(); return doUpdateBindings(row); } protected abstract String createSql(); - protected abstract DbAttributeBinding[] createBindings(); + protected abstract ParameterBinding[] createBindings(); - protected abstract DbAttributeBinding[] doUpdateBindings(BatchQueryRow row); + protected abstract ParameterBinding[] doUpdateBindings(BatchQueryRow row); /** * Appends the name of the column to the query buffer. Subclasses use this diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/procedure/ProcedureTranslator.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/procedure/ProcedureTranslator.java index 3639b0f65..8d329fd46 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/procedure/ProcedureTranslator.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/procedure/ProcedureTranslator.java @@ -27,7 +27,7 @@ import java.util.List; import java.util.Map; import org.apache.cayenne.access.translator.ParameterBinding; -import org.apache.cayenne.access.translator.ProcedureParameterBinding; +import org.apache.cayenne.access.translator.ParameterBinding; import org.apache.cayenne.access.types.ExtendedType; import org.apache.cayenne.dba.DbAdapter; import org.apache.cayenne.log.JdbcEventLogger; @@ -160,8 +160,8 @@ public class ProcedureTranslator { if(value instanceof NotInParam) { value = value.toString(); } - parameterBindings[i] = new ParameterBinding(value, - procedureParameter.getType(), procedureParameter.getPrecision()); + parameterBindings[i] = new ParameterBinding(procedureParameter.getType(), procedureParameter.getPrecision()) + .include(i + 1, value, null); } logger.logQuery(sqlStr, parameterBindings); } @@ -231,10 +231,8 @@ public class ProcedureTranslator { ? adapter.getExtendedTypes().getRegisteredType(val.getClass()) : adapter.getExtendedTypes().getDefaultType(); - ProcedureParameterBinding binding = new ProcedureParameterBinding(param); - binding.setStatementPosition(pos); - binding.setValue(val); - binding.setExtendedType(extendedType); + ParameterBinding binding = new ParameterBinding(adapter.preferredBindingType(param.getType()), param.getPrecision()) + .include(pos, val, extendedType); adapter.bindParameter(stmt, binding); } diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TranslatedSelect.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TranslatedSelect.java index ee1ec983f..973dc5793 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TranslatedSelect.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TranslatedSelect.java @@ -20,7 +20,7 @@ package org.apache.cayenne.access.translator.select; import org.apache.cayenne.access.jdbc.ColumnDescriptor; -import org.apache.cayenne.access.translator.DbAttributeBinding; +import org.apache.cayenne.access.translator.ParameterBinding; /** * An immutable result of translating a {@link org.apache.cayenne.query.Select} query to SQL, @@ -31,7 +31,7 @@ import org.apache.cayenne.access.translator.DbAttributeBinding; public record TranslatedSelect( String sql, ColumnDescriptor[] resultColumns, - DbAttributeBinding[] bindings, + ParameterBinding[] bindings, boolean suppressingDistinct, boolean hasJoins) { } diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TranslatorContext.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TranslatorContext.java index acb9a1ab8..8115192c1 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TranslatorContext.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/select/TranslatorContext.java @@ -24,7 +24,7 @@ import org.apache.cayenne.access.sqlbuilder.SQLBuilder; import org.apache.cayenne.access.sqlbuilder.SQLGenerationContext; import org.apache.cayenne.access.sqlbuilder.SelectBuilder; import org.apache.cayenne.access.sqlbuilder.sqltree.Node; -import org.apache.cayenne.access.translator.DbAttributeBinding; +import org.apache.cayenne.access.translator.ParameterBinding; import org.apache.cayenne.dba.DbAdapter; import org.apache.cayenne.dba.QuotingStrategy; import org.apache.cayenne.exp.parser.ASTAggregateFunctionCall; @@ -92,7 +92,7 @@ public class TranslatorContext implements SQLGenerationContext { * - order by expressions * - where expression (including qualifiers from all used DbEntities and ObjEntities) */ - private final Collection<DbAttributeBinding> bindings; + private final Collection<ParameterBinding> bindings; // Translated query private final TranslatableQueryWrapper query; @@ -161,7 +161,7 @@ public class TranslatorContext implements SQLGenerationContext { return new TranslatedSelect( getFinalSQL(), getColumnDescriptors().toArray(new ColumnDescriptor[0]), - getBindings().toArray(new DbAttributeBinding[0]), + getBindings().toArray(new ParameterBinding[0]), isDistinctSuppression(), getTableCount() > 1); } @@ -194,7 +194,7 @@ public class TranslatorContext implements SQLGenerationContext { return columnDescriptors; } - public Collection<DbAttributeBinding> getBindings() { + public Collection<ParameterBinding> getBindings() { return bindings; } diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/Context.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/Context.java index d4bc86bca..2821dd0c1 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/Context.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/Context.java @@ -28,6 +28,8 @@ import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.access.jdbc.ColumnDescriptor; import org.apache.cayenne.access.translator.ParameterBinding; import org.apache.cayenne.access.translator.sqltemplate.directive.Directive; +import org.apache.cayenne.access.types.ExtendedType; +import org.apache.cayenne.dba.DbAdapter; /** * @since 4.1 @@ -38,14 +40,17 @@ public class Context { private final Map<String, ?> objects; private final Map<String, String> parameterAliases; private final Map<String, Directive> directives; + private final DbAdapter adapter; private List<ParameterBinding> parameterBindings; private List<ColumnDescriptor> columnDescriptors; private int counter; - public Context(Map<String, Directive> directives, Map<String, ?> parameters, boolean positionalMode) { + public Context(Map<String, Directive> directives, Map<String, ?> parameters, boolean positionalMode, + DbAdapter adapter) { this.directives = directives; this.objects = parameters; + this.adapter = adapter; this.builder = new StringBuilder(); if(positionalMode) { parameterAliases = new HashMap<>(); @@ -54,6 +59,15 @@ public class Context { } } + /** + * Returns the JDBC type the target adapter prefers for binding the given type. + * + * @since 5.0 + */ + public int preferredBindingType(int jdbcType) { + return adapter.preferredBindingType(jdbcType); + } + public Directive getDirective(String name) { return directives.get(name); } @@ -98,13 +112,22 @@ public class Context { return null; } - public void addParameterBinding(ParameterBinding binding) { + public void addParameterBinding(ParameterBinding binding, Object value) { if(parameterBindings == null) { parameterBindings = new ArrayList<>(); } + // a binding's statement position is its 1-based ordinal among the bound parameters; the + // ExtendedType is resolved from the value via the adapter + binding.include(parameterBindings.size() + 1, value, extendedType(value)); parameterBindings.add(binding); } + private ExtendedType<?> extendedType(Object value) { + return value != null + ? adapter.getExtendedTypes().getRegisteredType(value.getClass()) + : adapter.getExtendedTypes().getDefaultType(); + } + public void addColumnDescriptor(ColumnDescriptor descriptor) { if(columnDescriptors == null) { columnDescriptors = new ArrayList<>(); diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/DefaultSQLTemplateTranslator.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/DefaultSQLTemplateTranslator.java index 0ddd36a55..a10dc8bf2 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/DefaultSQLTemplateTranslator.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/DefaultSQLTemplateTranslator.java @@ -20,6 +20,7 @@ package org.apache.cayenne.access.translator.sqltemplate; import org.apache.cayenne.CayenneRuntimeException; +import org.apache.cayenne.dba.DbAdapter; import org.apache.cayenne.di.Inject; import org.apache.cayenne.access.translator.sqltemplate.parser.Node; import org.apache.cayenne.access.translator.sqltemplate.parser.ParseException; @@ -49,19 +50,19 @@ public class DefaultSQLTemplateTranslator implements SQLTemplateTranslator { } @Override - public TranslatedSQL translate(String template, Map<String, ?> parameters) { - Context context = contextFactory.createContext(parameters); + public TranslatedSQL translate(String template, Map<String, ?> parameters, DbAdapter adapter) { + Context context = contextFactory.createContext(parameters, adapter); return process(template, context); } @Override - public TranslatedSQL translate(String template, List<Object> positionalParameters) { + public TranslatedSQL translate(String template, List<Object> positionalParameters, DbAdapter adapter) { Map<String, Object> parameters = new HashMap<>(); int i = 0; for (Object param : positionalParameters) { parameters.put(String.valueOf(i++), param); } - Context context = contextFactory.createContext(parameters, true); + Context context = contextFactory.createContext(parameters, true, adapter); return process(template, context); } diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/DefaultTemplateContextFactory.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/DefaultTemplateContextFactory.java index 715be0980..f3b35417f 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/DefaultTemplateContextFactory.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/DefaultTemplateContextFactory.java @@ -30,6 +30,7 @@ import org.apache.cayenne.access.translator.sqltemplate.directive.BindObjectEqua import org.apache.cayenne.access.translator.sqltemplate.directive.BindObjectNotEqual; import org.apache.cayenne.access.translator.sqltemplate.directive.Directive; import org.apache.cayenne.access.translator.sqltemplate.directive.Result; +import org.apache.cayenne.dba.DbAdapter; /** * @since 4.1 @@ -55,10 +56,10 @@ public class DefaultTemplateContextFactory implements TemplateContextFactory { } @Override - public Context createContext(Map<String, ?> parameters, boolean positionalMode) { + public Context createContext(Map<String, ?> parameters, boolean positionalMode, DbAdapter adapter) { Map<String, Object> realParameters = new HashMap<>(parameters.size() + 1); realParameters.putAll(parameters); realParameters.put("helper", helper); - return new Context(directives, realParameters, positionalMode); + return new Context(directives, realParameters, positionalMode, adapter); } } diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/SQLTemplateTranslator.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/SQLTemplateTranslator.java index 70a7df95f..3ee3cd1e4 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/SQLTemplateTranslator.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/SQLTemplateTranslator.java @@ -18,6 +18,8 @@ ****************************************************************/ package org.apache.cayenne.access.translator.sqltemplate; +import org.apache.cayenne.dba.DbAdapter; + import java.util.List; import java.util.Map; @@ -27,18 +29,17 @@ import java.util.Map; public interface SQLTemplateTranslator { /** - * Builds and returns a SQLStatement based on SQL template String and a map - * of parameters. + * Builds and returns a SQLStatement based on SQL template String and a map of parameters. * * @since 5.0 */ - TranslatedSQL translate(String template, Map<String, ?> parameters); + TranslatedSQL translate(String template, Map<String, ?> parameters, DbAdapter adapter); /** - * Builds and returns a SQLStatement based on SQL template String and a list - * of positional parameters. + * Builds and returns a SQLStatement based on SQL template String and a list of positional + * parameters. * * @since 5.0 */ - TranslatedSQL translate(String template, List<Object> positionalParameters); + TranslatedSQL translate(String template, List<Object> positionalParameters, DbAdapter adapter); } diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/TemplateContextFactory.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/TemplateContextFactory.java index cf1fe981f..8044482ea 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/TemplateContextFactory.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/TemplateContextFactory.java @@ -21,15 +21,23 @@ package org.apache.cayenne.access.translator.sqltemplate; import java.util.Map; +import org.apache.cayenne.dba.DbAdapter; + /** * @since 4.1 */ public interface TemplateContextFactory { - Context createContext(Map<String, ?> parameters, boolean positionalMode); + /** + * @since 5.0 + */ + Context createContext(Map<String, ?> parameters, boolean positionalMode, DbAdapter adapter); - default Context createContext(Map<String, ?> parameters) { - return createContext(parameters, false); + /** + * @since 5.0 + */ + default Context createContext(Map<String, ?> parameters, DbAdapter adapter) { + return createContext(parameters, false, adapter); } } diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/directive/Bind.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/directive/Bind.java index 2fc2a8c97..63f1b8e67 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/directive/Bind.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/directive/Bind.java @@ -67,11 +67,11 @@ public class Bind implements Directive { jdbcType = TypesMapping.getSqlTypeByName(TypesMapping.SQL_NULL); } - processBinding(context, new ParameterBinding(value, jdbcType, scale)); + processBinding(context, new ParameterBinding(context.preferredBindingType(jdbcType), scale), value); } - protected void processBinding(Context context, ParameterBinding binding) { - context.addParameterBinding(binding); + protected void processBinding(Context context, ParameterBinding binding, Object value) { + context.addParameterBinding(binding, value); context.getBuilder().append('?'); } } diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/directive/BindEqual.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/directive/BindEqual.java index 8a2ca26e3..38ce70428 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/directive/BindEqual.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/directive/BindEqual.java @@ -30,9 +30,9 @@ public class BindEqual extends Bind { public static final BindEqual INSTANCE = new BindEqual(); @Override - protected void processBinding(Context context, ParameterBinding binding) { - if (binding.getValue() != null) { - context.addParameterBinding(binding); + protected void processBinding(Context context, ParameterBinding binding, Object value) { + if (value != null) { + context.addParameterBinding(binding, value); context.getBuilder().append("= ?"); } else { context.getBuilder().append("IS NULL"); diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/directive/BindNotEqual.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/directive/BindNotEqual.java index 717b94165..723d0436e 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/directive/BindNotEqual.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/directive/BindNotEqual.java @@ -30,9 +30,9 @@ public class BindNotEqual extends Bind { public static final BindNotEqual INSTANCE = new BindNotEqual(); @Override - protected void processBinding(Context context, ParameterBinding binding) { - if (binding.getValue() != null) { - context.addParameterBinding(binding); + protected void processBinding(Context context, ParameterBinding binding, Object value) { + if (value != null) { + context.addParameterBinding(binding, value); context.getBuilder().append("<> ?"); } else { context.getBuilder().append("IS NOT NULL"); diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/directive/BindObjectEqual.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/directive/BindObjectEqual.java index 67f3b1576..2aaddc190 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/directive/BindObjectEqual.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/directive/BindObjectEqual.java @@ -81,7 +81,7 @@ public class BindObjectEqual implements Directive { int jdbcType = (value != null) ? TypesMapping.getSqlTypeByJava(value.getClass()) : Types.INTEGER; renderColumn(context, sqlColumnsArray[i], i); - render(context, new ParameterBinding(value, jdbcType, -1)); + render(context, new ParameterBinding(context.preferredBindingType(jdbcType), -1), value); } } @@ -93,9 +93,9 @@ public class BindObjectEqual implements Directive { context.getBuilder().append(columnName).append(' '); } - protected void render(Context context, ParameterBinding binding) { - if (binding.getValue() != null) { - context.addParameterBinding(binding); + protected void render(Context context, ParameterBinding binding, Object value) { + if (value != null) { + context.addParameterBinding(binding, value); context.getBuilder().append("= ?"); } else { context.getBuilder().append("IS NULL"); diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/directive/BindObjectNotEqual.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/directive/BindObjectNotEqual.java index 8ebd66198..fbf3cc847 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/directive/BindObjectNotEqual.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/sqltemplate/directive/BindObjectNotEqual.java @@ -39,9 +39,9 @@ public class BindObjectNotEqual extends BindObjectEqual { } @Override - protected void render(Context context, ParameterBinding binding) { - if (binding.getValue() != null) { - context.addParameterBinding(binding); + protected void render(Context context, ParameterBinding binding, Object value) { + if (value != null) { + context.addParameterBinding(binding, value); context.getBuilder().append("<> ?"); } else { context.getBuilder().append("IS NOT NULL"); diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/AutoAdapter.java b/cayenne/src/main/java/org/apache/cayenne/dba/AutoAdapter.java index e9a6464fc..8c2c7980b 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/AutoAdapter.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/AutoAdapter.java @@ -218,6 +218,14 @@ public class AutoAdapter implements DbAdapter { getAdapter().bindParameter(statement, parameterBinding); } + /** + * @since 5.0 + */ + @Override + public int preferredBindingType(int jdbcType) { + return getAdapter().preferredBindingType(jdbcType); + } + @Override public String tableTypeForTable() { return getAdapter().tableTypeForTable(); diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/DbAdapter.java b/cayenne/src/main/java/org/apache/cayenne/dba/DbAdapter.java index 2a7f53940..7d3e399de 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/DbAdapter.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/DbAdapter.java @@ -199,6 +199,19 @@ public interface DbAdapter { */ void bindParameter(PreparedStatement statement, ParameterBinding parameterBinding) throws Exception; + /** + * Returns the JDBC type that this adapter prefers to bind for a given mapped JDBC type. The + * default implementation is an identity function; adapters whose drivers require a different + * type (e.g. remapping the {@code N*} character types to their non-national counterparts) + * override this. The returned type is resolved into the {@link ParameterBinding} at binding + * creation time, so it matches the type actually sent to the PreparedStatement. + * + * @since 5.0 + */ + default int preferredBindingType(int jdbcType) { + return jdbcType; + } + /** * Returns the name of the table type (as returned by * <code>DatabaseMetaData.getTableTypes</code>) for a simple user table. diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/db2/DB2Adapter.java b/cayenne/src/main/java/org/apache/cayenne/dba/db2/DB2Adapter.java index 89c139cb3..801e7e35b 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/db2/DB2Adapter.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/db2/DB2Adapter.java @@ -164,7 +164,6 @@ public class DB2Adapter extends JdbcAdapter { if (binding.getValue() == null && (binding.getJdbcType() == 0 || binding.getJdbcType() == Types.BOOLEAN)) { statement.setNull(binding.getStatementPosition(), Types.VARCHAR); } else { - binding.setJdbcType(convertNTypes(binding.getJdbcType())); super.bindParameter(statement, binding); } } @@ -180,10 +179,11 @@ public class DB2Adapter extends JdbcAdapter { } /** - * @since 4.0 + * @since 5.0 */ - private int convertNTypes(int sqlType) { - switch (sqlType) { + @Override + public int preferredBindingType(int jdbcType) { + switch (jdbcType) { case Types.NCHAR: return Types.CHAR; case Types.NVARCHAR: @@ -194,7 +194,7 @@ public class DB2Adapter extends JdbcAdapter { return Types.CLOB; default: - return sqlType; + return jdbcType; } } diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/derby/DerbyAdapter.java b/cayenne/src/main/java/org/apache/cayenne/dba/derby/DerbyAdapter.java index 540b50245..a9007e37b 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/derby/DerbyAdapter.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/derby/DerbyAdapter.java @@ -199,13 +199,16 @@ public class DerbyAdapter extends JdbcAdapter { if (binding.getValue() == null && binding.getJdbcType() == 0) { statement.setNull(binding.getStatementPosition(), Types.VARCHAR); } else { - binding.setJdbcType(convertNTypes(binding.getJdbcType())); super.bindParameter(statement, binding); } } - private int convertNTypes(int sqlType) { - switch (sqlType) { + /** + * @since 5.0 + */ + @Override + public int preferredBindingType(int jdbcType) { + switch (jdbcType) { case Types.NCHAR: return Types.CHAR; case Types.NVARCHAR: @@ -216,7 +219,7 @@ public class DerbyAdapter extends JdbcAdapter { return Types.CLOB; default: - return sqlType; + return jdbcType; } } diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/mysql/MySQLAdapter.java b/cayenne/src/main/java/org/apache/cayenne/dba/mysql/MySQLAdapter.java index 71f796caa..907e94e6b 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/mysql/MySQLAdapter.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/mysql/MySQLAdapter.java @@ -227,18 +227,13 @@ public class MySQLAdapter extends JdbcAdapter { } @Override - public void bindParameter(PreparedStatement statement, ParameterBinding binding) throws Exception { - binding.setJdbcType(mapNTypes(binding.getJdbcType())); - super.bindParameter(statement, binding); - } - - private int mapNTypes(int sqlType) { - return switch (sqlType) { + public int preferredBindingType(int jdbcType) { + return switch (jdbcType) { case Types.NCHAR -> Types.CHAR; case Types.NCLOB -> Types.CLOB; case Types.NVARCHAR -> Types.VARCHAR; case Types.LONGNVARCHAR -> Types.LONGVARCHAR; - default -> sqlType; + default -> jdbcType; }; } diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBBatchAction.java b/cayenne/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBBatchAction.java index f19c35124..33159de64 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBBatchAction.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBBatchAction.java @@ -21,7 +21,7 @@ package org.apache.cayenne.dba.oracle; import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.access.OperationObserver; -import org.apache.cayenne.access.translator.DbAttributeBinding; +import org.apache.cayenne.access.translator.ParameterBinding; import org.apache.cayenne.access.types.ExtendedType; import org.apache.cayenne.dba.DbAdapter; import org.apache.cayenne.log.JdbcEventLogger; @@ -53,14 +53,6 @@ class Oracle8LOBBatchAction implements SQLAction { private final DbAdapter adapter; private final JdbcEventLogger logger; - private static void bind(DbAdapter adapter, PreparedStatement statement, DbAttributeBinding[] bindings) throws Exception { - - for (DbAttributeBinding b : bindings) { - DbAttributeBinding binding = new DbAttributeBinding(b.getAttribute()); - adapter.bindParameter(statement, binding); - } - } - Oracle8LOBBatchAction(BatchQuery query, DbAdapter adapter, JdbcEventLogger logger) { this.adapter = adapter; this.query = query; @@ -103,10 +95,14 @@ class Oracle8LOBBatchAction implements SQLAction { try (PreparedStatement statement = connection.prepareStatement(updateStr)) { - DbAttributeBinding[] bindings = translator.updateBindings(row); + ParameterBinding[] bindings = translator.updateBindings(row); logger.logQueryParameters("bind", bindings); - bind(adapter, statement, bindings); + for (ParameterBinding b : bindings) { + if (!b.isExcluded()) { + adapter.bindParameter(statement, b); + } + } updated = statement.executeUpdate(); logger.logUpdateCount(updated); @@ -138,9 +134,9 @@ class Oracle8LOBBatchAction implements SQLAction { String selectStr = queryBuilder.createLOBSelectString(lobAttributes, qualifierAttributes); try (PreparedStatement selectStatement = con.prepareStatement(selectStr)) { - DbAttributeBinding[] attributeBindings = null; + ParameterBinding[] attributeBindings = null; if(isLoggable) { - attributeBindings = new DbAttributeBinding[parametersSize]; + attributeBindings = new ParameterBinding[parametersSize]; } for (int i = 0; i < parametersSize; i++) { DbAttribute attribute = qualifierAttributes.get(i); @@ -149,10 +145,10 @@ class Oracle8LOBBatchAction implements SQLAction { ? adapter.getExtendedTypes().getRegisteredType(value.getClass()) : adapter.getExtendedTypes().getDefaultType(); - DbAttributeBinding binding = new DbAttributeBinding(attribute); - binding.setStatementPosition(i + 1); - binding.setValue(value); - binding.setExtendedType(extendedType); + ParameterBinding binding = new ParameterBinding( + adapter.preferredBindingType(attribute.getType()), attribute.getScale(), attribute + ); + binding.include(i + 1, value, extendedType); adapter.bindParameter(selectStatement, binding); if(isLoggable) { attributeBindings[i] = binding; diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBBatchTranslator.java b/cayenne/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBBatchTranslator.java index eb58e71d4..1e0dd739d 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBBatchTranslator.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBBatchTranslator.java @@ -19,12 +19,8 @@ package org.apache.cayenne.dba.oracle; -import java.sql.Types; -import java.util.Iterator; -import java.util.List; - import org.apache.cayenne.CayenneRuntimeException; -import org.apache.cayenne.access.translator.DbAttributeBinding; +import org.apache.cayenne.access.translator.ParameterBinding; import org.apache.cayenne.access.translator.batch.legacy.DefaultBatchTranslator; import org.apache.cayenne.access.types.ExtendedType; import org.apache.cayenne.dba.DbAdapter; @@ -34,6 +30,10 @@ import org.apache.cayenne.map.DbAttribute; import org.apache.cayenne.query.BatchQuery; import org.apache.cayenne.query.BatchQueryRow; +import java.sql.Types; +import java.util.Iterator; +import java.util.List; + /** * Superclass of query builders for the DML operations involving LOBs. * TODO: update to the new batch translation logic @@ -47,8 +47,6 @@ abstract class Oracle8LOBBatchTranslator extends DefaultBatchTranslator { super(query, adapter, trimFunction); } - abstract List<Object> getValuesForLOBUpdateParameters(BatchQueryRow row); - abstract String createSql(BatchQueryRow row); @Override @@ -111,27 +109,28 @@ abstract class Oracle8LOBBatchTranslator extends DefaultBatchTranslator { } @Override - protected DbAttributeBinding[] createBindings() { + protected ParameterBinding[] createBindings() { List<DbAttribute> dbAttributes = query.getDbAttributes(); int len = dbAttributes.size(); - DbAttributeBinding[] bindings = new DbAttributeBinding[len]; + ParameterBinding[] bindings = new ParameterBinding[len]; for (int i = 0; i < len; i++) { - bindings[i] = new DbAttributeBinding(dbAttributes.get(i)); + DbAttribute attribute = dbAttributes.get(i); + bindings[i] = new ParameterBinding(adapter.preferredBindingType(attribute.getType()), attribute.getScale(), attribute); } return bindings; } @Override - protected DbAttributeBinding[] doUpdateBindings(BatchQueryRow row) { + protected ParameterBinding[] doUpdateBindings(BatchQueryRow row) { int len = bindings.length; for (int i = 0, j = 1; i < len; i++) { - DbAttributeBinding b = bindings[i]; + ParameterBinding b = bindings[i]; Object value = row.getValue(i); DbAttribute attribute = b.getAttribute(); diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBInsertBatchTranslator.java b/cayenne/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBInsertBatchTranslator.java index e3f695577..2c4b27490 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBInsertBatchTranslator.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBInsertBatchTranslator.java @@ -25,7 +25,6 @@ import org.apache.cayenne.map.DbAttribute; import org.apache.cayenne.query.BatchQueryRow; import org.apache.cayenne.query.InsertBatchQuery; -import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -35,23 +34,6 @@ class Oracle8LOBInsertBatchTranslator extends Oracle8LOBBatchTranslator { super(query, adapter, trimFunction); } - @Override - List<Object> getValuesForLOBUpdateParameters(BatchQueryRow row) { - List<DbAttribute> dbAttributes = query.getDbAttributes(); - int len = dbAttributes.size(); - - List<Object> values = new ArrayList<>(len); - for (int i = 0; i < len; i++) { - Object value = row.getValue(i); - DbAttribute attribute = dbAttributes.get(i); - if (isUpdateableColumn(value, attribute.getType())) { - values.add(value); - } - } - - return values; - } - @Override public String createSql(BatchQueryRow row) { List<DbAttribute> dbAttributes = query.getDbAttributes(); diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBUpdateBatchTranslator.java b/cayenne/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBUpdateBatchTranslator.java index 3796c4cac..ffa3f9fef 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBUpdateBatchTranslator.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/oracle/Oracle8LOBUpdateBatchTranslator.java @@ -25,7 +25,6 @@ import org.apache.cayenne.map.DbAttribute; import org.apache.cayenne.query.BatchQueryRow; import org.apache.cayenne.query.UpdateBatchQuery; -import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -35,32 +34,6 @@ class Oracle8LOBUpdateBatchTranslator extends Oracle8LOBBatchTranslator { super(query, adapter, trimFunction); } - @Override - List<Object> getValuesForLOBUpdateParameters(BatchQueryRow row) { - int len = query.getDbAttributes().size(); - UpdateBatchQuery updateBatch = (UpdateBatchQuery) query; - - List<Object> values = new ArrayList<>(len); - List<DbAttribute> qualifierAttributes = updateBatch.getQualifierAttributes(); - List<DbAttribute> updatedDbAttributes = updateBatch.getUpdatedAttributes(); - - int updatedLen = updatedDbAttributes.size(); - int qualifierLen = qualifierAttributes.size(); - for (int i = 0; i < updatedLen; i++) { - DbAttribute attribute = updatedDbAttributes.get(i); - Object value = row.getValue(i); - if (isUpdateableColumn(value, attribute.getType())) { - values.add(value); - } - } - - for (int i = 0; i < qualifierLen; i++) { - values.add(row.getValue(updatedLen + i)); - } - - return values; - } - @Override public String createSql(BatchQueryRow row) { UpdateBatchQuery updateBatch = (UpdateBatchQuery) query; diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/postgres/PostgresAdapter.java b/cayenne/src/main/java/org/apache/cayenne/dba/postgres/PostgresAdapter.java index 9a43ce915..a18f5d68b 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/postgres/PostgresAdapter.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/postgres/PostgresAdapter.java @@ -135,18 +135,13 @@ public class PostgresAdapter extends JdbcAdapter { } @Override - public void bindParameter(PreparedStatement statement, ParameterBinding binding) throws Exception { - binding.setJdbcType(mapNTypes(binding.getJdbcType())); - super.bindParameter(statement, binding); - } - - private int mapNTypes(int sqlType) { - return switch (sqlType) { + public int preferredBindingType(int jdbcType) { + return switch (jdbcType) { case Types.NCHAR -> Types.CHAR; case Types.NCLOB -> Types.CLOB; case Types.NVARCHAR -> Types.VARCHAR; case Types.LONGNVARCHAR -> Types.LONGVARCHAR; - default -> sqlType; + default -> jdbcType; }; } diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerAdapter.java b/cayenne/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerAdapter.java index 876176049..9873b183d 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerAdapter.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerAdapter.java @@ -139,15 +139,12 @@ public class SQLServerAdapter extends JdbcAdapter { // SQL Server driver doesn't like CLOBs and BLOBs as parameters if (binding.getValue() == null) { - if (binding.getJdbcType() == Types.CLOB) { - binding.setJdbcType(Types.VARCHAR); - } else if (binding.getJdbcType() == Types.BLOB) { - binding.setJdbcType(Types.VARBINARY); - } - } - - if (binding.getValue() == null && binding.getJdbcType() == 0) { - statement.setNull(binding.getStatementPosition(), Types.VARCHAR); + int jdbcType = switch (binding.getJdbcType()) { + case Types.CLOB, 0 -> Types.VARCHAR; + case Types.BLOB -> Types.VARBINARY; + default -> binding.getJdbcType(); + }; + statement.setNull(binding.getStatementPosition(), jdbcType); } else { super.bindParameter(statement, binding); } diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/sybase/SybaseAdapter.java b/cayenne/src/main/java/org/apache/cayenne/dba/sybase/SybaseAdapter.java index 459f5e0ad..9ddff72a7 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/sybase/SybaseAdapter.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/sybase/SybaseAdapter.java @@ -19,10 +19,6 @@ package org.apache.cayenne.dba.sybase; -import java.sql.PreparedStatement; -import java.sql.Types; -import java.util.List; - import org.apache.cayenne.access.sqlbuilder.sqltree.SQLTreeProcessor; import org.apache.cayenne.access.translator.ParameterBinding; import org.apache.cayenne.access.translator.ejbql.EJBQLTranslatorFactory; @@ -38,12 +34,15 @@ import org.apache.cayenne.configuration.Constants; import org.apache.cayenne.configuration.RuntimeProperties; import org.apache.cayenne.dba.DefaultQuotingStrategy; import org.apache.cayenne.dba.JdbcAdapter; -import org.apache.cayenne.dba.PkGenerator; import org.apache.cayenne.dba.QuotingStrategy; import org.apache.cayenne.di.Inject; import org.apache.cayenne.map.DbAttribute; import org.apache.cayenne.resource.ResourceLocator; +import java.sql.PreparedStatement; +import java.sql.Types; +import java.util.List; + /** * DbAdapter implementation for <a href="http://www.sybase.com">Sybase RDBMS</a>. */ @@ -116,15 +115,12 @@ public class SybaseAdapter extends JdbcAdapter { // Sybase driver doesn't like CLOBs and BLOBs as parameters if (binding.getValue() == null) { - if (binding.getJdbcType() == Types.CLOB) { - binding.setJdbcType(Types.VARCHAR); - } else if (binding.getJdbcType() == Types.BLOB) { - binding.setJdbcType(Types.VARBINARY); - } - } - - if (binding.getValue() == null && binding.getJdbcType() == 0) { - statement.setNull(binding.getStatementPosition(), Types.VARCHAR); + int jdbcType = switch (binding.getJdbcType()) { + case Types.CLOB, 0 -> Types.VARCHAR; + case Types.BLOB -> Types.VARBINARY; + default -> binding.getJdbcType(); + }; + statement.setNull(binding.getStatementPosition(), jdbcType); } else { super.bindParameter(statement, binding); } diff --git a/cayenne/src/main/java/org/apache/cayenne/log/CompactSlf4jJdbcEventLogger.java b/cayenne/src/main/java/org/apache/cayenne/log/CompactSlf4jJdbcEventLogger.java index 1baaa276c..54bea360e 100644 --- a/cayenne/src/main/java/org/apache/cayenne/log/CompactSlf4jJdbcEventLogger.java +++ b/cayenne/src/main/java/org/apache/cayenne/log/CompactSlf4jJdbcEventLogger.java @@ -26,7 +26,6 @@ import java.util.Map; import java.util.regex.Pattern; import java.util.stream.Collectors; -import org.apache.cayenne.access.translator.DbAttributeBinding; import org.apache.cayenne.access.translator.ParameterBinding; import org.apache.cayenne.configuration.RuntimeProperties; import org.apache.cayenne.di.Inject; @@ -117,11 +116,9 @@ public class CompactSlf4jJdbcEventLogger extends Slf4jJdbcEventLogger { continue; } - if (b instanceof DbAttributeBinding) { - DbAttribute attribute = ((DbAttributeBinding) b).getAttribute(); - if (attribute != null) { - key = attribute.getName(); - } + DbAttribute attribute = b.getAttribute(); + if (attribute != null) { + key = attribute.getName(); } if (b.getExtendedType() != null) { diff --git a/cayenne/src/main/java/org/apache/cayenne/log/Slf4jJdbcEventLogger.java b/cayenne/src/main/java/org/apache/cayenne/log/Slf4jJdbcEventLogger.java index f64f9f645..484eab7c7 100644 --- a/cayenne/src/main/java/org/apache/cayenne/log/Slf4jJdbcEventLogger.java +++ b/cayenne/src/main/java/org/apache/cayenne/log/Slf4jJdbcEventLogger.java @@ -19,7 +19,6 @@ package org.apache.cayenne.log; import org.apache.cayenne.CayenneRuntimeException; -import org.apache.cayenne.access.translator.DbAttributeBinding; import org.apache.cayenne.access.translator.ParameterBinding; import org.apache.cayenne.configuration.Constants; import org.apache.cayenne.configuration.RuntimeProperties; @@ -115,12 +114,10 @@ public class Slf4jJdbcEventLogger implements JdbcEventLogger { buffer.append(j++); - if(b instanceof DbAttributeBinding) { - DbAttribute attribute = ((DbAttributeBinding) b).getAttribute(); - if (attribute != null) { - buffer.append("->"); - buffer.append(attribute.getName()); - } + DbAttribute attribute = b.getAttribute(); + if (attribute != null) { + buffer.append("->"); + buffer.append(attribute.getName()); } buffer.append(":"); diff --git a/cayenne/src/test/java/org/apache/cayenne/access/translator/batch/legacy/DefaultBatchTranslatorIT.java b/cayenne/src/test/java/org/apache/cayenne/access/translator/batch/legacy/DefaultBatchTranslatorIT.java index 1e56b5ff9..3db0aa773 100644 --- a/cayenne/src/test/java/org/apache/cayenne/access/translator/batch/legacy/DefaultBatchTranslatorIT.java +++ b/cayenne/src/test/java/org/apache/cayenne/access/translator/batch/legacy/DefaultBatchTranslatorIT.java @@ -19,7 +19,7 @@ package org.apache.cayenne.access.translator.batch.legacy; -import org.apache.cayenne.access.translator.DbAttributeBinding; +import org.apache.cayenne.access.translator.ParameterBinding; import org.apache.cayenne.dba.DbAdapter; import org.apache.cayenne.dba.JdbcAdapter; import org.apache.cayenne.map.DbAttribute; @@ -52,13 +52,13 @@ public class DefaultBatchTranslatorIT { } @Override - protected DbAttributeBinding[] createBindings() { - return new DbAttributeBinding[0]; + protected ParameterBinding[] createBindings() { + return new ParameterBinding[0]; } @Override - protected DbAttributeBinding[] doUpdateBindings(BatchQueryRow row) { - return new DbAttributeBinding[0]; + protected ParameterBinding[] doUpdateBindings(BatchQueryRow row) { + return new ParameterBinding[0]; } }; @@ -78,13 +78,13 @@ public class DefaultBatchTranslatorIT { } @Override - protected DbAttributeBinding[] createBindings() { - return new DbAttributeBinding[0]; + protected ParameterBinding[] createBindings() { + return new ParameterBinding[0]; } @Override - protected DbAttributeBinding[] doUpdateBindings(BatchQueryRow row) { - return new DbAttributeBinding[0]; + protected ParameterBinding[] doUpdateBindings(BatchQueryRow row) { + return new ParameterBinding[0]; } }; @@ -113,13 +113,13 @@ public class DefaultBatchTranslatorIT { } @Override - protected DbAttributeBinding[] createBindings() { - return new DbAttributeBinding[0]; + protected ParameterBinding[] createBindings() { + return new ParameterBinding[0]; } @Override - protected DbAttributeBinding[] doUpdateBindings(BatchQueryRow row) { - return new DbAttributeBinding[0]; + protected ParameterBinding[] doUpdateBindings(BatchQueryRow row) { + return new ParameterBinding[0]; } }; diff --git a/cayenne/src/test/java/org/apache/cayenne/access/translator/sqltemplate/DefaultSQLTemplateTranslatorTest.java b/cayenne/src/test/java/org/apache/cayenne/access/translator/sqltemplate/DefaultSQLTemplateTranslatorTest.java index d3f866d81..79e27c3e2 100644 --- a/cayenne/src/test/java/org/apache/cayenne/access/translator/sqltemplate/DefaultSQLTemplateTranslatorTest.java +++ b/cayenne/src/test/java/org/apache/cayenne/access/translator/sqltemplate/DefaultSQLTemplateTranslatorTest.java @@ -29,25 +29,34 @@ import org.apache.cayenne.GenericPersistentObject; import org.apache.cayenne.ObjectId; import org.apache.cayenne.Persistent; import org.apache.cayenne.access.translator.ParameterBinding; +import org.apache.cayenne.access.types.ExtendedTypeMap; +import org.apache.cayenne.dba.DbAdapter; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class DefaultSQLTemplateTranslatorTest { private DefaultSQLTemplateTranslator processor; + private DbAdapter adapter; @BeforeEach public void before() { processor = new DefaultSQLTemplateTranslator(new DefaultTemplateContextFactory()); + adapter = mock(DbAdapter.class); + when(adapter.preferredBindingType(anyInt())).thenAnswer(i -> i.getArgument(0)); + when(adapter.getExtendedTypes()).thenReturn(new ExtendedTypeMap()); } @Test public void processTemplateUnchanged1() throws Exception { String sqlTemplate = "SELECT * FROM ME"; - TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.emptyMap()); + TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.emptyMap(), adapter); assertEquals(sqlTemplate, compiled.sql()); assertEquals(0, compiled.bindings().length); @@ -57,7 +66,7 @@ public class DefaultSQLTemplateTranslatorTest { public void processTemplateUnchanged2() throws Exception { String sqlTemplate = "SELECT a.b as XYZ FROM $SYSTEM_TABLE"; - TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.emptyMap()); + TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.emptyMap(), adapter); assertEquals(sqlTemplate, compiled.sql()); assertEquals(0, compiled.bindings().length); @@ -68,7 +77,7 @@ public class DefaultSQLTemplateTranslatorTest { String sqlTemplate = "SELECT * FROM ME WHERE $a"; Map<String, Object> map = Collections.singletonMap("a", "VALUE_OF_A"); - TranslatedSQL compiled = processor.translate(sqlTemplate, map); + TranslatedSQL compiled = processor.translate(sqlTemplate, map, adapter); assertEquals("SELECT * FROM ME WHERE VALUE_OF_A", compiled.sql()); @@ -81,7 +90,7 @@ public class DefaultSQLTemplateTranslatorTest { String sqlTemplate = "SELECT * FROM ME WHERE " + "COLUMN1 = #bind($a 'VARCHAR') AND COLUMN2 = #bind($b 'INTEGER')"; Map<String, Object> map = Collections.singletonMap("a", "VALUE_OF_A"); - TranslatedSQL compiled = processor.translate(sqlTemplate, map); + TranslatedSQL compiled = processor.translate(sqlTemplate, map, adapter); assertEquals("SELECT * FROM ME WHERE COLUMN1 = ? AND COLUMN2 = ?", compiled.sql()); assertEquals(2, compiled.bindings().length); @@ -94,7 +103,7 @@ public class DefaultSQLTemplateTranslatorTest { String sqlTemplate = "SELECT * FROM ME WHERE COLUMN1 = #bind($a)"; Map<String, Object> map = Collections.singletonMap("a", "VALUE_OF_A"); - TranslatedSQL compiled = processor.translate(sqlTemplate, map); + TranslatedSQL compiled = processor.translate(sqlTemplate, map, adapter); assertEquals(1, compiled.bindings().length); assertBindingType(Types.VARCHAR, compiled.bindings()[0]); @@ -105,7 +114,7 @@ public class DefaultSQLTemplateTranslatorTest { String sqlTemplate = "SELECT * FROM ME WHERE COLUMN1 = #bind($a)"; Map<String, Object> map = Collections.singletonMap("a", 4); - TranslatedSQL compiled = processor.translate(sqlTemplate, map); + TranslatedSQL compiled = processor.translate(sqlTemplate, map, adapter); assertEquals(1, compiled.bindings().length); assertBindingType(Types.INTEGER, compiled.bindings()[0]); @@ -115,14 +124,14 @@ public class DefaultSQLTemplateTranslatorTest { public void processTemplateBindEqual() throws Exception { String sqlTemplate = "SELECT * FROM ME WHERE COLUMN #bindEqual($a 'VARCHAR')"; - TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.emptyMap()); + TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.emptyMap(), adapter); assertEquals("SELECT * FROM ME WHERE COLUMN IS NULL", compiled.sql()); assertEquals(0, compiled.bindings().length); Map<String, Object> map = Collections.singletonMap("a", "VALUE_OF_A"); - compiled = processor.translate(sqlTemplate, map); + compiled = processor.translate(sqlTemplate, map, adapter); assertEquals("SELECT * FROM ME WHERE COLUMN = ?", compiled.sql()); assertEquals(1, compiled.bindings().length); @@ -133,14 +142,14 @@ public class DefaultSQLTemplateTranslatorTest { public void processTemplateBindNotEqual() throws Exception { String sqlTemplate = "SELECT * FROM ME WHERE COLUMN #bindNotEqual($a 'VARCHAR')"; - TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.emptyMap()); + TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.emptyMap(), adapter); assertEquals("SELECT * FROM ME WHERE COLUMN IS NOT NULL", compiled.sql()); assertEquals(0, compiled.bindings().length); Map<String, Object> map = Collections.singletonMap("a", "VALUE_OF_A"); - compiled = processor.translate(sqlTemplate, map); + compiled = processor.translate(sqlTemplate, map, adapter); assertEquals("SELECT * FROM ME WHERE COLUMN <> ?", compiled.sql()); assertEquals(1, compiled.bindings().length); @@ -156,7 +165,7 @@ public class DefaultSQLTemplateTranslatorTest { Map<String, Object> map = Collections.singletonMap("a", persistent); - TranslatedSQL compiled = processor.translate(sqlTemplate, map); + TranslatedSQL compiled = processor.translate(sqlTemplate, map, adapter); assertEquals("SELECT * FROM ME WHERE COLUMN1 = ?", compiled.sql()); assertEquals(1, compiled.bindings().length); @@ -178,7 +187,7 @@ public class DefaultSQLTemplateTranslatorTest { Map<String, Object> map = Collections.singletonMap("a", persistent); - TranslatedSQL compiled = processor.translate(sqlTemplate, map); + TranslatedSQL compiled = processor.translate(sqlTemplate, map, adapter); assertEquals("SELECT * FROM ME WHERE COLUMN1 <> ? AND COLUMN2 <> ?", compiled.sql()); assertEquals(2, compiled.bindings().length); @@ -192,13 +201,13 @@ public class DefaultSQLTemplateTranslatorTest { Map<String, Object> map = Collections.singletonMap("a", "VALUE_OF_A"); - TranslatedSQL compiled = processor.translate(sqlTemplate, map); + TranslatedSQL compiled = processor.translate(sqlTemplate, map, adapter); assertEquals("SELECT * FROM ME WHERE COLUMN1 > ?", compiled.sql()); assertEquals(1, compiled.bindings().length); assertBindingValue("VALUE_OF_A", compiled.bindings()[0]); - compiled = processor.translate(sqlTemplate, Collections.emptyMap()); + compiled = processor.translate(sqlTemplate, Collections.emptyMap(), adapter); assertEquals("SELECT * FROM ME ", compiled.sql()); assertEquals(0, compiled.bindings().length); @@ -209,7 +218,7 @@ public class DefaultSQLTemplateTranslatorTest { String sqlTemplate = "SELECT * FROM ME WHERE COLUMN IN (#bind($list 'VARCHAR'))"; Map<String, Object> map = Collections.singletonMap("list", Arrays.asList("a", "b", "c")); - TranslatedSQL compiled = processor.translate(sqlTemplate, map); + TranslatedSQL compiled = processor.translate(sqlTemplate, map, adapter); assertEquals("SELECT * FROM ME WHERE COLUMN IN (?,?,?)", compiled.sql()); assertEquals(3, compiled.bindings().length); @@ -221,7 +230,7 @@ public class DefaultSQLTemplateTranslatorTest { @Test public void unknownDirective() throws Exception { String sqlTemplate = "SELECT #from(1) FROM a"; - TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.emptyMap()); + TranslatedSQL compiled = processor.translate(sqlTemplate, Collections.emptyMap(), adapter); assertEquals("SELECT FROM a", compiled.sql()); } diff --git a/cayenne/src/test/java/org/apache/cayenne/access/translator/sqltemplate/parser/SQLTemplateParserTest.java b/cayenne/src/test/java/org/apache/cayenne/access/translator/sqltemplate/parser/SQLTemplateParserTest.java index 55d4dba65..8121d768f 100644 --- a/cayenne/src/test/java/org/apache/cayenne/access/translator/sqltemplate/parser/SQLTemplateParserTest.java +++ b/cayenne/src/test/java/org/apache/cayenne/access/translator/sqltemplate/parser/SQLTemplateParserTest.java @@ -27,24 +27,33 @@ import java.util.Map; import org.apache.cayenne.access.translator.sqltemplate.Context; import org.apache.cayenne.access.translator.sqltemplate.DefaultTemplateContextFactory; import org.apache.cayenne.access.translator.sqltemplate.TemplateContextFactory; +import org.apache.cayenne.access.types.ExtendedTypeMap; +import org.apache.cayenne.dba.DbAdapter; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class SQLTemplateParserTest { private TemplateContextFactory contextFactory; + private DbAdapter adapter; @BeforeEach public void setUp() { contextFactory = new DefaultTemplateContextFactory(); + adapter = mock(DbAdapter.class); + when(adapter.preferredBindingType(anyInt())).thenAnswer(i -> i.getArgument(0)); + when(adapter.getExtendedTypes()).thenReturn(new ExtendedTypeMap()); } @Test public void unchangedParse() throws Exception { - Context context = contextFactory.createContext(Collections.emptyMap()); + Context context = contextFactory.createContext(Collections.emptyMap(), adapter); String template = "SELECT * FROM a"; String sql = parseString(template, context); @@ -53,7 +62,7 @@ public class SQLTemplateParserTest { @Test public void parameterParse() throws Exception { - Context context = contextFactory.createContext(Collections.singletonMap("a", true)); + Context context = contextFactory.createContext(Collections.singletonMap("a", true), adapter); String template = "SELECT $a FROM a"; String sql = parseString(template, context); @@ -62,13 +71,13 @@ public class SQLTemplateParserTest { @Test public void ifElseParse() throws Exception { - Context context = contextFactory.createContext(Collections.singletonMap("a", true)); + Context context = contextFactory.createContext(Collections.singletonMap("a", true), adapter); String template = "SELECT #if($a) * #else 1 #end FROM a"; String sql = parseString(template, context); assertEquals("SELECT * FROM a", sql); - context = contextFactory.createContext(Collections.singletonMap("a", false)); + context = contextFactory.createContext(Collections.singletonMap("a", false), adapter); template = "SELECT #if($a) * #else 1 #end FROM a"; sql = parseString(template, context); @@ -81,7 +90,7 @@ public class SQLTemplateParserTest { parameters.put("a", "var"); parameters.put("b", "bbb"); - Context context = contextFactory.createContext(parameters); + Context context = contextFactory.createContext(parameters, adapter); String template = "SELECT #if($a) #bind($a, 'INT' ,2) #else #bind($b, 'CHAR' ,2) #end FROM a"; String sql = parseString(template, context); @@ -109,7 +118,7 @@ public class SQLTemplateParserTest { " \tAND \n" + "COLUMN2 #bind($myHelper.cayenneExp($a, 'db:ID_COLUMN2'), 'VARCHAR')\n" + "#end\n"; - Context context = contextFactory.createContext(parameters); + Context context = contextFactory.createContext(parameters, adapter); String sql = parseString(template, context); assertEquals("SELECT * \n" + @@ -130,7 +139,7 @@ public class SQLTemplateParserTest { "#result('t0.ID' 'java.lang.Integer' 'ec0_1' 'ec0_1' 4) " + "FROM BIGDECIMAL_ENTITY t0 WHERE {fn ABS( t0.BIGDECIMAL_FIELD)} < #bind($id0 'DECIMAL')"; - Context context = contextFactory.createContext(Collections.singletonMap("id0", 123)); + Context context = contextFactory.createContext(Collections.singletonMap("id0", 123), adapter); String sql = parseString(tpl, context); assertEquals("SELECT " + @@ -152,7 +161,7 @@ public class SQLTemplateParserTest { "FROM ARTIST t0 " + "LEFT OUTER JOIN PAINTING t1 ON (t0.ARTIST_ID = t1.ARTIST_ID) " + "GROUP BY t0.ARTIST_NAME, t0.DATE_OF_BIRTH, t0.ARTIST_ID ORDER BY t0.ARTIST_NAME"; - Context context = contextFactory.createContext(Collections.emptyMap()); + Context context = contextFactory.createContext(Collections.emptyMap(), adapter); String sql = parseString(tpl, context); assertEquals(5, context.getColumnDescriptors().length); assertEquals("SELECT COUNT(*) AS sc0, t0.ARTIST_NAME AS ec1_0, " + @@ -165,7 +174,7 @@ public class SQLTemplateParserTest { @Test public void helperObject() throws Exception { String tpl = "($helper.cayenneExp($a, 'field'))"; - Context context = contextFactory.createContext(Collections.singletonMap("a", new TestBean(5))); + Context context = contextFactory.createContext(Collections.singletonMap("a", new TestBean(5)), adapter); String sql = parseString(tpl, context); assertEquals("(5)", sql); @@ -174,7 +183,7 @@ public class SQLTemplateParserTest { @Test public void methodCallArray() throws Exception { String tpl = "$a.arrayMethod(['1' '2' '3'])"; - Context context = contextFactory.createContext(Collections.singletonMap("a", new TestBean(5))); + Context context = contextFactory.createContext(Collections.singletonMap("a", new TestBean(5)), adapter); String sql = parseString(tpl, context); assertEquals("array_3", sql); @@ -183,7 +192,7 @@ public class SQLTemplateParserTest { @Test public void methodCallArray2() throws Exception { String tpl = "$a.arrayMethod(['1', '2', '3'])"; - Context context = contextFactory.createContext(Collections.singletonMap("a", new TestBean(5))); + Context context = contextFactory.createContext(Collections.singletonMap("a", new TestBean(5)), adapter); String sql = parseString(tpl, context); assertEquals("array_3", sql); @@ -192,7 +201,7 @@ public class SQLTemplateParserTest { @Test public void methodCallInt() throws Exception { String tpl = "$a.intMethod(42)"; - Context context = contextFactory.createContext(Collections.singletonMap("a", new TestBean(5))); + Context context = contextFactory.createContext(Collections.singletonMap("a", new TestBean(5)), adapter); String sql = parseString(tpl, context); assertEquals("int_42", sql); @@ -201,7 +210,7 @@ public class SQLTemplateParserTest { @Test public void methodCallString() throws Exception { String tpl = "$a.stringMethod(\"abc\")"; - Context context = contextFactory.createContext(Collections.singletonMap("a", new TestBean(5))); + Context context = contextFactory.createContext(Collections.singletonMap("a", new TestBean(5)), adapter); String sql = parseString(tpl, context); assertEquals("string_abc", sql); @@ -210,7 +219,7 @@ public class SQLTemplateParserTest { @Test public void methodCallFloat() throws Exception { String tpl = "$a.floatMethod(3.14)"; - Context context = contextFactory.createContext(Collections.singletonMap("a", new TestBean(5))); + Context context = contextFactory.createContext(Collections.singletonMap("a", new TestBean(5)), adapter); String sql = parseString(tpl, context); assertEquals("float_3.14", sql); @@ -220,7 +229,7 @@ public class SQLTemplateParserTest { @Disabled("Method overload not properly supported, this test can return m2_true") public void methodCallSelectByArgType1() throws Exception { String tpl = "$a.method(123)"; - Context context = contextFactory.createContext(Collections.singletonMap("a", new TestBean(5))); + Context context = contextFactory.createContext(Collections.singletonMap("a", new TestBean(5)), adapter); String sql = parseString(tpl, context); assertEquals("m1_123", sql); @@ -229,7 +238,7 @@ public class SQLTemplateParserTest { @Test public void methodCallSelectByArgType2() throws Exception { String tpl = "$a.method(true)"; - Context context = contextFactory.createContext(Collections.singletonMap("a", new TestBean(5))); + Context context = contextFactory.createContext(Collections.singletonMap("a", new TestBean(5)), adapter); String sql = parseString(tpl, context); assertEquals("m2_true", sql); @@ -238,7 +247,7 @@ public class SQLTemplateParserTest { @Test public void propertyAccess() throws Exception { String tpl = "$a.field()"; - Context context = contextFactory.createContext(Collections.singletonMap("a", new TestBean(5))); + Context context = contextFactory.createContext(Collections.singletonMap("a", new TestBean(5)), adapter); String sql = parseString(tpl, context); assertEquals("5", sql); @@ -247,7 +256,7 @@ public class SQLTemplateParserTest { @Test public void nestedBrackets() throws Exception { String tpl = "(#bind('A' 'b'))"; - String sql = parseString(tpl, contextFactory.createContext(Collections.emptyMap())); + String sql = parseString(tpl, contextFactory.createContext(Collections.emptyMap(), adapter)); assertEquals("(?)", sql); } @@ -256,11 +265,11 @@ public class SQLTemplateParserTest { String template = "\"$a\""; Map<String, Object> parameters = new HashMap<>(); parameters.put("a", "val"); - Context context = contextFactory.createContext(parameters); + Context context = contextFactory.createContext(parameters, adapter); String sql = parseString(template, context); assertEquals("\"val\"", sql); - context = contextFactory.createContext(parameters); + context = contextFactory.createContext(parameters, adapter); template = "'$a'"; sql = parseString(template, context); assertEquals("'val'", sql); @@ -271,7 +280,7 @@ public class SQLTemplateParserTest { String template = "$a,$a"; Map<String, Object> parameters = new HashMap<>(); parameters.put("a", "val"); - Context context = contextFactory.createContext(parameters); + Context context = contextFactory.createContext(parameters, adapter); String sql = parseString(template, context); assertEquals("val,val", sql); } diff --git a/cayenne/src/test/java/org/apache/cayenne/log/CompactSlf4jJdbcEventLoggerTest.java b/cayenne/src/test/java/org/apache/cayenne/log/CompactSlf4jJdbcEventLoggerTest.java index 32c1f7799..57a58a058 100644 --- a/cayenne/src/test/java/org/apache/cayenne/log/CompactSlf4jJdbcEventLoggerTest.java +++ b/cayenne/src/test/java/org/apache/cayenne/log/CompactSlf4jJdbcEventLoggerTest.java @@ -18,7 +18,7 @@ ****************************************************************/ package org.apache.cayenne.log; -import org.apache.cayenne.access.translator.DbAttributeBinding; +import org.apache.cayenne.access.translator.ParameterBinding; import org.apache.cayenne.access.types.BooleanType; import org.apache.cayenne.access.types.CharType; import org.apache.cayenne.access.types.ExtendedType; @@ -98,7 +98,7 @@ public class CompactSlf4jJdbcEventLoggerTest { @Test public void compactBindings() { StringBuilder buffer = new StringBuilder(); - DbAttributeBinding[] bindings = new DbAttributeBinding[] { + ParameterBinding[] bindings = new ParameterBinding[] { createBinding("t0.NAME", 1, "", new CharType(false, false)), createBinding("t0.NAME", 2, 52, new IntegerType()), createBinding("t0.NAME", 3, true, new BooleanType()), @@ -109,11 +109,10 @@ public class CompactSlf4jJdbcEventLoggerTest { assertEquals("[bind: 1->t0.NAME: {'', 52, 'true'}, 2->t0.F_KEY1: 'true']", buffer.toString()); } - private DbAttributeBinding createBinding(String name, int position, Object object, ExtendedType type){ - DbAttributeBinding dbAttributeBinding = new DbAttributeBinding(new DbAttribute(name)); - dbAttributeBinding.setValue(object); - dbAttributeBinding.setStatementPosition(position); - dbAttributeBinding.setExtendedType(type); - return dbAttributeBinding; + private ParameterBinding createBinding(String name, int position, Object object, ExtendedType type){ + DbAttribute attribute = new DbAttribute(name); + ParameterBinding binding = new ParameterBinding(attribute.getType(), -1, attribute); + binding.include(position, object, type); + return binding; } }
