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
This commit is contained in:
Pinaki Poddar 2008-06-26 22:04:06 +00:00
parent 2b3f7ec90c
commit ff4a96e707
17 changed files with 568 additions and 109 deletions

View File

@ -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;
@ -192,6 +197,20 @@ public class TableJDBCSeq
_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
* compatibility of auto-configuration.
@ -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);
}
}
}
/**

View File

@ -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<String,List<Unique>> _uniques;
/**
* The described class name.
@ -223,9 +225,10 @@ public class ClassMappingInfo
}
/**
* 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<String, List<Unique>>();
for (Entry<String, List<Unique>> entry : cinfo._uniques.entrySet())
if (!_uniques.containsKey(entry.getKey()))
_uniques.put(entry.getKey(), entry.getValue());
}
public void addUnique(Unique unique) {
}
/**
* 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<String,List<Unique>>();
unique.setTableName(table);
List<Unique> uniques = _uniques.get(table);
if (uniques == null) {
uniques = new ArrayList<Unique>();
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<Unique> 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();
List<Unique> result = new ArrayList<Unique>();
for (String tableName : _uniques.keySet()) {
List<Unique> uniqueConstraints = _uniques.get(tableName);
for (Unique template : uniqueConstraints) {
Column[] templateColumns = template.getColumns();
Column[] uniqueColumns = new Column[templateColumns.length];
boolean missingColumn = true;
Table table = getTable((ClassMapping)cm, tableName, adapt);
for (int i=0; i<uniqueColumns.length; i++) {
String columnName = templateColumns[i].getName();
Column uniqueColumn = table.getColumn(columnName);
missingColumn = (uniqueColumn == null);
if (missingColumn) {
throw new UserException(_loc.get("missing-unique-column",
cm, table, columnName));
if (!table.containsColumn(columnName)) {
throw new UserException(_loc.get("unique-missing-column",
new Object[]{cm, columnName, tableName,
table.getColumnNames()}));
}
Column uniqueColumn = table.getColumn(columnName);
uniqueColumns[i] = uniqueColumn;
}
Unique unique = super.createUnique(cm, "unique", template,
Unique unique = createUnique(cm, "unique", template,
uniqueColumns, adapt);
if (unique != null)
result.add(unique);
}
return (Unique[]) result.toArray(new Unique[result.size()]);
}
return result.toArray(new Unique[result.size()]);
}
public File getSourceFile() {

View File

@ -70,6 +70,7 @@ public class FieldMapping
private Index _idx = null;
private boolean _outer = false;
private int _fetchMode = Integer.MAX_VALUE;
private Unique[] _joinTableUniques; // Unique constraints on JoinTable
/**
* Constructor.
@ -183,6 +184,14 @@ public class FieldMapping
_unq = unq;
}
public Unique[] getJoinTableUniques() {
return _joinTableUniques;
}
public void setJoinTableUniques(Unique[] unqs) {
_joinTableUniques = unqs;
}
/**
* Index on join foreign key columns.
*/
@ -252,6 +261,13 @@ public class FieldMapping
_val.refSchemaComponents();
_key.refSchemaComponents();
_elem.refSchemaComponents();
if (_joinTableUniques != null) {
for (Unique joinUnique : _joinTableUniques) {
for (Column col : joinUnique.getColumns()) {
col.ref();
}
}
}
}
/**
@ -538,6 +554,7 @@ public class FieldMapping
_io = _info.getColumnIO();
_outer = _info.isJoinOuter();
_unq = _info.getJoinUnique(this, false, adapt);
_joinTableUniques = _info.getJoinTableUniques(this, false, adapt);
_idx = _info.getJoinIndex(this, adapt);
}
}

View File

@ -18,6 +18,8 @@
*/
package org.apache.openjpa.jdbc.meta;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.apache.openjpa.jdbc.schema.Column;
@ -31,7 +33,9 @@ import org.apache.openjpa.jdbc.schema.Unique;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.lib.xml.Commentable;
import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.meta.MetaDataContext;
import org.apache.openjpa.util.MetaDataException;
import org.apache.openjpa.util.UserException;
/**
* Information about the mapping from a field to the schema, in raw form.
@ -40,6 +44,7 @@ import org.apache.openjpa.util.MetaDataException;
* with the relevant pieces of information filled in.
*
* @author Abe White
* @author Pinaki Poddar
*/
public class FieldMappingInfo
extends MappingInfo
@ -53,6 +58,7 @@ public class FieldMappingInfo
private Column _orderCol = null;
private boolean _canOrderCol = true;
private String[] _comments = null;
private List<Unique> _joinTableUniques; // Unique constraints on the JoinTable
/**
* The user-supplied name of the table for this field.
@ -186,6 +192,45 @@ public class FieldMappingInfo
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<Unique>();
_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<Unique> uniques,
boolean def, boolean adapt) {
if (uniques == null || uniques.isEmpty())
return new Unique[0];
Collection<Unique> result = new ArrayList<Unique>();
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<uniqueColumns.length; i++) {
String columnName = templateColumns[i].getName();
Column uniqueColumn = table.getColumn(columnName);
uniqueColumns[i] = uniqueColumn;
}
Unique unique = createUnique(field, "unique", template,
uniqueColumns, adapt);
if (unique != null)
result.add(unique);
}
return result.toArray(new Unique[result.size()]);
}
/**
* Index on the field join.
*/
@ -261,6 +306,7 @@ public class FieldMappingInfo
syncIndex(field, field.getJoinIndex());
syncUnique(field, field.getJoinUnique());
syncJoinTableUniques(field, field.getJoinTableUniques());
syncOrderColumn(field);
syncStrategy(field);
}
@ -291,6 +337,24 @@ public class FieldMappingInfo
_orderCol = null;
}
/**
* Sets internal constraint information to match given mapped constraint.
*/
protected void syncJoinTableUniques(MetaDataContext context, Unique[] unqs) {
if (unqs == null) {
_joinTableUniques = null;
return;
}
_joinTableUniques = new ArrayList<Unique>();
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
|| _orderCol != null;

View File

@ -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,12 +60,14 @@ 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,'|'));
}
}

