hibernate学习之第九篇

hibernate中的集合类型

引入:
Hibernate可以持久化以下java集合的实例, 包括java.util.Map, java.util.Set, java.util.SortedMap, java.util.SortedSet, java.util.List, 和任何持久实体或值的数组。类型为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集合的接口 。)

集合实例在数据库中根据指向对应实体的外键而得到区别。这个外键被称为集合的关键字在Hibernate配置文件中使用<key> 元素来映射这个集合的关键字。

集合可以包含几乎所有的Hibernate类型, 包括所有的基本类型, 自定义类型,实体类型和组件。 集合不能包含其他集合。这些被包含的元素的类型被称为集合元素类型。 集合的元素在Hibernate中被映射为<element>, <composite-element>, <one-to-many>, <many-to-many> 或者 <many-to-any>。
set集合类型用的比较多。
这里举得例子是部门和员工的关系。我们知道部门和员工是一对多的关系。
员工类的源代码如下:

public class Employee {
        private int id;
        private String name;
        private Department depart;

        @Override
        public String toString() {
                return "id=" + this.id + " name=" + this.name;
        }

        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 Department getDepart() {
                return depart;
        }

        public void setDepart(Department depart) {
                this.depart = depart;
        }
}

 

员工的映射文件:

 

<?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="hibernate.collections">

    <class name="Employee" >
        <id name="id">
            <generator class="native"/>
        </id>
        <property name="name" unique="true"/>
        <many-to-one name="depart" column="depart_id"/>
    </class>
</hibernate-mapping>

 

充:hibernate不允许多的这一端放弃对关系的维护。即many-to-one没有inverse=“true”这样的配置。
部门类的源代码如下:

 

public class Department {
        private int id;
        private String name;

        private Set<Employee> emps;

        // private List<Employee> emps;

        // private Map<String, Employee> emps;

        // private Employee[] emps;

        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 Set<Employee> getEmps() {
                return emps;
        }

        public void setEmps(Set<Employee> emps) {
                this.emps = emps;
        }
}

 

部门类的映射文件department.hbm.xml

<?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="hibernate.collections">

        <class name="Department">
                <id name="id">
                        <generator class="native" />
                </id>
                <property name="name"/>

                <set name="emps" inverse="true">
                        <key column="depart_id"/>
                        <one-to-many class="Employee" />
                </set>
        </class>

</hibernate-mapping>

 

测试代码:

public class Main {
public static void main(String[] args){
    Department depart = new Department();
    depart.setName("depart");

    Employee e1 = new Employee();
    e1.setName("e1");
    e1.setDepart(depart);

    Employee e2 = new Employee();
    e2.setName("e2");
    e2.setDepart(depart);
  
    Set<Employee> emps = new HashSet<Employee>();
    emps.add(e1);
    emps.add(e2);
    depart.setEmps(emps);

    Session s = HibernateUtil.getSession();
    Transaction tx = s.beginTransaction();
    s.save(e1);
    s.save(e2);
    s.save(depart);
tx.commit();
}
}

 

执行后hibernate执行的sql语句如下:

Hibernate: insert into Employee (name, depart_id) values (?, ?)
Hibernate: insert into Employee (name, depart_id) values (?, ?)
Hibernate: insert into Department (name) values (?)
Hibernate: update Employee set name=?, depart_id=? where id=?
Hibernate: update Employee set name=?, depart_id=? where id=?
Hibernate: update Employee set depart_id=? where id=?
Hibernate: update Employee set depart_id=? where id=?
 



以上用的是set集合类型,它具有这样的特性:无重复和没有顺序。
但有时候我们需要记住元素的顺序,这时就可以采用list来保存元素。同样,在hibernate中有相应的List标签与之对应。他可以在保存的时候记住保存的顺序。例如,部门可以对其员工加入的先后进行记录。读取时,就会按照先后的顺序来获得。
只需在deparment类中把集合类型改用List,同时修改deparment.hbm.xml文件中的set标签:
                         <list name="emps">
                        <key column="depart_id" />
                        <list-index column="order_col" /> //该子元素用于记录保存的顺序,在对象模型中不存在,hibernate会在表中加入一列order_col
                        <one-to-many class="Employee" />
                     </list>
