zoukankan      html  css  js  c++  java
  • 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表。

  • 相关阅读:
    Balanced Binary Tree
    Convert Sorted List to Binary Search Tree
    Convert Sorted Array to Binary Search Tree
    Binary Tree Zigzag Level Order Traversal
    Validate Binary Search Tree
    Binary Tree Level Order Traversal II
    Binary Tree Level Order Traversal
    Maximum Depth of Binary Tree
    如何把U盘的两个盘或者多个盘合成一个
    bugku 想蹭网先解开密码
  • 原文地址:https://www.cnblogs.com/fysola/p/6233909.html
Copyright © 2011-2022 走看看