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
This commit is contained in:
Heath Thomann 2012-08-14 14:56:09 +00:00
parent 2243c76fe1
commit e11755e4b4
10 changed files with 170 additions and 16 deletions

View File

@ -25,6 +25,8 @@ import java.sql.Date;
import java.sql.Time; import java.sql.Time;
import java.sql.Timestamp; import java.sql.Timestamp;
import java.sql.Types; import java.sql.Types;
import java.util.HashSet;
import java.util.Set;
import org.apache.commons.lang.StringUtils; import org.apache.commons.lang.StringUtils;
import org.apache.openjpa.jdbc.identifier.DBIdentifier; import org.apache.openjpa.jdbc.identifier.DBIdentifier;
@ -78,11 +80,15 @@ public class Column
private String _comment = null; private String _comment = null;
private boolean _XML = false; private boolean _XML = false;
private boolean _isUni1MFK = false; private boolean _isUni1MFK = false;
private Set<Constraint> _constraints = new HashSet<Constraint>();
/** /**
* Default constructor. * Default constructor.
*/ */
public Column() { public Column() {
for (Constraint c : _constraints) {
addConstraint(c);
}
} }
/** /**
@ -886,4 +892,66 @@ public class Column
public void setUni1MFK(boolean isUni1MFK) { public void setUni1MFK(boolean isUni1MFK) {
_isUni1MFK = 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<? extends Constraint> type) {
return !getConstraints(type).isEmpty();
}
/**
* Gets all constrains attached this column.
*/
public Set<Constraint> getConstraints() {
return _constraints;
}
/**
* Gets all constrains of the given type attached to this column.
*/
public <T extends Constraint> Set<T> getConstraints(Class<T> type) {
Set<T> result = new HashSet<T>();
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);
}
} }

View File

