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 5aff3c51c5a88050eb7e1c95d55e02ca9f0300cb Author: Andrus Adamchik <[email protected]> AuthorDate: Thu Jun 25 17:44:01 2026 -0400 Sequence PkGenerator inheritance hierarchy cleanup --- ...lePkGenerator.java => SequencePkGenerator.java} | 63 ++++--- .../org/apache/cayenne/dba/db2/DB2PkGenerator.java | 4 +- .../apache/cayenne/dba/derby/DerbyPkGenerator.java | 4 +- .../org/apache/cayenne/dba/h2/H2PkGenerator.java | 4 +- .../cayenne/dba/ingres/IngresPkGenerator.java | 4 +- .../cayenne/dba/oracle/OraclePkGenerator.java | 197 +-------------------- .../cayenne/dba/postgres/PostgresPkGenerator.java | 4 +- ...neratorIT.java => SequencePkGeneratorTest.java} | 29 ++- 8 files changed, 54 insertions(+), 255 deletions(-) diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/oracle/OraclePkGenerator.java b/cayenne/src/main/java/org/apache/cayenne/dba/SequencePkGenerator.java similarity index 84% copy from cayenne/src/main/java/org/apache/cayenne/dba/oracle/OraclePkGenerator.java copy to cayenne/src/main/java/org/apache/cayenne/dba/SequencePkGenerator.java index aee6bffa3..628638988 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/oracle/OraclePkGenerator.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/SequencePkGenerator.java @@ -17,12 +17,10 @@ * under the License. ****************************************************************/ -package org.apache.cayenne.dba.oracle; +package org.apache.cayenne.dba; import org.apache.cayenne.CayenneRuntimeException; import org.apache.cayenne.access.DataNode; -import org.apache.cayenne.dba.JdbcAdapter; -import org.apache.cayenne.dba.JdbcPkGenerator; import org.apache.cayenne.map.DbEntity; import org.apache.cayenne.map.DbKeyGenerator; @@ -34,38 +32,37 @@ import java.util.ArrayList; import java.util.List; /** - * Sequence-based primary key generator implementation for Oracle. Uses Oracle - * sequences to generate primary key values. This approach is at least 50% - * faster when tested with Oracle compared to the lookup table approach. + * A common superclass for sequence-based primary key generators. Uses database + * sequences to generate primary key values, which is generally faster than the + * lookup table approach implemented by {@link JdbcPkGenerator}. Concrete + * subclasses provide database-specific SQL for selecting the next sequence value + * and for discovering existing sequences. * <p> - * When using Cayenne key caching mechanism, make sure that sequences in the - * database have "INCREMENT BY" greater or equal to OraclePkGenerator - * "pkCacheSize" property value. If this is not the case, you will need to - * adjust PkGenerator value accordingly. For example when sequence is - * incremented by 1 each time, use the following code: + * When using the Cayenne key caching mechanism, make sure that sequences in the + * database have "INCREMENT BY" greater or equal to the generator "pkCacheSize" + * property value. If this is not the case, you will need to adjust the + * PkGenerator value accordingly. For example when a sequence is incremented by 1 + * each time, use the following code: * </p> * * <pre> * dataNode.getAdapter().getPkGenerator().setPkCacheSize(1); * </pre> + * + * @since 5.0 */ -public class OraclePkGenerator extends JdbcPkGenerator { +public abstract class SequencePkGenerator extends JdbcPkGenerator { - /** - * Used by DI - * - * @since 4.1 - */ - public OraclePkGenerator() { + private static final String _SEQUENCE_PREFIX = "pk_"; + + public SequencePkGenerator() { super(); } - protected OraclePkGenerator(JdbcAdapter adapter) { + protected SequencePkGenerator(JdbcAdapter adapter) { super(adapter); } - private static final String _SEQUENCE_PREFIX = "pk_"; - @Override public void createAutoPk(DataNode node, List<DbEntity> dbEntities) { List<String> sequences = getExistingSequences(node); @@ -132,21 +129,21 @@ public class OraclePkGenerator extends JdbcPkGenerator { return "DROP SEQUENCE " + seqFQN; } - protected String selectNextValQuery(String pkGeneratingSequenceName) { - return "SELECT " + pkGeneratingSequenceName + ".nextval FROM DUAL"; - } + /** + * Returns a database-specific query to fetch the next value of the named + * sequence. + */ + protected abstract String selectNextValQuery(String pkGeneratingSequenceName); - protected String selectAllSequencesQuery() { - return "SELECT LOWER(SEQUENCE_NAME) FROM ALL_SEQUENCES"; - } + /** + * Returns a database-specific query that lists the names of all existing + * sequences. + */ + protected abstract String selectAllSequencesQuery(); /** - * Generates primary key by calling Oracle sequence corresponding to the - * <code>dbEntity</code>. Executed SQL looks like this: - * - * <pre> - * SELECT pk_table_name.nextval FROM DUAL - * </pre> + * Generates a primary key by calling the database sequence corresponding to + * the <code>dbEntity</code>. * * @since 3.0 */ diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/db2/DB2PkGenerator.java b/cayenne/src/main/java/org/apache/cayenne/dba/db2/DB2PkGenerator.java index 2a7c5f2c2..d9b513d9d 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/db2/DB2PkGenerator.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/db2/DB2PkGenerator.java @@ -19,13 +19,13 @@ package org.apache.cayenne.dba.db2; import org.apache.cayenne.dba.JdbcAdapter; -import org.apache.cayenne.dba.oracle.OraclePkGenerator; +import org.apache.cayenne.dba.SequencePkGenerator; import org.apache.cayenne.map.DbEntity; /** * A sequence-based PK generator used by {@link DB2Adapter}. */ -public class DB2PkGenerator extends OraclePkGenerator { +public class DB2PkGenerator extends SequencePkGenerator { /** * Used by DI diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/derby/DerbyPkGenerator.java b/cayenne/src/main/java/org/apache/cayenne/dba/derby/DerbyPkGenerator.java index b9e8bd5e1..876f62cd8 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/derby/DerbyPkGenerator.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/derby/DerbyPkGenerator.java @@ -20,7 +20,7 @@ package org.apache.cayenne.dba.derby; import org.apache.cayenne.dba.JdbcAdapter; -import org.apache.cayenne.dba.oracle.OraclePkGenerator; +import org.apache.cayenne.dba.SequencePkGenerator; import org.apache.cayenne.map.DbEntity; /** @@ -28,7 +28,7 @@ import org.apache.cayenne.map.DbEntity; * * @since 4.0 (old one used AUTO_PK_SUPPORT table) */ -public class DerbyPkGenerator extends OraclePkGenerator { +public class DerbyPkGenerator extends SequencePkGenerator { /** * Used by DI diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/h2/H2PkGenerator.java b/cayenne/src/main/java/org/apache/cayenne/dba/h2/H2PkGenerator.java index 5d3265c4a..ae45d710c 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/h2/H2PkGenerator.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/h2/H2PkGenerator.java @@ -20,7 +20,7 @@ package org.apache.cayenne.dba.h2; import org.apache.cayenne.dba.JdbcAdapter; -import org.apache.cayenne.dba.oracle.OraclePkGenerator; +import org.apache.cayenne.dba.SequencePkGenerator; import org.apache.cayenne.map.DbEntity; /** @@ -28,7 +28,7 @@ import org.apache.cayenne.map.DbEntity; * * @since 4.0 */ -public class H2PkGenerator extends OraclePkGenerator { +public class H2PkGenerator extends SequencePkGenerator { /** * Used by DI diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/ingres/IngresPkGenerator.java b/cayenne/src/main/java/org/apache/cayenne/dba/ingres/IngresPkGenerator.java index 304772d20..9fd76cefd 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/ingres/IngresPkGenerator.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/ingres/IngresPkGenerator.java @@ -20,14 +20,14 @@ package org.apache.cayenne.dba.ingres; import org.apache.cayenne.dba.JdbcAdapter; -import org.apache.cayenne.dba.oracle.OraclePkGenerator; +import org.apache.cayenne.dba.SequencePkGenerator; /** * Ingres-specific sequence based PK generator. * * @since 1.2 */ -public class IngresPkGenerator extends OraclePkGenerator { +public class IngresPkGenerator extends SequencePkGenerator { /** * Used by DI diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/oracle/OraclePkGenerator.java b/cayenne/src/main/java/org/apache/cayenne/dba/oracle/OraclePkGenerator.java index aee6bffa3..2c050b1d1 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/oracle/OraclePkGenerator.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/oracle/OraclePkGenerator.java @@ -19,37 +19,13 @@ package org.apache.cayenne.dba.oracle; -import org.apache.cayenne.CayenneRuntimeException; -import org.apache.cayenne.access.DataNode; import org.apache.cayenne.dba.JdbcAdapter; -import org.apache.cayenne.dba.JdbcPkGenerator; -import org.apache.cayenne.map.DbEntity; -import org.apache.cayenne.map.DbKeyGenerator; - -import java.sql.Connection; -import java.sql.ResultSet; -import java.sql.SQLException; -import java.sql.Statement; -import java.util.ArrayList; -import java.util.List; +import org.apache.cayenne.dba.SequencePkGenerator; /** - * Sequence-based primary key generator implementation for Oracle. Uses Oracle - * sequences to generate primary key values. This approach is at least 50% - * faster when tested with Oracle compared to the lookup table approach. - * <p> - * When using Cayenne key caching mechanism, make sure that sequences in the - * database have "INCREMENT BY" greater or equal to OraclePkGenerator - * "pkCacheSize" property value. If this is not the case, you will need to - * adjust PkGenerator value accordingly. For example when sequence is - * incremented by 1 each time, use the following code: - * </p> - * - * <pre> - * dataNode.getAdapter().getPkGenerator().setPkCacheSize(1); - * </pre> + * Sequence-based primary key generator implementation for Oracle. Uses Oracle sequences to generate primary key values. */ -public class OraclePkGenerator extends JdbcPkGenerator { +public class OraclePkGenerator extends SequencePkGenerator { /** * Used by DI @@ -64,178 +40,13 @@ public class OraclePkGenerator extends JdbcPkGenerator { super(adapter); } - private static final String _SEQUENCE_PREFIX = "pk_"; - - @Override - public void createAutoPk(DataNode node, List<DbEntity> dbEntities) { - List<String> sequences = getExistingSequences(node); - // create needed sequences - for (DbEntity dbEntity : dbEntities) { - if (!sequences.contains(sequenceName(dbEntity))) { - runUpdate(node, createSequenceString(dbEntity)); - } - } - } - - /** - * Creates a list of CREATE SEQUENCE statements for the list of DbEntities. - */ - @Override - public List<String> createAutoPkStatements(List<DbEntity> dbEntities) { - List<String> list = new ArrayList<>(dbEntities.size()); - for (DbEntity dbEntity : dbEntities) { - list.add(createSequenceString(dbEntity)); - } - - return list; - } - - /** - * Drops PK sequences for all specified DbEntities. - */ - @Override - public void dropAutoPk(DataNode node, List<DbEntity> dbEntities) { - List<String> sequences = getExistingSequences(node); - - // drop obsolete sequences - for (DbEntity dbEntity : dbEntities) { - if (sequences.contains(sequenceName(dbEntity))) { - runUpdate(node, dropSequenceString(dbEntity)); - } - } - } - - /** - * Creates a list of DROP SEQUENCE statements for the list of DbEntities. - */ @Override - public List<String> dropAutoPkStatements(List<DbEntity> dbEntities) { - List<String> list = new ArrayList<>(dbEntities.size()); - for (DbEntity dbEntity : dbEntities) { - list.add(dropSequenceString(dbEntity)); - } - - return list; - } - - protected String createSequenceString(DbEntity ent) { - String seqFQN = adapter.getQuotingStrategy(ent).quotedFQN(ent.getCatalog(), ent.getSchema(), sequenceName(ent)); - return "CREATE SEQUENCE " + seqFQN + " START WITH " + pkStartValue + " INCREMENT BY " + pkCacheSize(ent); - } - - /** - * Returns a SQL string needed to drop any database objects associated with - * automatic primary key generation process for a specific DbEntity. - */ - protected String dropSequenceString(DbEntity ent) { - String seqFQN = adapter.getQuotingStrategy(ent).quotedFQN(ent.getCatalog(), ent.getSchema(), sequenceName(ent)); - return "DROP SEQUENCE " + seqFQN; - } - protected String selectNextValQuery(String pkGeneratingSequenceName) { return "SELECT " + pkGeneratingSequenceName + ".nextval FROM DUAL"; } + @Override protected String selectAllSequencesQuery() { return "SELECT LOWER(SEQUENCE_NAME) FROM ALL_SEQUENCES"; } - - /** - * Generates primary key by calling Oracle sequence corresponding to the - * <code>dbEntity</code>. Executed SQL looks like this: - * - * <pre> - * SELECT pk_table_name.nextval FROM DUAL - * </pre> - * - * @since 3.0 - */ - @Override - protected long longPkFromDatabase(DataNode node, DbEntity entity) { - - DbKeyGenerator pkGenerator = entity.getPrimaryKeyGenerator(); - String pkGeneratingSequenceName; - if (pkGenerator != null && DbKeyGenerator.ORACLE_TYPE.equals(pkGenerator.getGeneratorType()) - && pkGenerator.getGeneratorName() != null) { - pkGeneratingSequenceName = pkGenerator.getGeneratorName(); - } else { - pkGeneratingSequenceName = adapter.getQuotingStrategy(entity).quotedFQN(entity.getCatalog(), - entity.getSchema(), sequenceName(entity)); - } - - try (Connection con = node.getDataSource().getConnection()) { - try (Statement st = con.createStatement()) { - String sql = selectNextValQuery(pkGeneratingSequenceName); - adapter.getJdbcEventLogger().log(sql); - - try (ResultSet rs = st.executeQuery(sql)) { - if (!rs.next()) { - throw new CayenneRuntimeException("Error generating pk for DbEntity %s", entity.getName()); - } - return rs.getLong(1); - } - } - } catch (SQLException e) { - throw new CayenneRuntimeException("Error generating pk for DbEntity %s", e, entity.getName()); - } - } - - protected int pkCacheSize(DbEntity entity) { - // use custom generator if possible - DbKeyGenerator keyGenerator = entity.getPrimaryKeyGenerator(); - if (keyGenerator != null && DbKeyGenerator.ORACLE_TYPE.equals(keyGenerator.getGeneratorType()) - && keyGenerator.getGeneratorName() != null) { - - Integer size = keyGenerator.getKeyCacheSize(); - return (size != null && size >= 1) ? size : getPkCacheSize(); - } else { - return getPkCacheSize(); - } - } - - protected String sequenceName(DbEntity entity) { - - // use custom generator if possible - DbKeyGenerator keyGenerator = entity.getPrimaryKeyGenerator(); - if (keyGenerator != null - && DbKeyGenerator.ORACLE_TYPE.equals(keyGenerator.getGeneratorType()) - && keyGenerator.getGeneratorName() != null) { - - return keyGenerator.getGeneratorName().toLowerCase(); - } else { - return getSequencePrefix() + entity.getName().toLowerCase(); - } - } - - protected String getSequencePrefix() { - return _SEQUENCE_PREFIX; - } - - /** - * Fetches a list of existing sequences that might match Cayenne generated - * ones. - */ - protected List<String> getExistingSequences(DataNode node) { - - // check existing sequences - try (Connection con = node.getDataSource().getConnection()) { - try (Statement sel = con.createStatement()) { - String sql = selectAllSequencesQuery(); - adapter.getJdbcEventLogger().log(sql); - - try (ResultSet rs = sel.executeQuery(sql)) { - List<String> sequenceList = new ArrayList<>(); - while (rs.next()) { - String name = rs.getString(1); - if (name != null) { - sequenceList.add(name.trim()); - } - } - return sequenceList; - } - } - } catch (SQLException e) { - throw new CayenneRuntimeException("Error fetching existing sequences", e); - } - } } diff --git a/cayenne/src/main/java/org/apache/cayenne/dba/postgres/PostgresPkGenerator.java b/cayenne/src/main/java/org/apache/cayenne/dba/postgres/PostgresPkGenerator.java index c8b9fbbf9..1a863b473 100644 --- a/cayenne/src/main/java/org/apache/cayenne/dba/postgres/PostgresPkGenerator.java +++ b/cayenne/src/main/java/org/apache/cayenne/dba/postgres/PostgresPkGenerator.java @@ -20,13 +20,13 @@ package org.apache.cayenne.dba.postgres; import org.apache.cayenne.dba.JdbcAdapter; -import org.apache.cayenne.dba.oracle.OraclePkGenerator; +import org.apache.cayenne.dba.SequencePkGenerator; import org.apache.cayenne.map.DbEntity; /** * Default PK generator for PostgreSQL that uses sequences for PK generation. */ -public class PostgresPkGenerator extends OraclePkGenerator { +public class PostgresPkGenerator extends SequencePkGenerator { /** * Used by DI diff --git a/cayenne/src/test/java/org/apache/cayenne/dba/oracle/OraclePkGeneratorIT.java b/cayenne/src/test/java/org/apache/cayenne/dba/SequencePkGeneratorTest.java similarity index 69% rename from cayenne/src/test/java/org/apache/cayenne/dba/oracle/OraclePkGeneratorIT.java rename to cayenne/src/test/java/org/apache/cayenne/dba/SequencePkGeneratorTest.java index a5a07d090..f760fdedb 100644 --- a/cayenne/src/test/java/org/apache/cayenne/dba/oracle/OraclePkGeneratorIT.java +++ b/cayenne/src/test/java/org/apache/cayenne/dba/SequencePkGeneratorTest.java @@ -17,43 +17,34 @@ * under the License. ****************************************************************/ -package org.apache.cayenne.dba.oracle; +package org.apache.cayenne.dba; -import org.apache.cayenne.di.AdhocObjectFactory; +import org.apache.cayenne.dba.oracle.OraclePkGenerator; import org.apache.cayenne.map.DbEntity; import org.apache.cayenne.map.DbKeyGenerator; -import org.apache.cayenne.unit.CayenneProjects; -import org.apache.cayenne.unit.CayenneTestsEnv; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.RegisterExtension; import static org.junit.jupiter.api.Assertions.assertEquals; -public class OraclePkGeneratorIT { +public class SequencePkGeneratorTest { - @RegisterExtension - static final CayenneTestsEnv env = CayenneTestsEnv.forProject(CayenneProjects.TESTMAP_PROJECT); - - protected AdhocObjectFactory objectFactory; - - private OraclePkGenerator pkGenerator; + private SequencePkGenerator pkGenerator; @BeforeEach - public void setUp() throws Exception { - objectFactory = env.adhocObjectFactory(); - OracleAdapter adapter = objectFactory.newInstance(OracleAdapter.class, OracleAdapter.class.getName()); - pkGenerator = new OraclePkGenerator(adapter); + public void setUp() { + // use a concrete subclass to exercise the shared sequence naming logic + pkGenerator = new OraclePkGenerator(); } @Test - public void sequenceNameDefault() throws Exception { + public void sequenceNameDefault() { DbEntity entity = new DbEntity("TEST_ENTITY"); assertEquals("pk_test_entity", pkGenerator.sequenceName(entity)); } @Test - public void sequenceNameCustom1() throws Exception { + public void sequenceNameCustom1() { DbEntity entity = new DbEntity("TEST_ENTITY"); DbKeyGenerator customGenerator = new DbKeyGenerator(); customGenerator.setGeneratorType(DbKeyGenerator.ORACLE_TYPE); @@ -63,7 +54,7 @@ public class OraclePkGeneratorIT { } @Test - public void sequenceNameCustom2() throws Exception { + public void sequenceNameCustom2() { DbEntity entity = new DbEntity("TEST_ENTITY"); DbKeyGenerator customGenerator = new DbKeyGenerator(); customGenerator.setGeneratorType(DbKeyGenerator.NAMED_SEQUENCE_TABLE_TYPE);
