custom hibernate type

前言

使用jpa或者其他orm框架的时候,因为框架帮我们做了一些类型装换,将Java对应的数据转换到可以存储数据库中的类型,读取的时候将数据库对应的类型封装成java中的对象,比如hibernate做这种事情是通过type来完成的,DateTypeLongType 等等,毫无疑问,hibernate提供了一些接口,供我们自定义我们自己的type来做一些转换,比如Money类型,存储到数据库中的策略等

实现我们自定义的type

1
To implement a custom type, implement either org.hibernate.UserType or org.hibernate.CompositeUserType and declare properties using the fully qualified classname of the type

根据文档中给出的说明,我们可以实现上述两个接口中的任意一个

1
public class MyUserType implements UserType

实体类

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
@Entity
public class MyUser {

@Override
public String toString() {
return "MyUser{" +
"id=" + id +
", userId=" + userId +
'}';
}

public User getUserId() {
return userId;
}

public void setUserId(User userId) {
this.userId = userId;
}

public Long getId() {
return id;
}

public MyUser(Long id, User userId) {
this.id = id;
this.userId = userId;
}

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false)
private Long id;



@Type(type = "com.example.demo.type.MyUserType")
private User userId;

public MyUser() {
}

public void setId(Long id) {
this.id = id;
}


}

User

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
public class User {

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User)) return false;
User user = (User) o;
return Objects.equals(num, user.num);
}

@Override
public int hashCode() {
return Objects.hash(num);
}

@Override
public String toString() {
return "User{" +
"num=" + num +
'}';
}

public Long getNum() {
return num;
}

public void setNum(Long num) {
this.num = num;
}

private Long num;


public User(Long num) {
this.num = num;
}
public User() {
}
}

重写sqlTypes和returnedClass,就可以创建出表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* Return the SQL type codes for the columns mapped by this type. The
* codes are defined on <tt>java.sql.Types</tt>.
*
* @return int[] the typecodes
* @see Types
*/
@Override
public int[] sqlTypes() {
return new int[]{Types.BIGINT};
}

/**
* The class returned by <tt>nullSafeGet()</tt>.
*
* @return Class
*/
@Override
public Class returnedClass() {
return User.class;
}

其中sqltype可以定义的类型很多,甚至可以定义结构体类型,测试原因我直接将我user对应成数据库中的bigint类型,合并替换值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* During merge, replace the existing (target) value in the entity we are merging to
* with a new (original) value from the detached entity we are merging. For immutable
* objects, or null values, it is safe to simply return the first parameter. For
* mutable objects, it is safe to return a copy of the first parameter. For objects
* with component values, it might make sense to recursively replace component values.
*
* @param original the value from the detached entity being merged
* @param target the value in the managed entity
* @param owner
* @return the value to be merged
*/
@Override
public Object replace(Object original, Object target, Object owner) throws HibernateException {
log.info("replace");
return this.deepCopy(original);
}

使用original值替换target,再然后就是调用nullSafeSet

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
  /**
* Write an instance of the mapped class to a prepared statement. Implementors
* should handle possibility of null values. A multi-column type should be written
* to parameters starting from <tt>index</tt>.
*
* @param st a JDBC prepared statement
* @param value the object to write
* @param index statement parameter index
* @param session
* @throws HibernateException
* @throws SQLException
*/
@Override
public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException {
log.info("");
if (value == null) {
st.setNull(index, Types.BIGINT);
}else {
if (value instanceof User) {
User user = (User) value;
Long num = user.getNum();
st.setLong(index,num);
}else {
st.setNull(index, Types.BIGINT);
}
}
}

/**
* Return a deep copy of the persistent state, stopping at entities and at
* collections. It is not necessary to copy immutable objects, or null
* values, in which case it is safe to simply return the argument.
*
* @param value the object to be cloned, which may be null
* @return Object a copy
*/
@Override
public Object deepCopy(Object value) throws HibernateException {
log.info("{}",value);
if (value==null) return null;
if (value instanceof User) {
User user = (User) value;
return user;
}
return null;
}

读取的时候将数据库中的内容转成java对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/**
* Retrieve an instance of the mapped class from a JDBC resultset. Implementors
* should handle possibility of null values.
*
* @param rs a JDBC result set
* @param names the column names
* @param session
* @param owner the containing entity @return Object
* @throws HibernateException
* @throws SQLException
*/
@Override
public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException {
log.info("");
long value = rs.getLong(names[0]);
User user = new User();
user.setNum(value);
return user;
}

至此自定义type就可以使用了

test

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@SpringBootApplication
public class DemoApplication implements ApplicationRunner {

public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}

@Autowired
private MyUserRepository myUserRepository;
@Override
public void run(ApplicationArguments args) throws Exception {
System.out.println();
MyUser myUser = new MyUser();
// myUser.setId(1L);
myUser.setUserId(new User(12333L));
myUserRepository.saveAndFlush(myUser);
List<MyUser> all =
myUserRepository.findAll();
System.out.println(all);
}
}

文档

https://docs.jboss.org/hibernate/stable/core.old/reference/en/html/mapping-types.html

完整源码

https://github.com/wqh0109663/custom-hibernate-type

0%