View File

@ -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.
*/
@ -261,6 +266,17 @@ public class Table
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.
*/

View File

@ -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;
}
/**
@ -46,6 +49,26 @@ public class Unique
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
* the given one (same table, same columns).

View File

@ -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}".

View File

@ -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;
@ -245,9 +247,15 @@ public class AnnotationPersistenceMappingParser
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<Column> joins;
boolean warnUnique = false;
List<Column> joins = null;
for (SecondaryTable table : tables) {
name = table.name();
if (StringUtils.isEmpty(name))
@ -476,14 +483,10 @@ public class AnnotationPersistenceMappingParser
joins = new ArrayList<Column>(table.pkJoinColumns().length);
for (PrimaryKeyJoinColumn join : table.pkJoinColumns())
joins.add(newColumn(join));
}
info.setSecondaryTableJoinColumns(name, joins);
addUniqueConstraints(name, cm, info, table.uniqueConstraints());
}
warnUnique |= table.uniqueConstraints().length > 0;
}
//### EJB3
if (warnUnique && log.isWarnEnabled())
log.warn(_loc.get("unique-constraints", cm));
}
/**
@ -494,9 +497,37 @@ 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<columnNames.length; i++) {
if (StringUtils.isEmpty(columnNames[i]))
throw new UserException(_loc.get("unique-empty-column",
Arrays.toString(columnNames), ctx));
Column column = new Column();
column.setName(columnNames[i]);
uniqueConstraint.addColumn(column);
}
return uniqueConstraint;
}
void addUniqueConstraints(String table, MetaDataContext ctx,
MappingInfo info, UniqueConstraint...uniqueConstraints) {
for (UniqueConstraint anno : uniqueConstraints) {
Unique unique = createUniqueConstraint(ctx, anno);
unique.setTableName(table);
if (info instanceof ClassMappingInfo)
((ClassMappingInfo)info).addUnique(table, unique);
else if (info instanceof FieldMappingInfo)
((FieldMappingInfo)info).addJoinTableUnique(unique);
else
throw new InternalException();
}
}
@ -1261,8 +1292,7 @@ public class AnnotationPersistenceMappingParser
}
unique |= (pcols[i].unique()) ? TRUE : FALSE;
secondary = trackSecondaryTable(fm, secondary,
pcols[i].table(), i);
secondary = trackSecondaryTable(fm, secondary, pcols[i].table(), i);
}
setColumns(fm, fm.getValueInfo(), cols, unique);
@ -1337,11 +1367,13 @@ public class AnnotationPersistenceMappingParser
* Parse @JoinTable.
*/
private void parseJoinTable(FieldMapping fm, JoinTable join) {
fm.getMappingInfo().setTableName(toTableName(join.schema(),
join.name()));
parseJoinColumns(fm, fm.getMappingInfo(), false, join.joinColumns());
FieldMappingInfo info = fm.getMappingInfo();
info.setTableName(toTableName(join.schema(), join.name()));
parseJoinColumns(fm, info, false, join.joinColumns());
parseJoinColumns(fm, fm.getElementMapping().getValueInfo(), false,
join.inverseJoinColumns());
addUniqueConstraints(info.getTableName(), fm, info,
join.uniqueConstraints());
}
/**
@ -1617,21 +1649,4 @@ public class AnnotationPersistenceMappingParser
col.setFlag (Column.FLAG_UNUPDATABLE, !join.updatable ());
return col;
}
private static Unique newUnique(ClassMapping cm, String name,
String[] columnNames) {
if (columnNames == null || columnNames.length == 0)
return null;
Unique uniqueConstraint = new Unique();
uniqueConstraint.setName(name);
for (int i=0; i<columnNames.length; i++) {
if (StringUtils.isEmpty(columnNames[i]))
throw new UserException(_loc.get("empty-unique-column",
Arrays.toString(columnNames), cm));
Column column = new Column();
column.setName(columnNames[i]);
uniqueConstraint.addColumn(column);
}
return uniqueConstraint;
}
}

View File

@ -221,12 +221,12 @@ public class AnnotationPersistenceMappingSerializer
AnnotationBuilder abTable = addAnnotation(Table.class, mapping);
serializeTable(info.getTableName(), Strings
.getClassName(mapping.getDescribedType()), null,
info.getUniques(), abTable);
info.getUniques(info.getTableName()), abTable);
serializeColumns(info, ColType.PK_JOIN, null, abTable, cls);
for (String second : info.getSecondaryTableNames()) {
AnnotationBuilder abSecTable =
addAnnotation(SecondaryTable.class, mapping);
serializeTable(second, null, info, null, abSecTable);
serializeTable(second, null, info, info.getUniques(second), abSecTable);
}
}

View File

@ -39,6 +39,7 @@ import org.apache.openjpa.jdbc.meta.ClassMapping;
import org.apache.openjpa.jdbc.meta.ClassMappingInfo;
import org.apache.openjpa.jdbc.meta.DiscriminatorMappingInfo;
import org.apache.openjpa.jdbc.meta.FieldMapping;
import org.apache.openjpa.jdbc.meta.MappingInfo;
import org.apache.openjpa.jdbc.meta.MappingRepository;
import org.apache.openjpa.jdbc.meta.QueryResultMapping;
import org.apache.openjpa.jdbc.meta.SequenceMapping;
@ -56,6 +57,8 @@ import org.apache.openjpa.meta.FieldMetaData;
import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.meta.MetaDataRepository;
import org.apache.openjpa.persistence.XMLPersistenceMetaDataParser;
import org.apache.openjpa.util.InternalException;
import static org.apache.openjpa.persistence.jdbc.MappingTag.*;
/**
@ -293,6 +296,9 @@ public class XMLPersistenceMappingParser
case COLUMN_NAME:
endColumnName();
break;
case TABLE_GEN:
endTableGenerator();
break;
}
}
@ -405,9 +411,14 @@ public class XMLPersistenceMappingParser
Object scope = (cur instanceof ClassMetaData)
? ((ClassMetaData) cur).getDescribedType() : null;
seq.setSource(getSourceFile(), scope, seq.SRC_XML);
pushElement(seq);
return true;
}
private void endTableGenerator() {
popElement();
}
/**
* Parse inheritance.
*/
@ -880,14 +891,10 @@ public class XMLPersistenceMappingParser
*/
private boolean startUniqueConstraint(Attributes attrs)
throws SAXException {
Object current = currentElement();
if (current instanceof ClassMapping && _secondaryTable == null) {
Unique unique = new Unique();
pushElement(unique);
return true;
}
return false;
}
/**
* Ends processing &lt;unique-constraint&gt; provided the tag occurs
@ -897,9 +904,23 @@ public class XMLPersistenceMappingParser
*/
private void endUniqueConstraint() {
Unique unique = (Unique) popElement();
Object current = currentElement();
if (current instanceof ClassMapping && _secondaryTable == null)
((ClassMapping) current).getMappingInfo().addUnique(unique);
Object ctx = currentElement();
String tableName = "?";
ClassMappingInfo info = null;
if (ctx instanceof ClassMapping) {
info = ((ClassMapping) ctx).getMappingInfo();
tableName = (_secondaryTable != null) ? info.getTableName() : _secondaryTable;
info.addUnique(tableName, unique);
} else if (ctx instanceof FieldMapping) {// JoinTable
info = ((FieldMapping)ctx).getDeclaringMapping().getMappingInfo();
tableName = info.getTableName();
info.addUnique(tableName, unique);
} else if (ctx instanceof SequenceMapping) {
tableName = ((SequenceMapping)ctx).getTable();
unique.setTableName(tableName);
} else {
throw new InternalException();
}
}
/**

View File

@ -186,9 +186,9 @@ public class XMLPersistenceMappingSerializer
ClassMappingInfo info = cls.getMappingInfo();
serializeTable(info.getTableName(), "table", Strings
.getClassName(mapping.getDescribedType()), null,
info.getUniques());
info.getUniques(info.getTableName()));
for (String second : info.getSecondaryTableNames())
serializeTable(second, "secondary-table", null, info, null);
serializeTable(second, "secondary-table", null, info, info.getUniques(second));
serializeColumns(info, ColType.PK_JOIN, null);
}

View File

@ -46,8 +46,13 @@ second-version: Version property "{0}" cannot map to a secondary table column. \
Version columns must always be in the primary table of the class.
not-embedded: Attempt to declare mapping overrides on non-embedded field "{0}".
no-gen-table: No generated table found at "{0}".
empty-unique-column: A unique constraint "{0}" specified in mapping of class \
unique-no-column: A unique constraint specified in mapping of "{0}" specified \
no column.
unique-empty-column: A unique constraint "{0}" specified in mapping of class \
"{1}" includes an empty column.
unique-many-on-seq-unsupported: More than one unique constraints is specified \
on sequence generator "{1}" in "{0}". But multiple unique constraint on \
sequence generator is currently not supported.
discriminator-on-abstract-class: A discriminator value has been specified for \
the abstract class "{0}". The discriminator will never be used and may be \
safely removed.

View File

@ -0,0 +1,78 @@
/*
* 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.Arrays;
import java.util.List;
import javax.persistence.EntityManager;
import org.apache.openjpa.persistence.test.SQLListenerTestCase;
public class TestUnique extends SQLListenerTestCase {
@Override
public void setUp(Object... props) {
super.setUp(UniqueA.class, UniqueB.class);
}
public void testMapping() {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
em.getTransaction().commit();
em.close();
// The above should trigger schema definition
List<String> 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<String> 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));
}
}

View File

@ -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<UniqueB> bs;
}

View File

@ -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;
}