JodaTime有JPA,PostgreSQL和NULL值

我试图将带有JPA的JodaTime DateTime字段保存到PostgreSQL,但是遇到了对数据库NULL值的空指针的麻烦。

我正在使用NetBeans 7 beta 2 IDE。 持久性实现是EclipseLink 2.2.0,我使用EclipseLink Converter来使映射工作。 这是我的领域的声明:

@Converter( name="dateTimeConverter", converterClass=ejb.util.DateTimeConverter.class ) @Column(columnDefinition="TIMESTAMP WITH TIME ZONE") @Convert("dateTimeConverter") private DateTime testdate; 

转换器类:

 public class DateTimeConverter implements Converter { private Logger log; private static final long serialVersionUID = 1L; @Override public Object convertObjectValueToDataValue(Object o, Session sn) { if (o == null) { log.info("convertObjectValueToDataValue returning null"); return null; } return ((DateTime)o).toDate(); } @Override public Object convertDataValueToObjectValue(Object o, Session sn) { if (o == null) { log.info("convertDataValueToObjectValue returning null"); return null; } return new DateTime(o); } @Override public boolean isMutable() { return true; } @Override public void initialize(DatabaseMapping dm, Session sn) { log = Logger.getLogger("ejb.util.DateTimeConverter"); } } 

只要有实际的DateTime设置,这就可以正常工作。 但是一旦没有设置,EclipseLink似乎就会假设一个字符串类型,并且postgresql开始抱怨类型字符的值变化。 我假设这是因为转换器类返回空指针而不是日期对象,而EclipseLink则返回默认值。

有没有办法让这个工作没有切换到普通的java.util.Date?

使用@Converter ,EclipseLink不知道类型,因此您需要初始化它。

initialize(DatabaseMapping dm, Session sn)方法中,您需要设置类型,

 dm.setFieldClassification(java.sql.Date.class); // or, dm.setFieldClassification(java.sql.Timestamp.class); 

这篇文章: http : //www.thoughts-on-java.org/persist-localdate-localdatetime-jpa/给了我一些关于将localdate持久保存到db的非常有用的信息,它与你的DateTimeConverter实现有一些重要的区别(特别是实现AttributeConverter而不仅仅是转换器和’Converter’的注释是在类级别

我们使用了不同的方法。 (并为我们的编码标准道歉!)

在POJO我们有:

 @Column(name="LAST_UPDATED") @Type(type="DateTime") @NotNull public LastUpdType getLastUpdated() { return mLastUpdated; } 

在package.info中我们有:

 @TypeDefs( { @TypeDef(name = "DateTime", typeClass = JodaDateTimeType.class) }) 

