From e11755e4b42dfcb01d8ca7318870fb5fb73c1d3b Mon Sep 17 00:00:00 2001 From: Heath Thomann Date: Tue, 14 Aug 2012 14:56:09 +0000 Subject: [PATCH] OPENJPA-1682: Backported to 2.0.x this JIRA and revision 941362 from OPENJPA-1387 since the two issues are related/coupled. git-svn-id: https://svn.apache.org/repos/asf/openjpa/branches/2.0.x@1372913 13f79535-47bb-0310-9956-ffa450edef68 --- .../apache/openjpa/jdbc/schema/Column.java | 68 ++++++++++++++++++ .../openjpa/jdbc/schema/ForeignKey.java | 2 + .../openjpa/jdbc/schema/LocalConstraint.java | 5 ++ .../apache/openjpa/jdbc/schema/Unique.java | 11 --- .../openjpa/jdbc/sql/DB2Dictionary.java | 1 + .../apache/openjpa/jdbc/sql/DBDictionary.java | 11 ++- .../openjpa/jdbc/sql/DerbyDictionary.java | 1 + .../org/apache/openjpa/jdbc/sql/RowImpl.java | 2 - .../persistence/nullity/NullValues.java | 15 ++++ .../nullity/TestBasicFieldNullity.java | 70 +++++++++++++++++++ 10 files changed, 170 insertions(+), 16 deletions(-) diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Column.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Column.java index fbe07f3d4..961797058 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Column.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Column.java @@ -25,6 +25,8 @@ import java.sql.Date; import java.sql.Time; import java.sql.Timestamp; import java.sql.Types; +import java.util.HashSet; +import java.util.Set; import org.apache.commons.lang.StringUtils; import org.apache.openjpa.jdbc.identifier.DBIdentifier; @@ -78,11 +80,15 @@ public class Column private String _comment = null; private boolean _XML = false; private boolean _isUni1MFK = false; + private Set _constraints = new HashSet(); /** * Default constructor. */ public Column() { + for (Constraint c : _constraints) { + addConstraint(c); + } } /** @@ -886,4 +892,66 @@ public class Column public void setUni1MFK(boolean isUni1MFK) { _isUni1MFK = isUni1MFK; } + + /** + * Adds the given constraint to this column. + */ + public void addConstraint(Constraint c) { + _constraints.add(c); + } + + /** + * Removes the given constraint from this column. + */ + public void removeConstraint(Constraint c) { + _constraints.remove(c); + } + + /** + * Affirms if this column has any constraint of given type. + */ + public boolean hasConstraint(Class type) { + return !getConstraints(type).isEmpty(); + } + + /** + * Gets all constrains attached this column. + */ + public Set getConstraints() { + return _constraints; + } + + /** + * Gets all constrains of the given type attached to this column. + */ + public Set getConstraints(Class type) { + Set result = new HashSet(); + for (Constraint c : _constraints) { + if (c.getClass() == type) { + result.add((T)c); + } + } + return result; + } + + /** + * Affirms if any unique constraint is attached to this column. + */ + public boolean isUniqueConstraint() { + return hasConstraint(Unique.class); + } + + /** + * Affirms if any index constraint is attached to this column. + */ + public boolean isIndex() { + return hasConstraint(Index.class); + } + + /** + * Affirms if any foreign key constraint is attached to this column. + */ + public boolean isForeignKey() { + return hasConstraint(ForeignKey.class); + } } diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/ForeignKey.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/ForeignKey.java index 60ed28cb4..def923e0c 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/ForeignKey.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/ForeignKey.java @@ -573,6 +573,7 @@ public class ForeignKey if (_joins == null) _joins = new LinkedHashMap(); _joins.put(local, toPK); + local.addConstraint(this); if (_joinsPK == null) _joinsPK = new LinkedHashMap(); _joinsPK.put(toPK, local); @@ -629,6 +630,7 @@ public class ForeignKey if (_joins != null) { rem = _joins.remove(col); + col.removeConstraint(this); if (rem != null) { _locals = null; _pks = null; diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/LocalConstraint.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/LocalConstraint.java index 9126bef16..c75155576 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/LocalConstraint.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/LocalConstraint.java @@ -67,6 +67,9 @@ public abstract class LocalConstraint */ void remove() { // remove all columns + for (Column c : _cols) { + c.removeConstraint(this); + } setColumns(null); super.remove(); } @@ -110,6 +113,7 @@ public abstract class LocalConstraint _colList.add(col); _cols = null; + col.addConstraint(this); } /** @@ -123,6 +127,7 @@ public abstract class LocalConstraint return false; if (_colList.remove(col)) { _cols = null; + col.removeConstraint(this); return true; } return false; diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Unique.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Unique.java index c3c6319ae..51fab69fc 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Unique.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/schema/Unique.java @@ -53,17 +53,6 @@ public class Unique 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) - */ - public void addColumn(Column col) { - super.addColumn(col); - col.setNotNull(true); - } - /** * Set the name of the constraint. This method cannot be called if the * constraint already belongs to a table. diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DB2Dictionary.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DB2Dictionary.java index a225283bb..b4f4b0230 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DB2Dictionary.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DB2Dictionary.java @@ -128,6 +128,7 @@ public class DB2Dictionary supportsDefaultDeleteAction = false; supportsAlterTableWithDropColumn = false; supportsLockingWithOrderClause = true; + supportsNullUniqueColumn = false; supportsNullTableForGetColumns = false; requiresCastForMathFunctions = true; diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java index fe3623cee..12eb4eb4c 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DBDictionary.java @@ -196,6 +196,7 @@ public class DBDictionary public boolean supportsRestrictDeleteAction = true; public boolean supportsCascadeDeleteAction = true; public boolean supportsNullDeleteAction = true; + public boolean supportsNullUniqueColumn = true; public boolean supportsDefaultDeleteAction = true; public boolean supportsRestrictUpdateAction = true; public boolean supportsCascadeUpdateAction = true; @@ -1303,8 +1304,12 @@ public class DBDictionary if (col != null && (col.getType() == Types.CLOB || col.getType() == Types.LONGVARCHAR)) setClobString(stmnt, idx, (String) val, col); - else - setString(stmnt, idx, (String) val, col); + else { + if (val instanceof String) + setString(stmnt, idx, (String) val, col); + else + setString(stmnt, idx, val.toString(), col); + } break; case JavaTypes.OBJECT: setBlobObject(stmnt, idx, val, col, store); @@ -3589,7 +3594,7 @@ public class DBDictionary if (!alter) { if (col.getDefaultString() != null && !col.isAutoAssigned()) buf.append(" DEFAULT ").append(col.getDefaultString()); - if (col.isNotNull()) + if (col.isNotNull() || (!supportsNullUniqueColumn && col.hasConstraint(Unique.class))) buf.append(" NOT NULL"); } if (col.isAutoAssigned()) { diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DerbyDictionary.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DerbyDictionary.java index 1a8839f9d..eec87d915 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DerbyDictionary.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/DerbyDictionary.java @@ -68,6 +68,7 @@ public class DerbyDictionary supportsSimpleCaseExpression = false; supportsComments = true; + supportsNullUniqueColumn = false; fixedSizeTypeNameSet.addAll(Arrays.asList(new String[]{ "BIGINT", "INTEGER", diff --git a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/RowImpl.java b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/RowImpl.java index 3b5e82e2c..aa3350d7b 100644 --- a/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/RowImpl.java +++ b/openjpa-jdbc/src/main/java/org/apache/openjpa/jdbc/sql/RowImpl.java @@ -690,8 +690,6 @@ public class RowImpl && col.getDefaultString() != null) return; } - if (val == null && col.isNotNull()) - val = JavaSQLTypes.getEmptyValue(col.getJavaType()); flush(col, val, metaType, true); } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/NullValues.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/NullValues.java index a64bc7664..b9c7d9e03 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/NullValues.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/NullValues.java @@ -23,6 +23,8 @@ import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.Id; +import javax.persistence.Table; +import javax.persistence.UniqueConstraint; import javax.persistence.Version; /** @@ -32,6 +34,7 @@ import javax.persistence.Version; * */ @Entity +@Table(uniqueConstraints=@UniqueConstraint(columnNames={"UNS"})) public class NullValues { @Id @GeneratedValue @@ -61,6 +64,9 @@ public class NullValues { @Basic(optional=false) private BlobValue notOptionalBlob; + @Column(name="UNS") + private String uniqueNullable; + @Version private int version; @@ -78,6 +84,7 @@ public class NullValues { setNotNullableBlob(new BlobValue()); setOptionalBlob(new BlobValue()); setNotOptionalBlob(new BlobValue()); + setUniqueNullable(""); } public long getId() { @@ -148,6 +155,14 @@ public class NullValues { this.notOptionalBlob = notOptionalBlob; } + public String getUniqueNullable() { + return uniqueNullable; + } + + public void setUniqueNullable(String s) { + uniqueNullable = s; + } + public int getVersion() { return version; } diff --git a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/TestBasicFieldNullity.java b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/TestBasicFieldNullity.java index 0dfbfaa48..095603ce7 100644 --- a/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/TestBasicFieldNullity.java +++ b/openjpa-persistence-jdbc/src/test/java/org/apache/openjpa/persistence/nullity/TestBasicFieldNullity.java @@ -18,8 +18,16 @@ */ package org.apache.openjpa.persistence.nullity; +import java.util.List; + +import javax.persistence.EntityManager; +import javax.persistence.Query; import javax.persistence.RollbackException; +import org.apache.openjpa.jdbc.conf.JDBCConfiguration; +import org.apache.openjpa.jdbc.sql.DBDictionary; +import org.apache.openjpa.jdbc.sql.OracleDictionary; +import org.apache.openjpa.jdbc.sql.SybaseDictionary; import org.apache.openjpa.persistence.InvalidStateException; import org.apache.openjpa.persistence.OpenJPAPersistence; @@ -33,8 +41,10 @@ import org.apache.openjpa.persistence.OpenJPAPersistence; */ public class TestBasicFieldNullity extends AbstractNullityTestCase { + private DBDictionary dict = null; public void setUp() { setUp(CLEAR_TABLES, NullValues.class); + dict = ((JDBCConfiguration)emf.getConfiguration()).getDBDictionaryInstance(); } public void testNullOnOptionalFieldIsAllowed() { @@ -113,5 +123,65 @@ public class TestBasicFieldNullity extends AbstractNullityTestCase { pc.setNotNullableBlob(null); assertCommitFails(pc, !NEW, RollbackException.class); } + + public void testUniqueStringColumnCanBeNull() { + if (!isUniqueColumnNullable()) { + return; + } + NullValues pc = new NullValues(); + pc.setUniqueNullable(null); + assertCommitSucceeds(pc, NEW); + } + + public void testUniqueStringColumnAsNull() { + if (!isUniqueColumnNullable()) { + return; + } + NullValues pc = new NullValues(); + pc.setUniqueNullable(null); + assertCommitSucceeds(pc, NEW); + + String jpql = "select n from NullValues n where n.uniqueNullable = :p"; + EntityManager em = emf.createEntityManager(); + List result = em.createQuery(jpql) + .setParameter("p", null) + .getResultList(); + assertFalse(result.isEmpty()); + for (NullValues n : result) + assertNull(n.getUniqueNullable()); + } + + public void testUniqueStringColumnAsEmpty() { + String EMPTY_STRING = ""; + NullValues pc = new NullValues(); + pc.setUniqueNullable(EMPTY_STRING); + assertCommitSucceeds(pc, NEW); + + String jpql = "select n from NullValues n where n.uniqueNullable = :p"; + if (dict instanceof OracleDictionary) + jpql = "select n from NullValues n where n.uniqueNullable IS NULL"; + EntityManager em = emf.createEntityManager(); + Query query = em.createQuery(jpql); + if (!(dict instanceof OracleDictionary)) + query.setParameter("p", EMPTY_STRING); + List result = query.getResultList(); + assertFalse(result.isEmpty()); + for (NullValues n : result) { + if (dict instanceof OracleDictionary) { + assertNull(n.getUniqueNullable()); + } + else if (dict instanceof SybaseDictionary) { + // Sybase converts empty strings to "" + assertEquals(" ", n.getUniqueNullable()); + } + else { + assertEquals(EMPTY_STRING, n.getUniqueNullable()); + } + } + } + + boolean isUniqueColumnNullable() { + return ((JDBCConfiguration)emf.getConfiguration()).getDBDictionaryInstance().supportsNullUniqueColumn; + } }