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/28 00:02:34 UTC

svn commit: r672406 - in /openjpa/trunk: openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/meta/ openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/ openjpa-persistence-jdbc/src/main/java/org/apache/openjpa/persistence/jdbc/ openjpa-persistence...

Author: ppoddar
Date: Fri Jun 27 15:02:34 2008
New Revision: 672406

URL: http://svn.apache.org/viewvc?rev=672406&view=rev
Log:
OPENJPA-340: UniqueConstraint supported via XML Descriptors

Added:
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/SQLSniffer.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/TestUniqueConstraint.java
      - copied, changed from r672038, 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/TestUniqueConstraintWithXMLDescriptor.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/jdbc/unique/
    openjpa/trunk/openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/jdbc/unique/orm.xml
Removed:
    openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/TestUnique.java
Modified:
    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/MappingInfo.java
    openjpa/trunk/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Unique.java
    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/XMLPersistenceMappingParser.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/test/PersistenceTestCase.java
    openjpa/trunk/openjpa-persistence-jdbc/src/test/resources/META-INF/persistence.xml

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=672406&r1=672405&r2=672406&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 Fri Jun 27 15:02:34 2008
@@ -20,6 +20,7 @@
 
 import java.io.File;
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -212,9 +213,17 @@
         }
         return (cols == null) ? Collections.EMPTY_LIST : cols;
     }
+    
+    /**
+     * Adds a Secondary table of given name to this mapping. A secondary table 
+     * must be known before unique constraints are added to a Secondary table.
+     */
+    public void addSecondaryTable(String second) {
+    	setSecondaryTableJoinColumns(second, null);
+    }
 
     /**
-     * Declare the given class-level join.
+     * Declare the given class-level join to the named (secondary) table.
      */
     public void setSecondaryTableJoinColumns(String tableName, List cols) {
         if (cols == null)
@@ -419,7 +428,7 @@
         			if (!table.containsColumn(columnName)) {
         				throw new UserException(_loc.get("unique-missing-column", 
                            new Object[]{cm, columnName, tableName, 
-        						table.getColumnNames()}));
+        						Arrays.toString(table.getColumnNames())}));
         			}
         			Column uniqueColumn = table.getColumn(columnName);
         			uniqueColumns[i] = uniqueColumn;

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=672406&r1=672405&r2=672406&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 Fri Jun 27 15:02:34 2008
@@ -928,6 +928,11 @@
             deferred = false;
         }
         
+        if (StringUtils.isEmpty(name)) {
+        	name = cols[0].getName();
+        	name = repos.getDBDictionary().getValidUniqueName(name, table);
+        }
+        
         Unique unq = table.addUnique(name);
         unq.setDeferred(deferred);
         unq.setColumns(cols);

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=672406&r1=672405&r2=672406&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 Fri Jun 27 15:02:34 2008
@@ -24,48 +24,83 @@
  * Represents a unique constraint. It can also represent a partial constraint.
  *
  * @author Abe White
+ * @author Pinaki Poddar
  */
 public class Unique
     extends LocalConstraint {
-	private boolean _isAutoSetName = false;
-    /**
-     * Default constructor.
+	private boolean _autoNaming = false;
+    
+	/**
+     * Default constructor without a name.
+     * Assumes that this constraint will set its own name automatically from
+     * the names of the columns added to it.
      */
     public Unique() {
-    	_isAutoSetName = true;
+    	_autoNaming = true;
     }
 
     /**
-     * Constructor.
-     *
+     * Construct with given name.
+     * Assumes that this constraint will not set its own name.
+     * 
      * @param name the name of the constraint, if any
      * @param table the table of the constraint
      */
     public Unique(String name, Table table) {
         super(name, table);
+    	_autoNaming = false;
     }
 
     public boolean isLogical() {
         return false;
     }
     
+    /**
+     * Adds the given column. 
+     * The added column is set to non-nullable because a unique constraint
+     * on the database requires that its constituent columns are NOT NULL. 
+     * @see Column#setNotNull(boolean)
+     * If this instance is constructing its own name, then this method also
+     * has the side effect of changing its own name by appending the newly 
+     * added column name to its own name. 
+     */
     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;
+    	if (_autoNaming && getTable() == null) {
+    		String prefix = createPrefix();
+    		setName(prefix + "_" + chop(col.getName(), 4));
+    		_autoNaming = true;
     	}
     }
     
