diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/NativeJDBCSeq.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/NativeJDBCSeq.java index 9ed5e5e0b..b49372693 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/NativeJDBCSeq.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/NativeJDBCSeq.java @@ -81,6 +81,8 @@ public class NativeJDBCSeq private String _tableName = "DUAL"; private boolean _subTable = false; + private String _schema = null; + /** * The sequence name. Defaults to OPENJPA_SEQUENCE. */ @@ -169,9 +171,12 @@ public class NativeJDBCSeq if (group.isKnownSequence(_seqName)) return; - String schemaName = Strings.getPackageName(_seqName); - if (schemaName.length() == 0) - schemaName = Schemas.getNewTableSchema(_conf); + String schemaName = getSchema(); + if (schemaName == null || schemaName.length() == 0) { + schemaName = Strings.getPackageName(_seqName); + if (schemaName.length() == 0) + schemaName = Schemas.getNewTableSchema(_conf); + } // create table in this group Schema schema = group.getSchema(schemaName); @@ -228,9 +233,17 @@ public class NativeJDBCSeq */ private void buildSequence() { String seqName = Strings.getClassName(_seqName); - String schemaName = Strings.getPackageName(_seqName); - if (schemaName.length() == 0) - schemaName = Schemas.getNewTableSchema(_conf); + // JPA 2 added schema as a configurable attribute on + // sequence generator. OpenJPA <= 1.x allowed this via + // schema.sequence on the sequence name. Specifying a schema + // name on the annotation or in the orm will override the old + // behavior. + String schemaName = _schema; + if (schemaName == null || schemaName.length() == 0) { + schemaName = Strings.getPackageName(_seqName); + if (schemaName.length() == 0) + schemaName = Schemas.getNewTableSchema(_conf); + } // build the sequence in one of the designated schemas SchemaGroup group = new SchemaGroup(); @@ -380,4 +393,12 @@ public class NativeJDBCSeq return false; return true; } + + public void setSchema(String _schema) { + this._schema = _schema; + } + + public String getSchema() { + return _schema; + } } diff --git a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/SequenceMetaData.java b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/SequenceMetaData.java index 1daf9cc78..a917cd6a3 100644 --- a/openjpa-kernel/src/main/java/org/apache/openjpa/meta/SequenceMetaData.java +++ b/openjpa-kernel/src/main/java/org/apache/openjpa/meta/SequenceMetaData.java @@ -67,6 +67,8 @@ public class SequenceMetaData private static final String PROP_INITIAL_VALUE = "InitialValue"; private static final String PROP_ALLOCATE = "Allocate"; private static final String PROP_INCREMENT = "Increment"; + private static final String PROP_SCHEMA = "Schema"; + private static final String PROP_CATALOG = "Catalog"; private static final Localizer _loc = Localizer.forPackage (SequenceMetaData.class); @@ -87,6 +89,8 @@ public class SequenceMetaData private int _increment = -1; private int _allocate = -1; private int _initial = -1; + private String _schema = null; + private String _catalog = null; // instantiated lazily private transient Seq _instance = null; @@ -306,6 +310,28 @@ public class SequenceMetaData } } + /* + * Set/Get the schema name + */ + public void setSchema(String schema) { + this._schema = schema; + } + + public String getSchema() { + return _schema; + } + + /* + * Set/Get the catalog name + */ + public void setCatalog(String catalog) { + this._catalog = catalog; + } + + public String getCatalog() { + return _catalog; + } + /** * Create a new plugin value for sequences. Returns a standard * {@link SeqValue} by default. @@ -322,6 +348,8 @@ public class SequenceMetaData appendProperty(props, PROP_INITIAL_VALUE, _initial); appendProperty(props, PROP_ALLOCATE, _allocate); appendProperty(props, PROP_INCREMENT, _increment); + appendProperty(props, PROP_SCHEMA, _schema); + appendProperty(props, PROP_CATALOG, _catalog); } /** diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/NativeORMSequenceEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/NativeORMSequenceEntity.java new file mode 100644 index 000000000..520e977e6 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/NativeORMSequenceEntity.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.jdbc.annotations; + +// This entity is defined via sequence-orm.xml +public class NativeORMSequenceEntity { + + public static final String SCHEMA_NAME = "ORMSCHEMA"; + public static final String SEQ_NAME = "ORMSEQ"; + + private int id; + + private String name; + + public void setId(int id) { + this.id = id; + } + + public int getId() { + return id; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/NativeSequenceEntity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/NativeSequenceEntity.java new file mode 100644 index 000000000..87a259468 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/NativeSequenceEntity.java @@ -0,0 +1,61 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.jdbc.annotations; + +import javax.persistence.Basic; +import javax.persistence.Entity; +import javax.persistence.GeneratedValue; +import javax.persistence.Table; + +import static javax.persistence.GenerationType.SEQUENCE; +import javax.persistence.Id; +import javax.persistence.SequenceGenerator; + +@Entity +@Table(name="NativeSeqEnt") +public class NativeSequenceEntity { + + public static final String SEQ_NAME = "SCHEMASEQ"; + public static final String SCHEMA_NAME = "SEQSCHEMA"; + + @Id + @SequenceGenerator(name="seq_with_schema", sequenceName=SEQ_NAME, + schema=SCHEMA_NAME, initialValue=10) + @GeneratedValue(strategy=SEQUENCE, generator="seq_with_schema") + private int id; + + @Basic + private String name; + + public void setId(int id) { + this.id = id; + } + + public int getId() { + return id; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestSequenceGenerator.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestSequenceGenerator.java new file mode 100644 index 000000000..a3080aa76 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/annotations/TestSequenceGenerator.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.openjpa.persistence.jdbc.annotations; + +import java.sql.Connection; +import java.sql.DatabaseMetaData; +import java.sql.SQLException; + +import org.apache.openjpa.jdbc.conf.JDBCConfiguration; +import org.apache.openjpa.jdbc.schema.Sequence; +import org.apache.openjpa.jdbc.sql.DBDictionary; +import org.apache.openjpa.persistence.OpenJPAEntityManagerSPI; +import org.apache.openjpa.persistence.test.AllowFailure; +import org.apache.openjpa.persistence.test.SingleEMFTestCase; + +/** + * Test for sequence generator. Some databases currently (Derby) do not support + * native sequences so this method is allowed to fail until such time when the + * test corpus can support database or dictionary support specific + * configuration. + * + * @author Jeremy Bauer + */ +@AllowFailure(true) +public class TestSequenceGenerator extends SingleEMFTestCase { + + public void setUp() + throws Exception { + setUp(NativeSequenceEntity.class, + NativeORMSequenceEntity.class, + CLEAR_TABLES); + } + + @Override + protected String getPersistenceUnitName() { + return "native-seq-pu"; + } + + /* + * Test use of the schema attribute on a native sequence generator. Some + * databases do not support native sequences so this method is + * currently allowed to fail. + */ + public void testSequenceSchema() { + OpenJPAEntityManagerSPI em = emf.createEntityManager(); + NativeSequenceEntity nse = new NativeSequenceEntity(); + nse.setName("Test"); + em.getTransaction().begin(); + em.persist(nse); + em.getTransaction().commit(); + em.refresh(nse); + // Validate the id is >= the initial value + // Assert the sequence was created in the DB + assertTrue(sequenceExists(em, NativeSequenceEntity.SCHEMA_NAME, + NativeSequenceEntity.SEQ_NAME)); + // Assert the id is >= the initial value + assertTrue(nse.getId() >= 10); + em.close(); + } + + /* + * Test use of the schema element on a native sequence generator. Some + * databases do not support native sequences so this method is + * currently allowed to fail. + */ + public void testORMSequenceSchema() { + OpenJPAEntityManagerSPI em = emf.createEntityManager(); + NativeORMSequenceEntity nse = new NativeORMSequenceEntity(); + nse.setName("TestORM"); + em.getTransaction().begin(); + em.persist(nse); + em.getTransaction().commit(); + em.refresh(nse); + // Assert the sequence was created in the DB + assertTrue(sequenceExists(em, NativeORMSequenceEntity.SCHEMA_NAME, + NativeORMSequenceEntity.SEQ_NAME)); + // Assert the id is >= the initial value + assertTrue(nse.getId() >= 2000); + em.close(); + } + + /** + * Method to verify a sequence was created for the given schema and + * sequence name. + */ + private boolean sequenceExists(OpenJPAEntityManagerSPI em, String schema, + String sequence) { + JDBCConfiguration conf = (JDBCConfiguration) emf.getConfiguration(); + DBDictionary dict = conf.getDBDictionaryInstance(); + Connection conn = (Connection)em.getConnection(); + try { + DatabaseMetaData dbmd = conn.getMetaData(); + Sequence[] seqs = dict.getSequences(dbmd, null, schema, + sequence, conn); + if (seqs != null && seqs.length == 1 && + seqs[0].getName().equalsIgnoreCase(sequence) && + seqs[0].getSchemaName().equalsIgnoreCase(schema)) + return true; + } catch (SQLException e) { + e.printStackTrace(); + } + return false; + } +} diff --git a/openjpa-persistence-jdbc/src/test/resources/META-INF/persistence.xml b/openjpa-persistence-jdbc/src/test/resources/META-INF/persistence.xml index 8f7373753..d11531faa 100644 --- a/openjpa-persistence-jdbc/src/test/resources/META-INF/persistence.xml +++ b/openjpa-persistence-jdbc/src/test/resources/META-INF/persistence.xml @@ -113,4 +113,14 @@ + + org/apache/openjpa/persistence/sequence/sequence-orm.xml + org.apache.openjpa.persistence.jdbc.annotations.NativeSequenceEntity + org.apache.openjpa.persistence.jdbc.annotations.NativeORMSequenceEntity + + + + + diff --git a/openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/sequence/sequence-orm.xml b/openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/sequence/sequence-orm.xml new file mode 100644 index 000000000..d11d681a0 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/sequence/sequence-orm.xml @@ -0,0 +1,43 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java index 20b754c13..02adf96ac 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataParser.java @@ -1570,6 +1570,8 @@ public class AnnotationPersistenceMetaDataParser String seq = gen.sequenceName(); int initial = gen.initialValue(); int allocate = gen.allocationSize(); + String schema = gen.schema(); + String catalog = gen.catalog(); // don't allow initial of 0 b/c looks like def value if (initial == 0) initial = 1; @@ -1593,6 +1595,8 @@ public class AnnotationPersistenceMetaDataParser meta.setSequence(seq); meta.setInitialValue(initial); meta.setAllocate(allocate); + meta.setSchema(schema); + meta.setCatalog(catalog); meta.setSource(getSourceFile(), (el instanceof Class) ? el : null, meta.SRC_ANNOTATIONS); } diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataSerializer.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataSerializer.java index b07336c64..66d1330ad 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataSerializer.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/AnnotationPersistenceMetaDataSerializer.java @@ -696,6 +696,10 @@ public class AnnotationPersistenceMetaDataSerializer ab.add("initialValue", meta.getInitialValue()); if (meta.getAllocate() != 50 && meta.getAllocate() != -1) ab.add("allocationSize", meta.getAllocate()); + if (meta.getSchema() != null) + ab.add("schema", meta.getSchema()); + if (meta.getCatalog() != null) + ab.add("catalog", meta.getCatalog()); } /** diff --git a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/XMLPersistenceMetaDataParser.java b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/XMLPersistenceMetaDataParser.java index a8179f9d3..785d05e5b 100644 --- a/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/XMLPersistenceMetaDataParser.java +++ b/openjpa-persistence/src/main/java/org/apache/openjpa/persistence/XMLPersistenceMetaDataParser.java @@ -958,6 +958,8 @@ public class XMLPersistenceMetaDataParser int initial = val == null ? 1 : Integer.parseInt(val); val = attrs.getValue("allocation-size"); int allocate = val == null ? 50 : Integer.parseInt(val); + String schema = attrs.getValue("schema"); + String catalog = attrs.getValue("catalog"); String clsName, props; if (seq == null || seq.indexOf('(') == -1) { @@ -973,6 +975,8 @@ public class XMLPersistenceMetaDataParser meta.setSequence(seq); meta.setInitialValue(initial); meta.setAllocate(allocate); + meta.setSchema(schema); + meta.setCatalog(catalog); Object cur = currentElement(); Object scope = (cur instanceof ClassMetaData)