测试代码只需把set改为是有List即可,其他部分不变。

hibernate帮助生成的表结构为:
+---+-------+-----------+-----------+
| id |name |depart_id|order_col|
+----+------+-----------+-----------+
|  1  | e1    |         1    |         0    |
|  2  | e2    |         1    |         1    |
+----+------+-----------+-----------+

有时候,我们虽然在java代码中试用了List集合类型,但我们保存时并不需要记住保存的先后顺序,这时可以用bag标签代替。
<bag name="employees" order-by="id desc">  //不关心顺序,与java代码中的list对应。
    <key column="depart_id"/>
    <one-to-many class="Employee"/>
</bag>
查看表结构为:
+----+------+-----------+
| id | name | depart_id |
+----+------+-----------+
|  1  | e1    |         1    |
|  2  | e2    |         1    |
+----+------+-----------+

map在java中也是一种常用的集合类型。假设员工的名字是不一样的,可以把员工的名字作为键,员工作为map的值。
department类修改为:
public class Department {
        private int id;
        private String name;
        private Map<String, Employee> emps;
}
department.hbm.xml配置文件修改:
                <map name="emps">
                        <key column="depart_id" />
                        <map-key type="string" column="name"/>
                        <one-to-many class="Employee" />
                </map>
测试代码只需把集合类型做相应的改变即可,生成的表结构为:
department表;
+----+--------+
| id | name   |
+----+--------+
|  1 | depart |
+----+--------+
employee表:
+----+------+-----------+
| id | name |depart_id|
+----+------+-----------+
|  1   | e1   |         1    |
|  2   | e2   |         1    |
+----+------+-----------+

介绍了这么多的集合类型,那么我们在实际运用中该如何选呢?
简单使用原则:大部分情况下使用set,需要保证集合中的顺序用list,想用java.util.List又不需要保证顺序用bag。

这些集合类都是Hibernate实现的类和JAVA中的集合类不完全一样,set,list,map分别和java中的set,list,map接口对应,bag映射成java的list;这些集合的使用和java集合中对应的接口基本一致;

注意:在java的实体类中集合只能定义成接口不能定义成具体类,因为集合会在运行时被替换成Hibernate的实现。为了实现懒加载 ,hibernate把java中的集合类都重新给予了实现。你在java中使用的集合类,在保存时hibernate内部进行了替换,使用的是内置的hibernate集合。所以你在程序中用你的集合类进行强制类型转换会出错。

比如下面代码:

static void query(int id) {
        String hql = "from deparment";
        Session s = HibernateUtil.getSession();
        Transaction tx = s.beginTransaction();
        Department depart = (Department)s.get(Department.class, id);
        System.out.println();
        System.out.println(depart.getEmps());
       
    }

 

在主函数中调用该段代码打印输出:{e1=id=1 name=e1, e2=id=2 name=e2}

将以上代码的打印语句改为:System.out.println((HashMap) depart.getEmps());
把它进行强制类型转换,再运行则会报错
出错信息为:Exception in thread "main" java.lang.ClassCastException: org.hibernate.collection.PersistentMap cannot be cast to java.util.HashMap

正如上面所指出的,hibernate对java中的集合类有自己的替代实现,对于hashmap是org.hibernate.collection.PersistentMap,该类继承了java中的Map类。在我们调用session的save语句后,hibernate会将实际的HashMap进行替换,实际上程序使用的是hibernate的自身实现。所以在程序中,再用HashMap进行强制类型转换就会报错。 但是用Map就可以,因为persistentMap是Map的子类。即执行:System.out.println((Map) depart.getEmps());就不会报错。所以在程序中尽量使用java接口,这也符合面向接口编程的道理。

你可能感兴趣的:(java,数据结构,编程,Hibernate,xml)