标签:collect 运行 for text script value row 电影 方案
集合按其内元素的数据类型分为两种:基本数据类型集合及复杂对象类型集合,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集合
标签:collect 运行 for text script value row 电影 方案
原文地址:http://www.cnblogs.com/smart-elf/p/7631595.html