我正在尝试将带有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?
答案 0 :(得分:7)
当使用@Converter
时,EclipseLink不知道类型,因此您需要初始化它。
在initialize(DatabaseMapping dm, Session sn)
方法中,您需要设置类型
dm.setFieldClassification(java.sql.Date.class);
// or, dm.setFieldClassification(java.sql.Timestamp.class);
答案 1 :(得分:1)
这篇文章:http://www.thoughts-on-java.org/persist-localdate-localdatetime-jpa/给了我一些关于将localdate持久保存到数据库的非常有用的信息,它与你的DateTimeConverter实现有一些重要的区别(具体实现了AttributeConverter而不仅仅是转换器和'Converter'的注释是班级
答案 2 :(得分:0)
我们使用了不同的方法。 (并为我们的编码标准道歉!)
在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;
}
}