+    private String createPrefix() {
+    	String currentName = getName();
+    	if (StringUtils.isEmpty(currentName)) {
+    		String tname = getTableName();
+    		if (StringUtils.isEmpty(tname))
+    			return "UNQ";
+    		else
+    			return "UNQ_" + chop(tname, 3);
+    	}
+    	return currentName;
+    }
+    
+    private String chop(String name, int head) {
+    	if (StringUtils.isEmpty(name))
+    		return name;
+    	return name.substring(0, Math.min(Math.max(1,head), name.length()));
+    }
+    
     /**
      * Set the name of the constraint. This method cannot be called if the
-     * constraint already belongs to a table.
+     * constraint already belongs to a table. Calling this method also has the
+     * side-effect of implying that the instance will not auto-generate its
+     * name.
      */
     public void setName(String name) {
         super.setName(name);
-        _isAutoSetName = false;
+        _autoNaming = false;
     }
 
 
@@ -76,4 +111,13 @@
     public boolean equalsUnique(Unique unq) {
         return equalsLocalConstraint(unq);
     }
+
+    /*
+     * Affirms if this instance is currently generating its own name. No 
+     * mutator because auto-naming is switched off as side-effect of user 
+     * calling setName() directly. 
+     */
+	public boolean isAutoNaming() {
+		return _autoNaming;
+	}
 }

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=672406&r1=672405&r2=672406&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 Fri Jun 27 15:02:34 2008
@@ -483,8 +483,10 @@
                 joins = new ArrayList<Column>(table.pkJoinColumns().length);
                 for (PrimaryKeyJoinColumn join : table.pkJoinColumns())
                     joins.add(newColumn(join));
-            } 
-            info.setSecondaryTableJoinColumns(name, joins);
+                info.setSecondaryTableJoinColumns(name, joins);
+            } else {
+            	info.addSecondaryTable(name);
+            }
             addUniqueConstraints(name, cm, info, table.uniqueConstraints());
         }
     }

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=672406&r1=672405&r2=672406&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 Fri Jun 27 15:02:34 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.FieldMappingInfo;
 import org.apache.openjpa.jdbc.meta.MappingInfo;
 import org.apache.openjpa.jdbc.meta.MappingRepository;
 import org.apache.openjpa.jdbc.meta.QueryResultMapping;
@@ -359,6 +360,8 @@
         throws SAXException {
         _secondaryTable = toTableName(attrs.getValue("schema"),
             attrs.getValue("name"));
+        ((ClassMapping)currentElement()).getMappingInfo()
+        	.addSecondaryTable(_secondaryTable);
         return true;
     }
 
@@ -906,18 +909,23 @@
         Unique unique = (Unique) popElement();
         Object ctx = currentElement();
         String tableName = "?";
-        ClassMappingInfo info = null;
         if (ctx instanceof ClassMapping) {
-        	info = ((ClassMapping) ctx).getMappingInfo();
-        	tableName = (_secondaryTable != null) ? info.getTableName() : _secondaryTable;
+        	ClassMappingInfo 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);
+        	FieldMappingInfo info = ((FieldMapping)ctx).getMappingInfo();
+        	info.addJoinTableUnique(unique);
         } else if (ctx instanceof SequenceMapping) {
-        	tableName = ((SequenceMapping)ctx).getTable();
-        	unique.setTableName(tableName);
+        	SequenceMapping seq = (SequenceMapping)ctx;
+        	unique.setTableName(seq.getTable());
+        	Column[] uniqueColumns = unique.getColumns();
+        	String[] columnNames = new String[uniqueColumns.length];
+        	int i = 0;
+        	for (Column uniqueColumn : uniqueColumns)
+        		columnNames[i++] = uniqueColumn.getName();
+        	seq.setUniqueColumns(columnNames);
         } else {
         	throw new InternalException();
         }

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/SQLSniffer.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/SQLSniffer.java?rev=672406&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/SQLSniffer.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/SQLSniffer.java Fri Jun 27 15:02:34 2008
@@ -0,0 +1,66 @@
+/*
+ * 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;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.regexp.RE;
+
+/**
+ * Utility class to verify whether a set of fragments appear in a list of
+ * possible SQL statement.
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+public class SQLSniffer {
+	
+	private static Map<String, RE> cache = new HashMap<String, RE>();
+	/**
+	 * Checks that the given set of regular expressions occur in at least one of
+	 * the given input SQL.
+	 */
+	public static boolean matches(List<String> SQLs, String...regexes) {
+		if (SQLs == null || regexes == null)
+			return false;
+		for (String sql : SQLs) {
+			boolean matched = true;
+			for (String key : regexes) {
+				RE regex = getRegularExpression(key);
+				if (!regex.match(sql)) {
+					matched = false;
+					break;
+				}
+			}
+			if (matched)
+				return true;
+		}
+		return false;
+	}
+	
+	private static RE getRegularExpression(String regex) {
+		if (cache.containsKey(regex)) 
+			return cache.get(regex);
+		RE re = new RE(regex);
+		cache.put(regex, re);
+		return re;
+	}
+}

