【问题标题】:JodaTime with JPA, PostgreSQL and NULL values带有 JPA、PostgreSQL 和 NULL 值的 JodaTime
【发布时间】:2011-07-09 18:11:57
【问题描述】:

我正在尝试将 JPA 的 JodaTime DateTime 字段持久保存到 PostgreSQL,但遇到了指向数据库 NULL 值的空指针的问题。

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

@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 的情况下工作?

【问题讨论】:

  • 您是否尝试将@Temporal 注释添加到private DateTime testdate 字段?
  • 是的,但是当我部署时,EclipseLink 实际上会记录一条消息,指出由于转换器,它将忽略@Temporal 注释。

标签: java postgresql jpa eclipselink jodatime


【解决方案1】:

使用@Converter时,EclipseLink不知道类型,所以需要初始化。

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

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

【讨论】:

  • 可惜,根据 netbeans 和文档,setFieldClassification 方法不存在,我使用的是 2.2.0。有一个同名的吸气剂。本来是一个不错的解决方案。
  • 找到了,它是在子类 AbstractDirectMapping 上定义的。因此,在检查类型并进行转换后,它就可以工作了。谢谢。
【解决方案2】:

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

【讨论】:

  • 谢谢,看起来不错,您可能想在答案中加入更多细节和/或示例,因为按照您现在的答案,它可能被标记为仅链接答案。顺便说一句,文章中的解决方案需要我最初发布问题时不存在的 JPA2.1。
  • 我不确定从源头复制/粘贴的“礼仪”,即链接到它与在此处完整共享它。我并不是要暗示您的解决方案是错误的(大约 5 年前就有人问过了!:))。我只是想在此处为其他从 cmets 朝着正确方向开始,然后需要额外帮助的人添加一些额外的信息。
【解决方案3】:

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

在 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;
  }
}

【讨论】:

猜你喜欢
  • 2020-12-25
  • 2013-01-26
  • 2016-02-11
  • 1970-01-01
  • 1970-01-01
  • 2021-01-24
  • 2023-03-27
  • 2011-01-05
  • 1970-01-01
相关资源
最近更新 更多