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
The following commit(s) were added to refs/heads/master by this push:
new 17d1154ff Aligning OptimisticLockException with CayenneSqlException
17d1154ff is described below
commit 17d1154ff02510a0853d6ce47ab4241374466682
Author: Andrus Adamchik <[email protected]>
AuthorDate: Sat Jul 4 17:46:25 2026 -0400
Aligning OptimisticLockException with CayenneSqlException
it can have access to TranslatedStatement
---
.../cayenne/access/OptimisticLockException.java | 54 ++-
.../apache/cayenne/access/jdbc/BatchAction.java | 531 +++++++++++----------
2 files changed, 298 insertions(+), 287 deletions(-)
diff --git
a/cayenne/src/main/java/org/apache/cayenne/access/OptimisticLockException.java
b/cayenne/src/main/java/org/apache/cayenne/access/OptimisticLockException.java
index 5d39ec91b..7362e3404 100644
---
a/cayenne/src/main/java/org/apache/cayenne/access/OptimisticLockException.java
+++
b/cayenne/src/main/java/org/apache/cayenne/access/OptimisticLockException.java
@@ -19,55 +19,64 @@
package org.apache.cayenne.access;
-import java.util.Collections;
-import java.util.Iterator;
-import java.util.Map;
-
import org.apache.cayenne.CayenneRuntimeException;
import org.apache.cayenne.ObjectContext;
import org.apache.cayenne.ObjectId;
+import org.apache.cayenne.access.translator.TranslatedStatement;
import org.apache.cayenne.exp.Expression;
import org.apache.cayenne.exp.ExpressionFactory;
import org.apache.cayenne.map.DbAttribute;
import org.apache.cayenne.map.DbEntity;
import org.apache.cayenne.query.ObjectSelect;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Objects;
+
/**
* An exception thrown on optimistic lock failure.
- *
+ *
* @since 1.1
*/
public class OptimisticLockException extends CayenneRuntimeException {
- protected ObjectId failedObjectId;
- protected String querySQL;
- protected DbEntity rootEntity;
- protected Map qualifierSnapshot;
+ private final ObjectId failedObjectId;
+ private final DbEntity rootEntity;
+ private final Map<String, ?> qualifierSnapshot;
+ private final TranslatedStatement statement;
- public OptimisticLockException(ObjectId id, DbEntity rootEntity, String
querySQL,
- Map qualifierSnapshot) {
+ public OptimisticLockException(ObjectId id, DbEntity rootEntity,
Map<String, ?> qualifierSnapshot, TranslatedStatement statement) {
super("Optimistic Lock Failure");
this.failedObjectId = id;
this.rootEntity = rootEntity;
- this.querySQL = querySQL;
- this.qualifierSnapshot = (qualifierSnapshot != null)
- ? qualifierSnapshot
- : Collections.EMPTY_MAP;
+ this.qualifierSnapshot = qualifierSnapshot != null ? qualifierSnapshot
: Collections.emptyMap();
+ this.statement = Objects.requireNonNull(statement);
+ }
+
+ /**
+ * @since 5.0
+ */
+ public TranslatedStatement getStatement() {
+ return statement;
}
public Map getQualifierSnapshot() {
return qualifierSnapshot;
}
+ /**
+ * @deprecated in favor of {@link #getStatement()}
+ */
+ @Deprecated(since = "5.0", forRemoval = true)
public String getQuerySQL() {
- return querySQL;
+ return statement.sql();
}
/**
- * Retrieves fresh snapshot for the failed row. Null row indicates that it
was
- * deleted.
- *
+ * Retrieves fresh snapshot for the failed row. Null row indicates that it
was deleted.
+ *
* @since 3.0
*/
public Map<?, ?> getFreshSnapshot(ObjectContext context) {
@@ -94,10 +103,7 @@ public class OptimisticLockException extends
CayenneRuntimeException {
@Override
public String getMessage() {
StringBuilder buffer = new StringBuilder(super.getMessage());
-
- if (querySQL != null) {
- buffer.append(", SQL: [").append(querySQL.trim()).append("]");
- }
+ buffer.append(", SQL: [").append(statement.sql()).append("]");
if (!qualifierSnapshot.isEmpty()) {
buffer.append(", WHERE clause bindings: [");
@@ -119,7 +125,7 @@ public class OptimisticLockException extends
CayenneRuntimeException {
/**
* Returns the ObjectId of the object that caused the
OptimisticLockException.
- *
+ *
* @since 3.1
*/
public ObjectId getFailedObjectId() {
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 975125503..1f21eeb11 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
@@ -56,267 +56,272 @@ import java.util.List;
*/
public class BatchAction extends BaseSQLAction {
- protected boolean runningAsBatch;
- protected BatchQuery query;
- protected RSColumn[] keyColumns;
-
- /**
- * @since 4.0
- */
- public BatchAction(BatchQuery query, DataNode dataNode, boolean
runningAsBatch) {
- super(dataNode);
- this.query = query;
- this.runningAsBatch = runningAsBatch;
- }
-
- /**
- * @return Query which originated this action
- */
- public BatchQuery getQuery() {
- return query;
- }
-
- @Override
- public void performAction(Connection connection, OperationObserver
observer) throws Exception {
- DbAdapter adapter = dataNode.getAdapter();
- TranslatedBatch translated = switch (query) {
- case InsertBatchQuery insert ->
dataNode.getInsertBatchTranslator().translate(insert, adapter);
- case UpdateBatchQuery update ->
dataNode.getUpdateBatchTranslator().translate(update, adapter);
- case DeleteBatchQuery delete ->
dataNode.getDeleteBatchTranslator().translate(delete, adapter);
- case null, default -> throw new
CayenneRuntimeException("Unsupported batch query: %s", query);
- };
-
- observer.nextStatement(query, translated);
-
- boolean isBatch = canRunAsBatch();
- boolean generatesKeys = hasGeneratedKeys() &&
supportsGeneratedKeys(isBatch);
-
- if (isBatch) {
- runAsBatch(connection, translated, observer,
generatesKeys);
- } else {
- runAsIndividualQueries(connection, translated,
observer, generatesKeys);
- }
- }
-
- protected boolean canRunAsBatch() {
- if(!runningAsBatch || query.getRows().size() <= 1) {
- return false;
- }
-
- if (hasGeneratedKeys()) {
- // turn off batch mode if we generate keys but can't do
so in a batch
- return supportsGeneratedKeys(true) &&
-
!dataNode.getEntityResolver().getEntitySorter().isReflexive(query.getDbEntity());
- }
-
- return true;
- }
-
- protected void runAsBatch(Connection con, TranslatedBatch translated,
OperationObserver delegate, boolean generatesKeys)
- throws Exception {
-
- String sql = translated.sql();
- DbAdapter adapter = dataNode.getAdapter();
-
- try (PreparedStatement statement = prepareStatement(con, sql,
adapter, generatesKeys)) {
- for (int row = 0; row < query.getRows().size(); row++) {
-
- PSParameter<?>[] bindings =
rowBindings(translated, row, adapter.getExtendedTypes());
- for (PSParameter<?> b : bindings) {
- adapter.bindParameter(statement, b);
- }
-
- statement.addBatch();
- }
-
- // execute the whole batch
- int[] results = statement.executeBatch();
- delegate.nextBatchCount(query, results);
-
- if (generatesKeys) {
- processGeneratedKeys(statement, delegate,
query.getRows());
- }
- }
- }
-
- /**
- * Executes batch as individual queries over the same prepared
statement.
- */
- protected void runAsIndividualQueries(Connection connection,
TranslatedBatch translated,
- OperationObserver delegate, boolean generatesKeys)
throws Exception {
-
- if(query.getRows().isEmpty()) {
- return;
- }
-
- boolean useOptimisticLock = query.isUsingOptimisticLocking();
-
- String queryStr = translated.sql();
- DbAdapter adapter = dataNode.getAdapter();
-
- try (PreparedStatement statement = prepareStatement(connection,
queryStr, adapter, generatesKeys)) {
- List<BatchQueryRow> rows = query.getRows();
- for (int i = 0; i < rows.size(); i++) {
- BatchQueryRow row = rows.get(i);
-
- PSParameter<?>[] bindings =
rowBindings(translated, i, adapter.getExtendedTypes());
- for (PSParameter<?> b : bindings) {
- adapter.bindParameter(statement, b);
- }
-
- int updated = statement.executeUpdate();
- if (useOptimisticLock && updated != 1) {
- throw new
OptimisticLockException(row.getObjectId(), query.getDbEntity(), queryStr,
- row.getQualifier());
- }
-
- delegate.nextCount(query, updated);
-
- if (generatesKeys) {
- processGeneratedKeys(statement,
delegate, row);
- }
- }
- }
- }
-
- /**
- * Resolves the given batch row against the translated batch
parameters, returning per-row bindings.
- */
- protected PSParameter<?>[] rowBindings(TranslatedBatch translated, int
row, ExtendedTypeMap extendedTypes) {
- PSBatchParameter[] template = translated.bindings();
- PSParameter<?>[] bindings = new PSParameter[template.length];
- for (int j = 0; j < template.length; j++) {
- bindings[j] = bind(template[j], row, extendedTypes);
- }
- return bindings;
- }
-
- @SuppressWarnings("unchecked")
- private static <T> PSParameter<T> bind(PSBatchParameter parameter, int
row, ExtendedTypeMap extendedTypes) {
- Object value = parameter.getValue(row);
-
- DbAttribute attribute = parameter.attribute();
- if (value == null && attribute.isPrimaryKey()) {
- String entity = attribute.getEntity() != null ?
attribute.getEntity().getName() : "<null>";
- throw new CayenneRuntimeException("Failed to generate
PK: %s.%s", entity, attribute.getName());
- }
-
- ExtendedType<T> extendedType = value != null
- ?
extendedTypes.getRegisteredType(value.getClass())
- : extendedTypes.getDefaultType();
- return new PSParameter<>((T) value, parameter.psPosition(),
parameter.psType(), parameter.psScale(),
- extendedType, parameter.attribute());
- }
-
-
- protected PreparedStatement prepareStatement(Connection connection,
String queryStr,
-
DbAdapter adapter, boolean generatedKeys) throws
SQLException {
- return (generatedKeys)
- ? connection.prepareStatement(queryStr,
Statement.RETURN_GENERATED_KEYS)
- : connection.prepareStatement(queryStr);
- }
-
- protected boolean supportsGeneratedKeys(boolean isBatch) {
- // see if we are configured to support generated keys
- return isBatch
- ?
dataNode.getAdapter().supportsGeneratedKeysForBatchInserts()
- : dataNode.getAdapter().supportsGeneratedKeys();
- }
-
- /**
- * Returns whether BatchQuery generates any keys.
- */
- protected boolean hasGeneratedKeys() {
- // see if the query needs them
- if (query instanceof InsertBatchQuery) {
-
- // see if any of the generated attributes is PK
- for (final DbAttribute attr :
query.getDbEntity().getGeneratedAttributes()) {
- if (attr.isPrimaryKey()) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- /**
- * Implements generated keys extraction supported in JDBC 3.0
specification.
- *
- * @since 4.0
- */
- protected void processGeneratedKeys(Statement statement,
OperationObserver observer, BatchQueryRow row)
- throws SQLException {
- processGeneratedKeys(statement, observer,
Collections.singletonList(row));
- }
-
- /**
- * @since 4.2
- */
- @SuppressWarnings({ "rawtypes", "unchecked" })
- protected void processGeneratedKeys(Statement statement,
OperationObserver observer, List<BatchQueryRow> rows)
- throws SQLException {
-
- ResultSet keysRS = statement.getGeneratedKeys();
-
- // TODO: andrus, 7/4/2007 - use a different form of
Statement.execute -
- // "execute(String,String[])" to be able to map generated
column names
- // (this way we can support multiple columns..
- // although need to check how well this works with most common
drivers)
-
- RSColumn.RowBuilder rowBuilder = RSColumn.rowBuilder();
-
- if (this.keyColumns == null) {
- // attempt to figure out the right descriptor from the
mapping...
- Collection<DbAttribute> generated =
query.getDbEntity().getGeneratedAttributes();
- if (generated.size() == 1 &&
keysRS.getMetaData().getColumnCount() == 1) {
- DbAttribute key = generated.iterator().next();
-
- // use column name from result set, but type
and Java class from DB attribute
- ResultSetMetaData md = keysRS.getMetaData();
- String columnName = md.getColumnLabel(1);
- if (columnName == null || columnName.isEmpty())
{
- columnName = md.getColumnName(1);
- if (columnName == null ||
columnName.isEmpty()) {
- columnName = "column_1";
- }
- }
- ExtendedType type =
dataNode.getAdapter().getExtendedTypes().getRegisteredType(typeForGeneratedPK(key));
- rowBuilder.columns(new RSColumn(columnName,
key.getType(), columnName, type, null));
- } else {
- rowBuilder.resultSet(keysRS);
- }
-
- this.keyColumns =
rowBuilder.build(dataNode.getAdapter().getExtendedTypes());
- }
-
- RowReader<?> rowReader = dataNode.getRowReaderFactory()
- .rowReader(keyColumns,
query.getMetaData(dataNode.getEntityResolver()), dataNode.getAdapter());
-
- // generated keys are small (one row per inserted row), so
materialize them here rather than passing a live,
- // single-use iterator to the observer
- List<DataRow> keys;
- try (ResultIterator<?> iterator = new RSIterator(null, keysRS,
rowReader)) {
- keys = (List<DataRow>) iterator.allRows();
- }
-
- List<ObjectId> objectIds = new ArrayList<>(rows.size());
- for(BatchQueryRow row : rows) {
- objectIds.add(row.getObjectId());
- }
- observer.nextGeneratedRows(query, keys, objectIds);
- }
-
- private String typeForGeneratedPK(DbAttribute key) {
- String entityName =
getQuery().getRows().getFirst().getObjectId().getEntityName();
- ObjEntity objEntity =
dataNode.getEntityResolver().getObjEntity(entityName);
- if(objEntity != null) {
- ObjAttribute attributeForDbAttribute =
objEntity.getAttributeForDbAttribute(key);
- if(attributeForDbAttribute != null) {
- return attributeForDbAttribute.getType();
- }
- }
- return key.getJavaClassName();
- }
+ protected boolean runningAsBatch;
+ protected BatchQuery query;
+ protected RSColumn[] keyColumns;
+
+ /**
+ * @since 4.0
+ */
+ public BatchAction(BatchQuery query, DataNode dataNode, boolean
runningAsBatch) {
+ super(dataNode);
+ this.query = query;
+ this.runningAsBatch = runningAsBatch;
+ }
+
+ /**
+ * @return Query which originated this action
+ */
+ public BatchQuery getQuery() {
+ return query;
+ }
+
+ @Override
+ public void performAction(Connection connection, OperationObserver
observer) throws Exception {
+ DbAdapter adapter = dataNode.getAdapter();
+ TranslatedBatch translated = switch (query) {
+ case InsertBatchQuery insert ->
dataNode.getInsertBatchTranslator().translate(insert, adapter);
+ case UpdateBatchQuery update ->
dataNode.getUpdateBatchTranslator().translate(update, adapter);
+ case DeleteBatchQuery delete ->
dataNode.getDeleteBatchTranslator().translate(delete, adapter);
+ case null, default -> throw new
CayenneRuntimeException("Unsupported batch query: %s", query);
+ };
+
+ observer.nextStatement(query, translated);
+
+ boolean isBatch = canRunAsBatch();
+ boolean generatesKeys = hasGeneratedKeys() &&
supportsGeneratedKeys(isBatch);
+
+ if (isBatch) {
+ runAsBatch(connection, translated, observer, generatesKeys);
+ } else {
+ runAsIndividualQueries(connection, translated, observer,
generatesKeys);
+ }
+ }
+
+ protected boolean canRunAsBatch() {
+ if (!runningAsBatch || query.getRows().size() <= 1) {
+ return false;
+ }
+
+ if (hasGeneratedKeys()) {
+ // turn off batch mode if we generate keys but can't do so in a
batch
+ return supportsGeneratedKeys(true) &&
+
!dataNode.getEntityResolver().getEntitySorter().isReflexive(query.getDbEntity());
+ }
+
+ return true;
+ }
+
+ protected void runAsBatch(
+ Connection con,
+ TranslatedBatch translated,
+ OperationObserver delegate,
+ boolean generatesKeys) throws Exception {
+
+ String sql = translated.sql();
+ DbAdapter adapter = dataNode.getAdapter();
+
+ try (PreparedStatement statement = prepareStatement(con, sql, adapter,
generatesKeys)) {
+ for (int row = 0; row < query.getRows().size(); row++) {
+
+ PSParameter<?>[] bindings = rowBindings(translated, row,
adapter.getExtendedTypes());
+ for (PSParameter<?> b : bindings) {
+ adapter.bindParameter(statement, b);
+ }
+
+ statement.addBatch();
+ }
+
+ // execute the whole batch
+ int[] results = statement.executeBatch();
+ delegate.nextBatchCount(query, results);
+
+ if (generatesKeys) {
+ processGeneratedKeys(statement, delegate, query.getRows());
+ }
+ }
+ }
+
+ /**
+ * Executes batch as individual queries over the same prepared statement.
+ */
+ protected void runAsIndividualQueries(
+ Connection connection,
+ TranslatedBatch translated,
+ OperationObserver delegate,
+ boolean generatesKeys) throws Exception {
+
+ if (query.getRows().isEmpty()) {
+ return;
+ }
+
+ boolean useOptimisticLock = query.isUsingOptimisticLocking();
+
+ String queryStr = translated.sql();
+ DbAdapter adapter = dataNode.getAdapter();
+
+ try (PreparedStatement statement = prepareStatement(connection,
queryStr, adapter, generatesKeys)) {
+ List<BatchQueryRow> rows = query.getRows();
+ for (int i = 0; i < rows.size(); i++) {
+ BatchQueryRow row = rows.get(i);
+
+ PSParameter<?>[] bindings = rowBindings(translated, i,
adapter.getExtendedTypes());
+ for (PSParameter<?> b : bindings) {
+ adapter.bindParameter(statement, b);
+ }
+
+ int updated = statement.executeUpdate();
+ if (useOptimisticLock && updated != 1) {
+ throw new OptimisticLockException(row.getObjectId(),
query.getDbEntity(), row.getQualifier(), translated);
+ }
+
+ delegate.nextCount(query, updated);
+
+ if (generatesKeys) {
+ processGeneratedKeys(statement, delegate, row);
+ }
+ }
+ }
+ }
+
+ /**
+ * Resolves the given batch row against the translated batch parameters,
returning per-row bindings.
+ */
+ protected PSParameter<?>[] rowBindings(TranslatedBatch translated, int
row, ExtendedTypeMap extendedTypes) {
+ PSBatchParameter[] template = translated.bindings();
+ PSParameter<?>[] bindings = new PSParameter[template.length];
+ for (int j = 0; j < template.length; j++) {
+ bindings[j] = bind(template[j], row, extendedTypes);
+ }
+ return bindings;
+ }
+
+ @SuppressWarnings("unchecked")
+ private static <T> PSParameter<T> bind(PSBatchParameter parameter, int
row, ExtendedTypeMap extendedTypes) {
+ Object value = parameter.getValue(row);
+
+ DbAttribute attribute = parameter.attribute();
+ if (value == null && attribute.isPrimaryKey()) {
+ String entity = attribute.getEntity() != null ?
attribute.getEntity().getName() : "<null>";
+ throw new CayenneRuntimeException("Failed to generate PK: %s.%s",
entity, attribute.getName());
+ }
+
+ ExtendedType<T> extendedType = value != null
+ ? extendedTypes.getRegisteredType(value.getClass())
+ : extendedTypes.getDefaultType();
+ return new PSParameter<>((T) value, parameter.psPosition(),
parameter.psType(), parameter.psScale(),
+ extendedType, parameter.attribute());
+ }
+
+
+ protected PreparedStatement prepareStatement(Connection connection, String
queryStr,
+ DbAdapter adapter, boolean
generatedKeys) throws SQLException {
+ return (generatedKeys)
+ ? connection.prepareStatement(queryStr,
Statement.RETURN_GENERATED_KEYS)
+ : connection.prepareStatement(queryStr);
+ }
+
+ protected boolean supportsGeneratedKeys(boolean isBatch) {
+ // see if we are configured to support generated keys
+ return isBatch
+ ? dataNode.getAdapter().supportsGeneratedKeysForBatchInserts()
+ : dataNode.getAdapter().supportsGeneratedKeys();
+ }
+
+ /**
+ * Returns whether BatchQuery generates any keys.
+ */
+ protected boolean hasGeneratedKeys() {
+ // see if the query needs them
+ if (query instanceof InsertBatchQuery) {
+
+ // see if any of the generated attributes is PK
+ for (final DbAttribute attr :
query.getDbEntity().getGeneratedAttributes()) {
+ if (attr.isPrimaryKey()) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Implements generated keys extraction supported in JDBC 3.0
specification.
+ *
+ * @since 4.0
+ */
+ protected void processGeneratedKeys(Statement statement, OperationObserver
observer, BatchQueryRow row)
+ throws SQLException {
+ processGeneratedKeys(statement, observer,
Collections.singletonList(row));
+ }
+
+ /**
+ * @since 4.2
+ */
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ protected void processGeneratedKeys(Statement statement, OperationObserver
observer, List<BatchQueryRow> rows)
+ throws SQLException {
+
+ ResultSet keysRS = statement.getGeneratedKeys();
+
+ // TODO: andrus, 7/4/2007 - use a different form of Statement.execute -
+ // "execute(String,String[])" to be able to map generated column names
+ // (this way we can support multiple columns..
+ // although need to check how well this works with most common drivers)
+
+ RSColumn.RowBuilder rowBuilder = RSColumn.rowBuilder();
+
+ if (this.keyColumns == null) {
+ // attempt to figure out the right descriptor from the mapping...
+ Collection<DbAttribute> generated =
query.getDbEntity().getGeneratedAttributes();
+ if (generated.size() == 1 && keysRS.getMetaData().getColumnCount()
== 1) {
+ DbAttribute key = generated.iterator().next();
+
+ // use column name from result set, but type and Java class
from DB attribute
+ ResultSetMetaData md = keysRS.getMetaData();
+ String columnName = md.getColumnLabel(1);
+ if (columnName == null || columnName.isEmpty()) {
+ columnName = md.getColumnName(1);
+ if (columnName == null || columnName.isEmpty()) {
+ columnName = "column_1";
+ }
+ }
+ ExtendedType type =
dataNode.getAdapter().getExtendedTypes().getRegisteredType(typeForGeneratedPK(key));
+ rowBuilder.columns(new RSColumn(columnName, key.getType(),
columnName, type, null));
+ } else {
+ rowBuilder.resultSet(keysRS);
+ }
+
+ this.keyColumns =
rowBuilder.build(dataNode.getAdapter().getExtendedTypes());
+ }
+
+ RowReader<?> rowReader = dataNode.getRowReaderFactory()
+ .rowReader(keyColumns,
query.getMetaData(dataNode.getEntityResolver()), dataNode.getAdapter());
+
+ // generated keys are small (one row per inserted row), so materialize
them here rather than passing a live,
+ // single-use iterator to the observer
+ List<DataRow> keys;
+ try (ResultIterator<?> iterator = new RSIterator(null, keysRS,
rowReader)) {
+ keys = (List<DataRow>) iterator.allRows();
+ }
+
+ List<ObjectId> objectIds = new ArrayList<>(rows.size());
+ for (BatchQueryRow row : rows) {
+ objectIds.add(row.getObjectId());
+ }
+ observer.nextGeneratedRows(query, keys, objectIds);
+ }
+
+ private String typeForGeneratedPK(DbAttribute key) {
+ String entityName =
getQuery().getRows().getFirst().getObjectId().getEntityName();
+ ObjEntity objEntity =
dataNode.getEntityResolver().getObjEntity(entityName);
+ if (objEntity != null) {
+ ObjAttribute attributeForDbAttribute =
objEntity.getAttributeForDbAttribute(key);
+ if (attributeForDbAttribute != null) {
+ return attributeForDbAttribute.getType();
+ }
+ }
+ return key.getJavaClassName();
+ }
}