This is an automated email from the ASF dual-hosted git repository. asf-gitbox-commits pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/cayenne.git
commit 178cd4b1e79cacf8cc5f9e007eab2075f228576e Author: Andrus Adamchik <[email protected]> AuthorDate: Fri Jul 3 16:55:05 2026 -0400 Introducing CSParameter for translated stored procedures --- .../CSParameter.java} | 17 +++-- .../cayenne/access/jdbc/ProcedureAction.java | 12 +-- .../procedure/DefaultProcedureTranslator.java | 19 ++--- .../translator/procedure/TranslatedProcedure.java | 5 +- .../java/org/apache/cayenne/dba/AutoAdapter.java | 7 ++ .../java/org/apache/cayenne/dba/DbAdapter.java | 9 +++ .../java/org/apache/cayenne/dba/JdbcAdapter.java | 32 ++++++-- .../org/apache/cayenne/dba/db2/DB2Adapter.java | 11 +-- .../apache/cayenne/dba/db2/DB2ProcedureAction.java | 2 +- .../org/apache/cayenne/dba/derby/DerbyAdapter.java | 12 +-- .../apache/cayenne/dba/ingres/IngresAdapter.java | 12 +-- .../cayenne/dba/mysql/MySQLProcedureAction.java | 2 +- .../apache/cayenne/dba/oracle/OracleAdapter.java | 21 ++---- .../cayenne/dba/sqlserver/SQLServerAdapter.java | 15 ++-- .../dba/sqlserver/SQLServerProcedureAction.java | 2 +- .../apache/cayenne/dba/sybase/SybaseAdapter.java | 15 ++-- .../org/apache/cayenne/log/JdbcEventLogger.java | 6 ++ .../apache/cayenne/log/NoopJdbcEventLogger.java | 5 ++ .../apache/cayenne/log/Slf4jJdbcEventLogger.java | 87 +++++++++++++++------- 19 files changed, 188 insertions(+), 103 deletions(-) diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/procedure/TranslatedProcedure.java b/cayenne/src/main/java/org/apache/cayenne/access/jdbc/CSParameter.java similarity index 70% copy from cayenne/src/main/java/org/apache/cayenne/access/translator/procedure/TranslatedProcedure.java copy to cayenne/src/main/java/org/apache/cayenne/access/jdbc/CSParameter.java index 66b08651f..56994423b 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/procedure/TranslatedProcedure.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/jdbc/CSParameter.java @@ -16,16 +16,23 @@ * specific language governing permissions and limitations * under the License. ****************************************************************/ +package org.apache.cayenne.access.jdbc; -package org.apache.cayenne.access.translator.procedure; - -import org.apache.cayenne.access.jdbc.PSParameter; +import org.apache.cayenne.access.types.ExtendedType; import org.apache.cayenne.map.ProcedureParameter; /** - * An immutable result of translating a {@link org.apache.cayenne.query.ProcedureQuery} to a stored procedure call. + * Describes a single immutable CallableStatement parameter binding, pairing a bound value with the + * {@link ProcedureParameter} that describes it. * * @since 5.0 */ -public record TranslatedProcedure(String sql, PSParameter<?>[] bindings, ProcedureParameter[] callParams) { +public record CSParameter<T>( + T value, + int psPosition, + int psType, + int psScale, + ExtendedType<T> binder, + ProcedureParameter param +) { } diff --git a/cayenne/src/main/java/org/apache/cayenne/access/jdbc/ProcedureAction.java b/cayenne/src/main/java/org/apache/cayenne/access/jdbc/ProcedureAction.java index 6befd015d..22ad8c1e5 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/jdbc/ProcedureAction.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/jdbc/ProcedureAction.java @@ -73,7 +73,7 @@ public class ProcedureAction extends BaseSQLAction { TranslatedProcedure translated = dataNode.getProcedureTranslator() .translate(query, dataNode.getAdapter(), dataNode.getEntityResolver()); - dataNode.getJdbcEventLogger().logQuery(translated.sql(), translated.bindings()); + dataNode.getJdbcEventLogger().logQuery(translated.sql(), translated.params()); try (CallableStatement statement = connection.prepareCall(translated.sql())) { initStatement(statement); @@ -129,11 +129,11 @@ public class ProcedureAction extends BaseSQLAction { */ protected void bindParameters(CallableStatement statement, TranslatedProcedure translated) throws Exception { DbAdapter adapter = dataNode.getAdapter(); - ProcedureParameter[] callParams = translated.callParams(); - PSParameter<?>[] bindings = translated.bindings(); + CSParameter<?>[] params = translated.params(); - for (int i = 0; i < callParams.length; i++) { - ProcedureParameter param = callParams[i]; + for (int i = 0; i < params.length; i++) { + CSParameter<?> binding = params[i]; + ProcedureParameter param = binding.param(); if (param.isOutParam()) { int precision = param.getPrecision(); @@ -145,7 +145,7 @@ public class ProcedureAction extends BaseSQLAction { } if (param.isInParameter()) { - adapter.bindParameter(statement, bindings[i]); + adapter.bindParameter(statement, binding); } } } diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/procedure/DefaultProcedureTranslator.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/procedure/DefaultProcedureTranslator.java index 1294ca38b..86743164f 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/procedure/DefaultProcedureTranslator.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/procedure/DefaultProcedureTranslator.java @@ -19,7 +19,7 @@ package org.apache.cayenne.access.translator.procedure; -import org.apache.cayenne.access.jdbc.PSParameter; +import org.apache.cayenne.access.jdbc.CSParameter; import org.apache.cayenne.access.types.ExtendedType; import org.apache.cayenne.dba.DbAdapter; import org.apache.cayenne.map.EntityResolver; @@ -63,25 +63,25 @@ public class DefaultProcedureTranslator implements ProcedureTranslator { ProcedureParameter[] callParams = procedure.getCallParameters().toArray(new ProcedureParameter[0]); Map<String, ?> queryValues = query.getParameters(); - PSParameter[] bindings = new PSParameter[callParams.length]; + CSParameter<?>[] params = new CSParameter[callParams.length]; for (int i = 0; i < callParams.length; i++) { - bindings[i] = createBinding(adapter, callParams[i], queryValues, i + 1); + params[i] = createBinding(adapter, callParams[i], queryValues, i + 1); } String sql = createSqlString(procedure, callParams.length); - return new TranslatedProcedure(sql, bindings, callParams); + return new TranslatedProcedure(sql, params); } /** - * Builds a {@link PSParameter} for a single call parameter. IN (and INOUT) parameters carry the actual value + * Builds a {@link CSParameter} for a single call parameter. IN (and INOUT) parameters carry the actual value * and its {@link ExtendedType}; pure OUT parameters carry an "[OUT]" marker value so that logging renders nicely. */ - protected PSParameter<?> createBinding(DbAdapter adapter, ProcedureParameter param, + protected CSParameter<?> createBinding(DbAdapter adapter, ProcedureParameter param, Map<String, ?> queryValues, int position) { // match values with parameters in the correct order, assuming a missing value is NULL if (param.getDirection() == ProcedureParameter.OUT_PARAMETER) { - return new PSParameter<>(OUT_PARAM, position, param.getType(), param.getPrecision(), null, null); + return new CSParameter<>(OUT_PARAM, position, param.getType(), param.getPrecision(), null, param); } Object value = queryValues.get(param.getName()); @@ -89,8 +89,9 @@ public class DefaultProcedureTranslator implements ProcedureTranslator { ? adapter.getExtendedTypes().getRegisteredType(value.getClass()) : adapter.getExtendedTypes().getDefaultType(); - return new PSParameter<>( - value, position, adapter.preferredBindingType(param.getType()), param.getPrecision(), extendedType, null); + return new CSParameter<>( + value, position, adapter.preferredBindingType(param.getType()), param.getPrecision(), extendedType, + param); } /** diff --git a/cayenne/src/main/java/org/apache/cayenne/access/translator/procedure/TranslatedProcedure.java b/cayenne/src/main/java/org/apache/cayenne/access/translator/procedure/TranslatedProcedure.java index 66b08651f..1aca953c2 100644 --- a/cayenne/src/main/java/org/apache/cayenne/access/translator/procedure/TranslatedProcedure.java +++ b/cayenne/src/main/java/org/apache/cayenne/access/translator/procedure/TranslatedProcedure.java @@ -19,13 +19,12 @@ package org.apache.cayenne.access.translator.procedure; -import org.apache.cayenne.access.jdbc.PSParameter; -import org.apache.cayenne.map.ProcedureParameter; +import org.apache.cayenne.access.jdbc.CSParameter; /** * An immutable result of translating a {@link org.apache.cayenne.query.ProcedureQuery} to a stored procedure call. * * @since 5.0 */ -public record TranslatedProcedure(String sql, PSParameter<?>[] bindings, ProcedureParameter[] callParams) { +public record TranslatedProcedure(String sql, CSParameter<?>[] params) { } 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 cf18fbc03..1c32d5c8b 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/AutoAdapter.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/AutoAdapter.java @@ -22,6 +22,7 @@ package org.apache.cayenne.dba; import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.access.DataNode; import org.apache.cayenne.access.sqlbuilder.sqltree.SQLTreeProcessor; +import org.apache.cayenne.access.jdbc.CSParameter; import org.apache.cayenne.access.jdbc.PSParameter; import org.apache.cayenne.access.translator.ejbql.EJBQLTranslator; import org.apache.cayenne.access.translator.procedure.ProcedureTranslator; @@ -38,6 +39,7 @@ import org.apache.cayenne.query.Select; import org.apache.cayenne.query.Query; import org.apache.cayenne.query.SQLAction; +import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.util.Collection; import java.util.List; @@ -243,6 +245,11 @@ public class AutoAdapter implements DbAdapter { getAdapter().bindParameter(statement, parameter); } + @Override + public void bindParameter(CallableStatement statement, CSParameter<?> parameter) throws Exception { + getAdapter().bindParameter(statement, parameter); + } + /** * @since 5.0 */ 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 de0c00f4b..e81f4ed08 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/DbAdapter.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/DbAdapter.java @@ -21,6 +21,7 @@ package org.apache.cayenne.dba; import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.access.DataNode; import org.apache.cayenne.access.sqlbuilder.sqltree.SQLTreeProcessor; +import org.apache.cayenne.access.jdbc.CSParameter; import org.apache.cayenne.access.jdbc.PSParameter; import org.apache.cayenne.access.translator.ejbql.EJBQLTranslator; import org.apache.cayenne.access.translator.procedure.ProcedureTranslator; @@ -35,6 +36,7 @@ import org.apache.cayenne.query.Query; import org.apache.cayenne.query.SQLAction; import org.apache.cayenne.query.Select; +import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.util.Collection; import java.util.List; @@ -253,6 +255,13 @@ public interface DbAdapter { */ void bindParameter(PreparedStatement statement, PSParameter<?> parameter) throws Exception; + /** + * Binds an object value to CallableStatement's parameter. + * + * @since 5.0 + */ + void bindParameter(CallableStatement statement, CSParameter<?> parameter) 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 diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/JdbcAdapter.java b/cayenne/src/main/java/org/apache/cayenne/dba/JdbcAdapter.java index f62bfb7f9..cb55bb985 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/JdbcAdapter.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/JdbcAdapter.java @@ -22,6 +22,7 @@ package org.apache.cayenne.dba; import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.access.DataNode; import org.apache.cayenne.access.sqlbuilder.sqltree.SQLTreeProcessor; +import org.apache.cayenne.access.jdbc.CSParameter; import org.apache.cayenne.access.jdbc.PSParameter; import org.apache.cayenne.access.translator.ejbql.EJBQLTranslator; import org.apache.cayenne.access.translator.ejbql.JdbcEJBQLTranslator; @@ -49,6 +50,7 @@ import org.apache.cayenne.query.Select; import org.apache.cayenne.query.Query; import org.apache.cayenne.query.SQLAction; +import java.sql.CallableStatement; import java.sql.PreparedStatement; import java.sql.Types; import java.util.ArrayList; @@ -623,16 +625,30 @@ public class JdbcAdapter implements DbAdapter { @Override public void bindParameter(PreparedStatement statement, PSParameter<?> parameter) throws Exception { + bind(statement, parameter.value(), parameter.psPosition(), parameter.psType(), parameter.psScale(), + parameter.binder()); + } + + @Override + public void bindParameter(CallableStatement statement, CSParameter<?> parameter) throws Exception { + bind(statement, parameter.value(), parameter.psPosition(), parameter.psType(), parameter.psScale(), + parameter.binder()); + } - if (parameter.value() == null) { - statement.setNull(parameter.psPosition(), parameter.psType()); + /** + * Binds a raw value to a statement parameter. This is the single extension point shared by both + * {@code bindParameter} overloads; adapters that need database-specific handling (e.g. of NULLs) should + * override this method rather than the public overloads. + * + * @since 5.0 + */ + @SuppressWarnings({"rawtypes", "unchecked"}) + protected void bind(PreparedStatement statement, Object value, int psPosition, int psType, int psScale, + ExtendedType binder) throws Exception { + if (value == null) { + statement.setNull(psPosition, psType); } else { - ExtendedType t = parameter.binder(); - t.setJdbcObject(statement, - parameter.value(), - parameter.psPosition(), - parameter.psType(), - parameter.psScale()); + binder.setJdbcObject(statement, value, psPosition, psType, psScale); } } 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 99ca2b389..8907a52af 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 @@ -22,7 +22,6 @@ package org.apache.cayenne.dba.db2; import org.apache.cayenne.dba.NativeColumnType; import org.apache.cayenne.access.DataNode; import org.apache.cayenne.access.sqlbuilder.sqltree.SQLTreeProcessor; -import org.apache.cayenne.access.jdbc.PSParameter; import org.apache.cayenne.access.translator.ejbql.EJBQLTranslator; import org.apache.cayenne.access.translator.ejbql.JdbcEJBQLTranslator; import org.apache.cayenne.access.types.BooleanType; @@ -190,11 +189,13 @@ public class DB2Adapter extends JdbcAdapter { } @Override - public void bindParameter(PreparedStatement statement, PSParameter<?> parameter) throws Exception { - if (parameter.value() == null && (parameter.psType() == 0 || parameter.psType() == Types.BOOLEAN)) { - statement.setNull(parameter.psPosition(), Types.VARCHAR); + @SuppressWarnings("rawtypes") + protected void bind(PreparedStatement statement, Object value, int psPosition, int psType, int psScale, + ExtendedType binder) throws Exception { + if (value == null && (psType == 0 || psType == Types.BOOLEAN)) { + statement.setNull(psPosition, Types.VARCHAR); } else { - super.bindParameter(statement, parameter); + super.bind(statement, value, psPosition, psType, psScale, binder); } } diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/db2/DB2ProcedureAction.java b/cayenne/src/main/java/org/apache/cayenne/dba/db2/DB2ProcedureAction.java index cb26979cc..4792dc12f 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/db2/DB2ProcedureAction.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/db2/DB2ProcedureAction.java @@ -49,7 +49,7 @@ class DB2ProcedureAction extends ProcedureAction { TranslatedProcedure translated = dataNode.getProcedureTranslator() .translate(query, dataNode.getAdapter(), dataNode.getEntityResolver()); - dataNode.getJdbcEventLogger().logQuery(translated.sql(), translated.bindings()); + dataNode.getJdbcEventLogger().logQuery(translated.sql(), translated.params()); try (CallableStatement statement = connection.prepareCall(translated.sql());) { initStatement(statement); 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 e5b55e1b1..ce7122d88 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 @@ -20,7 +20,6 @@ package org.apache.cayenne.dba.derby; import org.apache.cayenne.access.DataNode; -import org.apache.cayenne.access.jdbc.PSParameter; import org.apache.cayenne.access.sqlbuilder.sqltree.SQLTreeProcessor; import org.apache.cayenne.access.translator.ejbql.EJBQLTranslator; import org.apache.cayenne.access.translator.ejbql.JdbcEJBQLTranslator; @@ -214,12 +213,13 @@ public class DerbyAdapter extends JdbcAdapter { } @Override - public void bindParameter(PreparedStatement statement, PSParameter<?> parameter) throws Exception { - - if (parameter.value() == null && parameter.psType() == 0) { - statement.setNull(parameter.psPosition(), Types.VARCHAR); + @SuppressWarnings("rawtypes") + protected void bind(PreparedStatement statement, Object value, int psPosition, int psType, int psScale, + ExtendedType binder) throws Exception { + if (value == null && psType == 0) { + statement.setNull(psPosition, Types.VARCHAR); } else { - super.bindParameter(statement, parameter); + super.bind(statement, value, psPosition, psType, psScale, binder); } } diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/ingres/IngresAdapter.java b/cayenne/src/main/java/org/apache/cayenne/dba/ingres/IngresAdapter.java index 83bfdb64d..6bf8fcd14 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/ingres/IngresAdapter.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/ingres/IngresAdapter.java @@ -22,7 +22,6 @@ package org.apache.cayenne.dba.ingres; import org.apache.cayenne.dba.NativeColumnType; import org.apache.cayenne.access.DataNode; import org.apache.cayenne.access.sqlbuilder.sqltree.SQLTreeProcessor; -import org.apache.cayenne.access.jdbc.PSParameter; import org.apache.cayenne.access.types.ExtendedType; import org.apache.cayenne.access.types.ExtendedTypeFactory; import org.apache.cayenne.access.types.ExtendedTypeMap; @@ -116,12 +115,13 @@ public class IngresAdapter extends JdbcAdapter { } @Override - public void bindParameter(PreparedStatement statement, PSParameter<?> parameter) throws Exception { - - if (parameter.value() == null && (parameter.psType() == Types.BIT)) { - statement.setNull(parameter.psPosition(), Types.SMALLINT); + @SuppressWarnings("rawtypes") + protected void bind(PreparedStatement statement, Object value, int psPosition, int psType, int psScale, + ExtendedType binder) throws Exception { + if (value == null && (psType == Types.BIT)) { + statement.setNull(psPosition, Types.SMALLINT); } else { - super.bindParameter(statement, parameter); + super.bind(statement, value, psPosition, psType, psScale, binder); } } diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/mysql/MySQLProcedureAction.java b/cayenne/src/main/java/org/apache/cayenne/dba/mysql/MySQLProcedureAction.java index 7c541730c..91841872f 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/mysql/MySQLProcedureAction.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/mysql/MySQLProcedureAction.java @@ -48,7 +48,7 @@ class MySQLProcedureAction extends ProcedureAction { TranslatedProcedure translated = dataNode.getProcedureTranslator() .translate(query, dataNode.getAdapter(), dataNode.getEntityResolver()); - dataNode.getJdbcEventLogger().logQuery(translated.sql(), translated.bindings()); + dataNode.getJdbcEventLogger().logQuery(translated.sql(), translated.params()); try (CallableStatement statement = connection.prepareCall(translated.sql());) { bindParameters(statement, translated); diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/oracle/OracleAdapter.java b/cayenne/src/main/java/org/apache/cayenne/dba/oracle/OracleAdapter.java index ffa747483..c3463f403 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/oracle/OracleAdapter.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/oracle/OracleAdapter.java @@ -23,7 +23,6 @@ import org.apache.cayenne.dba.NativeColumnType; import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.access.DataNode; import org.apache.cayenne.access.sqlbuilder.sqltree.SQLTreeProcessor; -import org.apache.cayenne.access.jdbc.PSParameter; import org.apache.cayenne.access.translator.ejbql.EJBQLTranslator; import org.apache.cayenne.access.types.ByteType; import org.apache.cayenne.access.types.ExtendedType; @@ -261,21 +260,17 @@ public class OracleAdapter extends JdbcAdapter { } @Override - public void bindParameter(PreparedStatement statement, PSParameter<?> parameter) throws Exception { + @SuppressWarnings({"rawtypes", "unchecked"}) + protected void bind(PreparedStatement statement, Object value, int psPosition, int psType, int psScale, + ExtendedType binder) throws Exception { - // Oracle doesn't support BOOLEAN even when binding NULL, so have to - // intercept - // NULL Boolean here, as super doesn't pass it through ExtendedType... - if (parameter.value() == null && parameter.psType() == Types.BOOLEAN) { + // Oracle doesn't support BOOLEAN even when binding NULL, so have to intercept NULL Boolean here, as super + // doesn't pass it through ExtendedType... + if (value == null && psType == Types.BOOLEAN) { ExtendedType typeProcessor = getExtendedTypes().getRegisteredType(Boolean.class); - typeProcessor.setJdbcObject( - statement, - parameter.value(), - parameter.psPosition(), - parameter.psType(), - parameter.psScale()); + typeProcessor.setJdbcObject(statement, value, psPosition, psType, psScale); } else { - super.bindParameter(statement, parameter); + super.bind(statement, value, psPosition, psType, psScale, binder); } } 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 3d7143679..a40391166 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 @@ -23,7 +23,6 @@ import org.apache.cayenne.dba.NativeColumnType; import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.access.DataNode; import org.apache.cayenne.access.sqlbuilder.sqltree.SQLTreeProcessor; -import org.apache.cayenne.access.jdbc.PSParameter; import org.apache.cayenne.access.translator.ejbql.EJBQLTranslator; import org.apache.cayenne.access.types.ByteArrayType; import org.apache.cayenne.access.types.ByteType; @@ -138,18 +137,20 @@ public class SQLServerAdapter extends JdbcAdapter { } @Override - public void bindParameter(PreparedStatement statement, PSParameter<?> parameter) throws Exception { + @SuppressWarnings("rawtypes") + protected void bind(PreparedStatement statement, Object value, int psPosition, int psType, int psScale, + ExtendedType binder) throws Exception { // SQL Server driver doesn't like CLOBs and BLOBs as parameters - if (parameter.value() == null) { - int jdbcType = switch (parameter.psType()) { + if (value == null) { + int jdbcType = switch (psType) { case Types.CLOB, 0 -> Types.VARCHAR; case Types.BLOB -> Types.VARBINARY; - default -> parameter.psType(); + default -> psType; }; - statement.setNull(parameter.psPosition(), jdbcType); + statement.setNull(psPosition, jdbcType); } else { - super.bindParameter(statement, parameter); + super.bind(statement, value, psPosition, psType, psScale, binder); } } diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerProcedureAction.java b/cayenne/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerProcedureAction.java index d40ed6696..0af3bb37d 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerProcedureAction.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/sqlserver/SQLServerProcedureAction.java @@ -60,7 +60,7 @@ public class SQLServerProcedureAction extends ProcedureAction { TranslatedProcedure translated = dataNode.getProcedureTranslator() .translate(query, dataNode.getAdapter(), dataNode.getEntityResolver()); - dataNode.getJdbcEventLogger().logQuery(translated.sql(), translated.bindings()); + dataNode.getJdbcEventLogger().logQuery(translated.sql(), translated.params()); try (CallableStatement statement = connection.prepareCall(translated.sql());) { bindParameters(statement, translated); 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 98450cfa1..8720855e3 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 @@ -21,7 +21,6 @@ package org.apache.cayenne.dba.sybase; import org.apache.cayenne.dba.NativeColumnType; import org.apache.cayenne.access.sqlbuilder.sqltree.SQLTreeProcessor; -import org.apache.cayenne.access.jdbc.PSParameter; import org.apache.cayenne.access.translator.ejbql.EJBQLTranslator; import org.apache.cayenne.access.types.ByteArrayType; import org.apache.cayenne.access.types.ByteType; @@ -143,18 +142,20 @@ public class SybaseAdapter extends JdbcAdapter { } @Override - public void bindParameter(PreparedStatement statement, PSParameter<?> parameter) throws Exception { + @SuppressWarnings("rawtypes") + protected void bind(PreparedStatement statement, Object value, int psPosition, int psType, int psScale, + ExtendedType binder) throws Exception { // Sybase driver doesn't like CLOBs and BLOBs as parameters - if (parameter.value() == null) { - int jdbcType = switch (parameter.psType()) { + if (value == null) { + int jdbcType = switch (psType) { case Types.CLOB, 0 -> Types.VARCHAR; case Types.BLOB -> Types.VARBINARY; - default -> parameter.psType(); + default -> psType; }; - statement.setNull(parameter.psPosition(), jdbcType); + statement.setNull(psPosition, jdbcType); } else { - super.bindParameter(statement, parameter); + super.bind(statement, value, psPosition, psType, psScale, binder); } } diff --git a/cayenne/src/main/java/org/apache/cayenne/log/JdbcEventLogger.java b/cayenne/src/main/java/org/apache/cayenne/log/JdbcEventLogger.java index 7abd65dc7..78731c4dc 100644 --- a/cayenne/src/main/java/org/apache/cayenne/log/JdbcEventLogger.java +++ b/cayenne/src/main/java/org/apache/cayenne/log/JdbcEventLogger.java @@ -18,6 +18,7 @@ ****************************************************************/ package org.apache.cayenne.log; +import org.apache.cayenne.access.jdbc.CSParameter; import org.apache.cayenne.access.jdbc.PSParameter; import org.apache.cayenne.map.DbAttribute; @@ -40,6 +41,11 @@ public interface JdbcEventLogger { */ void logQuery(String sql, PSParameter<?>[] bindings); + /** + * @since 5.0 + */ + void logQuery(String sql, CSParameter<?>[] bindings); + /** * @since 4.0 */ diff --git a/cayenne/src/main/java/org/apache/cayenne/log/NoopJdbcEventLogger.java b/cayenne/src/main/java/org/apache/cayenne/log/NoopJdbcEventLogger.java index 052fe0a8b..0b8053c74 100644 --- a/cayenne/src/main/java/org/apache/cayenne/log/NoopJdbcEventLogger.java +++ b/cayenne/src/main/java/org/apache/cayenne/log/NoopJdbcEventLogger.java @@ -18,6 +18,7 @@ ****************************************************************/ package org.apache.cayenne.log; +import org.apache.cayenne.access.jdbc.CSParameter; import org.apache.cayenne.access.jdbc.PSParameter; import org.apache.cayenne.map.DbAttribute; @@ -49,6 +50,10 @@ public class NoopJdbcEventLogger implements JdbcEventLogger { public void logQuery(String sql, PSParameter<?>[] bindings) { } + @Override + public void logQuery(String sql, CSParameter<?>[] bindings) { + } + @Override public void logQueryParameters(String label, PSParameter<?>[] bindings) { } 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 5f0c8b42c..059df1cb8 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,9 @@ package org.apache.cayenne.log; import org.apache.cayenne.CayenneRuntimeException; +import org.apache.cayenne.access.jdbc.CSParameter; import org.apache.cayenne.access.jdbc.PSParameter; +import org.apache.cayenne.access.types.ExtendedType; import org.apache.cayenne.configuration.Constants; import org.apache.cayenne.configuration.RuntimeProperties; import org.apache.cayenne.di.Inject; @@ -75,6 +77,19 @@ public class Slf4jJdbcEventLogger implements JdbcEventLogger { } } + @Override + public void logQuery(String sql, CSParameter<?>[] bindings) { + if (isLoggable()) { + + StringBuilder buffer = new StringBuilder(sql).append(" "); + appendParameters(buffer, "bind", bindings); + + if (!buffer.isEmpty()) { + LOGGER.info(buffer.toString()); + } + } + } + @Override public void logQueryParameters(String label, PSParameter<?>[] bindings) { @@ -89,7 +104,6 @@ public class Slf4jJdbcEventLogger implements JdbcEventLogger { } } - @SuppressWarnings("unchecked") protected void appendParameters(StringBuilder buffer, String label, PSParameter<?>[] bindings) { int len = bindings.length; @@ -98,41 +112,64 @@ public class Slf4jJdbcEventLogger implements JdbcEventLogger { boolean hasIncluded = false; for (int i = 0, j = 1; i < len; i++) { - PSParameter b = bindings[i]; - - if (hasIncluded) { - buffer.append(", "); - } else { - hasIncluded = true; - buffer.append("[").append(label).append(": "); - } + PSParameter<?> b = bindings[i]; + DbAttribute attribute = b.attribute(); + appendParameter(buffer, label, hasIncluded, j++, + attribute != null ? attribute.getName() : null, b.binder(), b.value()); + hasIncluded = true; + } + buffer.append("]"); + } + } - buffer.append(j++); + protected void appendParameters(StringBuilder buffer, String label, CSParameter<?>[] bindings) { - DbAttribute attribute = b.attribute(); - if (attribute != null) { - buffer.append("->"); - buffer.append(attribute.getName()); - } + int len = bindings.length; + if (len > 0) { - buffer.append(":"); + boolean hasIncluded = false; - if (b.binder() != null) { - buffer.append(b.binder().toString(b.value())); - } else if (b.value() == null) { - buffer.append("NULL"); - } else { - buffer.append(b.value().getClass().getName()) - .append("@") - .append(System.identityHashCode(b.value())); - } + for (int i = 0, j = 1; i < len; i++) { + CSParameter<?> b = bindings[i]; + appendParameter(buffer, label, hasIncluded, j++, b.param().getName(), b.binder(), b.value()); + hasIncluded = true; } buffer.append("]"); } } + @SuppressWarnings({"rawtypes", "unchecked"}) + private void appendParameter(StringBuilder buffer, String label, boolean hasIncluded, int position, String name, + ExtendedType binder, Object value) { + + if (hasIncluded) { + buffer.append(", "); + } else { + buffer.append("[").append(label).append(": "); + } + + buffer.append(position); + + if (name != null) { + buffer.append("->"); + buffer.append(name); + } + + buffer.append(":"); + + if (binder != null) { + buffer.append(binder.toString(value)); + } else if (value == null) { + buffer.append("NULL"); + } else { + buffer.append(value.getClass().getName()) + .append("@") + .append(System.identityHashCode(value)); + } + } + @Override public void logSelectCount(int count, long time) { logSelectCount(count, time, null);
