mirror of https://github.com/apache/openjpa.git
OPENJPA-340: UniqueConstraint supported via XML Descriptors
git-svn-id: https://svn.apache.org/repos/asf/openjpa/trunk@672406 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
ff4a96e707
commit
6868b1cc4a
|
@ -20,6 +20,7 @@ package org.apache.openjpa.jdbc.meta;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
|
@ -214,7 +215,15 @@ public class ClassMappingInfo
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Declare the given class-level join.
|
* 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 to the named (secondary) table.
|
||||||
*/
|
*/
|
||||||
public void setSecondaryTableJoinColumns(String tableName, List cols) {
|
public void setSecondaryTableJoinColumns(String tableName, List cols) {
|
||||||
if (cols == null)
|
if (cols == null)
|
||||||
|
@ -419,7 +428,7 @@ public class ClassMappingInfo
|
||||||
if (!table.containsColumn(columnName)) {
|
if (!table.containsColumn(columnName)) {
|
||||||
throw new UserException(_loc.get("unique-missing-column",
|
throw new UserException(_loc.get("unique-missing-column",
|
||||||
new Object[]{cm, columnName, tableName,
|
new Object[]{cm, columnName, tableName,
|
||||||
table.getColumnNames()}));
|
Arrays.toString(table.getColumnNames())}));
|
||||||
}
|
}
|
||||||
Column uniqueColumn = table.getColumn(columnName);
|
Column uniqueColumn = table.getColumn(columnName);
|
||||||
uniqueColumns[i] = uniqueColumn;
|
uniqueColumns[i] = uniqueColumn;
|
||||||
|
|
|
@ -928,6 +928,11 @@ public abstract class MappingInfo
|
||||||
deferred = false;
|
deferred = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (StringUtils.isEmpty(name)) {
|
||||||
|
name = cols[0].getName();
|
||||||
|
name = repos.getDBDictionary().getValidUniqueName(name, table);
|
||||||
|
}
|
||||||
|
|
||||||
Unique unq = table.addUnique(name);
|
Unique unq = table.addUnique(name);
|
||||||
unq.setDeferred(deferred);
|
unq.setDeferred(deferred);
|
||||||
unq.setColumns(cols);
|
unq.setColumns(cols);
|
||||||
|
|
|
@ -24,48 +24,83 @@ import org.apache.commons.lang.StringUtils;
|
||||||
* Represents a unique constraint. It can also represent a partial constraint.
|
* Represents a unique constraint. It can also represent a partial constraint.
|
||||||
*
|
*
|
||||||
* @author Abe White
|
* @author Abe White
|
||||||
|
* @author Pinaki Poddar
|
||||||
*/
|
*/
|
||||||
public class Unique
|
public class Unique
|
||||||
extends LocalConstraint {
|
extends LocalConstraint {
|
||||||
private boolean _isAutoSetName = false;
|
private boolean _autoNaming = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Default constructor.
|
* 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() {
|
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 name the name of the constraint, if any
|
||||||
* @param table the table of the constraint
|
* @param table the table of the constraint
|
||||||
*/
|
*/
|
||||||
public Unique(String name, Table table) {
|
public Unique(String name, Table table) {
|
||||||
super(name, table);
|
super(name, table);
|
||||||
|
_autoNaming = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isLogical() {
|
public boolean isLogical() {
|
||||||
return false;
|
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) {
|
public void addColumn(Column col) {
|
||||||
super.addColumn(col);
|
super.addColumn(col);
|
||||||
col.setNotNull(true);
|
col.setNotNull(true);
|
||||||
if (_isAutoSetName && getTable() == null) {
|
if (_autoNaming && getTable() == null) {
|
||||||
String pre = StringUtils.isEmpty(getName()) ? "UNQ" : getName();
|
String prefix = createPrefix();
|
||||||
setName(pre + "_" + col.getName());
|
setName(prefix + "_" + chop(col.getName(), 4));
|
||||||
_isAutoSetName = true;
|
_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
|
* 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) {
|
public void setName(String name) {
|
||||||
super.setName(name);
|
super.setName(name);
|
||||||
_isAutoSetName = false;
|
_autoNaming = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -76,4 +111,13 @@ public class Unique
|
||||||
public boolean equalsUnique(Unique unq) {
|
public boolean equalsUnique(Unique unq) {
|
||||||
return equalsLocalConstraint(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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -483,8 +483,10 @@ public class AnnotationPersistenceMappingParser
|
||||||
joins = new ArrayList<Column>(table.pkJoinColumns().length);
|
joins = new ArrayList<Column>(table.pkJoinColumns().length);
|
||||||
for (PrimaryKeyJoinColumn join : table.pkJoinColumns())
|
for (PrimaryKeyJoinColumn join : table.pkJoinColumns())
|
||||||
joins.add(newColumn(join));
|
joins.add(newColumn(join));
|
||||||
}
|
|
||||||
info.setSecondaryTableJoinColumns(name, joins);
|
info.setSecondaryTableJoinColumns(name, joins);
|
||||||
|
} else {
|
||||||
|
info.addSecondaryTable(name);
|
||||||
|
}
|
||||||
addUniqueConstraints(name, cm, info, table.uniqueConstraints());
|
addUniqueConstraints(name, cm, info, table.uniqueConstraints());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ import org.apache.openjpa.jdbc.meta.ClassMapping;
|
||||||
import org.apache.openjpa.jdbc.meta.ClassMappingInfo;
|
import org.apache.openjpa.jdbc.meta.ClassMappingInfo;
|
||||||
import org.apache.openjpa.jdbc.meta.DiscriminatorMappingInfo;
|
import org.apache.openjpa.jdbc.meta.DiscriminatorMappingInfo;
|
||||||
import org.apache.openjpa.jdbc.meta.FieldMapping;
|
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.MappingInfo;
|
||||||
import org.apache.openjpa.jdbc.meta.MappingRepository;
|
import org.apache.openjpa.jdbc.meta.MappingRepository;
|
||||||
import org.apache.openjpa.jdbc.meta.QueryResultMapping;
|
import org.apache.openjpa.jdbc.meta.QueryResultMapping;
|
||||||
|
@ -359,6 +360,8 @@ public class XMLPersistenceMappingParser
|
||||||
throws SAXException {
|
throws SAXException {
|
||||||
_secondaryTable = toTableName(attrs.getValue("schema"),
|
_secondaryTable = toTableName(attrs.getValue("schema"),
|
||||||
attrs.getValue("name"));
|
attrs.getValue("name"));
|
||||||
|
((ClassMapping)currentElement()).getMappingInfo()
|
||||||
|
.addSecondaryTable(_secondaryTable);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -906,18 +909,23 @@ public class XMLPersistenceMappingParser
|
||||||
Unique unique = (Unique) popElement();
|
Unique unique = (Unique) popElement();
|
||||||
Object ctx = currentElement();
|
Object ctx = currentElement();
|
||||||
String tableName = "?";
|
String tableName = "?";
|
||||||
ClassMappingInfo info = null;
|
|
||||||
if (ctx instanceof ClassMapping) {
|
if (ctx instanceof ClassMapping) {
|
||||||
info = ((ClassMapping) ctx).getMappingInfo();
|
ClassMappingInfo info = ((ClassMapping) ctx).getMappingInfo();
|
||||||
tableName = (_secondaryTable != null) ? info.getTableName() : _secondaryTable;
|
tableName = (_secondaryTable == null)
|
||||||
|
? info.getTableName() : _secondaryTable;
|
||||||
info.addUnique(tableName, unique);
|
info.addUnique(tableName, unique);
|
||||||
} else if (ctx instanceof FieldMapping) {// JoinTable
|
} else if (ctx instanceof FieldMapping) {// JoinTable
|
||||||
info = ((FieldMapping)ctx).getDeclaringMapping().getMappingInfo();
|
FieldMappingInfo info = ((FieldMapping)ctx).getMappingInfo();
|
||||||
tableName = info.getTableName();
|
info.addJoinTableUnique(unique);
|
||||||
info.addUnique(tableName, unique);
|
|
||||||
} else if (ctx instanceof SequenceMapping) {
|
} else if (ctx instanceof SequenceMapping) {
|
||||||
tableName = ((SequenceMapping)ctx).getTable();
|
SequenceMapping seq = (SequenceMapping)ctx;
|
||||||
unique.setTableName(tableName);
|
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 {
|
} else {
|
||||||
throw new InternalException();
|
throw new InternalException();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,12 +23,24 @@ import java.util.List;
|
||||||
|
|
||||||
import javax.persistence.EntityManager;
|
import javax.persistence.EntityManager;
|
||||||
|
|
||||||
|
import org.apache.openjpa.persistence.jdbc.SQLSniffer;
|
||||||
import org.apache.openjpa.persistence.test.SQLListenerTestCase;
|
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
|
@Override
|
||||||
public void setUp(Object... props) {
|
public void setUp(Object... props) {
|
||||||
super.setUp(UniqueA.class, UniqueB.class);
|
super.setUp(DROP_TABLES, UniqueA.class, UniqueB.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testMapping() {
|
public void testMapping() {
|
||||||
|
@ -40,39 +52,28 @@ public class TestUnique extends SQLListenerTestCase {
|
||||||
|
|
||||||
List<String> sqls = super.sql;
|
List<String> sqls = super.sql;
|
||||||
|
|
||||||
assertSQLFragnment(sqls, "CREATE TABLE UNIQUE_A",
|
assertSQLFragnments(sqls, "CREATE TABLE UNIQUE_A",
|
||||||
"UNIQUE (a1, a2)",
|
"UNIQUE \\w*\\(a1, a2\\)",
|
||||||
"UNIQUE (a3, a4)");
|
"UNIQUE \\w*\\(a3, a4\\)");
|
||||||
assertSQLFragnment(sqls, "CREATE TABLE UNIQUE_B",
|
assertSQLFragnments(sqls, "CREATE TABLE UNIQUE_B",
|
||||||
"UNIQUE (b1, b2)");
|
"UNIQUE \\w*\\(b1, b2\\)");
|
||||||
assertSQLFragnment(sqls, "CREATE TABLE UNIQUE_SECONDARY",
|
assertSQLFragnments(sqls, "CREATE TABLE UNIQUE_SECONDARY",
|
||||||
"UNIQUE (sa1)");
|
"UNIQUE \\w*\\(sa1\\)");
|
||||||
assertSQLFragnment(sqls, "CREATE TABLE UNIQUE_GENERATOR",
|
assertSQLFragnments(sqls, "CREATE TABLE UNIQUE_GENERATOR",
|
||||||
"UNIQUE (GEN1, GEN2)");
|
"UNIQUE \\w*\\(GEN1, GEN2\\)");
|
||||||
assertSQLFragnment(sqls, "CREATE TABLE UNIQUE_JOINTABLE",
|
assertSQLFragnments(sqls, "CREATE TABLE UNIQUE_JOINTABLE",
|
||||||
"UNIQUE (UNIQUEA_AID, BS_BID)");
|
"UNIQUE \\w*\\(FK_A, FK_B\\)");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void assertSQLFragnment(List<String> list, String...keys) {
|
void assertSQLFragnments(List<String> list, String... keys) {
|
||||||
for (String sql : list) {
|
if (SQLSniffer.matches(list, keys))
|
||||||
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;
|
return;
|
||||||
}
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for (String sql : list) {
|
for (String sql : list) {
|
||||||
i++;
|
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));
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -23,6 +23,7 @@ import java.util.Collection;
|
||||||
import javax.persistence.Column;
|
import javax.persistence.Column;
|
||||||
import javax.persistence.Entity;
|
import javax.persistence.Entity;
|
||||||
import javax.persistence.Id;
|
import javax.persistence.Id;
|
||||||
|
import javax.persistence.JoinColumn;
|
||||||
import javax.persistence.JoinTable;
|
import javax.persistence.JoinTable;
|
||||||
import javax.persistence.ManyToMany;
|
import javax.persistence.ManyToMany;
|
||||||
import javax.persistence.SecondaryTable;
|
import javax.persistence.SecondaryTable;
|
||||||
|
@ -72,6 +73,8 @@ public class UniqueA {
|
||||||
|
|
||||||
@ManyToMany
|
@ManyToMany
|
||||||
@JoinTable(name="UNIQUE_JOINTABLE",
|
@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;
|
private Collection<UniqueB> bs;
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,7 @@ public abstract class PersistenceTestCase
|
||||||
* database tables should be cleared.
|
* database tables should be cleared.
|
||||||
*/
|
*/
|
||||||
protected static final Object CLEAR_TABLES = new Object();
|
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.
|
* The {@link TestResult} instance for the current test run.
|
||||||
|
@ -95,6 +96,10 @@ public abstract class PersistenceTestCase
|
||||||
map.put("openjpa.jdbc.SynchronizeMappings",
|
map.put("openjpa.jdbc.SynchronizeMappings",
|
||||||
"buildSchema(ForeignKeys=true,"
|
"buildSchema(ForeignKeys=true,"
|
||||||
+ "SchemaAction='add,deleteTableContents')");
|
+ "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)
|
} else if (props[i] instanceof Class)
|
||||||
types.add((Class) props[i]);
|
types.add((Class) props[i]);
|
||||||
else if (props[i] != null)
|
else if (props[i] != null)
|
||||||
|
|
|
@ -89,4 +89,11 @@
|
||||||
value="buildSchema(ForeignKeys=true)"/>
|
value="buildSchema(ForeignKeys=true)"/>
|
||||||
</properties>
|
</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>
|
</persistence>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue