每日一博|让 Hibernate 识别数据库特有字段

栏目: Hibernate · 发布时间: 8年前

内容简介:每日一博|让 Hibernate 识别数据库特有字段

Hibernate提供了丰富的数据类型支持,但对于部分数据库专有的数据类型,提供的支持就很有限了。比如PostgreSQL的Interval类型,对于保存一个"时间段"数据就非常方便。

在开发中,我们期望将Interval类型映射为 Java 8 的Duration类型。但是Hibernate默认对Duration类型的映射是直接映射到数据库的BigInt类型,直接保存纳秒值。显然对于不直接支持Interval类型的数据库来说,是比较合适的,但是我们仍然期望直接映射到数据库的Interval类型。

为此,我们需要调整Hibernate对于两种数据类型(Java世界的Duration和Db世界的Interval)的映射关系。

幸运的是,Hibernate提供了非常方便的方法可以实现数据类型的映射。

为此,我们需要一个实现org.hibernate.usertype.UserType接口的类,来实现两个世界的数据转换/映射工作。

Hibernate的自定义类型(UserType)

UserType是Hibernate提供的一个自定义数据类型的接口。所有自定义数据均需实现此接口,或者从org.hibernate.usertype中定义的接口中选择一个合适的接口。

鉴于我们的场景比较简单,直接实现UserType即可满足需求。此接口提供了如下一组方法需要自己实现:

  • assemble(Serializable cached, Object owner)

    从序列化中重新构建(Java)对象。

  • deepCopy(Object value)

    返回深度副本。

  • disassemble(Object value)

    转换对象的序列化数据。

  • equals(Object x, Object y)

    返回两个映射的数据是否相等。

  • hashCode(Object x)

    获取对象的散列。

  • isMutable()

    返回对象是否是可变类型。

  • nullSafeGet(ResultSet rs, String[] names, Object owner)

    从数据库类型的数据,返回对应的Java对象。 核心实现方法

  • nullSafeSet(PreparedStatement st, Object value, int index)

    从Java对象,返回对应的数据库类型的数据。 核心实现方法

  • replace(Object original, Object target, Object owner)

    合并期间,将实体中的目标值(target)替换为原始值(original)。

  • returnedClass()

    nullSafeGet返回的类。

  • sqlTypes()

    返回对应的数据库类型。

实例

package framework.postgresql;

import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.usertype.UserType;
import org.postgresql.util.PGInterval;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.time.Duration;

/**
 * PostgreSql Inteval字段与java.time.Duration映射
 * 目前只支持到最多1个月(30天)的间隔
 * <p>
 * 使用方法:
 * 在实体类上增加
 * \@TypeDef(name="interval", typeClass = IntervalType.class)
 * 在字段定义上增加:
 * \@Type(type = "interval")
 * <p>
 * http://stackoverflow.com/questions/1945615/how-to-map-the-type-interval-in-hibernate/6139581#6139581
 *
 * @version 1.0
 * @since 1.0
 */
public class IntervalType implements UserType {

    public Object assemble(Serializable cached, Object owner) throws HibernateException {
        return cached;
    }

    public Object deepCopy(Object value) throws HibernateException {
        return value;
    }

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

    public boolean equals(Object arg0, Object arg1) throws HibernateException {
        return arg0 != null && arg1 != null && arg0.equals(arg1) || arg0 == null && arg1 == null;
    }

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


    @Override
    public Object nullSafeGet(ResultSet resultSet, String[] names, SharedSessionContractImplementor sessionImplementor, Object o) throws HibernateException, SQLException {
        String interval = resultSet.getString(names[0]);
        if (resultSet.wasNull() || interval == null) {
            return null;
        }
        PGInterval pgInterval = new PGInterval(interval);

        return getDuration(pgInterval);
    }

    @Override
    public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor sessionImplementor) throws HibernateException, SQLException {
        if (value == null) {
            st.setNull(index, Types.OTHER);
        } else {
            //this http://postgresql.1045698.n5.nabble.com/Inserting-Information-in-PostgreSQL-interval-td2175203.html#a2175205
            Duration duration = (Duration) value;
            st.setObject(index, getInterval(duration), Types.OTHER);
        }
    }

    public static Duration getDuration(PGInterval pgInterval) {
        return Duration.ofSeconds(pgInterval.getDays() * 24 * 3600 +
                pgInterval.getHours() * 3600 +
                pgInterval.getMinutes() * 60 +
                (int) pgInterval.getSeconds());
    }

    private static PGInterval getInterval(Duration value) {
        long seconds = value.getSeconds();
        int days = (int) (seconds / (24 * 3600));
        seconds -= days * 24 * 3600;
        int hours = (int) (seconds / 3600);
        seconds -= hours * 3600;
        int minutes = (int) (seconds / 60);
        seconds -= minutes * 60;
        seconds = Math.abs(seconds);
        return new PGInterval(0, 0, days, hours, minutes, seconds);
    }


    public boolean isMutable() {
        return false;
    }


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

    public Class returnedClass() {
        return Duration.class;
    }

    public int[] sqlTypes() {
        return new int[]{Types.OTHER};
    }

}

使用自定义类型

至此,我们已经定义好了自己的数据类型。但Hibernate还不知道怎么使用它。为此,我们需要通过在Entity上使用使用TypeDef注解,并在属性上使用Type注解。

比如:

...
@Entity
@TypeDef(name = "interval", typeClass = IntervalType.class)
public class PaperStatis implements Serializable {
...
    @Column(name = "avg_duration")
    @Type(type = "interval")
    public Duration getAvgDuration() {
        return this.avgDuration;
    }
...
}

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

精通正则表达式

精通正则表达式

弗里德尔 / 东南大学出版社 / 2005-9 / 72.00元

正则表达式是一种用来操作文本和数据的强大工具。近年来,它们快速广泛传播,并被多种流行工具和语言作为标准特性提供,如Perl、Java、VB.NET、C#(及任何使用.NET框架的语言)、PHP、Python、Ruby、Tcl、MySQL、awk、Emacs等。 如果还未使用过正则表达式,从本书中您将发现一个掌控数据的全新世界。如果使用过它们,您将会充分意识到本书空前的深度和广度。如果您认为自己已经......一起来看看 《精通正则表达式》 这本书的介绍吧!

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具

HSV CMYK 转换工具
HSV CMYK 转换工具

HSV CMYK互换工具