Hibernate学习笔记 --- 映射基本数据类型的List集合

Posted smart_妖

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Hibernate学习笔记 --- 映射基本数据类型的List集合相关的知识,希望对你有一定的参考价值。

集合按其内元素的数据类型分为两种:基本数据类型集合及复杂对象类型集合,Hibernate对于两类集合提供不同的映射方式。(在类上以@Embeddable注解的复杂对象数据类型处理方式同基本数据类型集合一致,此处只讨论以@Entity注解的对象)

对于基本数据类型集合,直接在属性上添加@ElementCollection即可,Hibernate在存储数据时会自动为该集合单独创建一张表,这个表包含一个指向该属性所在类ID的外键

看一个例子,假如之前的Movie数据类要新增加一个actors属性,标识该电影的演员表(只保存姓名),更改后的Movie.java代码如下:

 1 package study.hibernate.model;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 import javax.persistence.Column;
 7 import javax.persistence.Convert;
 8 import javax.persistence.ElementCollection;
 9 import javax.persistence.Embeddable;
10 import javax.persistence.Entity;
11 import javax.persistence.EnumType;
12 import javax.persistence.Enumerated;
13 import javax.persistence.Id;
14 import javax.persistence.Table;
15 
16 import org.hibernate.annotations.Type;
17 
18 /**
19  * 电影数据类
20  * @author yaoyao
21  *
22  */
23 @Entity
24 @Table(name="MOVIE")
25 public class Movie {
26     @Id
27     @Column(name="MOVIE_ID")
28     private int id;
29 
30     @Column(name="NAME")
31     @Type(type="string")
32     private String name;
33 
34     @Column(name="DESCRIPTION")
35     @Type(type="text")
36     private String description;
37     
38     @Column(name="TYPE")
39     @Convert(converter=MovieTypeConvertor.class)
40     private MovieType type;
41     
42     @Column(name="ACTORS")
43     @ElementCollection
44     private List<String> actors = new ArrayList<String>();
45 
46     public int getId() {
47         return id;
48     }
49 
50     public void setId(int id) {
51         this.id = id;
52     }
53 
54     public String getName() {
55         return name;
56     }
57 
58     public void setName(String name) {
59         this.name = name;
60     }
61 
62     public String getDescription() {
63         return description;
64     }
65 
66     public void setDescription(String description) {
67         this.description = description;
68     }
69 
70     public MovieType getType() {
71         return type;
72     }
73 
74     public void setType(MovieType type) {
75         this.type = type;
76     }
77 
78     public List<String> getActors() {
79         return actors;
80     }
81 
82     public void setActors(List<String> actors) {
83         this.actors = actors;
84     }
85     
86 }

可以看到,新增了一个List<String>的属性,并对该属性添加了@ElementCollection的注解。

在启动程序中对该属性赋值并保存数据到数据库:

 1             Movie movie = new Movie();
 2             movie.setId(1);
 3             movie.setName("速度与激情8");
 4             movie.setDescription("多米尼克(范·迪塞尔 Vin Diesel 饰)与莱蒂(米歇尔·罗德里格兹 Michelle Rodriguez 饰)共度蜜月,布莱恩与米娅退出了赛车界,这支曾环游世界的顶级飞车家族队伍的生活正渐趋平淡。然而,一位神秘女子Cipher(查理兹·塞隆 Charlize T heron 饰)的出现,令整个队伍卷入信任与背叛的危机,面临前所未有的考验。");
 5             movie.setType(MovieType.CARTOON);
 6             
 7             List<String> actors = new ArrayList<String>();
 8             actors.add("范·迪塞尔");
 9             actors.add("米歇尔·罗德里格兹");
10             movie.setActors(actors);
11             
12             session.beginTransaction();
13             session.save(movie);
14             session.getTransaction().commit();

从Hibernate日志中可以看出,在启动时创建了两张表:一张是Movie,这是我们显式指定要创建的表,另一张是Movie_actors,这一张是Hibernate自己创建的,我们并没有在哪里配置要创建这个表

1 Hibernate: drop table if exists MOVIE
2 Hibernate: drop table if exists Movie_actors
3 
4 Hibernate: create table MOVIE (MOVIE_ID integer not null, DESCRIPTION longtext, NAME varchar(255), TYPE varchar(255), primary key (MOVIE_ID)) engine=MyISAM
5 
6 Hibernate: create table Movie_actors (Movie_MOVIE_ID integer not null, ACTORS varchar(255)) engine=MyISAM
7 
8 Hibernate: alter table Movie_actors add constraint FKcx4l8fplo42ncmh0hpoc6oyvl foreign key (Movie_MOVIE_ID) references MOVIE (MOVIE_ID)

