Hibernate學習筆記(四) --- 映射基本數據類型的List集合

集合按其內元素的數據類型分爲兩種:基本數據類型集合及複雜對象類型集合,Hibernate對於兩類集合提供不一樣的映射方式。(在類上以@Embeddable註解的複雜對象數據類型處理方式同基本數據類型集合一致,此處只討論以@Entity註解的對象)java

對於基本數據類型集合,直接在屬性上添加@ElementCollection便可,Hibernate在存儲數據時會自動爲該集合單首創建一張表,這個表包含一個指向該屬性所在類ID的外鍵mysql

看一個例子,假如以前的Movie數據類要新增長一個actors屬性,標識該電影的演員表(只保存姓名),更改後的Movie.java代碼以下:sql

 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的註解。數據庫

在啓動程序中對該屬性賦值並保存數據到數據庫:session

 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本身建立的,咱們並無在哪裏配置要建立這個表this

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的表,並且該表中有兩條數據,存儲代碼中添加的兩個演員的名稱spa

 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語句會變的很是多hibernate

假如如今要向演員列表中新增一個名字並保存到數據庫:日誌

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

查看Hibernate日誌,發現它是將Movie_actors表中全部電影ID爲1的數據全刪除,而後再依次添加該電影全部的演員信息(三個插入語句)code

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註解來實現,這樣不存在多個表的數據更新問題了

相關文章
相關標籤/搜索