If your database schema was designed properly you'll never need this. But, if you happen to be working with a legacy database schema where a numeric type database column could possibly be <NULL> you'll get a hibernate error if you try mapping to primative data types.
I encountered this problem. The database column was a type 'int'. The POJO property I was mapping to was an 'int'. When I attempted to fetch a row that contained <NULL> in this column, hibernate threw an exception.
14:13:28,339 INFO DefaultLoadEventListener:95 - Error performing load command
org.hibernate.PropertyAccessException: exception setting property value with CGLIB (set hibernate.cglib.use_reflection_optimizer=false for more info) setter of com.perse.onestaff.wsm.domain.concurrency.LockInfo.setPositionId
at ...
Caused by: net.sf.cglib.beans.BulkBeanException
at com.perse.onestaff.wsm.domain.concurrency.LockInfo$$BulkBeanByCGLIB$$32df94d6.setPropertyValues(<generated>)
at org.hibernate.tuple.PojoEntityTuplizer.setPropertyValuesWithOptimizer(PojoEntityTuplizer.java:212)
... 46 more
Caused by: java.lang.NullPointerException
... 48 more
So, to get around this problem I created a custom UserType that checks for null when getting the value from the resultset and defaults to 0 (zero).
This only works if you don't need to know that the value was actually <NULL> and defaulting to 0 is ok.
------------------------------------------------------------------
package com.mycompany.hibernate;
import org.hibernate.HibernateException;
import org.hibernate.usertype.UserType;
import org.hibernate.usertype.ParameterizedType;
import java.sql.*;
import java.util.Properties;
import java.io.Serializable;
/**
* Hibernate UserType for numeric database values that could have <NULL> values.
* POJO property data type can be any Java Primitive Data Type (see below).
*
*
* Java's Primitive Data Types
*
* boolean: 1-bit. May take on the values true and false only.
* byte: 1 signed byte (two's complement). Covers values from -128 to 127.
* short: 2 bytes, signed (two's complement), -32,768 to 32,767
* int: 4 bytes, signed (two's complement). -2,147,483,648 to 2,147,483,647. Like all numeric types ints may be cast into other numeric types (byte, short, long, float, double). When lossy casts are done (e.g. int to byte) the conversion is done modulo the length of the smaller type.
* long: 8 bytes signed (two's complement). Ranges from -9,223,372,036,854,775,808 to +9,223,372,036,854,775,807.
* float: 4 bytes, IEEE 754. Covers a range from 1.40129846432481707e-45 to 3.40282346638528860e+38 (positive or negative).
* double: 8 bytes IEEE 754. Covers a range from 4.94065645841246544e-324d to 1.79769313486231570e+308d (positive or negative).
*/
public class NullSafeNumericUserType implements UserType, ParameterizedType {
public static final String DATA_TYPE_PROPERTY = "dataType";
private int[] SQL_TYPES = {Types.NUMERIC};
private enum DataType {
booleanDT,byteDT,shortDT,intDT,longDT,floatDT,doubleDT }
private DataType dataType;
public int[] sqlTypes() {
return SQL_TYPES;
}
public void setParameterValues(Properties parameters) {
String sType = parameters.getProperty(DATA_TYPE_PROPERTY) + "DT";
try {
dataType = DataType.valueOf(sType);
}
catch (Exception e) {
dataType = DataType.intDT;
}
}
public Class returnedClass() {
Class dataTypeClass = Integer.class;
switch (dataType) {
case booleanDT:
dataTypeClass = Boolean.class;
break;
case byteDT:
dataTypeClass = Byte.class;
break;
case shortDT:
dataTypeClass = Short.class;
break;
case intDT:
dataTypeClass = Integer.class;
break;
case longDT:
dataTypeClass = Long.class;
break;
case floatDT:
dataTypeClass = Float.class;
break;
case doubleDT:
dataTypeClass = Double.class;
break;
}
return dataTypeClass;
}
public boolean equals(Object x, Object y) throws HibernateException {
if (x == y) {
return true;
}
else if (x == null || y == null) {
return false;
}
else {
return x.equals(y);
}
}
public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner)
throws HibernateException, SQLException {
Object result = null;
switch (dataType) {
case booleanDT:
result = new Boolean(false);
boolean booleanValue = resultSet.getBoolean(names[0]);
if (!resultSet.wasNull()) {
result = booleanValue ==
false ? result : new Boolean(booleanValue);
}
break;
case byteDT:
result = new Byte((byte) 0);
byte byteValue = resultSet.getByte(names[0]);
if (!resultSet.wasNull()) {
result = byteValue ==
0 ? result : new Byte(byteValue);
}
break;
case shortDT:
result = new Short((short) 0);
short shortValue = resultSet.getShort(names[0]);
if (!resultSet.wasNull()) {
result = shortValue ==
0 ? result : new Integer(shortValue);
}
break;
case intDT:
result = new Integer(0);
int intValue = resultSet.getInt(names[0]);
if (!resultSet.wasNull()) {
result = intValue ==
0 ? result : new Integer(intValue);
}
break;
case longDT:
result = new Long((long) 0);
long longValue = resultSet.getLong(names[0]);
if (!resultSet.wasNull()) {
result = longValue ==
(long) 0 ? result : new Long(longValue);
}
break;
case floatDT:
result = new Float((float) 0);
float floatValue = resultSet.getFloat(names[0]);
if (!resultSet.wasNull()) {
result = floatValue ==
(float) 0 ? result : new Float(floatValue);
}
break;
case doubleDT:
result = new Double((double) 0);
double doubleValue = resultSet.getDouble(names[0]);
if (!resultSet.wasNull()) {
result = doubleValue ==
(double) 0 ? result : new Double(doubleValue);
}
break;
}
return result;
}
public void nullSafeSet(
PreparedStatement statement, Object value, int index)
throws HibernateException, SQLException {
switch (dataType) {
case booleanDT:
if (value == null) statement.setBoolean(index, false);
else statement.setBoolean(
index, ((Boolean) value).booleanValue());
break;
case byteDT:
if (value == null) statement.setByte(index, (byte) 0);
else statement.setByte(index, ((Byte) value).byteValue());
break;
case shortDT:
if (value == null) statement.setShort(index, (short) 0);
else statement.setShort(index, ((Short) value).shortValue());
break;
case intDT:
if (value == null) statement.setInt(index, 0);
else statement.setInt(index, ((Integer) value).intValue());
break;
case longDT:
if (value == null) statement.setLong(index, (long) 0);
else statement.setLong(index, ((Long) value).longValue());
break;
case floatDT:
if (value == null) statement.setFloat(index, (float) 0);
else statement.setFloat(index, ((Float) value).floatValue());
break;
case doubleDT:
if (value == null) statement.setDouble(index, (double) 0);
else statement.setDouble(index, ((Double) value).doubleValue());
break;
}
}
public Object deepCopy(Object value) throws HibernateException {
return value;
}
public boolean isMutable() {
return false;
}
public Object assemble(Serializable cached, Object owner)
throws HibernateException {
return cached;
}
public Serializable disassemble(Object value) throws HibernateException {
return (Serializable) value;
}
public int hashCode(Object x) throws HibernateException {
return x.hashCode();
}
public Object replace(Object original, Object target, Object owner)
throws HibernateException {
return original;
}
}
-------------------------------------------------------------------
I'd like to hear if this makes sense or if there is a better way.
Thanks,
Scott
|