Hibernate的映射文件

映射文件的结构和属性

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

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

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

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

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

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

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

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

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

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

映射主键(<id...>

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

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

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

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

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

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

 <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>标签支持以下属性,

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

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

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

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

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

下面演示一下formula的用法,

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

 private String fullContent;

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

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

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

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

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

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

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

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

 drop database test;
create database test;
use test;
create table person_inf
(
id auto_increment primary key,
title varchar(255) not null,
content varchar(255)
full_content varchar(255)
);
DELEMITER |
create trigger t_full_content_gen BEFORE INSERT ON person_inf
FOR EACH ROW BEGIN
set person_inf.full_content=concat(person_inf.title, person_inf.content);
END;
|
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集合等等,比如下面这样的实体类定义,

 public class SetPerson {
private int id;
private String name;
private int age;
private Set<String> schools = new HashSet<String>();
...

这里的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.

 package hib;

 import java.util.ArrayList;
import java.util.List; public class ListPerson {
private int id;
private String name;
private int age;
private List<String> schools = new ArrayList<String>(); public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
} public List<String> getSchools() {
return this.schools;
}
public void setSchools(List<String> schools) {
this.schools = schools;
} }

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

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

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

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

 package hib;

 import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet; import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.tool.hbm2ddl.SchemaExport; public class PersonManager {
private static void exec(String resource, Object obj) {
// 打开线程安全的session
Configuration conf = new Configuration().configure();
conf.addResource("hib/"+resource);
// 用Configuration创建SessionFactory
SessionFactory sf = conf.buildSessionFactory();
// 用SessionFactory打开Session
Session sess = sf.openSession();
Transaction tx = sess.beginTransaction();
sess.save(obj);
tx.commit();
sess.close();
sf.close();
}
private static void listTest() {
ListPerson lp = new ListPerson();
lp.setAge(20);
lp.setName("xiao ming");
List<String> schools = new ArrayList<String>();
schools.add("小学");
schools.add("中学");
lp.setSchools(schools);
exec("ListPerson.hbm.xml", lp);
} public static void main(String[] args) {
listTest();
}
}

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

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

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

eclipse中执行结果,

 Hibernate: drop table if exists Person_inf
Hibernate: drop table if exists school
Hibernate: create table Person_inf (person_id integer not null auto_increment, name varchar(255), age integer, primary key (person_id))
Hibernate: create table school (personid integer not null, school_name varchar(255), list_order integer not null, primary key (personid, list_order))
Hibernate: alter table school add constraint FK_48amgp8tko414xxqs1q1vaat4 foreign key (personid) references Person_inf (person_id)
十二月 29, 2016 11:11:30 下午 org.hibernate.tool.hbm2ddl.SchemaExport execute
INFO: HHH000230: Schema export complete
Hibernate: insert into Person_inf (name, age) values (?, ?)
Hibernate: insert into school (personid, list_order, school_name) values (?, ?, ?)
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集合修改成数组集合,代码如下,

持久化类,

 package hib;

 import java.util.ArrayList;
import java.util.List; public class ArrayPerson {
private int id;
private String name;
private int age; public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
} public String[] getSchools() {
return schools;
}
public void setSchools(String[] schools) {
this.schools = schools;
} private String[] schools;
}

映射文件,

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

</class>
</hibernate-mapping>

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

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

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

 ...
Hibernate: insert into person_inf (name, age) values (?, ?)
Hibernate: insert into school (personid, list_order, school_name) values (?, ?, ?)
Hibernate: insert into school (personid, list_order, school_name) values (?, ?, ?)
Hibernate: insert into school (personid, list_order, school_name) values (?, ?, ?)

mysql中的数据,

Hibernate的映射文件

person_inf表,

Hibernate的映射文件

school表,

Hibernate的映射文件

Set集合属性

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

持久化类,

 package hib;

 import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set; public class SetPerson {
private int id;
private String name;
private int age; public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
} public Set<String> getSchools() {
return this.schools;
}
public void setSchools(Set<String> schools) {
this.schools = schools;
}
private Set<String> schools = new HashSet<String>();

}

映射文件,

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

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

 private static void setTest() {
SetPerson sp = new SetPerson();
sp.setAge(20);
sp.setName("xiao ming");
Set<String> schools = new HashSet<String>();
schools.add("小学");
schools.add("中学");
sp.setSchools(schools);
exec("SetPerson.hbm.xml", sp);
}

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

bag集合元素映射

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

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

持久化类,

 package hib;

 import java.util.ArrayList;
import java.util.Collection;
import java.util.List; public class BagPerson {
private int id;
private String name;
private int age; public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
} public Collection<String> getSchools() {
return schools;
}
public void setSchools(Collection<String> schools) {
this.schools = schools;
} private Collection<String> schools = new ArrayList<String>();
}

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

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

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

 private static void bagTest() {
BagPerson sp = new BagPerson();
sp.setAge(20);
sp.setName("xiao ming");
Set<String> schools = new HashSet<String>();
schools.add("小学");
schools.add("中学");
sp.setSchools(schools);
exec("BagPerson.hbm.xml", sp);
}

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

Hibernate的映射文件

Map集合属性

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

 package hib;

 import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map; public class MapPerson {
private int id;
private String name;
private int age; public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
} public Map<String, Float> getScores() {
return scores;
}
public void setScores(Map<String, Float> scores) {
this.scores = scores;
} private Map<String, Float> scores = new HashMap<String, Float>();

}

映射文件,

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

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

 private static void mapTest() {
MapPerson mp = new MapPerson();
mp.setAge(20);
mp.setName("xiao ming");
Map<String, Float> scores = new HashMap<String, Float>();
scores.put("语文", new Float(100.01));
scores.put("数学", new Float(130.05));
mp.setScores(scores);
exec("MapPerson.hbm.xml", mp);
}

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

Hibernate的映射文件

SortedSet和SortedMap集合映射

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

下面演示一个SortedSet的用法,

持久化类,

 package hib;

 import java.util.HashMap;
import java.util.Map;
import java.util.SortedSet;
import java.util.TreeSet; public class SortedPerson {
private int id;
private String name;
private int age; public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
} public SortedSet<String> getTrainings() {
return trainings;
}
public void setTrainings(SortedSet<String> trainings) {
this.trainings = trainings;
} private SortedSet<String> trainings = new TreeSet<String>();
}

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

 <?xml version="1.0"  encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd"> <hibernate-mapping package="hib">
<class name="SortedPerson" table="person_inf">
<id name="id" column="person_id" type="int">
<generator class="identity" />
</id>
<property name="name" type="string" />
<property name="age" type="int" />
<set name="trainings" table="training" sort="natural">
<!-- 外键列-->
<key column="person_id" not-null="true"/>
<!-- 数据列 -->
<element column="training_name" type="string" not-null="true"/>
</set>
</class>
</hibernate-mapping>

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

 private static void sortedSetTest() {
SortedPerson ssp = new SortedPerson();
ssp.setAge(20);
ssp.setName("xiao ming");
SortedSet<String> trainings = new TreeSet<String>();
trainings.add("Wild Java Camp");
trainings.add("Sun SCJP");
ssp.setTrainings(trainings);
exec("SortedPerson.hbm.xml", ssp);
}

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

Hibernate的映射文件

数据库SQL排序

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

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

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

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

 <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中有效,

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

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

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

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

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

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

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

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

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

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

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

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

上一篇:下载频道--IT资源关东煮第二期[申明:来源于网络]


下一篇:Mysql中实现row_number