使用Hibernate与H2数据库和joda DateTime

3
我正在使用Hibernate和H2内存数据库。数据库表根据我在persistence.xml文件中提供的bean信息自动创建。
H2将类型为org.joda.time.DateTime的字段“created”映射到类型为VARBINARY(255)的列中。
当我尝试使用值created=DateTime.now()持久化我的bean时,我收到以下错误:
    Caused by: org.h2.jdbc.JdbcSQLException: Value too long for column "CREATED BINARY(255)": "X'aced0005737200166f72672e6a6f64612e74696d652e4461746554696d65b83c78646a5bddf90200007872001f6f72672e6a6f64612e74696d652e62617365... (273)";  
    SQL statement:  
    insert into mytable (id, created) values (null, ?) [22001-168]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:329)

你知道为什么会出现这种情况吗?有什么解决方法吗?

很遗憾,我不能改变bean的定义,所以我必须继续使用joda DateTime。

谢谢!

P.S. 这是我的persistence.xml文件。

<persistence-unit name="my-test" transaction-type="RESOURCE_LOCAL">
    <provider>org.hibernate.ejb.HibernatePersistence</provider>

    <class>com.model.MyTable</class>

    <exclude-unlisted-classes>true</exclude-unlisted-classes>
    <shared-cache-mode>ALL</shared-cache-mode>

    <properties>
        <property name="javax.persistence.jdbc.driver" value="org.h2.Driver" />
        <property name="javax.persistence.jdbc.url" value="jdbc:h2:tcp://localhost/c:\UnitTest;INIT=CREATE SCHEMA IF NOT EXISTS UnitTest" />
        <property name="javax.persistence.jdbc.user" value="SA" />
        <property name="javax.persistence.jdbc.password" value="" />
        <property name="hibernate.hbm2ddl.auto" value="create-drop" />
        <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect" />
        <property name="hibernate.show_sql" value = "true" />

        <property name="hibernate.cache.use_query_cache" value="true" />
        <property name="hibernate.max_fetch_depth" value="4" />
        <property name="hibernate.cache.use_second_level_cache" value="true" />
        <property name="hibernate.cache.use_query_cache" value="true" />
        <property name="hibernate.cache.region.factory_class" value="net.sf.ehcache.hibernate.EhCacheRegionFactory" />
    </properties>

</persistence-unit>

并且由Hibernate / H2自动生成的表:

MYTABLE
    ID BIGINT(19) NOT NULL
    CREATED VARBINARY(255)

这是我的Bean:

@Entity
@Table(name = "MyTable")
@Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
public class MyTable implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    @Column(name = "id")
    private Long id;

    public Long getId() {
        return id;
    }

    @SuppressWarnings("unused")
    private void setId(Long id) {
        this.id = id;
    }

    @Column(name = "created")
    private DateTime created;

    public DateTime getCreated() {
        return created;
    }

    @SuppressWarnings("unused")
    private void setCreated(DateTime created) {
        this.created = created;
    }

    public MyTable() {
    }

    public MyTable(DateTime created) {
        this.created = created;
    }

    @Override
    public boolean equals(Object obj) {
        ...
    }

    @Override
    public int hashCode() {
        ...
    }
}

1
你能给我们展示一下你的映射表吗? - Jon Skeet
嗨Jon,你想看豆子、生成的H2 SQL表还是persistence.xml文件? - Gep
持久化配置文件 persistence.xml - 只需相关部分。(如果您能创建一个简短但完整的示例来演示问题,那将是理想的。) - Jon Skeet
3个回答

3
你应该使用Joda-Time-Hibernate映射:

@Type(type="org.joda.time.contrib.hibernate.PersistentDateTime")


(注:该映射可帮助处理日期和时间数据类型在Hibernate中的存储和检索)

2

以下是您问题的演示(和解释):

ByteArrayOutputStream bos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(bos);
oos.writeObject(new DateTime());
oos.close();
System.out.println("Serialized size: " + bos.toByteArray().length); // output: 273
DateTime对象序列化为字节数组需要273个字节。由于列类型接受字节数组,但配置为最多只接受255个字节,因此会出现SQL异常。如果想要保留JodaTime和字节数组存储机制,唯一的解决方法是将列大小增加到至少273(需要编辑persistence.xml)。
编辑:你是否可以重新考虑存储机制?列类型VARBINARY似乎不是最佳选择,因为其大小较大(甚至不能在SQL中应用比较运算符)。替代方案是VARCHAR(但请使用ISO-8601标准表示法)或SQL-TIMESTAMP。特别关注此Joda-Hibernate-support,以使用Type-annotations和更好的列类型。

如果您无法编辑bean定义,请更改数据库模式。TIMESTAMP比VARBINARY更适合存储时间戳的DB类型。哦,还有,除非您使用Java 8,否则您会很高兴使用Joda-Time。 :) - Mikkel Løkke
2
仅需注意:JDK-GregorianCalendar甚至需要灾难性的2385字节!因此,JodaTime要好得多。而JSR-310(和我的库)在序列化行为方面得到了进一步的改进(大约100个字节-实际上无法测试)。 - Meno Hochschild
谢谢Meno,这是一个很好的解释。存储机制(即VARBINARY)是由Hibernate/H2自动生成的。我想我可以手动指定DB表,但我有大约100个表...所以我希望有一个解决方法或一种告诉Hibernate以不同方式转换joda DateTime的方法。 - Gep
@user1005007,Hibernate似乎会自动生成VARBINARY列,因为joda-type不被直接支持。我建议您尝试使用Hibernate-Type-annotation - 请参见我编辑答案中的新链接。 - Meno Hochschild
谢谢Meno,如果我能编辑那些我没有权限编辑的bean/entity,那就太好了。不过我已经找到一种方法将自动生成的数据库模式写入文件,从那里我应该能够编辑VARBINARY并强制Hibernate使用它...希望如此 :) - Gep

0
您可以在MYTABLE.groovy领域中映射CREATED列的长度:
static mapping = {
    CREATED column: "CREATED", sqlType: "VARBINARY", length:273 }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接