查看数据库,发现确认多了一张Movie_actors的表,而且该表中有两条数据,存储代码中添加的两个演员的名称

 1 mysql> show tables;
 2 +--------------------+
 3 | Tables_in_movie_db |
 4 +--------------------+
 5 | movie              |
 6 | movie_actors       |
 7 +--------------------+
 8 2 rows in set (0.00 sec)
 9 
10 mysql> select * from movie_actors;
11 +----------------+----------------------------+
12 | Movie_MOVIE_ID | ACTORS                     |
13 +----------------+----------------------------+
14 |              1 | 范·迪塞尔                   |
15 |              1 | 米歇尔·罗德里格兹            |
16 +----------------+----------------------------+
17 2 rows in set (0.00 sec)

 

通过@ElementCollection注解映射的集合,在对该集合进行数据更新时效率比较低,因为Hibernate会将关联表(Movie_actors)中相关的数据全部删除然后依次重新添加,这样在集合中数据比较多的时候,执行的SQL语句会变的非常多

假如现在要向演员列表中新增一个名字并保存到数据库:

1             session.beginTransaction();
2             movie.getActors().add("查理兹·塞隆");
3             session.update(movie);
4             session.getTransaction().commit();

查看Hibernate日志,发现它是将Movie_actors表中所有电影ID为1的数据全删除,然后再依次添加该电影所有的演员信息(三个插入语句)

1 Hibernate: delete from Movie_actors where Movie_MOVIE_ID=?
2 
3 Hibernate: insert into Movie_actors (Movie_MOVIE_ID, ACTORS) values (?, ?)
4 Hibernate: insert into Movie_actors (Movie_MOVIE_ID, ACTORS) values (?, ?)
5 Hibernate: insert into Movie_actors (Movie_MOVIE_ID, ACTORS) values (?, ?)

 

对于@ElementCollection注解效率低的问题,有一个不算太好的解决方案是同时引入@OrderColumn属性,该属性在关联表(Movie_actors)中会新增一列用于标识元素在集合中的位置,列名由该注解的name属性指定:

1     @Column(name="ACTORS")
2     @ElementCollection
3     @OrderColumn(name="position")
4     private List<String> actors = new ArrayList<String>();

运行程序并查看数据库,发现Movie_actors表中多了一列position,其值即是对应元素在List集合中的位置:

1 mysql> select * from movie_actors;
2 +----------------+----------------------------+----------+
3 | Movie_MOVIE_ID | ACTORS                     | position |
4 +----------------+----------------------------+----------+
5 |              1 | 范·迪塞尔                   |        0 |
6 |              1 | 米歇尔·罗德里格兹            |        1 |
7 |              1 | 查理兹·塞隆                 |        2 |
8 +----------------+----------------------------+----------+
9 3 rows in set (0.00 sec)

 

使用了@OrderColumn注解后,在对集合数据做更改时,并不会将所有相关数据都删除然后重新添加,而是会只删除、添加有变更的数据,对该数据之后位置的数据做更新操作:

1             session.beginTransaction();
2             movie.getActors().remove(2);
3             session.update(movie);
4             session.getTransaction().commit();
1 Hibernate: delete from Movie_actors where Movie_MOVIE_ID=? and position=?

因此@OrderColumn并不能完全解决对集合元素更改操作效率低的问题,因为该方案只能在变更元素位于集合末尾的时候效率才比较高,如果操作的是集合头部的元素,则效率同没有使用该标注是差不多的

1             session.beginTransaction();
2             movie.getActors().remove(0);
3             session.update(movie);
4             session.getTransaction().commit();
1 Hibernate: delete from Movie_actors where Movie_MOVIE_ID=? and position=?
2 Hibernate: update Movie_actors set ACTORS=? where Movie_MOVIE_ID=? and position=?
3 Hibernate: update Movie_actors set ACTORS=? where Movie_MOVIE_ID=? and position=?

 

综上,在映射集合元素时,可以添加@ElementCollection及@OrderColumn注解来完成映射工作,但需考虑不同场景下数据操作效率快慢的问题。个人建议,基本数据类型的集合使用@Convert注解来实现,这样不存在多个表的数据更新问题了

以上是关于Hibernate学习笔记 --- 映射基本数据类型的List集合的主要内容,如果未能解决你的问题,请参考以下文章

springboot+kotlin+gradle+hibernate学习笔记

JavaEE学习笔记之SSH—Hibernate

Hibernate学习笔记---hibernate关联关系映射

springboot+kotlin+gradle+hibernate学习笔记

Hibernate学习笔记

Hibernate学习笔记