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

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

内容简介:每日一博|让 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;
    }
...
}

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

查看所有标签

猜你喜欢:

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

Masterminds of Programming

Masterminds of Programming

Federico Biancuzzi、Chromatic / O'Reilly Media / 2009-03-27 / USD 39.99

Description Masterminds of Programming features exclusive interviews with the creators of several historic and highly influential programming languages. Think along with Adin D. Falkoff (APL), Jame......一起来看看 《Masterminds of Programming》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

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

HEX HSV 互换工具