@ -573,6 +573,7 @@ public class ForeignKey
if (_joins == null) if (_joins == null)
_joins = new LinkedHashMap(); _joins = new LinkedHashMap();
_joins.put(local, toPK); _joins.put(local, toPK);
local.addConstraint(this);
if (_joinsPK == null) if (_joinsPK == null)
_joinsPK = new LinkedHashMap(); _joinsPK = new LinkedHashMap();
_joinsPK.put(toPK, local); _joinsPK.put(toPK, local);
@ -629,6 +630,7 @@ public class ForeignKey
if (_joins != null) { if (_joins != null) {
rem = _joins.remove(col); rem = _joins.remove(col);
col.removeConstraint(this);
if (rem != null) { if (rem != null) {
_locals = null; _locals = null;
_pks = null; _pks = null;

View File

@ -67,6 +67,9 @@ public abstract class LocalConstraint
*/ */
void remove() { void remove() {
// remove all columns // remove all columns
for (Column c : _cols) {
c.removeConstraint(this);
}
setColumns(null); setColumns(null);
super.remove(); super.remove();
} }
@ -110,6 +113,7 @@ public abstract class LocalConstraint
_colList.add(col); _colList.add(col);
_cols = null; _cols = null;
col.addConstraint(this);
} }
/** /**
@ -123,6 +127,7 @@ public abstract class LocalConstraint
return false; return false;
if (_colList.remove(col)) { if (_colList.remove(col)) {
_cols = null; _cols = null;
col.removeConstraint(this);
return true; return true;
} }
return false; return false;

View File

@ -53,17 +53,6 @@ public class Unique
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)
*/
public void addColumn(Column col) {
super.addColumn(col);
col.setNotNull(true);
}
/** /**
* 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.

View File

@ -128,6 +128,7 @@ public class DB2Dictionary
supportsDefaultDeleteAction = false; supportsDefaultDeleteAction = false;
supportsAlterTableWithDropColumn = false; supportsAlterTableWithDropColumn = false;
supportsLockingWithOrderClause = true; supportsLockingWithOrderClause = true;
supportsNullUniqueColumn = false;
supportsNullTableForGetColumns = false; supportsNullTableForGetColumns = false;
requiresCastForMathFunctions = true; requiresCastForMathFunctions = true;

View File

@ -196,6 +196,7 @@ public class DBDictionary
public boolean supportsRestrictDeleteAction = true; public boolean supportsRestrictDeleteAction = true;
public boolean supportsCascadeDeleteAction = true; public boolean supportsCascadeDeleteAction = true;
public boolean supportsNullDeleteAction = true; public boolean supportsNullDeleteAction = true;
public boolean supportsNullUniqueColumn = true;
public boolean supportsDefaultDeleteAction = true; public boolean supportsDefaultDeleteAction = true;
public boolean supportsRestrictUpdateAction = true; public boolean supportsRestrictUpdateAction = true;
public boolean supportsCascadeUpdateAction = true; public boolean supportsCascadeUpdateAction = true;
@ -1303,8 +1304,12 @@ public class DBDictionary
if (col != null && (col.getType() == Types.CLOB if (col != null && (col.getType() == Types.CLOB
|| col.getType() == Types.LONGVARCHAR)) || col.getType() == Types.LONGVARCHAR))
setClobString(stmnt, idx, (String) val, col); setClobString(stmnt, idx, (String) val, col);
else else {
if (val instanceof String)
setString(stmnt, idx, (String) val, col); setString(stmnt, idx, (String) val, col);
else
setString(stmnt, idx, val.toString(), col);
}
break; break;
case JavaTypes.OBJECT: case JavaTypes.OBJECT:
setBlobObject(stmnt, idx, val, col, store); setBlobObject(stmnt, idx, val, col, store);
@ -3589,7 +3594,7 @@ public class DBDictionary
if (!alter) { if (!alter) {
if (col.getDefaultString() != null && !col.isAutoAssigned()) if (col.getDefaultString() != null && !col.isAutoAssigned())
buf.append(" DEFAULT ").append(col.getDefaultString()); buf.append(" DEFAULT ").append(col.getDefaultString());
if (col.isNotNull()) if (col.isNotNull() || (!supportsNullUniqueColumn && col.hasConstraint(Unique.class)))
buf.append(" NOT NULL"); buf.append(" NOT NULL");
} }
if (col.isAutoAssigned()) { if (col.isAutoAssigned()) {

View File

@ -68,6 +68,7 @@ public class DerbyDictionary
supportsSimpleCaseExpression = false; supportsSimpleCaseExpression = false;
supportsComments = true; supportsComments = true;
supportsNullUniqueColumn = false;
fixedSizeTypeNameSet.addAll(Arrays.asList(new String[]{ fixedSizeTypeNameSet.addAll(Arrays.asList(new String[]{
"BIGINT", "INTEGER", "BIGINT", "INTEGER",

View File

@ -690,8 +690,6 @@ public class RowImpl
&& col.getDefaultString() != null) && col.getDefaultString() != null)
return; return;
} }
if (val == null && col.isNotNull())
val = JavaSQLTypes.getEmptyValue(col.getJavaType());
flush(col, val, metaType, true); flush(col, val, metaType, true);
} }

View File

@ -23,6 +23,8 @@ import javax.persistence.Column;
import javax.persistence.Entity; import javax.persistence.Entity;
import javax.persistence.GeneratedValue; import javax.persistence.GeneratedValue;
import javax.persistence.Id; import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import javax.persistence.Version; import javax.persistence.Version;
/** /**
@ -32,6 +34,7 @@ import javax.persistence.Version;
* *
*/ */
@Entity @Entity
@Table(uniqueConstraints=@UniqueConstraint(columnNames={"UNS"}))
public class NullValues { public class NullValues {
@Id @Id
@GeneratedValue @GeneratedValue
@ -61,6 +64,9 @@ public class NullValues {
@Basic(optional=false) @Basic(optional=false)
private BlobValue notOptionalBlob; private BlobValue notOptionalBlob;
@Column(name="UNS")
private String uniqueNullable;
@Version @Version
private int version; private int version;
@ -78,6 +84,7 @@ public class NullValues {
setNotNullableBlob(new BlobValue()); setNotNullableBlob(new BlobValue());
setOptionalBlob(new BlobValue()); setOptionalBlob(new BlobValue());
setNotOptionalBlob(new BlobValue()); setNotOptionalBlob(new BlobValue());
setUniqueNullable("");
} }
public long getId() { public long getId() {
@ -148,6 +155,14 @@ public class NullValues {
this.notOptionalBlob = notOptionalBlob; this.notOptionalBlob = notOptionalBlob;
} }
public String getUniqueNullable() {
return uniqueNullable;
}
public void setUniqueNullable(String s) {
uniqueNullable = s;
}
public int getVersion() { public int getVersion() {
return version; return version;
} }

View File

@ -18,8 +18,16 @@
*/ */
package org.apache.openjpa.persistence.nullity; package org.apache.openjpa.persistence.nullity;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import javax.persistence.RollbackException; 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.InvalidStateException;
import org.apache.openjpa.persistence.OpenJPAPersistence; import org.apache.openjpa.persistence.OpenJPAPersistence;
@ -33,8 +41,10 @@ import org.apache.openjpa.persistence.OpenJPAPersistence;
*/ */
public class TestBasicFieldNullity extends AbstractNullityTestCase { public class TestBasicFieldNullity extends AbstractNullityTestCase {
private DBDictionary dict = null;
public void setUp() { public void setUp() {
setUp(CLEAR_TABLES, NullValues.class); setUp(CLEAR_TABLES, NullValues.class);
dict = ((JDBCConfiguration)emf.getConfiguration()).getDBDictionaryInstance();
} }
public void testNullOnOptionalFieldIsAllowed() { public void testNullOnOptionalFieldIsAllowed() {
@ -113,5 +123,65 @@ public class TestBasicFieldNullity extends AbstractNullityTestCase {
pc.setNotNullableBlob(null); pc.setNotNullableBlob(null);
assertCommitFails(pc, !NEW, RollbackException.class); 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<NullValues> 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<NullValues> 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;
}
} }