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();
+    }
 }

Reply via email to