然后我们有类JodaDateTimeType

 package uk.co.foo.hibernateutils.type; import java.io.Serializable; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; import java.sql.Types; import org.apache.log4j.Logger; import org.hibernate.HibernateException; import org.hibernate.cfg.Environment; import org.hibernate.usertype.UserType; import org.joda.time.DateTime; public class JodaDateTimeType implements UserType { private Logger mLogger = Logger.getLogger(getClass()); /** * Implementation taken from org.hibernate.type.MutableType via * org.hibernate.type.CalendarType. * @return true if the field is mutable, false otherwise. * * @see org.hibernate.type.Type#isMutable() */ public boolean isMutable() { return true; } /** * @param aRs A JDBC result set * @param aNames The column names * @param aOwner The containing entity * @return The retrieved value. * @throws HibernateException If a HibernateException occurs. * @throws SQLException If a SQLException occurs. * * @see org.hibernate.usertype.UserType * #nullSafeGet(java.sql.ResultSet, java.lang.String[], * java.lang.Object) */ public Object nullSafeGet(ResultSet aRs, String[] aNames, Object aOwner) throws HibernateException, SQLException { return nullSafeGet(aRs, aNames[0]); } /** * Implementation taken mainly from org.hibernate.type.NullableType. * * @param aRs The resultset containing db data. * @param aName The name of the required value. * @return The retrieved value. * @throws HibernateException If a HibernateException occurs. * @throws SQLException If a SQLException occurs. */ public Object nullSafeGet(ResultSet aRs, String aName) throws HibernateException, SQLException { try { Object value = get(aRs, aName); if (value == null || aRs.wasNull()) { if (mLogger.isDebugEnabled()) { mLogger.debug("returning null as column: " + aName); } return null; } else if (mLogger.isDebugEnabled()) { mLogger .debug("returning '" + toString(value) + "' as column: " + aName); } return value; } catch (RuntimeException re) { mLogger.info("could not read column value from result set: " + aName + "; " + re.getMessage()); throw re; } catch (SQLException se) { mLogger.info("could not read column value from result set: " + aName + "; " + se.getMessage()); throw se; } } /** * Implementation mainly taken from org.hibernate.type.CalendarType. * * @param aRs The resultset containing db data. * @param aName The name of the required value. * @return The retrieved value. * @throws HibernateException If a HibernateException occurs. * @throws SQLException If a SQLException occurs. */ protected Object get(ResultSet aRs, String aName) throws HibernateException, SQLException { Timestamp ts = aRs.getTimestamp(aName); if (ts != null) { DateTime dateTime; if (Environment.jvmHasTimestampBug()) { dateTime = new DateTime(ts.getTime() + ts.getNanos() / 1000000); } else { dateTime = new DateTime(ts.getTime()); } return dateTime; } return null; } /** * Implementation taken mainly from org.hibernate.type.NullableType. * * @param aSt A JDBC prepared statement * @param aValue The object to write * @param aIndex Statement parameter index * @throws HibernateException If a HibernateException occurs. * @throws SQLException If a SQLException occurs. */ public void nullSafeSet(PreparedStatement aSt, Object aValue, int aIndex) throws HibernateException, SQLException { try { if (aValue == null) { if (mLogger.isDebugEnabled()) { mLogger.debug("binding null to parameter: " + aIndex); } aSt.setNull(aIndex, sqlType()); } else { if (mLogger.isDebugEnabled()) { mLogger.debug("binding '" + toString(aValue) + "' to parameter: " + aIndex); } set(aSt, aValue, aIndex); } } catch (RuntimeException re) { mLogger.info("could not bind value '" + nullSafeToString(aValue) + "' to parameter: " + aIndex + "; " + re.getMessage()); throw re; } catch (SQLException se) { mLogger.info("could not bind value '" + nullSafeToString(aValue) + "' to parameter: " + aIndex + "; " + se.getMessage()); throw se; } } /** * Implementation mainly taken from org.hibernate.type.CalendarType. * * @param aSt A JDBC prepared statement * @param aValue The object to write * @param aIndex Statement parameter index * @throws HibernateException If a HibernateException occurs. * @throws SQLException If a SQLException occurs. */ protected void set(PreparedStatement aSt, Object aValue, int aIndex) throws HibernateException, SQLException { aSt.setTimestamp(aIndex, new Timestamp(((DateTime) aValue).getMillis())); } /** * Implementation mainly taken from org.hibernate.type.NullableType. A * null-safe version of {@link #toString(Object)}. Specifically we are * worried about null safeness in regards to the incoming value parameter, not * the return. * * @param aValue The value to convert to a string representation; may be null. * @return The string representation; may be null. * @throws HibernateException Thrown by {@link #toString(Object)}, which this * calls. */ private String nullSafeToString(Object aValue) throws HibernateException { if (aValue != null) { return toString(aValue); } return null; } /** * @param aValue value of the correct type. * @return A string representation of the given value. * @throws HibernateException If a HibernateException occurs. */ private String toString(Object aValue) throws HibernateException { return ((DateTime) aValue).toString(); } /** * * @return Types.DATE */ private int sqlType() { return Types.TIMESTAMP; } /** * @return The class returned by nullSafeGet. * @see org.hibernate.usertype.UserType#returnedClass() */ public Class returnedClass() { return DateTime.class; } /** * @param aX First object of type returned by returnedClass. * @param aY Second object of type returned by returnedClass. * @return True if the objects are equal, false otherwise. * @see org.hibernate.usertype.UserType * #equals(java.lang.Object, java.lang.Object) * @throws HibernateException to conform to superclass signature */ public boolean equals(Object aX, Object aY) throws HibernateException { if (aX == null) { return aY == null; } return aX.equals(aY); } /** * @param aX Object of type returned by returnedClass. * @return Hashcode of given object. * @see org.hibernate.usertype.UserType#hashCode(java.lang.Object) * @throws HibernateException to conform to superclass signature */ public int hashCode(Object aX) throws HibernateException { if (aX == null) { return -1; } return aX.hashCode(); } /** * @return The sql typecodes. * @see org.hibernate.usertype.UserType#sqlTypes() */ public int[] sqlTypes() { return new int[] {sqlType()}; } /** * Implementation taken from * org.springframework.orm.hibernate3.support.AbstractLobType. * @param aCached The object to be cached. * @param aOwner The owner of the cached object. * @return A reconstructed object from the cachable representation. * @throws HibernateException If a HibernateException occurs. * * @see org.hibernate.usertype.UserType * #assemble(java.io.Serializable, java.lang.Object) */ public Object assemble(Serializable aCached, Object aOwner) throws HibernateException { return aCached; } /** * @param aValue the object to be cloned, which may be null. * @return A copy of the given object. * @see org.hibernate.usertype.UserType#deepCopy(java.lang.Object) * @throws HibernateException to conform to superclass signature */ public Object deepCopy(Object aValue) throws HibernateException { if (aValue != null) { return new DateTime(((DateTime) aValue).getMillis()); } return null; } /** * Implementation taken from * org.springframework.orm.hibernate3.support.AbstractLobType. * @param aValue The object to be cached. * @return A cachable representation of the object. * * @see org.hibernate.usertype.UserType#disassemble(java.lang.Object) * @throws HibernateException to conform to superclass signature */ public Serializable disassemble(Object aValue) throws HibernateException { return (Serializable) aValue; } /** * Implementation taken from * org.springframework.orm.hibernate3.support.AbstractLobType. * @param aOriginal The value from the detached entity being merged * @param aTarget The value in the managed entity * @param aOwner The owner of the cached object. * @return The value to be merged * * @see org.hibernate.usertype.UserType * #replace(java.lang.Object, java.lang.Object, java.lang.Object) * @throws HibernateException to conform to superclass signature */ public Object replace(Object aOriginal, Object aTarget, Object aOwner) throws HibernateException { return aOriginal; } } 
Interesting Posts