Hi,
i'm using Hibernate 3.1.2 and i'm having, not a little problem to understand how hibernate decide to do an INSERT + and UPDATE in a specific case.
The problem :
When performing a persist operation on a brand new object without any reference to any objects, hibernate try to make two statements, 1 INSERT and 1 UPDATE.
Test #0:
As is
Result: 1 INSERT, 1 UPDATE
Explanation:????????? I need to understand :-)
Test #1:
Remove the property
Result : Only 1 INSERT
Test #2:
Add not-null="true"
Result: 1 INSERT
Test #3:
Add update="false"
Result: 1 INSERT
Test #4:
Remove optimistic-lock="false"
Result: 1 INSERT, 2 UPDATE
Explanation: Hibernate update also my "Base Class" responsible of the version control
Below you can find a subset of the mapping
Code:
<joined-subclass
.....
<property name="actionRequest"
column="AR_SRV_ACT_REQ_ID"
optimistic-lock="false">
<type name="xxx.xxx.hibernate.AgileEnumerationUserType">
<param name="targetClass">xxx.xxx.enumeration.EnmServerAction
</param>
</type>
</property>
....
</joined-subclass>
Code:
public class AgileEnumerationUserType implements UserType, ParameterizedType {
private Class targetClass;
private static final int[] SQL_TYPES = {Types.BIGINT};
public int[] sqlTypes() {
return SQL_TYPES;
}
public Class returnedClass() {
return targetClass;
}
public boolean equals(Object x, Object y) throws HibernateException {
if(x == null || y == null)
return false;
return x.equals(y);
}
public Object deepCopy(Object value) throws HibernateException {
return value;
}
public boolean isMutable() {
return false;
}
public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner) throws HibernateException, SQLException {
Integer key = new Integer(resultSet.getInt(names[0]));
AgileEnumeration enumeration;
try {
enumeration = (AgileEnumeration) targetClass.newInstance();
enumeration.setKey(key);
} catch (InstantiationException e) {
throw new HibernateException(e);
} catch (IllegalAccessException e) {
throw new HibernateException(e);
}
return resultSet.wasNull() ? null : enumeration;
}
public void nullSafeSet(PreparedStatement statement, Object value, int index) throws HibernateException, SQLException {
if (value == null) {
statement.setNull(index, Types.BIGINT);
} else {
statement.setInt(index,((AgileEnumeration)value).intValue());
}
}
public int hashCode(Object arg0) throws HibernateException {
return arg0.hashCode();
}
public Serializable disassemble(Object value) throws HibernateException {
return (Serializable)value;
}
public Object assemble(Serializable cached, Object owner) throws HibernateException {
return cached;
}
public Object replace(Object arg0, Object arg1, Object arg2) throws HibernateException {
return arg0;
}
public void setParameterValues(Properties parameters) {
if(parameters == null)
throw new HibernateException(ExceptionMessages.HIBERNATE_PLEASE_PROVIDE_TARGET_CLASS_FOR_ENUMERATION);
String targetClassName = parameters.getProperty("targetClass");
if(targetClassName == null)
throw new HibernateException(ExceptionMessages.HIBERNATE_PLEASE_PROVIDE_TARGET_CLASS_FOR_ENUMERATION);
try {
targetClass = Class.forName(targetClassName);
if (!(AgileEnumeration.class.isAssignableFrom( targetClass ))) {
throw new HibernateException(ExceptionMessages.HIBERNATE_NOT_AN_ENUMERATION + ":" + targetClass);
}
} catch (ClassNotFoundException e) {
throw new HibernateException(ExceptionMessages.HIBERNATE_PLEASE_PROVIDE_TARGET_CLASS_FOR_ENUMERATION);
}
}
}
Code:
public class AgileEnumeration {
private Integer key;
private static Map reservedKeys = new HashMap();
public AgileEnumeration() {
key = null;
}
public AgileEnumeration(Integer key) {
this.key = key;
}
public AgileEnumeration(int i) {
key = new Integer(i);
if(reservedKeys.containsKey(key)) {
throw new AgileRuntimeException(ExceptionMessages.ENUMERATION_DUPLICATE_KEY +
":" +
getClass() +
":" +
i +
" -> " +
reservedKeys.get(key).getClass() +
":" +
reservedKeys.get(key).toString());
}
reservedKeys.put(key, this);
}
public byte byteValue() {
return key.byteValue();
}
public int compareTo(Integer anotherInteger) {
return key.compareTo(anotherInteger);
}
public int compareTo(Object o) {
if(o == null)
return -1;
if(!(o instanceof AgileEnumeration))
return -1;
return key.compareTo(((AgileEnumeration)o).key);
}
public double doubleValue() {
return key.doubleValue();
}
public boolean equals(Object obj) {
if(obj != null && getClass().isAssignableFrom(obj.getClass())) {
AgileEnumeration other = (AgileEnumeration)obj;
if(key == null && other.key == null)
return true;
return key != null && key.equals(other.key);
}
return false;
}
public int hashCode() {
if(key == null)
return 0;
return key.hashCode();
}
public float floatValue() {
return key.floatValue();
}
public int intValue() {
return key.intValue();
}
public long longValue() {
return key.longValue();
}
public short shortValue() {
return key.shortValue();
}
/**
* toString is overridden to display a user and developer friendly string
* instead of the numeric value. We could and need to read the mapping
* from a file to permit internationalization. As of now, the implementation
* uses reflection to use the field name as string.
*
* TODO: we need to change this to use a properties file
*/
public String toString() {
Field fieldList[] = this.getClass().getDeclaredFields();
for(int i = 0; i < fieldList.length; i++) {
try {
if(this.getClass().equals(fieldList[i].getType()) &&
fieldList[i].get(this) != null && fieldList[i].get(this).equals(this)) {
return fieldList[i].getName();
}
} catch (IllegalArgumentException e) {
throw new AgileRuntimeException(ExceptionMessages.ENUMERATION_NO_SUCH_FIELD_VALUE, e);
} catch (IllegalAccessException e) {
throw new AgileRuntimeException(ExceptionMessages.ENUMERATION_NO_SUCH_FIELD_VALUE, e);
}
}
//throw new AgileRuntimeException(ExceptionMessages.ENUMERATION_NO_SUCH_FIELD_VALUE);
//possible if the field is not actually static such as in DuplicateKeyIsDetected test...
return null;
}
public Integer getKey() {
return key;
}
public void setKey(Integer key) {
this.key = key;
}
/**
* Get all possible values creates a Set (because unique values only) of all possible
* constants exposed by this class. It filters other field by checking that the field instance
* is of the same type as the Enumeration class where it is located
*
* @param enumerationClass The class we want to get a list from
* @return All possible values of the constants contained in this class
*
* TODO: add extra conditions to make sure the constants selected are public final static
*/
static public Set getPossibleValues(Class enumerationClass) {
Field fieldList[] = enumerationClass.getDeclaredFields();
Set results = new HashSet();
for(int i = 0; i < fieldList.length; i++) {
if(fieldList[i].getType().equals(enumerationClass)) {
try {
results.add(fieldList[i].get(null));
} catch (IllegalArgumentException e) {
throw new AgileRuntimeException(ExceptionMessages.ENUMERATION_COULD_NOT_GET_ALL_FIELD_VALUES, e);
} catch (IllegalAccessException e) {
throw new AgileRuntimeException(ExceptionMessages.ENUMERATION_COULD_NOT_GET_ALL_FIELD_VALUES, e);
}
}
}
return results;
}
Code:
public class EnmServerAction extends AgileEnumeration {
public static final int IDBASE = 3600;
public static final EnmServerAction START = new EnmServerAction(IDBASE + 1);
public static final EnmServerAction PAUSE = new EnmServerAction(IDBASE + 2);
public static final EnmServerAction STOP = new EnmServerAction(IDBASE + 3);
public EnmServerAction() {
super();
}
private EnmServerAction(int persistentValue) {
super(persistentValue);
}
public static Set getInstances() {
return AgileEnumeration.getPossibleValues(EnmServerAction.class);
}
}