一、集合映射
1.集合小介
集合映射也是基本的映射,但在开发过程中不会经常用到,所以不需要深刻了解,只需要理解基本的使用方法即可,等在开发过程中遇到了这种问题时能够查询到解决方法就可以了。对应集合映射它其实是指将java中的集合映射到对应的表中,是一种集合对象的映射,在java中有四种类型的集合,分别是Set、Map、List还有普通的数组,它们之间有很大的区别:
(1)Set,不可以有重复的对象,对象是无序的;
(2)List,可以与重复的对象,对象之间有顺序;
(3)Map,它是键值成对出现的;
(4)数组,可以重复,对象之间有顺序。
它们之间的区别决定了在开发时使用哪种集合,通常在开发时会使用Set,它内部的对象是无需的,并可以使用迭代器获取内部对象。这几种集合想要映射到相应的关系模型的话就必须使用Hibernate提供的映射标签,<set>、<list>、<map>、<array>。
2.映射小介
继续讨论集合映射的关系模型,集合映射是指一个对象对应着另一个对象集合,在保存时Hibernate会把数据集合保存到相应的表中,并按照自己分配的id把数据保存到数据表中,如果单独为集合分配了新表,那么会将id分配给集合表的id,那么对应的关系表如下图:
3.类文件
集合映射是如何通过代码实现的,接下来具体分析。这里把所有的集合封存到一个类中,这个类我们称之为CollectionMapping.java,那么它对应的内部代码如下:
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
|
package com.hibernate; import java.util.List; import java.util.Map; import java.util.Set; @SuppressWarnings ( "rawtypes" ) public class CollectionMapping { //id private int id; public int getId() { return id; } public void setId( int id) { this .id = id; } //名字 private String name; public String getName() { return name; } public void setName(String name) { this .name = name; } //Set集合 private Set setValues; public Set getSetValues() { return setValues; } public void setSetValues(Set setValues) { this .setValues = setValues; } //List集合 private List listValues; public List getListValues() { return listValues; } public void setListValues(List listValues) { this .listValues = listValues; } //数组集合 private String[] arrayValues; public String[] getArrayValues() { return arrayValues; } public void setArrayValues(String[] arrayValues) { this .arrayValues = arrayValues; } //Map集合 private Map mapValues; public Map getMapValues() { return mapValues; } public void setMapValues(Map mapValues) { this .mapValues = mapValues; } } |
该类中封装了几种常用的集合,想要转化为关系模型,就必须来看下文的映射。
4.集合映射
集合的映射其实相当的简单,只需要添加对应的集合标签,Hibernate分别提供了集合标签<set>、<map>、<list>、<array>,通过使用集中标签来将集合映射为对应的关系表,另外通过添加<key>标签来实现表外键的关联,其它的属性通过使用<element>来添加。
CollectionMapping.hbm.xml
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
|
<? xml version = "1.0" ?> <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> < hibernate-mapping > < class name = "com.hibernate.CollectionMapping" table = "t_collection_mapping" > < id name = "id" > < generator class = "native" /> </ id > < property name = "name" /> < set name = "setValues" table = "t_set_values" > < key column = "set_id" ></ key > < element type = "string" column = "set_value" ></ element > </ set > < list name = "listValues" table = "t_list_values" > < key column = "list_id" /> < list-index column = "list_index" /> < element type = "string" column = "list_value" /> </ list > < map name = "mapValues" table = "t_map_values" > < key column = "map_id" /> < map-key type = "string" column = "map_key" /> < element type = "string" column = "map_value" /> </ map > < array name = "arrayValues" table = "t_array_value" > < key column = "array_id" /> < index column = "array_index" type = "integer" ></ index > < element type = "string" column = "array_value" /> </ array > </ class > </ hibernate-mapping > |
需要注意的是list标签和array标签,这两种集合内的对象是有顺序的,所以在添加映射标签时需要使用list-index或者index标签来标明对象的顺序,而且在添加子标签时一定要按照顺序添加,也就是说先添加<key>标签,后添加<list-index>标签,最后添加<element>标签,否则的话会出现如下错误:
The content of element type "list" must match "(meta*,subselect?,cache?,synchronize*,comment?,key,(index|list-index),(element|one-to-many|many-to-many|composite-element|many-to-any),loader?,sql-insert?,sql-update?,sql-delete?,sql-delete-all?,filter*)".
5.关系模型
将配置好的对象模型转化为相应的关系模型,生成的SQL语句如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
alter table t_array_value drop foreign key FK2E0DD0C067676B68 alter table t_list_values drop foreign key FKE01EC98BF4FCB03 alter table t_map_values drop foreign key FKD169BA107402B585 alter table t_set_values drop foreign key FK7BB8D04A7E79F8BF drop table if exists t_array_value drop table if exists t_collection_mapping drop table if exists t_list_values drop table if exists t_map_values drop table if exists t_set_values create table t_array_value (array_id integer not null , array_value varchar (255), array_index integer not null , primary key (array_id, array_index)) create table t_collection_mapping (id integer not null auto_increment, name varchar (255), primary key (id)) create table t_list_values (list_id integer not null , list_value varchar (255), list_index integer not null , primary key (list_id, list_index)) create table t_map_values (map_id integer not null , map_value varchar (255), map_key varchar (255) not null , primary key (map_id, map_key)) create table t_set_values (set_id integer not null , set_value varchar (255)) alter table t_array_value add index FK2E0DD0C067676B68 (array_id), add constraint FK2E0DD0C067676B68 foreign key (array_id) references t_collection_mapping (id) alter table t_list_values add index FKE01EC98BF4FCB03 (list_id), add constraint FKE01EC98BF4FCB03 foreign key (list_id) references t_collection_mapping (id) alter table t_map_values add index FKD169BA107402B585 (map_id), add constraint FKD169BA107402B585 foreign key (map_id) references t_collection_mapping (id) alter table t_set_values add index FK7BB8D04A7E79F8BF (set_id), add constraint FK7BB8D04A7E79F8BF foreign key (set_id) references t_collection_mapping (id) |
生成的对应的数据库视图如下:
二、数据操作
1.数据写入
写入数据操作,将数据写入时需要注意创建数据对象,其中的List、Set、Map需要创建数据对象,将数据对象写入到数据库中,因为它们三者都是对象接口,所以需要创建一个对象,将对象写入到数据库中,具体代码如下:
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
|
@SuppressWarnings ({ "unchecked" , "rawtypes" }) public void testsave(){ Session session= null ; try { session=HibernateUtils.getSession(); session.beginTransaction(); CollectionMapping cm= new CollectionMapping(); cm.setName( "zhangsan" ); Set set= new HashSet(); set.add( "a" ); set.add( "b" ); cm.setSetValues(set); List list= new ArrayList(); list.add( "list1" ); list.add( "list2" ); cm.setListValues(list); String[] str= new String[]{ "array1" , "array2" }; cm.setArrayValues(str); Map map= new HashMap(); map.put( "k1" , "v1" ); map.put( "k2" , "v2" ); cm.setMapValues(map); session.save(cm); session.getTransaction().commit(); } catch (Exception e){ e.printStackTrace(); session.getTransaction().rollback(); } finally { HibernateUtils.closeSession(session); } } |
生成的SQL语句如下:
1
2
3
4
5
6
7
8
9
|
Hibernate: insert into t_collection_mapping ( name ) values (?) Hibernate: insert into t_set_values (set_id, set_value) values (?, ?) Hibernate: insert into t_set_values (set_id, set_value) values (?, ?) Hibernate: insert into t_list_values (list_id, list_index, list_value) values (?, ?, ?) Hibernate: insert into t_list_values (list_id, list_index, list_value) values (?, ?, ?) Hibernate: insert into t_map_values (map_id, map_key, map_value) values (?, ?, ?) Hibernate: insert into t_map_values (map_id, map_key, map_value) values (?, ?, ?) Hibernate: insert into t_array_value (array_id, array_index, array_value) values (?, ?, ?) Hibernate: insert into t_array_value (array_id, array_index, array_value) values (?, ?, ?) |
2.加载数据
加载数据的方法很简单,它会将表中的数据按照集合加载到对象中,然后只需要获取相应的对象集合即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
public void testload(){ Session session= null ; try { session=HibernateUtils.getSession(); session.beginTransaction(); CollectionMapping cm=(CollectionMapping)session.load(CollectionMapping. class , 1 ); System.out.println( "cm.name= " +cm.getName()); System.out.println( "cm.list= " +cm.getListValues()); System.out.println( "cm.map= " +cm.getMapValues()); System.out.println( "cm.array= " +cm.getArrayValues()); System.out.println( "cm.set= " +cm.getSetValues()); session.getTransaction().commit(); } catch (Exception e){ e.printStackTrace(); session.getTransaction().rollback(); } finally { HibernateUtils.closeSession(session); } } |
生成的结果:
1
2
3
4
5
6
7
8
9
10
|
Hibernate: select collection0_.id as id0_0_, collection0_. name as name0_0_ from t_collection_mapping collection0_ where collection0_.id=? Hibernate: select arrayvalue0_.array_id as array1_0_, arrayvalue0_.array_value as array2_0_, arrayvalue0_.array_index as array3_0_ from t_array_value arrayvalue0_ where arrayvalue0_.array_id=? cm. name = zhangsan Hibernate: select listvalues0_.list_id as list1_0_, listvalues0_.list_value as list2_0_, listvalues0_.list_index as list3_0_ from t_list_values listvalues0_ where listvalues0_.list_id=? cm.list= [list1, list2] Hibernate: select mapvalues0_.map_id as map1_0_, mapvalues0_.map_value as map2_0_, mapvalues0_.map_key as map3_0_ from t_map_values mapvalues0_ where mapvalues0_.map_id=? cm.map= {k1=v1, k2=v2} cm.array= [Ljava.lang.String;@758d8478 Hibernate: select setvalues0_.set_id as set1_0_, setvalues0_.set_value as set2_0_ from t_set_values setvalues0_ where setvalues0_.set_id=? cm. set = [b, a] |
三、总结
Hibernate可以持久化以下java集合的实例, 包括java.util.Map, java.util.Set, java.util.SortedMap, java.util.SortedSet, java.util.List, 和任何持久实体或值的数组(使用Set集合类型是最好的选择)。类型为java.util.Collection或者java.util.List的属性还可以使用"bag"语义来持久。用于持久化的集合,除了集合接口外,不能保留任何实现这些接口的类所附加的语义(例如:LinkedHashSet带来的迭代顺序)。所有的持久化集合,实际上都各自按照 HashMap, HashSet, TreeMap, TreeSet 和 ArrayList 的语义直接工作。更深入地说,对于一个包含集合的属性来说,必须把Java类型定义为接口 (也就是Map, Set 或者List等),而绝不能是HashMap, TreeSet 或者 ArrayList。存在这个限制的原因是,在你不知道的时候,Hibernate暗中把你的Map, Set 和 List 的实例替换成了它自己的关于Map, Set 或者 List 的实现。(所以在你的程序中,谨慎使用==操作符。)(说明: 为了提高性能等方面的原因,在Hibernate中实现了几乎所有的Java集合的接口(为了实现懒加载的一些特性) 。)所有的有序集合类(maps, lists, arrays)都拥有一个由<key> 和 <index> 组成的主键。 这种情况下集合类的更新是非常高效的——主键已经被有效的索引,因此当Hibernate试图更新或删除一行时,可以迅速找到该行数据。集合(sets)的主键由<key> 和其他元素字段构成。 对于有些元素类型来说,这很低效,特别是组合元素或者大文本、大二进制字段; 数据库可能无法有效的对复杂的主键进行索引。 另一方面,对于一对多、多对多关联,特别是合成的标识符来说,集合也可以达到同样的高效性能。( 附注:如果你希望SchemaExport 为你的<set> 创建主键, 你必须把所有的字段都声明为not-null="true" 。)<idbag> 映射定义了代理键,因此它总是可以很高效的被更新。事实上, <idbag> 拥有着最好的性能表现。Bag是最差的。因为bag允许重复的元素值 ,也没有索引字段,因此不可能定义主键。 Hibernate无法判断出重复的行。当这种集合被更改时,Hibernate将会先完整地移除 (通过一个(in a single DELETE ))整个集合,然后再重新创建整个集合。 因此Bag是非常低效的。