From ff4a96e707337123c3e94ba641b504101dd05c79 Mon Sep 17 00:00:00 2001 From: Pinaki Poddar Date: Thu, 26 Jun 2008 22:04:06 +0000 Subject: [PATCH] OPENJPA-340: Support for @UniqueConstraints on @TableGenerator, @SecondaryTable and @JoinTable. git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@672038 13f79535-47bb-0310-9956-ffa450edef68 --- .../openjpa/jdbc/kernel/TableJDBCSeq.java | 40 +++++- .../openjpa/jdbc/meta/ClassMappingInfo.java | 134 ++++++++++++------ .../openjpa/jdbc/meta/FieldMapping.java | 19 ++- .../openjpa/jdbc/meta/FieldMappingInfo.java | 66 ++++++++- .../apache/openjpa/jdbc/meta/MappingInfo.java | 4 +- .../openjpa/jdbc/meta/SequenceMapping.java | 23 ++- .../org/apache/openjpa/jdbc/schema/Table.java | 16 +++ .../apache/openjpa/jdbc/schema/Unique.java | 25 +++- .../openjpa/jdbc/meta/localizer.properties | 8 +- .../AnnotationPersistenceMappingParser.java | 93 +++++++----- ...nnotationPersistenceMappingSerializer.java | 4 +- .../jdbc/XMLPersistenceMappingParser.java | 41 ++++-- .../jdbc/XMLPersistenceMappingSerializer.java | 4 +- .../persistence/jdbc/localizer.properties | 7 +- .../persistence/jdbc/unique/TestUnique.java | 78 ++++++++++ .../persistence/jdbc/unique/UniqueA.java | 77 ++++++++++ .../persistence/jdbc/unique/UniqueB.java | 38 +++++ 17 files changed, 568 insertions(+), 109 deletions(-) create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/TestUnique.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/UniqueA.java create mode 100644 openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/UniqueB.java diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/TableJDBCSeq.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/TableJDBCSeq.java index 1003a7ff9..c2eedf5b1 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/TableJDBCSeq.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/TableJDBCSeq.java @@ -28,6 +28,7 @@ import java.util.HashMap; import javax.transaction.NotSupportedException; +import org.apache.commons.lang.StringUtils; import org.apache.openjpa.jdbc.conf.JDBCConfiguration; import org.apache.openjpa.jdbc.conf.JDBCConfigurationImpl; import org.apache.openjpa.jdbc.meta.ClassMapping; @@ -38,6 +39,7 @@ import org.apache.openjpa.jdbc.schema.SchemaGroup; import org.apache.openjpa.jdbc.schema.SchemaTool; import org.apache.openjpa.jdbc.schema.Schemas; import org.apache.openjpa.jdbc.schema.Table; +import org.apache.openjpa.jdbc.schema.Unique; import org.apache.openjpa.jdbc.sql.DBDictionary; import org.apache.openjpa.jdbc.sql.RowImpl; import org.apache.openjpa.jdbc.sql.SQLBuffer; @@ -49,6 +51,8 @@ import org.apache.openjpa.lib.util.Localizer; import org.apache.openjpa.lib.util.Options; import org.apache.openjpa.meta.JavaTypes; import org.apache.openjpa.util.InvalidStateException; +import org.apache.openjpa.util.UserException; + import serp.util.Numbers; import serp.util.Strings; @@ -86,6 +90,7 @@ public class TableJDBCSeq private String _table = "OPENJPA_SEQUENCE_TABLE"; private String _seqColumnName = "SEQUENCE_VALUE"; private String _pkColumnName = "ID"; + private String[] _uniqueColumnNames; private Column _seqColumn = null; private Column _pkColumn = null; @@ -191,6 +196,20 @@ public class TableJDBCSeq public void setInitialValue(int intValue) { _intValue = intValue; } + + /** + * Sets the names of the columns on which a unique constraint is set. + * @param columnsNames are passed as a single String concatenated with + * a '|' character. This method parses it back to array of Strings. + */ + public void setUniqueColumns(String columnNames) { + _uniqueColumnNames = (StringUtils.isEmpty(columnNames)) + ? null : StringUtils.split(columnNames, '|'); + } + + public String getUniqueColumns() { + return StringUtils.join(_uniqueColumnNames, '|'); + } /** * @deprecated Use {@link #setAllocate}. Retained for backwards @@ -235,7 +254,12 @@ public class TableJDBCSeq if (schema == null) schema = group.addSchema(schemaName); - schema.importTable(_pkColumn.getTable()); + Table copy = schema.importTable(_pkColumn.getTable()); + // importTable() does not import unique constraints + Unique[] uniques = _pkColumn.getTable().getUniques(); + for (Unique u : uniques) { + copy.importUnique(u); + } // we need to reset the table name in the column with the // fully qualified name for matching the table name from the // Column. @@ -244,7 +268,6 @@ public class TableJDBCSeq // some databases require to create an index for the sequence table _conf.getDBDictionaryInstance().createIndexIfNecessary(schema, _table, _pkColumn); - } } @@ -361,6 +384,19 @@ public class TableJDBCSeq (_seqColumnName, table)); _seqColumn.setType(dict.getPreferredType(Types.BIGINT)); _seqColumn.setJavaType(JavaTypes.LONG); + + if (_uniqueColumnNames != null) { + String uniqueName = dict.getValidUniqueName("UNQ", table); + Unique u = table.addUnique(uniqueName); + for (String columnName : _uniqueColumnNames) { + if (!table.containsColumn(columnName)) + throw new UserException(_loc.get("unique-missing-column", + columnName, table.getName(), table.getColumnNames())); + Column col = table.getColumn(columnName); + u.addColumn(col); + } + } + } /** diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ClassMappingInfo.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ClassMappingInfo.java index 0099b1315..f39b55219 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ClassMappingInfo.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ClassMappingInfo.java @@ -19,13 +19,13 @@ package org.apache.openjpa.jdbc.meta; import java.io.File; +import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Collection; -import java.util.ArrayList; +import java.util.Map.Entry; import org.apache.commons.lang.StringUtils; import org.apache.openjpa.jdbc.meta.strats.FullClassStrategy; @@ -38,6 +38,7 @@ import org.apache.openjpa.jdbc.schema.Unique; import org.apache.openjpa.lib.meta.SourceTracker; import org.apache.openjpa.lib.util.Localizer; import org.apache.openjpa.lib.xml.Commentable; +import org.apache.openjpa.meta.MetaDataContext; import org.apache.openjpa.util.UserException; /** @@ -64,7 +65,8 @@ public class ClassMappingInfo private File _file = null; private int _srcType = SRC_OTHER; private String[] _comments = null; - private Collection _uniques = null;//Unique + // Unique constraints indexed by primary or secondary table name + private Map> _uniques; /** * The described class name. @@ -221,11 +223,12 @@ public class ClassMappingInfo _seconds = new HashMap(); _seconds.put(tableName, cols); } - + /** - * Return the table for the given class. + * Return the named table for the given class. */ - public Table getTable(final ClassMapping cls, boolean adapt) { + public Table getTable(final ClassMapping cls, String tableName, + boolean adapt) { Table t = createTable(cls, new TableDefaults() { public String get(Schema schema) { // delay this so that we don't do schema reflection for unique @@ -233,13 +236,20 @@ public class ClassMappingInfo return cls.getMappingRepository().getMappingDefaults(). getTableName(cls, schema); } - }, _schemaName, _tableName, adapt); + }, _schemaName, tableName, adapt); t.setComment(cls.getTypeAlias() == null ? cls.getDescribedType().getName() : cls.getTypeAlias()); return t; } - + + /** + * Return the primary table for the given class. + */ + public Table getTable(final ClassMapping cls, boolean adapt) { + return getTable(cls, _tableName, adapt); + } + /** * Return the datastore identity columns for the given class, based on the * given templates. @@ -340,51 +350,87 @@ public class ClassMappingInfo _seconds.put(key, cinfo._seconds.get(key)); } } - if (cinfo._uniques != null) - _uniques = new ArrayList(cinfo._uniques); - } + if (cinfo._uniques != null) { + if (_uniques == null) + _uniques = new HashMap>(); + for (Entry> entry : cinfo._uniques.entrySet()) + if (!_uniques.containsKey(entry.getKey())) + _uniques.put(entry.getKey(), entry.getValue()); + } - public void addUnique(Unique unique) { - if (unique == null) - return; + } + + /** + * Add a unique constraint for the given table. + * @param table must be primary table or secondary table name added a + * priori to this receiver. + * @param unique the unique constraint. null means no-op. + */ + public void addUnique(String table, Unique unique) { + if (!StringUtils.equals(_tableName, table) && + (_seconds == null || !_seconds.containsKey(table))) { + throw new UserException(_loc.get("unique-no-table", + new Object[]{table, _className, _tableName, + ((_seconds == null) ? "" : _seconds.keySet())})); + } + if (unique == null) + return; if (_uniques == null) - _uniques = new ArrayList(); - _uniques.add(unique); + _uniques = new HashMap>(); + unique.setTableName(table); + List uniques = _uniques.get(table); + if (uniques == null) { + uniques = new ArrayList(); + uniques.add(unique); + _uniques.put(table, uniques); + } else { + uniques.add(unique); + } } - public Unique[] getUniques() { - return (_uniques == null) ? new Unique[0] : - (Unique[])_uniques.toArray(new Unique[_uniques.size()]); + /** + * Get the unique constraints of the given primary or secondary table. + */ + public Unique[] getUniques(String table) { + if (_uniques == null || _uniques.isEmpty() + || _uniques.containsKey(table)) + return new Unique[0]; + List uniques = _uniques.get(table); + return uniques.toArray(new Unique[uniques.size()]); } - public Unique[] getUniques(ClassMapping cm, boolean adapt) { + /** + * Get all the unique constraints associated with both the primary and/or + * secondary tables. + * + */ + public Unique[] getUniques(MetaDataContext cm, boolean adapt) { if (_uniques == null || _uniques.isEmpty()) return new Unique[0]; - - Iterator uniqueConstraints = _uniques.iterator(); - Table table = cm.getTable(); - Collection result = new ArrayList(); - while (uniqueConstraints.hasNext()) { - Unique template = (Unique) uniqueConstraints.next(); - Column[] templateColumns = template.getColumns(); - Column[] uniqueColumns = new Column[templateColumns.length]; - boolean missingColumn = true; - for (int i=0; i result = new ArrayList(); + for (String tableName : _uniques.keySet()) { + List uniqueConstraints = _uniques.get(tableName); + for (Unique template : uniqueConstraints) { + Column[] templateColumns = template.getColumns(); + Column[] uniqueColumns = new Column[templateColumns.length]; + Table table = getTable((ClassMapping)cm, tableName, adapt); + for (int i=0; i _joinTableUniques; // Unique constraints on the JoinTable /** * The user-supplied name of the table for this field. @@ -185,8 +191,47 @@ public class FieldMappingInfo getJoinUnique(field, fk.getTable(), fk.getColumns()); return createUnique(field, "join", unq, fk.getColumns(), adapt); } - + /** + * Add Unique Constraint to the Join Table. + */ + public void addJoinTableUnique(Unique u) { + if (_joinTableUniques == null) + _joinTableUniques = new ArrayList(); + _joinTableUniques.add(u); + } + + /** + * Get the unique constraints associated with the Sequence table. + */ + public Unique[] getJoinTableUniques(FieldMapping field, boolean def, + boolean adapt) { + return getUniques(field, _joinTableUniques, def, adapt); + } + + private Unique[] getUniques(FieldMapping field, List uniques, + boolean def, boolean adapt) { + if (uniques == null || uniques.isEmpty()) + return new Unique[0]; + Collection result = new ArrayList(); + for (Unique template : uniques) { + Column[] templateColumns = template.getColumns(); + Column[] uniqueColumns = new Column[templateColumns.length]; + Table table = getTable(field, true, adapt); + for (int i=0; i(); + for (Unique unique:unqs) { + Unique copy = new Unique(); + copy.setName(unique.getName()); + copy.setDeferred(unique.isDeferred()); + _joinTableUniques.add(unique); + } + } + public boolean hasSchemaComponents() { return super.hasSchemaComponents() || _tableName != null diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingInfo.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingInfo.java index 9172ff63a..ec20b8f74 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingInfo.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingInfo.java @@ -927,7 +927,7 @@ public abstract class MappingInfo context, dict.platform)); deferred = false; } - + Unique unq = table.addUnique(name); unq.setDeferred(deferred); unq.setColumns(cols); @@ -1534,7 +1534,7 @@ public abstract class MappingInfo _unq.setName(unq.getName()); _unq.setDeferred(unq.isDeferred()); } - + /** * Sets internal constraint and column information to match given mapped * constraint. diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/SequenceMapping.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/SequenceMapping.java index adc2c3f44..981d4ae06 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/SequenceMapping.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/SequenceMapping.java @@ -19,11 +19,16 @@ package org.apache.openjpa.jdbc.meta; import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import org.apache.commons.lang.StringUtils; import org.apache.openjpa.jdbc.conf.JDBCSeqValue; import org.apache.openjpa.jdbc.kernel.ClassTableJDBCSeq; import org.apache.openjpa.jdbc.kernel.TableJDBCSeq; import org.apache.openjpa.jdbc.kernel.ValueTableJDBCSeq; +import org.apache.openjpa.jdbc.schema.Unique; import org.apache.openjpa.lib.conf.PluginValue; import org.apache.openjpa.meta.SequenceMetaData; @@ -55,13 +60,15 @@ public class SequenceMapping private static final String PROP_SEQUENCE_COL = "SequenceColumn"; private static final String PROP_PK_COL = "PrimaryKeyColumn"; private static final String PROP_PK_VALUE = "PrimaryKeyValue"; + private static final String PROP_UNIQUE = "UniqueColumns"; private File _mapFile = null; private String _table = null; private String _sequenceColumn = null; private String _primaryKeyColumn = null; private String _primaryKeyValue = null; - + private String[] _uniqueColumns = null; + public SequenceMapping(String name, MappingRepository repos) { super(name, repos); } @@ -138,6 +145,14 @@ public class SequenceMapping _primaryKeyValue = primaryKeyValue; } + public void setUniqueColumns(String[] cols) { + _uniqueColumns = cols; + } + + public String[] getUniqueColumns() { + return _uniqueColumns; + } + protected PluginValue newPluginValue(String property) { return new JDBCSeqValue(property); } @@ -148,5 +163,11 @@ public class SequenceMapping appendProperty(props, PROP_SEQUENCE_COL, _sequenceColumn); appendProperty(props, PROP_PK_COL, _primaryKeyColumn); appendProperty(props, PROP_PK_VALUE, _primaryKeyValue); + // Array of unique column names are passed to configuration + // as a single string "x|y|z". The configurable (TableJDBCSeq) must + // parse it back. + if (_uniqueColumns != null && _uniqueColumns.length > 0) + appendProperty(props, PROP_UNIQUE, + StringUtils.join(_uniqueColumns,'|')); } } diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Table.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Table.java index a79522a7d..e30d0f648 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Table.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Table.java @@ -252,6 +252,11 @@ public class Table return _rels; } + public String[] getColumnNames() { + return _colMap == null ? new String[0] : + (String[])_colMap.keySet().toArray(new String[_colMap.size()]); + } + /** * Return the column with the given name, or null if none. */ @@ -260,6 +265,17 @@ public class Table return null; return (Column) _colMap.get(name.toUpperCase()); } + + /** + * Affirms if this table contains the column of the given name without any + * side-effect. + * @see Table#getColumn(String) can have side-effect of creating a column + * for dynamic table implementation. + */ + public boolean containsColumn(String name) { + return name != null && _colMap != null + && _colMap.containsKey(name.toUpperCase()); + } /** * Add a column to the table. diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Unique.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Unique.java index 35338e796..231ebdda7 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Unique.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Unique.java @@ -18,6 +18,8 @@ */ package org.apache.openjpa.jdbc.schema; +import org.apache.commons.lang.StringUtils; + /** * Represents a unique constraint. It can also represent a partial constraint. * @@ -25,11 +27,12 @@ package org.apache.openjpa.jdbc.schema; */ public class Unique extends LocalConstraint { - + private boolean _isAutoSetName = false; /** * Default constructor. */ public Unique() { + _isAutoSetName = true; } /** @@ -45,6 +48,26 @@ public class Unique public boolean isLogical() { return false; } + + public void addColumn(Column col) { + super.addColumn(col); + col.setNotNull(true); + if (_isAutoSetName && getTable() == null) { + String pre = StringUtils.isEmpty(getName()) ? "UNQ" : getName(); + setName(pre + "_" + col.getName()); + _isAutoSetName = true; + } + } + + /** + * Set the name of the constraint. This method cannot be called if the + * constraint already belongs to a table. + */ + public void setName(String name) { + super.setName(name); + _isAutoSetName = false; + } + /** * Return true if the structure of this primary key matches that of diff --git a/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/meta/localizer.properties b/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/meta/localizer.properties index bcd8de3e0..e53d1361e 100644 --- a/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/meta/localizer.properties +++ b/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/meta/localizer.properties @@ -410,6 +410,8 @@ untraversable-path: Result path "{2}" in result type "{1}" of mapping "{0}" \ attempts to traverse through a non-relation field. num-cols-path: Result path "{2}" in result type "{1}" of mapping "{0}" \ attempts to map a field that does not have exactly 1 column. -missing-unique-column: A unique constraint specified in mapping of class "{0}" \ - to table "{1}" includes a column "{2}". However, the column does not \ - exist in "{1}" table. +unique-missing-column: The column "{1}" in a unique constraint in "{0}" on \ + table "{2}" can not be found in the list of available columns "{3}". +unique-no-table: A unique constraint on table "{0}" can not be added to \ + mapping of class "{1}" because the table does neither match its primary \ + table "{2}" nor any of its secondary table(s) "{3}". \ No newline at end of file diff --git a/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java b/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java index 8bd781835..d9908d71c 100644 --- a/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java +++ b/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java @@ -61,6 +61,7 @@ import org.apache.openjpa.jdbc.meta.ClassMapping; import org.apache.openjpa.jdbc.meta.ClassMappingInfo; import org.apache.openjpa.jdbc.meta.Discriminator; import org.apache.openjpa.jdbc.meta.FieldMapping; +import org.apache.openjpa.jdbc.meta.FieldMappingInfo; import org.apache.openjpa.jdbc.meta.MappingInfo; import org.apache.openjpa.jdbc.meta.MappingRepository; import org.apache.openjpa.jdbc.meta.QueryResultMapping; @@ -80,6 +81,7 @@ import org.apache.openjpa.lib.util.Localizer; import org.apache.openjpa.meta.ClassMetaData; import org.apache.openjpa.meta.FieldMetaData; import org.apache.openjpa.meta.JavaTypes; +import org.apache.openjpa.meta.MetaDataContext; import org.apache.openjpa.persistence.AnnotationPersistenceMetaDataParser; import static org.apache.openjpa.persistence.jdbc.MappingTag.*; import org.apache.openjpa.util.InternalException; @@ -244,10 +246,16 @@ public class AnnotationPersistenceMappingParser meta.setAllocate(gen.allocationSize()); meta.setSource(getSourceFile(), (el instanceof Class) ? el : null, meta.SRC_ANNOTATIONS); - - //### EJB3 - if (gen.uniqueConstraints().length > 0 && log.isWarnEnabled()) - log.warn(_loc.get("unique-constraints", name)); + + switch (gen.uniqueConstraints().length) { + case 0: + break; // nothing to do + case 1: + meta.setUniqueColumns(gen.uniqueConstraints()[0].columnNames()); + break; + default: + log.warn(_loc.get("unique-many-on-seq-unsupported", el, name)); + } } @Override @@ -464,8 +472,7 @@ public class AnnotationPersistenceMappingParser Log log = getLog(); String name; - List joins; - boolean warnUnique = false; + List joins = null; for (SecondaryTable table : tables) { name = table.name(); if (StringUtils.isEmpty(name)) @@ -476,14 +483,10 @@ public class AnnotationPersistenceMappingParser joins = new ArrayList(table.pkJoinColumns().length); for (PrimaryKeyJoinColumn join : table.pkJoinColumns()) joins.add(newColumn(join)); - info.setSecondaryTableJoinColumns(name, joins); - } - warnUnique |= table.uniqueConstraints().length > 0; + } + info.setSecondaryTableJoinColumns(name, joins); + addUniqueConstraints(name, cm, info, table.uniqueConstraints()); } - - //### EJB3 - if (warnUnique && log.isWarnEnabled()) - log.warn(_loc.get("unique-constraints", cm)); } /** @@ -494,10 +497,38 @@ public class AnnotationPersistenceMappingParser if (tableName != null) cm.getMappingInfo().setTableName(tableName); - for (UniqueConstraint uniqueConstraint:table.uniqueConstraints()) { - Unique unique = newUnique(cm, null, uniqueConstraint.columnNames()); - cm.getMappingInfo().addUnique(unique); - } + addUniqueConstraints(tableName, cm, cm.getMappingInfo(), + table.uniqueConstraints()); + } + + Unique createUniqueConstraint(MetaDataContext ctx, UniqueConstraint anno) { + String[] columnNames = anno.columnNames(); + if (columnNames == null || columnNames.length == 0) + throw new UserException(_loc.get("unique-no-column", ctx)); + Unique uniqueConstraint = new Unique(); + for (int i=0; i sqls = super.sql; + + assertSQLFragnment(sqls, "CREATE TABLE UNIQUE_A", + "UNIQUE (a1, a2)", + "UNIQUE (a3, a4)"); + assertSQLFragnment(sqls, "CREATE TABLE UNIQUE_B", + "UNIQUE (b1, b2)"); + assertSQLFragnment(sqls, "CREATE TABLE UNIQUE_SECONDARY", + "UNIQUE (sa1)"); + assertSQLFragnment(sqls, "CREATE TABLE UNIQUE_GENERATOR", + "UNIQUE (GEN1, GEN2)"); + assertSQLFragnment(sqls, "CREATE TABLE UNIQUE_JOINTABLE", + "UNIQUE (UNIQUEA_AID, BS_BID)"); + + } + + void assertSQLFragnment(List list, String...keys) { + for (String sql : list) { + String SQL = sql.toUpperCase(); + boolean matched = true; + for (String key : keys) { + String KEY = key.toUpperCase(); + if (SQL.indexOf(KEY) == -1) { + matched = false; + break; + } + } + if (matched) + return; + } + int i = 0; + for (String sql : list) { + i++; + System.out.println(""+i+":"+sql); + } + fail("None of the above SQL contains all keys " + Arrays.toString(keys)); + } +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/UniqueA.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/UniqueA.java new file mode 100644 index 000000000..08d59f741 --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/UniqueA.java @@ -0,0 +1,77 @@ +/* + * 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.unique; + +import java.util.Collection; + +import javax.persistence.Column; +import javax.persistence.Entity; +import javax.persistence.Id; +import javax.persistence.JoinTable; +import javax.persistence.ManyToMany; +import javax.persistence.SecondaryTable; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; + +/** + * Data structures for testing unique constraint settings + * on ORM Annotatations. + * + * Unique columns must be non-nullable. + * + * @author Pinaki Poddar + * + */ +@Entity +@Table(name="UNIQUE_A", + uniqueConstraints={@UniqueConstraint(columnNames={"a1","a2"}), + @UniqueConstraint(columnNames={"a3","a4"})}) +@SecondaryTable(name="UNIQUE_SECONDARY", + uniqueConstraints=@UniqueConstraint(columnNames={"sa1"})) + +public class UniqueA { + @Id + private int aid; + + @Column(unique=true, nullable=false) + private int a1; + + @Column(nullable=false) + private int a2; + + @Column(nullable=false) + private int a3; + + @Column(nullable=false) + private int a4; + + + private int a5; + private int a6; + + @Column(table="UNIQUE_SECONDARY", nullable=false) + private short sa1; + @Column(table="UNIQUE_SECONDARY") + private short sa2; + + @ManyToMany + @JoinTable(name="UNIQUE_JOINTABLE", + uniqueConstraints=@UniqueConstraint(columnNames={"UNIQUEA_AID","BS_BID"})) + private Collection bs; +} diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/UniqueB.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/UniqueB.java new file mode 100644 index 000000000..db39129af --- /dev/null +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/UniqueB.java @@ -0,0 +1,38 @@ +/* + * 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.unique; + +import javax.persistence.*; + +@Entity +@Table(name="UNIQUE_B", + uniqueConstraints={@UniqueConstraint(columnNames={"b1","b2"})}) +public class UniqueB { + @Id + @GeneratedValue(strategy=GenerationType.TABLE, generator="testGenerator") + @TableGenerator(name="testGenerator", table="UNIQUE_GENERATOR", + pkColumnName="GEN1", valueColumnName="GEN2", + uniqueConstraints={@UniqueConstraint(columnNames={"GEN1","GEN2"})}) + private int bid; + + @Column(nullable=false) + private int b1; + @Column(nullable=false) + private int b2; +}