Copied: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/TestUniqueConstraint.java (from r672038, 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/TestUniqueConstraint.java?p2=openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/TestUniqueConstraint.java&p1=openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/TestUnique.java&r1=672038&r2=672406&rev=672406&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/TestUnique.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/TestUniqueConstraint.java Fri Jun 27 15:02:34 2008
@@ -23,12 +23,24 @@
 
 import javax.persistence.EntityManager;
 
+import org.apache.openjpa.persistence.jdbc.SQLSniffer;
 import org.apache.openjpa.persistence.test.SQLListenerTestCase;
 
-public class TestUnique extends SQLListenerTestCase {
+/**
+ * Tests unique constraints specified via annotations for primary/secondary
+ * table, sequence generator, join tables have been defined on database by
+ * examining DDL statements.
+ * 
+ * @see resources/org/apache/openjpa/persistence/jdbc/unique/orm.xml defines
+ * the ORM mapping.
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+public class TestUniqueConstraint extends SQLListenerTestCase {
     @Override
     public void setUp(Object... props) {
-    	super.setUp(UniqueA.class, UniqueB.class);    			    
+    	super.setUp(DROP_TABLES, UniqueA.class, UniqueB.class);    			    
     }
     
 	public void testMapping() {
@@ -40,39 +52,28 @@
 		
 		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)");
-		
+		assertSQLFragnments(sqls, "CREATE TABLE UNIQUE_A",
+				"UNIQUE \\w*\\(a1, a2\\)", 
+				"UNIQUE \\w*\\(a3, a4\\)");
+		assertSQLFragnments(sqls, "CREATE TABLE UNIQUE_B",
+				"UNIQUE \\w*\\(b1, b2\\)");
+		assertSQLFragnments(sqls, "CREATE TABLE UNIQUE_SECONDARY",
+				"UNIQUE \\w*\\(sa1\\)");
+		assertSQLFragnments(sqls, "CREATE TABLE UNIQUE_GENERATOR",
+				"UNIQUE \\w*\\(GEN1, GEN2\\)");
+		assertSQLFragnments(sqls, "CREATE TABLE UNIQUE_JOINTABLE",
+				"UNIQUE \\w*\\(FK_A, FK_B\\)");
 	}
 	
-	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;
-		}
+	void assertSQLFragnments(List<String> list, String... keys) {
+		if (SQLSniffer.matches(list, keys))
+			return;
 		int i = 0;
 		for (String sql : list) {
 			i++;
-			System.out.println(""+i+":"+sql);
+			System.out.println("" + i + ":" + sql);
 		}
-		fail("None of the above SQL contains all keys " + Arrays.toString(keys));
+		fail("None of the " + sql.size() + " SQL contains all keys "
+				+ Arrays.toString(keys));
 	}
 }

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/TestUniqueConstraintWithXMLDescriptor.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/TestUniqueConstraintWithXMLDescriptor.java?rev=672406&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/TestUniqueConstraintWithXMLDescriptor.java (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/TestUniqueConstraintWithXMLDescriptor.java Fri Jun 27 15:02:34 2008
@@ -0,0 +1,86 @@
+/*
+ * 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.jdbc.SQLSniffer;
+import org.apache.openjpa.persistence.test.SQLListenerTestCase;
+
+/**
+ * Tests unique constraints specified via XML Descriptor for primary/secondary
+ * table, sequence generator, join tables have been defined on database by
+ * examining DDL statements.
+ * 
+ * @see resources/org/apache/openjpa/persistence/jdbc/unique/orm.xml 
+ * defines the ORM mapping. 
+ * 
+ * @author Pinaki Poddar
+ *
+ */
+public class TestUniqueConstraintWithXMLDescriptor extends SQLListenerTestCase {
+	@Override
+	public void setUp(Object... props) {
+		super.setUp(DROP_TABLES, UniqueA.class, UniqueB.class);
+	}
+
+	protected String getPersistenceUnitName() {
+		return "test-unique-constraint";
+	}
+
+	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;
+		assertFalse("No SQL DDL registered", sqls.isEmpty());
+
+		// Following verification techniques is fragile as databases DDL
+		// syntax vary greatly on UNIQUE CONSTRAINT
+		assertSQLFragnments(sqls, "CREATE TABLE UNIQUE_A_XML",
+				"UNIQUE \\w*\\(a1x, a2x\\)", 
+				"UNIQUE \\w*\\(a3x, a4x\\)");
+		assertSQLFragnments(sqls, "CREATE TABLE UNIQUE_B_XML",
+				"UNIQUE \\w*\\(b1x, b2x\\)");
+		assertSQLFragnments(sqls, "CREATE TABLE UNIQUE_SECONDARY_XML",
+				"UNIQUE \\w*\\(sa1x\\)");
+		assertSQLFragnments(sqls, "CREATE TABLE UNIQUE_GENERATOR_XML",
+				"UNIQUE \\w*\\(GEN1_XML, GEN2_XML\\)");
+		assertSQLFragnments(sqls, "CREATE TABLE UNIQUE_JOINTABLE_XML",
+				"UNIQUE \\w*\\(FK_A_XML, FK_B_XML\\)");
+	}
+
+	void assertSQLFragnments(List<String> list, String... keys) {
+		if (SQLSniffer.matches(list, keys))
+			return;
+		int i = 0;
+		for (String sql : list) {
+			i++;
+			System.out.println("" + i + ":" + sql);
+		}
+		fail("None of the " + sql.size() + " SQL contains all keys "
+				+ Arrays.toString(keys));
+	}
+}

Modified: 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=672406&r1=672405&r2=672406&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/UniqueA.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/jdbc/unique/UniqueA.java Fri Jun 27 15:02:34 2008
@@ -23,6 +23,7 @@
 import javax.persistence.Column;
 import javax.persistence.Entity;
 import javax.persistence.Id;
+import javax.persistence.JoinColumn;
 import javax.persistence.JoinTable;
 import javax.persistence.ManyToMany;
 import javax.persistence.SecondaryTable;
@@ -72,6 +73,8 @@
 	
 	@ManyToMany
 	@JoinTable(name="UNIQUE_JOINTABLE",
-			uniqueConstraints=@UniqueConstraint(columnNames={"UNIQUEA_AID","BS_BID"}))
+			joinColumns={@JoinColumn(name="FK_A", nullable=false, referencedColumnName="aid")},
+			inverseJoinColumns={@JoinColumn(name="FK_B", nullable=false, referencedColumnName="bid")},
+			uniqueConstraints=@UniqueConstraint(columnNames={"FK_A","FK_B"}))
 	private Collection<UniqueB> bs;
 }

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/PersistenceTestCase.java
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/PersistenceTestCase.java?rev=672406&r1=672405&r2=672406&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/PersistenceTestCase.java (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/test/PersistenceTestCase.java Fri Jun 27 15:02:34 2008
@@ -47,6 +47,7 @@
      * database tables should be cleared.
      */
     protected static final Object CLEAR_TABLES = new Object();
+    protected static final Object DROP_TABLES = new Object();
 
     /**
      * The {@link TestResult} instance for the current test run.
@@ -95,6 +96,10 @@
                 map.put("openjpa.jdbc.SynchronizeMappings",
                     "buildSchema(ForeignKeys=true," 
                     + "SchemaAction='add,deleteTableContents')");
+            } else if (props[i] == DROP_TABLES) {
+                map.put("openjpa.jdbc.SynchronizeMappings",
+                    "buildSchema(ForeignKeys=true," 
+                    + "SchemaAction='drop,add')");
             } else if (props[i] instanceof Class)
                 types.add((Class) props[i]);
             else if (props[i] != null)

Modified: openjpa/trunk/openjpa-persistence-jdbc/src/test/resources/META-INF/persistence.xml
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/resources/META-INF/persistence.xml?rev=672406&r1=672405&r2=672406&view=diff
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/resources/META-INF/persistence.xml (original)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/resources/META-INF/persistence.xml Fri Jun 27 15:02:34 2008
@@ -88,5 +88,12 @@
             <property name="openjpa.jdbc.SynchronizeMappings"
                   value="buildSchema(ForeignKeys=true)"/>
         </properties>
-    </persistence-unit>
+    </persistence-unit>
+    
+    <persistence-unit name="test-unique-constraint">
+        <mapping-file>org/apache/openjpa/persistence/jdbc/unique/orm.xml</mapping-file>
+        <class>org.apache.openjpa.persistence.jdbc.unique.UniqueA</class>
+        <class>org.apache.openjpa.persistence.jdbc.unique.UniqueB</class>
+    </persistence-unit>
+    
 </persistence>

Added: openjpa/trunk/openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/jdbc/unique/orm.xml
URL: http://svn.apache.org/viewvc/openjpa/trunk/openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/jdbc/unique/orm.xml?rev=672406&view=auto
==============================================================================
--- openjpa/trunk/openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/jdbc/unique/orm.xml (added)
+++ openjpa/trunk/openjpa-persistence-jdbc/src/test/resources/org/apache/openjpa/persistence/jdbc/unique/orm.xml Fri Jun 27 15:02:34 2008
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+	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.
+-->
+<entity-mappings xmlns="http://java.sun.com/xml/ns/persistence/orm"
+	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://java.sun.com/xml/ns/persistence/orm orm_1_0.xsd"
+	version="1.0">
+	
+	<persistence-unit-metadata>
+		<xml-mapping-metadata-complete>
+		</xml-mapping-metadata-complete>
+	</persistence-unit-metadata>
+	
+	
+	<package>org.apache.openjpa.persistence.jdbc.unique</package>
+	<entity name="UniqueA" class="UniqueA">
+		<table name="UNIQUE_A_XML">
+			<unique-constraint>
+				<column-name>a1x</column-name>
+				<column-name>a2x</column-name>
+			</unique-constraint>
+			<unique-constraint>
+				<column-name>a3x</column-name>
+				<column-name>a4x</column-name>
+			</unique-constraint>
+		</table>
+		<secondary-table name="UNIQUE_SECONDARY_XML">
+			<unique-constraint>
+				<column-name>sa1x</column-name>
+			</unique-constraint>
+		</secondary-table>
+		<attributes>
+			<id name="aid">
+			</id>
+			<basic name="a1">
+				<column name="a1x"/>
+			</basic>
+			<basic name="a2">
+				<column name="a2x"/>
+			</basic>
+			<basic name="a3">
+				<column name="a3x"/>
+			</basic>
+			<basic name="a4">
+				<column name="a4x"/>
+			</basic>
+			<basic name="a5">
+				<column name="a5x"/>
+			</basic>
+			<basic name="a6">
+				<column name="a6x"/>
+			</basic>
+			
+			<basic name="sa1">
+				<column name="sa1x" table="UNIQUE_SECONDARY_XML" />
+			</basic>
+			<basic name="sa2">
+				<column name="sa2x" table="UNIQUE_SECONDARY_XML" />
+			</basic>
+
+			<many-to-many name="bs">
+				<join-table name="UNIQUE_JOINTABLE_XML">
+					<join-column name="FK_A_XML" referenced-column-name="aid" nullable="false">
+					</join-column>
+					<inverse-join-column name="FK_B_XML" referenced-column-name="bid" nullable="false">
+					</inverse-join-column>
+					<unique-constraint>
+						<column-name>FK_A_XML</column-name>
+						<column-name>FK_B_XML</column-name>
+					</unique-constraint>
+				</join-table>
+			</many-to-many>
+		</attributes>
+	</entity>
+
+	<entity name="UniqueB" class="UniqueB">
+		<table name="UNIQUE_B_XML">
+			<unique-constraint>
+				<column-name>b1x</column-name>
+				<column-name>b2x</column-name>
+			</unique-constraint>
+		</table>
+		<attributes>
+			<id name="bid">
+				<generated-value strategy="TABLE"
+					generator="testGeneratorXML" />
+				<table-generator name="testGeneratorXML"
+					table="UNIQUE_GENERATOR_XML" pk-column-name="GEN1_XML"
+					value-column-name="GEN2_XML">
+					<unique-constraint>
+						<column-name>GEN1_XML</column-name>
+						<column-name>GEN2_XML</column-name>
+					</unique-constraint>
+				</table-generator>
+			</id>
+			<basic name="b1">
+				<column name="b1x"/>
+			</basic>
+			<basic name="b2">
+				<column name="b2x"/>
+			</basic>
+		</attributes>
+	</entity>
+</entity-mappings>
+
+