2016-10-24 104 views
0

有什么办法可以支持Hibernate 5.1.1中java.time.ZoneId持久映射到字符串吗?它现在以二进制形式保存ZoneId。Grails(Hibernate)将java.time.ZoneId映射到数据库

我刚升级到具有Hibernate 5.1.1的Grails 3.2.1。保存java.time.Instant例如可以正常工作,但java.time.ZoneId只能以二进制形式存储。

我认为Hibernate没有支持。那么我如何编码我自己的映射。我试过使用Jadira Framework,但这不可能,因为在启动grails应用程序时有一些冲突(例外)。

+1

为什么不保存zoneId.getId()作为字符串,然后初始化它使用ZoneId.of( “了zoneid”)? – aviad

+0

这实际上是我的解决方法,但不知何故,我觉得它可以自动完成。至少Jadira是这样做的(我在从Grails 3.1.9升级到Grails 3.2.1之前已经使用过) – kuceram

+1

我明白,你可以在实体中做一个@Transient方法来完成从字符串到区域ID,所以它将是透明的 – aviad

回答

0

所以我终于找到了一个很好的方法来实现自定义的hibernate用户类型。坚持java.time.ZoneId为varchar实施下列用户类型的类:

import org.hibernate.HibernateException 
import org.hibernate.engine.spi.SessionImplementor 
import org.hibernate.type.StandardBasicTypes 
import org.hibernate.usertype.EnhancedUserType 

import java.sql.PreparedStatement 
import java.sql.ResultSet 
import java.sql.SQLException 
import java.sql.Types 
import java.time.ZoneId 

/** 
* A type that maps between {@link java.sql.Types#VARCHAR} and {@link ZoneId}. 
*/ 
class ZoneIdUserType implements EnhancedUserType, Serializable { 

    private static final int[] SQL_TYPES = [Types.VARCHAR] 

    @Override 
    public int[] sqlTypes() { 
     return SQL_TYPES 
    } 

    @Override 
    public Class returnedClass() { 
     return ZoneId.class 
    } 

    @Override 
    public boolean equals(Object x, Object y) throws HibernateException { 
     if (x == y) { 
      return true 
     } 
     if (x == null || y == null) { 
      return false 
     } 
     ZoneId zx = (ZoneId) x 
     ZoneId zy = (ZoneId) y 
     return zx.equals(zy) 
    } 

    @Override 
    public int hashCode(Object object) throws HibernateException { 
     return object.hashCode() 
    } 

    @Override 
    public Object nullSafeGet(ResultSet resultSet, String[] names, SessionImplementor session, Object owner) 
     throws HibernateException, SQLException { 
     Object zoneId = StandardBasicTypes.STRING.nullSafeGet(resultSet, names, session, owner) 
     if (zoneId == null) { 
      return null 
     } 
     return ZoneId.of(zoneId) 
    } 

    @Override 
    public void nullSafeSet(PreparedStatement preparedStatement, Object value, int index, SessionImplementor session) 
     throws HibernateException, SQLException { 
     if (value == null) { 
      StandardBasicTypes.STRING.nullSafeSet(preparedStatement, null, index, session) 
     } else { 
      def zoneId = (ZoneId) value 
      StandardBasicTypes.STRING.nullSafeSet(preparedStatement, zoneId.getId(), index, session) 
     } 
    } 

    @Override 
    public Object deepCopy(Object value) throws HibernateException { 
     return value 
    } 

    @Override 
    public boolean isMutable() { 
     return false 
    } 

    @Override 
    public Serializable disassemble(Object value) throws HibernateException { 
     return (Serializable) value 
    } 

    @Override 
    public Object assemble(Serializable cached, Object value) throws HibernateException { 
     return cached 
    } 

    @Override 
    public Object replace(Object original, Object target, Object owner) throws HibernateException { 
     return original 
    } 

    @Override 
    public String objectToSQLString(Object object) { 
     throw new UnsupportedOperationException() 
    } 

    @Override 
    public String toXMLString(Object object) { 
     return object.toString() 
    } 

    @Override 
    public Object fromXMLString(String string) { 
     return ZoneId.of(string) 
    } 
} 

然后,你需要在你的Grails应用的conf/application.groovy注册自定义用户类型:

grails.gorm.default.mapping = { 
    'user-type'(type: ZoneIdUserType, class: ZoneId) 
} 

比你可以简单地用java .time.ZoneId在您的域类:

import java.time.ZoneId 

class MyDomain { 
    ZoneId zoneId 
} 

参见:

  1. http://docs.grails.org/latest/ref/Database%20Mapping/Usage.html
  2. http://blog.progs.be/550/java-time-hibernate
0

您可以使用JPA 2.1定义的自定义属性转换器。声明转换器类,像这样:

@Converter 
public static class ZoneIdConverter implements AttributeConverter<ZoneId, String> { 

    @Override 
    public String convertToDatabaseColumn(ZoneId attribute) { 
     return attribute.getId(); 
    } 

    @Override 
    public ZoneId convertToEntityAttribute(String dbData) { 
     return ZoneId.of(dbData); 
    } 
} 

,然后请参阅从ZoneId类型的实体属性给它:

@Convert(converter = ZoneIdConverter.class) 
private ZoneId zoneId; 

持续/加载zoneId属性时,转换器将自动被调用。

+0

我以前见过这个,但它在使用Hibernate 5的Grails 3中不起作用。我找到了实现我的自定义用户类型的解决方案。请参阅下面的答案。无论如何,你指出我的方向是好的...... – kuceram

+0

是的,你总是可以选择用户类型,尽管转换器要简单得多。你有没有任何细节为什么在Grails 3中不起作用?我对此感到惊讶。 – Gunnar

+0

不,我对GORM没有任何见解...... http://gorm.grails.org/latest/ – kuceram