You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openjpa.apache.org by pp...@apache.org on 2008/06/27 00:04:07 UTC

svn commit: r672038 - in /openjpa/trunk: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/ openjpa-jdbc/src/main/resources/org/...

Author: ppoddar
Date: Thu Jun 26 15:04:06 2008
New Revision: 672038

URL: http://svn.apache.org/viewvc?rev=672038&view=rev
Log:
OPENJPA-340: Support for @UniqueConstraints on @TableGenerator, @SecondaryTable and @JoinTable.

Added:
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/TestUnique.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/UniqueA.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/UniqueB.java
Modified:
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/TableJDBCSeq.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ClassMappingInfo.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/FieldMapping.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/FieldMappingInfo.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingInfo.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/SequenceMapping.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Table.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Unique.java
    openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/meta/localizer.properties
    openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java
    openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingSerializer.java
    openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/XMLPersistenceMappingParser.java
    openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/XMLPersistenceMappingSerializer.java
    openjpa/trunk/openjpa-persistence-jdbc/src/main/resources/org/apache/openjpa/persistence/jdbc/localizer.properties

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/TableJDBCSeq.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/TableJDBCSeq.java?rev=672038&r1=672037&r2=672038&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/TableJDBCSeq.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/kernel/TableJDBCSeq.java Thu Jun 26 15:04:06 2008
@@ -28,6 +28,7 @@
 
 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.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.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 @@
     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 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 @@
             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 @@
             // some databases require to create an index for the sequence table
             _conf.getDBDictionaryInstance().createIndexIfNecessary(schema,
                     _table, _pkColumn);
-         
         }
     }
 
@@ -361,6 +384,19 @@
             (_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);
+    		}
+        }
+        
     }
 
     /**

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ClassMappingInfo.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ClassMappingInfo.java?rev=672038&r1=672037&r2=672038&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ClassMappingInfo.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ClassMappingInfo.java Thu Jun 26 15:04:06 2008
@@ -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.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 @@
     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.
@@ -221,11 +223,12 @@
             _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 @@
                 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 @@
                     _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) {
-        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<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();
-            Column[] templateColumns = template.getColumns();
-            Column[] uniqueColumns = new Column[templateColumns.length];
-            boolean missingColumn = true;
-            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));
-                }
-                uniqueColumns[i] = uniqueColumn;
-            }
-            Unique unique = super.createUnique(cm, "unique", template, 
-                uniqueColumns, adapt);
-            if (unique != null)
-                result.add(unique);
+        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];
+        		Table table = getTable((ClassMapping)cm, tableName, adapt);
+        		for (int i=0; i<uniqueColumns.length; i++) {
+        			String columnName = templateColumns[i].getName();
+        			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 = 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() {

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/FieldMapping.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/FieldMapping.java?rev=672038&r1=672037&r2=672038&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/FieldMapping.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/FieldMapping.java Thu Jun 26 15:04:06 2008
@@ -70,7 +70,8 @@
     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 @@
         _unq = unq;
     }
 
+    public Unique[] getJoinTableUniques() {
+    	return _joinTableUniques;
+    }
+    
+    public void setJoinTableUniques(Unique[] unqs) {
+    	_joinTableUniques = unqs;
+    }
+    
     /**
      * Index on join foreign key columns.
      */
@@ -252,6 +261,13 @@
         _val.refSchemaComponents();
         _key.refSchemaComponents();
         _elem.refSchemaComponents();
+        if (_joinTableUniques != null) {
+        	for (Unique joinUnique : _joinTableUniques) {
+        		for (Column col : joinUnique.getColumns()) {
+        			col.ref();
+        		}
+        	}
+        }
     }
 
     /**
@@ -538,6 +554,7 @@
             _io = _info.getColumnIO();
             _outer = _info.isJoinOuter();
             _unq = _info.getJoinUnique(this, false, adapt);
+            _joinTableUniques = _info.getJoinTableUniques(this, false, adapt);
             _idx = _info.getJoinIndex(this, adapt);
         }
     }

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/FieldMappingInfo.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/FieldMappingInfo.java?rev=672038&r1=672037&r2=672038&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/FieldMappingInfo.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/FieldMappingInfo.java Thu Jun 26 15:04:06 2008
@@ -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.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 @@
  * with the relevant pieces of information filled in.
  *
  * @author Abe White
+ * @author Pinaki Poddar
  */
 public class FieldMappingInfo
     extends MappingInfo
@@ -53,6 +58,7 @@
     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.
@@ -185,8 +191,47 @@
                 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<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.
      */
     public Index getJoinIndex(FieldMapping field, boolean adapt) {
@@ -261,6 +306,7 @@
 
         syncIndex(field, field.getJoinIndex());
         syncUnique(field, field.getJoinUnique());
+        syncJoinTableUniques(field, field.getJoinTableUniques());
         syncOrderColumn(field);
         syncStrategy(field);
     }
