Hibernate识别数据库特有字段实例详解

前言:

Hibernate已经为绝大多数常用的数据库数据类型提供了内置支持,但对于某些数据库的专属字段支持就不够好了。 这些特殊数据类型往往提供了比常规数据类型更好的数据表达能力,更符合我们的业务场景。比如PostgreSQL的Interval类型,可以非常方便的保存一个时间段的数据。 本文以添加Interval类型支持为例,说明为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;
 }
...
}

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

Hibernate识别数据库特有字段实例详解的更多相关文章

  1. Hibernate 主清单文件配制的详细介绍

    这篇文章主要介绍了Hibernate 主清单文件配制的详细介绍的相关资料,希望通过本文能帮助到大家,需要的朋友可以参考下

  2. Hibernate 查询方式总结

    get() and load() HQL 等查询方式

  3. jsp hibernate的分页代码第1/3页

    在查询分页代码中使用Hibernate的一大好处是,既兼顾了查询分页的性能,同时又保证了代码在不同的数据库之间的可移植性。

  4. JSP 开发之hibernate配置二级缓存的方法

    这篇文章主要介绍了JSP 开发之hibernate配置二级缓存的方法的相关资料,这里提供两种配置二级缓存的方法,需要的朋友可以参考下

  5. jsp hibernate 数据保存操作的原理

    当执行到session.save()方法时,Hibernate并不会马上生成insert SQL语句来进行数据的保存,而是当稍后清理session的缓存时才有可能执行insert SQL语句,那么session.save()方法到底会执行哪些步骤呢?

  6. JSP开发之hibernate之单向多对一关联的实例

    这篇文章主要介绍了JSP开发之hibernate之单向多对一关联的实例的相关资料,希望通过本文能帮助到大家,让大家实现这样的功能,需要的朋友可以参考下

  7. 详解hibernate自动创建表的配置

    这篇文章主要介绍了详解hibernate自动创建表的配置的相关资料,需要的朋友可以参考下

  8. Spring Boot深入学习数据访问之Spring Data JPA与Hibernate的应用

    Spring Data JPA是Spring Data的子项目,在使用Spring Data JPA之前,先了解一下Hibernate,因为Spring Data JPA是由Hibernate默认实现的

  9. Hibernate实体对象继承的三种方法

    这篇文章主要介绍了Hibernate实体对象继承的方法的相关资料,需要的朋友可以参考下

  10. Hibernate识别数据库特有字段实例详解

    这篇文章主要介绍了Hibernate识别数据库特有字段实例详解的相关资料,需要的朋友可以参考下

随机推荐

  1. 基于EJB技术的商务预订系统的开发

    用EJB结构开发的应用程序是可伸缩的、事务型的、多用户安全的。总的来说,EJB是一个组件事务监控的标准服务器端的组件模型。基于EJB技术的系统结构模型EJB结构是一个服务端组件结构,是一个层次性结构,其结构模型如图1所示。图2:商务预订系统的构架EntityBean是为了现实世界的对象建造的模型,这些对象通常是数据库的一些持久记录。

  2. Java利用POI实现导入导出Excel表格

    这篇文章主要为大家详细介绍了Java利用POI实现导入导出Excel表格,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

  3. Mybatis分页插件PageHelper手写实现示例

    这篇文章主要为大家介绍了Mybatis分页插件PageHelper手写实现示例,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  4. (jsp/html)网页上嵌入播放器(常用播放器代码整理)

    网页上嵌入播放器,只要在HTML上添加以上代码就OK了,下面整理了一些常用的播放器代码,总有一款适合你,感兴趣的朋友可以参考下哈,希望对你有所帮助

  5. Java 阻塞队列BlockingQueue详解

    本文详细介绍了BlockingQueue家庭中的所有成员,包括他们各自的功能以及常见使用场景,通过实例代码介绍了Java 阻塞队列BlockingQueue的相关知识,需要的朋友可以参考下

  6. Java异常Exception详细讲解

    异常就是不正常,比如当我们身体出现了异常我们会根据身体情况选择喝开水、吃药、看病、等 异常处理方法。 java异常处理机制是我们java语言使用异常处理机制为程序提供了错误处理的能力,程序出现的错误,程序可以安全的退出,以保证程序正常的运行等

  7. Java Bean 作用域及它的几种类型介绍

    这篇文章主要介绍了Java Bean作用域及它的几种类型介绍,Spring框架作为一个管理Bean的IoC容器,那么Bean自然是Spring中的重要资源了,那Bean的作用域又是什么,接下来我们一起进入文章详细学习吧

  8. 面试突击之跨域问题的解决方案详解

    跨域问题本质是浏览器的一种保护机制,它的初衷是为了保证用户的安全,防止恶意网站窃取数据。那怎么解决这个问题呢?接下来我们一起来看

  9. Mybatis-Plus接口BaseMapper与Services使用详解

    这篇文章主要为大家介绍了Mybatis-Plus接口BaseMapper与Services使用详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

  10. mybatis-plus雪花算法增强idworker的实现

    今天聊聊在mybatis-plus中引入分布式ID生成框架idworker,进一步增强实现生成分布式唯一ID,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

返回
顶部