mirror of https://github.com/apache/openjpa.git
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:
parent
2243c76fe1
commit
e11755e4b4
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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",
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue