Hibernate的映射文件

映射文件的结构和属性

一个映射文件(mapping file)由一个根节点<hibernate-mapping>和多个<class>节点组成,

首先看看根节点<hibernate-mapping>支持什么属性:

1 <hibernate-mapping
2          schema="schemaName"                               (1)
3          catalog="catalogName"                             (2)
4          default-cascade="cascade_style"                   (3)
5          default-access="field|property|ClassName"         (4)
6          default-lazy="true|false"                         (5)
7          auto-import="true|false"                          (6)
8          package="package.name"                            (7)
9  />

这8个属性都是可选的,下面解释几个重要的属性,

default-lazy:延迟加载,默认为true,Hibernate通过默认加载提升性能,通常建议设置为true。在后面的配置属性和集合映射时也可以指定lazy值

auto-import:是否允许查询语句中使用非全限定类名,默认为true,当一个映射文件中有两个持久化类,包名不同,类名相同时,需要设置auto-import为false才能区分。

package:设置该值之后,映射文件就不再需要指定全限定类名

 

再看看<class>标签支持什么样的属性,

 1 <class
 2         name="ClassName"                                   (1)
 3         table="tableName"                                  (2)
 4         discriminator-value="discriminator_value"          (3)
 5         mutable="true|false"                               (4)
 6         schema="owner"                                     (5)
 7         catalog="catalog"                                  (6)
 8         proxy="ProxyInterface"                             (7)
 9         dynamic-update="true|false"                        (8)
10         dynamic-insert="true|false"                        (9)
11         select-before-update="true|false"                  (10)
12         polymorphism="implicit|explicit"                   (11)
13         where="arbitrary sql where condition"              (12)
14         persister="PersisterClass"                         (13)
15         batch-size="N"                                     (14)
16         optimistic-lock="none|version|dirty|all"           (15)
17         lazy="true|false"                                  (16)
18         entity-name="EntityName"                           (17)
19         check="arbitrary sql check condition"              (18)
20         rowxml:id="rowid"                                  (19)
21         subselect="SQL expression"                         (20)
22         abstract="true|false"                              (21)
23         node="element-name"
24 />

 

一对<class>就代表一个持久化实体类,即数据库中的表,在<class>元素下,通常还会有<id>和<property>子元素,用来构成表的各个字段,<id>通常用来映射主键,<property>则用来映射普通字段,另外还可以通过<set> <map>等映射集合字段,下面一一讲解。