@@ -290,6 +336,24 @@
         else
             _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

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingInfo.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingInfo.java?rev=672038&r1=672037&r2=672038&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingInfo.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/MappingInfo.java Thu Jun 26 15:04:06 2008
@@ -927,7 +927,7 @@
                     context, dict.platform));
             deferred = false;
         }
-
+        
         Unique unq = table.addUnique(name);
         unq.setDeferred(deferred);
         unq.setColumns(cols);
@@ -1534,7 +1534,7 @@
         _unq.setName(unq.getName());
         _unq.setDeferred(unq.isDeferred());
     }
-
+    
     /**
      * Sets internal constraint and column information to match given mapped
      * constraint.

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/SequenceMapping.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/SequenceMapping.java?rev=672038&r1=672037&r2=672038&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/SequenceMapping.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/SequenceMapping.java Thu Jun 26 15:04:06 2008
@@ -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 @@
     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 @@
         _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 @@
         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,'|'));
     }
 }

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Table.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Table.java?rev=672038&r1=672037&r2=672038&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Table.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Table.java Thu Jun 26 15:04:06 2008
@@ -252,6 +252,11 @@
         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 @@
             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.

Modified: openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Unique.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Unique.java?rev=672038&r1=672037&r2=672038&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Unique.java (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Unique.java Thu Jun 26 15:04:06 2008
@@ -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 @@
  */
 public class Unique
     extends LocalConstraint {
-
+	private boolean _isAutoSetName = false;
     /**
      * Default constructor.
      */
     public Unique() {
+    	_isAutoSetName = true;
     }
 
     /**
@@ -45,6 +48,26 @@
     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

Modified: openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/meta/localizer.properties
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/meta/localizer.properties?rev=672038&r1=672037&r2=672038&view=diff
==============================================================================
--- openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/meta/localizer.properties (original)
+++ openjpa/trunk/openjpa-jdbc/src/main/resources/org/apache/openjpa/jdbc/meta/localizer.properties Thu Jun 26 15:04:06 2008
@@ -410,6 +410,8 @@
 	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

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java?rev=672038&r1=672037&r2=672038&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingParser.java Thu Jun 26 15:04:06 2008
@@ -61,6 +61,7 @@
 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.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 @@
         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 @@
         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 @@
                 joins = new ArrayList<Column>(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 @@
         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 @@
             }
 
             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 @@
      * 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 @@
 		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;
-    }
 }

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingSerializer.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingSerializer.java?rev=672038&r1=672037&r2=672038&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingSerializer.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/AnnotationPersistenceMappingSerializer.java Thu Jun 26 15:04:06 2008
@@ -221,12 +221,12 @@
         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);
         }
     }
 

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/XMLPersistenceMappingParser.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/XMLPersistenceMappingParser.java?rev=672038&r1=672037&r2=672038&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/XMLPersistenceMappingParser.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/XMLPersistenceMappingParser.java Thu Jun 26 15:04:06 2008
@@ -39,6 +39,7 @@
 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.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 @@
             case COLUMN_NAME:
                 endColumnName();
                 break;
+            case TABLE_GEN:
+            	endTableGenerator();
+            	break;
         }
     }
 
@@ -405,8 +411,13 @@
         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,13 +891,9 @@
      */
     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;
+        Unique unique = new Unique();
+        pushElement(unique);
+        return true;
     }
     
     /**
@@ -897,9 +904,23 @@
      */
     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();
+        }
     }
     
     /**

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/XMLPersistenceMappingSerializer.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/XMLPersistenceMappingSerializer.java?rev=672038&r1=672037&r2=672038&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/XMLPersistenceMappingSerializer.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/XMLPersistenceMappingSerializer.java Thu Jun 26 15:04:06 2008
@@ -186,9 +186,9 @@
         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);
     }
 

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/main/resources/org/apache/openjpa/persistence/jdbc/localizer.properties
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/main/resources/org/apache/openjpa/persistence/jdbc/localizer.properties?rev=672038&r1=672037&r2=672038&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/main/resources/org/apache/openjpa/persistence/jdbc/localizer.properties (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/main/resources/org/apache/openjpa/persistence/jdbc/localizer.properties Thu Jun 26 15:04:06 2008
@@ -46,8 +46,13 @@
 	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.

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/TestUnique.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/TestUnique.java?rev=672038&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/TestUnique.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/TestUnique.java Thu Jun 26 15:04:06 2008
@@ -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));
+	}
+}

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/UniqueA.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/UniqueA.java?rev=672038&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/UniqueA.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/UniqueA.java Thu Jun 26 15:04:06 2008
@@ -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;
+}

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/UniqueB.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/UniqueB.java?rev=672038&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/UniqueB.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/UniqueB.java Thu Jun 26 15:04:06 2008
@@ -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;
+}