gofor's blog

  • 首页

  • 关于

  • 标签

  • 分类

  • 归档

  • 搜索

Hibernate自定义类型

发表于 2017-01-03 更新于 2020-01-10 分类于 技术 阅读次数: Valine:

Hibernate对一些基本类型的映射提供了很好的支持,但有时候我们需要映射自定义或更复杂的数据类型,比如一个List集合,可以通过基本类型映射实现,需要在数据库中新建一张表,这种方式增加了数据库开销;也可以将List集合中的数据拼接成字符串再存储,这种方式导致程序可读性不友好,同时增加代码的复杂度;Hibernate提供了DiscriminatorType和UserType接口,方便用户自定义需要映射的数据类型。这里以UserType为例实现。

假设用户需要自定义一个类型,名称为ctype,它是16位字符的char数组,需要映射到数据库VARCHAR类型。

首先自定义java类型ctype:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
public class ctype implements Serializable {

// mtype 的长度
private static int TYPE_LENGTH = 16;

// mtype 实际上就是 char 数组
private char[] mtype = new char[TYPE_LENGTH];

// 默认构造函数
public ctype() {
this.mtype = new char[0];
}

// 通过构造函数转换为ctype类型
public ctype(Object object) {
String str = String.valueOf(object);
if (StrUtil.isBlank(str))
this.mtype = new char[0];
this.mtype = str.toCharArray();
}

public boolean isEmpty() {
return String.valueOf(this.mtype).replace(" ", "").length() < TYPE_LENGTH;
}

public String toString() {
return String.valueOf(this.mtype);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ctype ctype = (ctype) o;
return Arrays.equals(mtype, ctype.mtype);
}

@Override
public int hashCode() {
return Arrays.hashCode(mtype);
}
}

接下来实现HibernateUserType接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
/**
* Hibernate自定义类型
*/
public class customType implements UserType {

private static int[] TYPES = new int[]{Types.VARCHAR};

/**
* 修改类型对应的SQL类型(这里用VARCHAR
*/
@Override
public int[] sqlTypes() {
return TYPES;
}

/**
* 修改customType对应的java类型(此处java类型为ctype
*/
@Override
public Class returnedClass() {
return ctype.class;
}

/**
* 自定义数据类型比对方法
* 用作脏数据检查,o,o1为两个副本
*/
@Override
public boolean equals(Object o, Object o1) throws HibernateException {
return Objects.equals(o, o1);
}

/**
* 返回给定类型的hashCode
*/
@Override
public int hashCode(Object o) throws HibernateException {
return Objects.hashCode(o);
}

/**
* 读取数据转换为自定义类型返回
* strings包含了自定义类型的映射字段名称
*/
@Override
public Object nullSafeGet(ResultSet resultSet, String[] strings,
SessionImplementor sessionImplementor, Object o)
throws HibernateException, SQLException {
String s = (String) resultSet.getObject(strings[0]);
if (resultSet.wasNull()) return null;
if (null != s) {
if (s.replaceAll(" ", "").isEmpty())
return null;
return new ctype(s);
}
return null;
}

/**
* 数据保存时被调用
*/
@Override
public void nullSafeSet(PreparedStatement preparedStatement, Object o, int i,
SessionImplementor sessionImplementor)
throws HibernateException, SQLException {
if (null == o) {
preparedStatement.setNull(i, Types.VARCHAR); //保存空值
} else {
String stringValue = String.valueOf(o);
preparedStatement.setString(i, stringValue);
}
}

/**
* 自定义类型的完全复制方法,构造返回对象
* 1. 当nullSafeGet方法调用之后,我们获得了自定义数据对象,
* 在向用户返回自定义数据之前,deepCopy方法被调用,
* 它将根据自定义数据对象构造一个完全拷贝,把拷贝返还给客户使用。
* 2. 此时我们就得到了自定义数据对象的两个版本
* 原始版本由hibernate维护,用作脏数据检查依据;
* 复制版本由用户使用,hibernate将在脏数据检查过程中比较这两个版本的数据;
*/
@Override
public Object deepCopy(Object o) throws HibernateException {
if (o == null) return null;
ctype c = (ctype) o;
ctype nc = new ctype();
nc = c;
return nc;
}

/**
* 表示本类型实例是否可变
*/
@Override
public boolean isMutable() {
return true;
}

/**
* method called when Hibernate puts the data in a second level cache. The
* data is stored in a serializable form (官方文档
*/
@Override
public Serializable disassemble(Object o) throws HibernateException {
return (ctype) deepCopy(o);
}

/**
* Returns the object from the 2 level cache (官方文档
*/
@Override
public Object assemble(Serializable serializable, Object o)
throws HibernateException{
return deepCopy(serializable);
}

@Override
public Object replace(Object o, Object o1, Object o2)
throws HibernateException {
return deepCopy(o);
}
}

通过注解方式使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* Entity -> Item
*/
@Entity
@Table(name = "d_item")
@TypeDef(name = "myType", typeClass = customType.class)
public class Item implements Serializable {

@Id
@Type(type = "myType")
private ctype uid;

... ...

}

完成。

  • 本文作者: gofor
  • 本文链接: https://acehjm.github.io/2017/01/03/Hibernate自定义类型/
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
# springboot # java
读看见
Kotlin & Spring boot 使用@Valid校验无效解决方法
gofor

gofor

Programming technology
14 日志
4 分类
14 标签
RSS
GitHub StackOverflow
Creative Commons
© 2016 – 2020 gofor
由 Hexo 强力驱动 v3.9.0
|
主题 – NexT.Pisces v7.3.0
0%