映射主键(<id...>

Hibernate建议为持久化类定义一个标识属性(identifier property),如private int id, 标识属性映射数据库底层的主键字段(primary key column )。在映射文件中,标识属性用<id>表示,<id>标签支持以下属性:

 1 <id
 2         name="propertyName"                                (1)
 3         type="typename"                                    (2)
 4         column="column_name"                               (3)
 5         unsaved-value="null|any|none|undefined|id_value"   (4)
 6         access="field|property|ClassName">                 (5)
 7         node="element-name|@attribute-name|element/@attribute|."
 8 
 9         <generator class="generatorClass"/>
10 </id>

unsaved-value:指定刚创建尚未保存时的标识属性值,用来区分已经加载到session但未再次持久化的实例,Hibernate 3中已经不需要指定这个属性。

上面几个属性通常只需要指定 name, type, column这三个属性,在泛型中,type属性也不需要指定,Hibernate会通过反射获取字段类型

在Hibernate中,提供了一个逻辑主键生成器,在映射文件中通过<id>元素的子元素<generator>表示,可以为每个持久化类生成唯一逻辑主键,

主键生成器<generator>的class属性支持以下值,代表不同种类的主键生成器策略,

1 <generator class="increment|identity|sequence|hilo|seqhilo|uuid|guid|native|assigned|select|foreign" />

其中最常用的identiry,适合DB2,MySQL, MSSQL等支持自增长字段的数据库,可以返回long, short, int等。

sequence,适合DB2,Oracle等。

映射普通属性(<property...>

<property>标签支持以下属性,

 1 <property
 2         name="propertyName"                                (1)
 3         column="column_name"                               (2)
 4         type="typename"                                    (3)
 5         update="true|false"                                (4)
 6         insert="true|false"                                (4)
 7         formula="arbitrary SQL expression"                 (5)
 8         access="field|property|ClassName"                  (6)
 9         lazy="true|false"                                  (7)
10         unique="true|false"                                (8)
11         not-null="true|false"                              (9)
12         optimistic-lock="true|false"                       (10)
13         generated="never|insert|always"                    (11)
14         node="element-name|@attribute-name|element/@attribute|."
15         index="index_name"
16         unique_key="unique_key_id"
17         length="L"
18         precision="P"
19         scale="S"
20 />

通常情况下只需指定name, column, type的值, 另外部分属性解释如下

update和insert:输入属性值不需要Hibernate生成,则指定为false

formula=(sql):直接写SQL获取数据,括号不能少

lazy:是否延迟加载,默认为false

下面演示一下formula的用法,

如果我们在实体类Person.java中有一个普通属性 private String fullContent;

1 private String fullContent;

在映射文件中,这个属性的值是通过formula的方式获取的,则映射文件应该这么写,

1 ...
2 <property name="fullContent" formula="(select concat(p.name,p.age) from person_inf p where p.person_id = person_id)" />
3 ...

这样,我们在测试类中如果要获取这个属性值的话,就会取formula的值

1 Person p = (Person)sess.get(Person.class, 3);
2 System.out.println(p.getFullContent());

上面的例子只是得到一个实体类的属性字段值,但是在数据库层面并没有生成这个字段,

要想数据库也生成这个字段,则要通过<property>标签的generated属性来指定, 例如

1 ...
2 <property name="fullContent" column="full_content" type="string" generated="insert" />
3 ...

同时,需要有一个数据库的触发器配合使用才能实现上面的功能,

 1 drop database test;
 2 create database test;
 3 use test;
 4 create table person_inf
 5 (
 6 id auto_increment primary key,
 7 title varchar(255) not null,
 8 content varchar(255)
 9 full_content varchar(255)
10 );
11 DELEMITER |
12 create trigger t_full_content_gen BEFORE INSERT ON person_inf
13     FOR EACH ROW BEGIN
14         set person_inf.full_content=concat(person_inf.title, person_inf.content);
15     END;
16 |
17 DELIMITER ;

先执行上面的SQL脚本,然后才执行java程序,另外需要将hibernate.cfg.xml的hbm2ddl.auto改为update,避免重建数据库时丢失触发器。

之后,便可以像上面那样访问这个属性了,

1 Person p = (Person)sess.get(Person.class, 3);
2 System.out.println(p.getFullContent());

 

映射集合属性

在一个实体类中,有标志属性,普通属性,经常还会有集合属性,集合属性中会包含多个值,多个值与实体类形成一对多的关联关系。

例如Set集合,List集合,Map集合等等,比如下面这样的实体类定义,

1 public class SetPerson {
2         private int id;
3         private String name;
4         private int age;
5         private Set<String> schools = new HashSet<String>();
6 ...

 

这里的schools就是一个集合属性。

Hibernate要求集合属性必须声明为接口而不是实现类,可以是java.util.Set, java.util.List, java.util.Map等。

集合属性将会保存在单独的表中,当实体类对象保存时,集合属性也会自动持久化(即保存到单独表中去),当实体类对象删除时,集合属性也会自动删除;当集合从一个实体类对象传递到另一个持久化对象时,集合记录也会从一个表转移到另一个表。

-集合标签

常用的集合属性在映射文件中的元素有 <list>, <set>, <map>, <array>, <bag>,<idbag>, <rimitive-array>等,这些集合属性元素标签也有自己的属性,部分属性解释如下:

table:表名(集合属性元素需要单独存在一张表里)

lazy:默认为true,延迟加载

inverse:反转集合属性与实体类关联的控制权,这个对于Set之类的无序集合在性能提升上有用

sort:有序集合排序属性

order-by:也是指定排序,不过是通过SQL语句实现

-外键

由于集合元素和实体类是一个从属关系,所以在集合元素中需要有一个外键,与实体类的标志属性相关联。这个外键在映射文件中通常用<key>标签标识,<key>标签也有自己的属性,如column, on-delete, property-ref,update,unique等等。

-索引

对于有序集合,都需要指定一个索引列,无序集合(Set,bag)则不需要。例如在

<list-index>,List或者数组集合中用来表示索引,

<map-key>,映射Map集合,基本数据列的索引

<map-key-many-to-many>, 映射Map集合,实体引用类索引

<composite-map-key>, 映射Map集合,复合数据类型的索引

-数据列

在映射文件中的集合元素,通常还会有一个数据列,根据不同的数据类型,在映射文件中的标签可能不同,通常会有以下几种

<element> 基本数据类型,或者其包装类型

<composite-element>复合类型

<one-to-many>或<many-to-many>引用类型

 

下面,将针对List, Set, Map, SortedMap等不同的集合属性具体说明每种集合属性的特性

List集合属性

List是有序集合, 因此需要一个索引列来表示集合的次序。 下面演示List集合属性的用法,

首先定义一个持久化实体类ListPerson, 类中定义了一个List集合属性 schools.

 1 package hib;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 public class ListPerson {
 7     private int id;
 8     private String name;
 9     private int age;
10     private List<String> schools = new ArrayList<String>();
11     
12     public int getId() {
13         return id;
14     }
15     public void setId(int id) {
16         this.id = id;
17     }
18     public String getName() {
19         return name;
20     }
21     public void setName(String name) {
22         this.name = name;
23     }
24     public int getAge() {
25         return age;
26     }
27     public void setAge(int age) {
28         this.age = age;
29     }
30     
31     public List<String> getSchools() {
32         return this.schools;
33     }
34     public void setSchools(List<String> schools) {
35         this.schools = schools;
36     }
37     
38 }

 

对于这个持久化类, 其映射文件如下, 在映射文件中,我们定义了外键标签<key>用来关联集合属性和持久化类,

定义了索引列<list-index>来记录集合属性元素的次序,定义了数据列<element>来保存集合元素的值

 1 <?xml version="1.0"  encoding="UTF-8"?>    
 2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
 3 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  
 4 
 5 <hibernate-mapping package="hib">
 6     <class name="ListPerson" table="Person_inf">
 7         <id name="id" column="person_id" type="int">
 8             <generator class="identity" />
 9         </id>
10         <property name="name" type="string" />
11         <property name="age" type="int" />
12         <list name="schools" table="school">
13             <!-- 外键列-->
14             <key column="personid" not-null="true"/>
15             <!-- 索引列 -->
16             <list-index column="list_order"/>
17             <!-- 数据列,这里的type属性其实可以省略,因为在实体类中使用了泛型,Hibernate会通过反射取得元素数据类型-->
18             <element type="string" column="school_name" />
19         </list>
20     </class>
21 </hibernate-mapping>

 

下面写一个测试类用来测试上面的持久化类,

 1 package hib;
 2 
 3 import java.util.ArrayList;
 4 import java.util.HashMap;
 5 import java.util.HashSet;
 6 import java.util.List;
 7 import java.util.Map;
 8 import java.util.Set;
 9 import java.util.SortedSet;
10 import java.util.TreeSet;
11 
12 import org.hibernate.Session;
13 import org.hibernate.SessionFactory;
14 import org.hibernate.Transaction;
15 import org.hibernate.cfg.Configuration;
16 import org.hibernate.tool.hbm2ddl.SchemaExport;
17 
18 public class PersonManager {
19     private static void exec(String resource, Object obj) {
20         // 打开线程安全的session
21         Configuration conf = new Configuration().configure();
22         conf.addResource("hib/"+resource);
23         // 用Configuration创建SessionFactory
24         SessionFactory sf = conf.buildSessionFactory();
25         // 用SessionFactory打开Session
26         Session sess = sf.openSession();
27         Transaction tx = sess.beginTransaction();
28         sess.save(obj);
29         tx.commit();
30         sess.close();
31         sf.close();
32     }
33     private static void listTest() {
34         ListPerson    lp = new ListPerson();
35         lp.setAge(20);
36         lp.setName("xiao ming");
37         List<String> schools = new ArrayList<String>();
38         schools.add("小学");
39         schools.add("中学");
40         lp.setSchools(schools);
41         exec("ListPerson.hbm.xml", lp);
42     }
43     
44     public static void main(String[] args) {
45         listTest();
46     }
47 }

PersonManager类中定义了一个通用方法private static void exec(String resource, Object obj)用来保存持久化类实例,

在方法listTest()中,实例化了一个持久化类ListPerson, 并且实例化了一个集合属性schools并赋值。

运行PersonManager.java之后,发现数据库中多了两张表,person_inf和school,且两张表通过personid关联(即在school表中增加了外键约束如下:FK_48amgp8tko414xxqs1q1vaat4)。

eclipse中执行结果,

 1 Hibernate: drop table if exists Person_inf
 2 Hibernate: drop table if exists school
 3 Hibernate: create table Person_inf (person_id integer not null auto_increment, name varchar(255), age integer, primary key (person_id))
 4 Hibernate: create table school (personid integer not null, school_name varchar(255), list_order integer not null, primary key (personid, list_order))
 5 Hibernate: alter table school add constraint FK_48amgp8tko414xxqs1q1vaat4 foreign key (personid) references Person_inf (person_id)
 6 十二月 29, 2016 11:11:30 下午 org.hibernate.tool.hbm2ddl.SchemaExport execute
 7 INFO: HHH000230: Schema export complete
 8 Hibernate: insert into Person_inf (name, age) values (?, ?)
 9 Hibernate: insert into school (personid, list_order, school_name) values (?, ?, ?)
10 Hibernate: insert into school (personid, list_order, school_name) values (?, ?, ?)

mysql中的数据,

总共有两张表

MariaDB [information_schema]>
MariaDB [information_schema]> select table_name from tables where table_schema =
'test';
+------------+
| table_name |
+------------+
| person_inf |
| school |
+------------+
2 rows in set (0.00 sec)

实体类表

MariaDB [(none)]> use test;
Database changed
MariaDB [test]>
MariaDB [test]> select * from person_inf;
+-----------+-----------+------+
| person_id | name | age |
+-----------+-----------+------+
| 1 | xiao ming | 20 |
+-----------+-----------+------+
1 row in set (0.00 sec)

MariaDB [test]>

 

集合属性表

MariaDB [test]> select * from school;
+----------+-------------+------------+
| personid | school_name | list_order |
+----------+-------------+------------+
| 1     | 小学      | 0     |
| 1     | 中学       | 1     |
+----------+-------------+------------+
2 rows in set (0.00 sec)

 

数组集合属性

Hibernate对数组和List的处理非常相似,只是前者长度不可变,后者可变而已,也因此数组集合一般是无法实现延迟加载的(长度不可变)。

将上面的list集合修改成数组集合,代码如下,

持久化类,

 1 package hib;
 2 
 3 import java.util.ArrayList;
 4 import java.util.List;
 5 
 6 public class ArrayPerson {
 7     private int id;
 8     private String name;
 9     private int age;
10     
11     public int getId() {
12         return id;
13     }
14     public void setId(int id) {
15         this.id = id;
16     }
17     public String getName() {
18         return name;
19     }
20     public void setName(String name) {
21         this.name = name;
22     }
23     public int getAge() {
24         return age;
25     }
26     public void setAge(int age) {
27         this.age = age;
28     }
29     
30     public String[] getSchools() {
31         return schools;
32     }
33     public void setSchools(String[] schools) {
34         this.schools = schools;
35     }
36 
37     private String[] schools;
38 }

 

映射文件,

 1 <?xml version="1.0"  encoding="UTF-8"?>    
 2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
 3 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  
 4 
 5 <hibernate-mapping package="hib">
 6     <class name="ArrayPerson" table="Person_inf">
 7         <id name="id" column="person_id" type="int">
 8             <generator class="identity" />
 9         </id>
10         <property name="name" type="string" />
11         <property name="age" type="int" />
12         <array name="schools" table="school">
13             <!-- 外键列-->
14             <key column="personid" not-null="true"/>
15             <!-- 索引列 -->
16             <list-index column="list_order"/>
17             <!-- 数据列,这里的type属性其实可以省略,因为在实体类中使用了泛型,Hibernate会通过反射取得元素数据类型-->
18             <element type="string" column="school_name" />
19         </array>
20     </class>
21 </hibernate-mapping>

 

在测试类PersonManager.java中,新家一个测试方法arrayTest(),

1 private static void arrayTest() {
2     ArrayPerson    ap = new ArrayPerson();
3     ap.setAge(20);
4     ap.setName("xiao ming");
5     String[] schools = new String[] {"小学","中学","大学"};
6     ap.setSchools(schools);
7     exec("ArrayPerson.hbm.xml", ap);
8 }

 

执行PersonManager.java, 看到hibernate生成的sql如下,

1 ...
2 Hibernate: insert into person_inf (name, age) values (?, ?)
3 Hibernate: insert into school (personid, list_order, school_name) values (?, ?, ?)
4 Hibernate: insert into school (personid, list_order, school_name) values (?, ?, ?)
5 Hibernate: insert into school (personid, list_order, school_name) values (?, ?, ?)

 

mysql中的数据,

person_inf表,

school表,

Set集合属性

Set集合与List集合不同,Set是无序集合,因此映射文件中就没有像list那样的索引列<list-index>。作为一个与持久化实例管理的集合属性,Set集合也是需要一个<key>外键来关联的,Set集合演示如下,

持久化类,

 1 package hib;
 2 
 3 import java.util.ArrayList;
 4 import java.util.HashSet;
 5 import java.util.List;
 6 import java.util.Set;
 7 
 8 public class SetPerson {
 9         private int id;
10         private String name;
11         private int age;
12         
13         public int getId() {
14             return id;
15         }
16         public void setId(int id) {
17             this.id = id;
18         }
19         public String getName() {
20             return name;
21         }
22         public void setName(String name) {
23             this.name = name;
24         }
25         public int getAge() {
26             return age;
27         }
28         public void setAge(int age) {
29             this.age = age;
30         }
31         
32         public Set<String> getSchools() {
33             return this.schools;
34         }
35         public void setSchools(Set<String> schools) {
36             this.schools = schools;
37         }
38         private Set<String> schools = new HashSet<String>();
39         
40     }

 

映射文件,

 1 <?xml version="1.0"  encoding="UTF-8"?>    
 2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
 3 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  
 4 
 5 <hibernate-mapping package="hib">
 6     <class name="SetPerson" table="Person_inf">
 7         <id name="id" column="person_id" type="int">
 8             <generator class="identity" />
 9         </id>
10         <property name="name" type="string" />
11         <property name="age" type="int" />
12         <set name="schools" table="school">
13             <!-- 外键列-->
14             <key column="personid" not-null="true"/>
15             <!-- 数据列,这里的type属性其实可以省略,因为在实体类中使用了泛型,Hibernate会通过反射取得元素数据类型-->
16             <element type="string" column="school_name" not-null="true" />
17         </set>
18     </class>
19 </hibernate-mapping>

 

在PersonManager.java中新增一个测试方法setTest(),

 1 private static void setTest() {
 2     SetPerson sp = new SetPerson();
 3     sp.setAge(20);
 4     sp.setName("xiao ming");
 5     Set<String> schools = new HashSet<String>();
 6     schools.add("小学");
 7     schools.add("中学");
 8     sp.setSchools(schools);
 9     exec("SetPerson.hbm.xml", sp);
10 }

运行PersonManager.java,查看mysql,能得到前面类似的结果

bag集合元素映射

<bag>可以映射list,映射Set,甚至映射Collection,但是不论映射成哪种集合,<bag>都会将它们映射为无序集合。

<bag>集合对应的表没有主键,在映射文件中通过<key>与持久化类关联,用<element>保存元素值,下面演示<bag>集合映射成Collection集合属性,

持久化类,

 1 package hib;
 2 
 3 import java.util.ArrayList;
 4 import java.util.Collection;
 5 import java.util.List;
 6 
 7 public class BagPerson {
 8     private int id;
 9     private String name;
10     private int age;
11     
12     public int getId() {
13         return id;
14     }
15     public void setId(int id) {
16         this.id = id;
17     }
18     public String getName() {
19         return name;
20     }
21     public void setName(String name) {
22         this.name = name;
23     }
24     public int getAge() {
25         return age;
26     }
27     public void setAge(int age) {
28         this.age = age;
29     }
30     
31     public Collection<String> getSchools() {
32         return schools;
33     }
34     public void setSchools(Collection<String> schools) {
35         this.schools = schools;
36     }
37 
38     private Collection<String> schools = new ArrayList<String>();
39 }

 

映射文件, 注意在无序集合中<bag>标签中,可以添加一个order-by属性,直接以SQL实现,这个排序将会添加进hibernate生成的SQL中,通过数据库来实现排序,而不是在程序运行内存中排序。

 1 <?xml version="1.0"  encoding="UTF-8"?>    
 2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
 3 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  
 4 
 5 <hibernate-mapping package="hib">
 6     <class name="BagPerson" table="Person_inf">
 7         <id name="id" column="person_id" type="int">
 8             <generator class="identity" />
 9         </id>
10         <property name="name" type="string" />
11         <property name="age" type="int" />
12         <bag name="schools" table="school" order-by="school_name asc">
13             <!-- 外键列-->
14             <key column="personid" not-null="true"/>
15             <!-- 数据列,这里的type属性其实可以省略,因为在实体类中使用了泛型,Hibernate会通过反射取得元素数据类型-->
16             <element type="string" column="school_name" not-null="true"/>
17         </bag>
18     </class>
19 </hibernate-mapping>

 

在PersonManager.java中,添加一个新的测试方法bagTest(),

 1 private static void bagTest() {
 2     BagPerson sp = new BagPerson();
 3     sp.setAge(20);
 4     sp.setName("xiao ming");
 5     Set<String> schools = new HashSet<String>();
 6     schools.add("小学");
 7     schools.add("中学");
 8     sp.setSchools(schools);
 9     exec("BagPerson.hbm.xml", sp);
10 }

 

执行PersonManager.java后会发现school表中只有两列,外键列和数据列,没有索引列,因为<bag>是无序集合,

Map集合属性

Map集合属性使用<map>标签,需要使用<key>列作为外键列和持久化类关联。Map是有序集合,因此还需要<may-key>类作为Map集合的索引。Map集合对应的表将以外键列和索引列作为联合主键。下面演示Map集合用法,

 1 package hib;
 2 
 3 import java.util.ArrayList;
 4 import java.util.HashMap;
 5 import java.util.List;
 6 import java.util.Map;
 7 
 8 public class MapPerson {
 9     private int id;
10     private String name;
11     private int age;
12     
13     public int getId() {
14         return id;
15     }
16     public void setId(int id) {
17         this.id = id;
18     }
19     public String getName() {
20         return name;
21     }
22     public void setName(String name) {
23         this.name = name;
24     }
25     public int getAge() {
26         return age;
27     }
28     public void setAge(int age) {
29         this.age = age;
30     }
31     
32 
33     public Map<String, Float> getScores() {
34         return scores;
35     }
36     public void setScores(Map<String, Float> scores) {
37         this.scores = scores;
38     }
39 
40     private Map<String, Float> scores = new HashMap<String, Float>();
41     
42 }

 

映射文件,

 1 <?xml version="1.0"  encoding="UTF-8"?>    
 2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
 3 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  
 4 
 5 <hibernate-mapping package="hib">
 6     <class name="MapPerson" table="Person_inf">
 7         <id name="id" column="person_id" type="int">
 8             <generator class="identity" />
 9         </id>
10         <property name="name" type="string" />
11         <property name="age" type="int" />
12         <map name="scores" table="score">
13             <!-- 外键列-->
14             <key column="person_id" not-null="true"/>
15             <!-- 索引列 -->
16             <map-key column="subject" type="string"/>
17             <!-- 数据列,这里的type属性其实可以省略,因为在实体类中使用了泛型,Hibernate会通过反射取得元素数据类型-->
18             <element column="grade" type="float" />
19         </map>
20     </class>
21 </hibernate-mapping>

 

在PersonManager.java中新增一个测试方法mapTest(),

 1 private static void mapTest() {
 2     MapPerson mp = new MapPerson();
 3     mp.setAge(20);
 4     mp.setName("xiao ming");
 5     Map<String, Float> scores = new HashMap<String, Float>();
 6     scores.put("语文", new Float(100.01));
 7     scores.put("数学", new Float(130.05));
 8     mp.setScores(scores);
 9     exec("MapPerson.hbm.xml", mp);
10 }

 

执行PersonManager.java, mysql中score表的数据如下,subject将作为map集合索引, person_id + subject 将作为表的联合主键。

 

SortedSet和SortedMap集合映射

使用这两个集合作为集合实现类时,需要为<set>或者<map>标签指定sort属性,sort支持unsorted, natural, 以及通过java.utilComparator自定义排序。

下面演示一个SortedSet的用法,

持久化类,

 1 package hib;
 2 
 3 import java.util.HashMap;
 4 import java.util.Map;
 5 import java.util.SortedSet;
 6 import java.util.TreeSet;
 7 
 8 public class SortedPerson {
 9     private int id;
10     private String name;
11     private int age;
12     
13     public int getId() {
14         return id;
15     }
16     public void setId(int id) {
17         this.id = id;
18     }
19     public String getName() {
20         return name;
21     }
22     public void setName(String name) {
23         this.name = name;
24     }
25     public int getAge() {
26         return age;
27     }
28     public void setAge(int age) {
29         this.age = age;
30     }
31     
32     public SortedSet<String> getTrainings() {
33         return trainings;
34     }
35     public void setTrainings(SortedSet<String> trainings) {
36         this.trainings = trainings;
37     }
38 
39     private SortedSet<String> trainings = new TreeSet<String>();
40 }

 

映射文件, 需要在<set>上添加sort属性,

 1 <?xml version="1.0"  encoding="UTF-8"?>    
 2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
 3 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  
 4 
 5 <hibernate-mapping package="hib">
 6     <class name="SortedPerson" table="person_inf">
 7         <id name="id" column="person_id" type="int">
 8             <generator class="identity" />
 9         </id>
10         <property name="name" type="string" />
11         <property name="age" type="int" />
12         <set name="trainings" table="training" sort="natural">
13             <!-- 外键列-->
14             <key column="person_id" not-null="true"/>
15             <!-- 数据列 -->
16             <element column="training_name" type="string" not-null="true"/>
17         </set>
18     </class>
19 </hibernate-mapping>

 

在PersonManager.java中添加测试方法sortedSetTest(),

 1 private static void sortedSetTest() {
 2     SortedPerson ssp = new SortedPerson();
 3     ssp.setAge(20);
 4     ssp.setName("xiao ming");
 5     SortedSet<String> trainings = new TreeSet<String>();
 6     trainings.add("Wild Java Camp");
 7     trainings.add("Sun SCJP");
 8     ssp.setTrainings(trainings);
 9     exec("SortedPerson.hbm.xml", ssp);
10 }

 

执行PersonManager.java,发现mysql中training表记录已经按照element列按字母排序,

数据库SQL排序

上面这种排序是在程序运行内存中实现的,之后再将已排序记录持久化到数据库中。

如果我们不希望用程序内心来排序,而是希望通过数据来排序,则可以在<set> <map> <bag>中添加order-by属性。

这种方式是通过继承LinkedHashSet或者LinkedHashMap来实现的,因此确保你的JDK版本至少大于1.4

例如我们可以在上面的SetPerson.hbm.xml中,在<set>标签中添加order-by属性,

1 <set name="schools" table="school" order-by="school_name asc">

 

集合属性的性能分析

延迟加载

Hibernate对集合属性默认采用延迟加载策略,即lazy="true". 因为集合属性很可能有成千上万条数据,而持久化类在实例化的时候并不一定会马上用关联的集合属性。

修改属性(增删改)的性能

 集合可分两类

有序集合:集合元素可根据key或者index访问

无序集合:集合元素只能遍历

对于有序集合,因为有联合主键存在(主键已被索引),因此可以直接定位到相应元素进行修改,效率高。

对于无序集合,只有外键,没有索引,也就没有联合主键,也就没法按主键索引定位元素(可能可以建立索引,但性能可能非常差),修改这种集合性能将会很低。

因此对于<bag>这种集合,即没有索引字段,同时还不允许元素重复,hibernate无法直接判断元素是否重复,当需要修改集合属性值时,只能先删除全部集合,再重新创建集合,因此性能是最差的。

所以,对于有序集合属性的增,删,改将会有比较好的性能。(数组也是有序集合,但数组属性不支持延迟加载(因为长度固定),所以数组属性性能其实很低的)

延迟更新

对于多对多关联,值数据而言,如果改变Set集合的某个元素,hibernate并不能立即更新该元素对应的数据行。对于Set而言,只有在插入和删除操作时改变才有效。

反向集合(inverse="true")

修改Set集合属性性能并不高,但Hibernate还是处处采用了Set集合,这一方面是因为Set更贴近集合语意,

另一方面,在1-N关联中,1的一端通常都带有inverse="true"属性,即这种关联关系不再由一的一端维护,更新操作将在N的一端处理,这种情况下,无序考虑集合性能。

 

映射数据库对象

最后,还要来看看映射文件映射数据库对象,数据库对象指的是触发器,存储过程等等。Hibernate中使用<database-object>标签来映射数据库对象,用<create><drop>等标签标识具体的数据库操作,

下面演示映射创建数据表(DDL语句),

首先要定义一个映射文件,映射文件中可以用<create><drop>等指定具体的操作,然后直接将sql语句写在标签中。还可以添加一些<dialect>之类的标签指定更多属性,例如只在MySQL中有效,

 1 <?xml version="1.0"  encoding="UTF-8"?>    
 2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"  
 3 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">  
 4 
 5 <hibernate-mapping>
 6     <database-object>
 7         <create>create table test(t_name varchar(255));</create>
 8         <drop></drop>
 9     </database-object>
10 </hibernate-mapping>

 

此映射文件不需要持久化类,我们直接在测试类中初始化一个Configuration对象并加载配置文件,打开一个SessionFactory,此映射文件就会生效,

在PersonManager.java中,新增测试方法dbObject()

1 private static void dbObject() {
2     //加载数据触发器来创建表
3     Configuration conf = new Configuration().configure();
4     conf.addResource("hib/DbObject.hbm.xml");
5     SessionFactory sf = conf.buildSessionFactory();
6 }

 

注意,通过这种方式创建表,好一定要将hibernate.cfg.xml中的hbm2ddl.auto设置为create才能生效

1 <property name="hbm2ddl.auto">create</property>

 

执行PersonManager.java将会创建一张test表,

 

除了上面根据配置文件生成表的方法之外,还可以使用hibernate自带工具SchemaExport(可直接命令行执行), 使用工具的代码如下,

映射文件保持不变,在PersonManager.java中,将dbObject()方法修改为,

 1 private static void dbObject() {
 2     //加载数据触发器来创建表
 3     Configuration conf = new Configuration().configure();
 4     conf.addResource("hib/DbObject.hbm.xml");
 5     //SessionFactory sf = conf.buildSessionFactory();
 6 
 7     SchemaExport se = new SchemaExport(conf);
 8     se.setFormat(true).setOutputFile("new.sql").create(true, true);
 9     
10 }

 

注意如果通过SchemaExport的方式创建表,hibernate.cfg.xml中的hbm2ddl.auto就不需要强制设置为create了,update也行。

执行PersonManger.java,也能创建test表。

 

转载于:https://www.cnblogs.com/fysola/p/6233909.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值