【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】

13 篇文章 0 订阅

【Java EE (Struts2 + Spring + Hibernate)开发】 :Hibernate(二)之【关联映射|继承映射|批量处理】
本文地址:http://blog.csdn.net/shanglianlm/article/details/49975511

1 Hibernate 的关联映射

1-1 单项 N-1 关联

public class Person
{
    //标识属性
    private Integer id;
    //Person的name属性
    private String name;
    //保留Person的age属性
    private int age;
    //引用关联实体的属性
    private Address address;
    ....
    //address属性的setter和getter方法
    public void setAddress(Address address)
    {
        this.address = address;
    }
    public Address getAddress()
    {
        return this.address;
    }
}

Address.java

public class Address
{
    //标识属性
    private Integer addressId;
    //地址详细信息
    private String addressDetail;
    //无参数的构造器
    public Address()
    {
    }
    //初始化全部属性的构造器
    public Address(String addressDetail)
    {
        this.addressDetail = addressDetail;
    }
    ...
}

1-1-1 无连接表的 N-1 关联

Hibernate 使用 < many-to-one …/ > 元素映射 N-1 的关联实体。
< many-to-one …/ > 元素的可选属性:
这里写图片描述

<?xml version="1.0" encoding="GBK"?>
<!-- 指定Hibernate的DTD信息 -->
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.crazyit.app.domain">
    <!-- 映射Person持久化类-->
    <class name="Person" table="person_inf">
        <!-- 映射标识属性 -->
        <id name="id" column="person_id">
            <!-- 定义主键生成器策略 -->
            <generator class="identity"/>
        </id>
        <!-- 用于映射普通属性 -->
        <property name="name" type="string"/>
        <property name="age" type="int"/>
        <!-- 用于映射N-1关联实体,指定关联实体类为Address
        指定外键列名为address_id,并指定级联全部操作 -->
        <many-to-one name="address" cascade="create"
            class="Address" column="address_id"/>
    </class>
</hibernate-mapping>

Address 无需访问 Person 端,所以 Address 类的映射文件无需改变。

<?xml version="1.0" encoding="GBK"?>
<!-- 指定Hibernate的DTD信息 -->
<!DOCTYPE hibernate-mapping PUBLIC 
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="org.crazyit.app.domain">
    <!-- Address持久化类-->
    <class name="Address" table="address_inf">
        <!-- 映射标识属性addressid -->
        <id name="addressId" column="address_id">
            <!-- 指定主键生成器策略 -->
            <generator class="identity"/>
        </id>
        <!-- 映射普通属性addressDetail -->
        <property name="addressDetail"/>
    </class>
</hibernate-mapping>

1-1-2 有连接表的 N-1 关联

这里写图片描述
这里写图片描述

<hibernate-mapping package="org.crazyit.app.domain">
    <!-- 映射Person持久化类-->
    <class name="Person" table="person_inf">
        ...
        <!-- 使用join元素强制使用连接表 -->
        <join table="person_address" >
            <!-- 映射连接表中参照本表主键的外键列 -->
            <key column="person_id"/>
            <!-- 映射连接表中参照关联实体的外键列 -->
            <many-to-one name="address" cascade="all"
                class="Address" column="address_id"/>
        </join>
    </class>
</hibernate-mapping>

1-2 单项 1-1 关联

单向 1-1 只需要在 N-1 原有的 < many-to-one /> 元素增加 unique=”true” 属性即可。

1-2-1 基于外键的单向 1-1

<hibernate-mapping package="org.crazyit.app.domain">
    <!-- 映射Person持久化类-->
    <class name="Person" table="person_inf">
        ...
        <!-- 用于映射 1-1 关联实体,指定关联实体类为 Address 指定外键列名为address_id,并指定级联全部操作 -->
        <many-to-one name="address" cascade="all" unique="true" class="Address" column="address_id"/>
    </class>
</hibernate-mapping>

1-2-2 有连接表的单向 1-1

<hibernate-mapping package="org.crazyit.app.domain">
    <!-- 映射Person持久化类-->
    <class name="Person" table="person_inf">
        ...
        <!-- 使用join元素强制使用连接表 -->
        <join table="person_address" >
            <!-- 映射连接表中参照本表主键的外键列 -->
            <key column="person_id"/>
            <!-- 映射连接表中参照关联实体的外键列 -->
            <many-to-one name="address" cascade="all"
                unique="true" class="Address"
                column="address_id"/>
        </join>
    </class>
</hibernate-mapping>

1-2-3 基于主键的单向 1-1

这里写图片描述

<hibernate-mapping package="org.crazyit.app.domain">
    <!-- 映射Person持久化类-->
    <class name="Person" table="person_inf">
        <!-- 映射标识属性person_id -->
        <id name="id" column="person_id">
            <!--  基于主键关联时,主键生成策略是foreign,表明根据关联类的主键来生成该实体的主键 -->
            <generator class="foreign">
                <!-- 指定引用关联实体的属性名 -->
                <param name="property">address</param>
            </generator>
        </id>
        <!-- 用于映射普通属性 -->
        <property name="name" type="string"/>
        <property name="age" type="int"/>
        <!-- 下面映射基于主键的1-1关联 -->
        <one-to-one name="address"/>
    </class>
</hibernate-mapping>

1-3 单项 1-N 关联

这里写图片描述

public class Person
{
    ...
    //1-N关联关系,使用Set来保存关联实体
    private Set<Address> addresses = new HashSet<Address>();
    ...
    //addresses属性的setter和getter方法
    public void setAddresses(Set<Address> addresses)
    {
        this.addresses = addresses;
    }
    public Set<Address> getAddresses()
    {
        return this.addresses;
    }
}

1-3-1 无连接表的单向 1-N

这里写图片描述

<hibernate-mapping package="org.crazyit.app.domain">
    <!-- 映射Person持久化类-->
    <class name="Person" table="person_inf">
        ...
        <!-- 映射集合属性,集合元素是其他持久化实体没有指定cascade属性 -->
        <set name="addresses">
            <!-- 指定关联的外键列 -->
            <key column="person_id"/>
            <!-- 用以映射到关联类属性 -->
            <one-to-many class="Address"/>
        </set>
    </class>
</hibernate-mapping>

1-3-2 有连接表的单向 1-N

这里写图片描述
这里写图片描述

<hibernate-mapping package="org.crazyit.app.domain">
    <!-- 映射Person持久化类-->
    <class name="Person" table="person_inf">
        ...
        <!-- 映射集合属性,集合元素是其他持久化实体指定连接表的表名-->
        <set name="addresses" table="person_address">
            <!-- 指定连接表中参照本表记录的外键列名 -->
            <key column="person_id" />
            <!-- 使用many-to-many来映射1-N关联,增加unique="true" -->
            <many-to-many class="Address" column="address_id" unique="true"/>
        </set>
    </class>
</hibernate-mapping>

1-4 单项 N-N 关联

这里写图片描述

<hibernate-mapping package="org.crazyit.app.domain">
    <!-- 映射Person持久化类-->
    <class name="Person" table="person_inf">
        ...
        <!-- 映射集合属性,集合元素是其他持久化实体指定连接表的表名-->
        <set name="addresses" table="person_address">
            <!-- 指定连接表中参照本表记录的外键列名 -->
            <key column="person_id" />
            <!-- 使用many-to-many来映射N-N关联 -->
            <many-to-many class="Address" column="address_id"/>
        </set>
    </class>
</hibernate-mapping>

1-5 双项 1-N 关联

这里写图片描述

Person.java

public class Person
{
    ...
    //1-N关联关系,使用Set来保存关联实体
    private Set<Address> addresses = new HashSet<Address>();
    ...
    //addresses属性的setter和getter方法
    public void setAddresses(Set<Address> addresses)
    {
        this.addresses = addresses;
    }
    public Set<Address> getAddresses()
    {
        return this.addresses;
    }
}

Address.java

public class Address
{
    //标识属性
    private int addressId;
    //地址详细信息
    private String addressDetail;
    //记录关联实体的person属性
    private Person person;

    //无参数的构造器
    public Address()
    {
    }
    //初始化全部属性的构造器
    public Address(String addressDetail)
    {
        this.addressDetail = addressDetail;
    }
    ...
    //person属性的setter和getter方法
    public void setPerson(Person person)
    {
        this.person = person;
    }
    public Person getPerson()
    {
        return this.person;
    }
}

1-5-1 无连接表的双向 1-N 关联

这里写图片描述
这里写图片描述
Person.hbm.xml

<hibernate-mapping package="org.crazyit.app.domain">
    <!-- 映射Person持久化类-->
    <class name="Person" table="person_inf">
        ...
        <!-- 映射集合属性,集合元素是其他持久化实体没有指定cascade属性,指定不控制关联关系 -->
        <set name="addresses" inverse="true">
            <!-- 指定关联的外键列 -->
            <key column="person_id"/>
            <!-- 用以映射到关联类属性 -->
            <one-to-many class="Address"/>
        </set>
    </class>
</hibernate-mapping>

Address.hbm.xml

<hibernate-mapping package="org.crazyit.app.domain">
    <!-- Address持久化类-->
    <class name="Address" table="address_inf">
        ...
        <!-- 必须指定列名为person_id,与关联实体中key元素的column属性值相同 -->
        <many-to-one name="person" class="Person"
            column="person_id" not-null="true"/>
    </class>
</hibernate-mapping>

使用

private void testPerson()
{
    Session session = HibernateUtil.currentSession();
    Transaction tx = session.beginTransaction();
    //创建一个Person对象
    Person p = new Person();
    //设置Person的Name为Yeeku字符串
    p.setName("Yeeku");
    p.setAge(29);
    //持久化Person对象(对应于插入主表记录)
    session.save(p);
    //创建一个瞬态的Address对象
    Address a = new Address("广州天河");
    //先设置Person和Address之间的关联关系
    a.setPerson(p);
    //再持久化Address对象(对应于插入从表记录)
    session.persist(a);
    //创建一个瞬态的Address对象
    Address a2 = new Address("上海虹口");
    //先设置Person和Address之间的关联关系
    a2.setPerson(p);
    //再持久化Address对象(对应于插入从表记录)
    session.persist(a2);
    tx.commit();
    HibernateUtil.closeSession();
}

1-5-2 有连接表的双向 1-N 关联

这里写图片描述
这里写图片描述
Person.hbm.xml

<hibernate-mapping package="org.crazyit.app.domain">
    <!-- 映射Person持久化类-->
    <class name="Person" table="person_inf">
        ...
        <!-- 映射集合属性,集合元素是其他持久化实体没有指定cascade属性 -->
        <set name="addresses" inverse="true" table="person_address">
            <!-- 指定关联的外键列 -->
            <key column="person_id"/>
            <!-- 用以映射到关联类属性 -->
            <many-to-many class="Address" column="address_id" unique="true"/>
        </set>
    </class>
</hibernate-mapping>

Address.hbm.xml

<hibernate-mapping package="org.crazyit.app.domain">
    <!-- Address持久化类-->
    <class name="Address" table="address_inf">
        ...
        <!-- 显式使用join元素确定连接表 -->
        <join table="person_address">
            <!-- key映射外键列 -->
            <key column="address_id"/>
            <!-- 使用many-to-one映射N-1关联实体 -->
            <many-to-one name="person" column="person_id" not-null="true"/>
        </join>
    </class>
</hibernate-mapping>

1-6 双项 N-N 关联

这里写图片描述

Person.java

public class Person
{
    ...
    //1-N关联关系,使用Set来保存关联实体
    private Set<Address> addresses = new HashSet<Address>();
    ...
    //addresses属性的setter和getter方法
    public void setAddresses(Set<Address> addresses)
    {
        this.addresses = addresses;
    }
    public Set<Address> getAddresses()
    {
        return this.addresses;
    }
}

Address.java

public class Address
{
    //标识属性
    private int addressId;
    //地址详细信息
    private String addressDetail;
    //记录关联实体的person属性
    private Set<Person> persons = new HashSet<Person>();

    //无参数的构造器
    public Address()
    {
    }
    //初始化全部属性的构造器
    public Address(String addressDetail)
    {
        this.addressDetail = addressDetail;
    }
    ...
    //persons属性的setter和getter方法
    public void setPersons(Set<Person> persons)
    {
        this.persons = persons;
    }
    public Set<Person> getPersons()
    {
        return this.persons;
    }
}

这里写图片描述
Person.hbm.xml

<hibernate-mapping package="org.crazyit.app.domain">
    <!-- 映射Person持久化类-->
    <class name="Person" table="person_inf">
        ...
        <!-- 映射N-N关联实体,两边的table属性值要相同 -->
        <set name="addresses" table="person_address">
            <!-- 指定关联的外键列 -->
            <key column="person_id"/>
            <!-- 用以映射到关联类属性 -->
            <many-to-many class="Address" column="address_id"/>
        </set>
    </class>
</hibernate-mapping>

Address.hbm.xml

<hibernate-mapping package="org.crazyit.app.domain">
    <!-- Address持久化类-->
    <class name="Address" table="address_inf">
        ...
        <!-- 映射N-N关联实体,两边的table属性值要相同 -->
        <set name="persons" table="person_address">
            <!-- 指定关联的外键列 -->
            <key column="address_id"/>
            <!-- 用以映射到关联类属性 -->
            <many-to-many class="Address"
                column="person_id"/>
        </set>
    </class>
</hibernate-mapping>

这里写图片描述

1-7 双项 1-1 关联

1-7-1 基于外键的双向 1-1 关联

这里写图片描述
这里写图片描述
Person.hbm.xml

<hibernate-mapping package="org.crazyit.app.domain">
    <!-- 映射Person持久化类-->
    <class name="Person" table="person_inf">
        ...
        <!-- one-to-one元素映射关联属性,外键列在对方的表内 property-ref指定引用关联类的属性。即:在address属性所属的Address类内,必须有person属性的setter和getter方法 -->
        <one-to-one name="address" property-ref="person"/>
    </class>
</hibernate-mapping>

Address.hbm.xml

<hibernate-mapping package="org.crazyit.app.domain">
    <!-- Address持久化类-->
    <class name="Address" table="address_inf">
        ...
        <!-- 使用many-to-one映射1-1关联实体 unique="true"确定为1-1-->
        <many-to-one name="person" unique="true"
            column="person_id" not-null="true"/>
    </class>
</hibernate-mapping>

两边的映射策略可以互换,但是不能两边都使用相同的元素映射关联属性。

1-7-2 基于主键的双向 1-1 关联

这里写图片描述

Person.hbm.xml

<hibernate-mapping package="org.crazyit.app.domain">
    <!-- 映射Person持久化类-->
    <class name="Person" table="person_inf">
        ...
        <!-- 用于映射关联实体 -->
        <one-to-one name="address"/>
    </class>
</hibernate-mapping>

Address.hbm.xml

<hibernate-mapping package="org.crazyit.app.domain">
    <!-- Address持久化类-->
    <class name="Address" table="address_inf">
        <!-- 映射标识属性addressId -->
        <id name="addressId" column="address_id">
            <!-- 指定foreign主键生成器策略-->
            <generator class="foreign">
                <!-- 指定该主键值将根据person属性引用的
                    关联实体的主键来生成-->
                <param name="property">person</param>
            </generator>
        </id>
        <!-- 映射普通属性addressdetail -->
        <property name="addressDetail"/>
        <!-- 用于映射关联实体 -->
        <one-to-one name="person"/>
    </class>
</hibernate-mapping>

1-7-3 有连接表的双向 1-1 关联(不推荐)

这里写图片描述

Person.hbm.xml

<hibernate-mapping package="org.crazyit.app.domain">
    <!-- 映射Person持久化类-->
    <class name="Person" table="person_inf">
        ...
        <!-- 使用join元素强制使用连接表 -->
        <join table="person_address" inverse="true">
            <!-- 映射连接表中参照本实体主键的外键列 -->
            <key column="person_id" unique="true"/>
            <!-- 映射1-1关联实体 -->
            <many-to-one name="address" class="Address"
                column="address_id" unique="true"/>
        </join>
    </class>
</hibernate-mapping>

Address.hbm.xml

<hibernate-mapping package="org.crazyit.app.domain">
    <!-- Address持久化类-->
    <class name="Address" table="address_inf">
        ...
        <!-- 使用join元素强制使用连接表 -->
        <join table="person_address" optional="true">
            <!-- 映射连接表中参照本实体主键的外键列 -->
            <key column="address_id" unique="true"/>
            <!-- 映射1-1关联实体 -->
            <many-to-one name="person" class="Person"
                column="person_id" unique="true"/>
        </join>
    </class>
</hibernate-mapping>

这里写图片描述

1-8 组合属性包含的关联实体

Address 当作组件属性来处理。
Person.java

public class Person
{
    //标识属性
    private Integer id;
    //Person的name属性
    private String name;
    //保留Person的age属性
    private int age;
    //定义一个组件属性
    private Address address;
    ...
    //address属性的setter和getter方法
    public void setAddress(Address address)
    {
        this.address = address;
    }
    public Address getAddress()
    {
        return this.address;
    }
}

Address.java

public class Address
{
    //标识属性
    private int addressId;
    //地址详细信息
    private String addressDetail;
    //定义引用包含实体的属性
    private Person person;
    //定义保留关联实体的Set
    private Set<School> schools = new HashSet<School>();

    //无参数的构造器
    public Address()
    {
    }
    //初始化addressDetail属性的构造器
    public Address(String addressDetail)
    {
        this.addressDetail = addressDetail;
    }
    ...
    //schools属性的setter和getter方法
    public void setSchools(Set<School> schools)
    {
        this.schools = schools;
    }
    public Set<School> getSchools()
    {
        return this.schools;
    }
}

School.java

public class School
{
    //定义该学校实体的id属性
    private Integer id;
    //定义该学校的name属性
    private String name;

    //无参数的构造器
    public School()
    {
    }
    //初始化全部属性的构造器
    public School(String name)
    {
        this.name = name;
    }
    ...
}

Person.hbm.xml

<hibernate-mapping package="lee">
    <!-- 映射Person持久化类-->
    <class name="Person" table="person_inf">
        <!-- 映射标识属性 -->
        <id name="id" column="person_id">
            <!-- 定义主键生成器策略 -->
            <generator class="identity"/>
        </id>
        <!-- 用于映射普通属性 -->
        <property name="name" type="string"/>
        <property name="age" type="int"/>
        <!-- 映射组件元素 -->
        <component name="address" class="Address">
            <!-- 映射组件的person属性指向包含实体 -->
            <parent name="person"/>
            <!-- 映射普通属性 -->
            <property name="addressDetail"/>
            <!-- 映射集合属性,集合元素是其他持久化实体
                没有指定cascade属性 -->
            <set name="schools">
                <!-- 指定关联的外键列 -->
                <key column="address_id"/>
                <!-- 用以映射到关联类属性 -->
                <one-to-many class="School"/>
            </set>
        </component>
    </class>
</hibernate-mapping>

没有 Address.hbm.xml 文件

PersonManager.java

public class PersonManager
{
    public static void main(String[] args)
    {
        PersonManager mgr = new PersonManager();
        mgr.testPerson();
        HibernateUtil.sessionFactory.close();
    }
    //保存Person和Address对象
    private void testPerson()
    {
        Session session = HibernateUtil.currentSession();
        Transaction tx = session.beginTransaction();
        //创建一个Person对象
        Person p = new Person();
        //设置Person的Name为Yeeku字符串
        p.setName("Yeeku");
        p.setAge(29);
        session.save(p);
        //创建一个Address对象
        Address a = new Address("广州天河");
        //设置Person对象的Address属性
        p.setAddress(a);
        //创建2个School对象
        School s1 = new School("疯狂Java项目冲刺班");
        School s2 = new School("疯狂Java训练营");
        //保存2个School实体
        session.save(s1);
        session.save(s2);
        //设置Address对象和两个School的关联关系
        a.getSchools().add(s1);
        a.getSchools().add(s2);
        tx.commit();
        HibernateUtil.closeSession();
    }
}

1-9 基于复合主键的关联关系(不推荐)

Person.java

public class Person implements java.io.Serializable
{
    //定义first属性,作为标识属性的成员
    private String first;
    //定义last属性,作为标识属性的成员
    private String last;
    //普通属性age
    private int age;
    //记录关联实体
    private Set<Address> addresses = new HashSet<Address>();
    ...
    //重写equals方法,根据first、last进行判断
    public boolean equals(Object obj)
    {
        if (this == obj)
        {
            return true;
        }
        if (obj != null 
&& obj.getClass() == Person.class)
        {
            Person target = (Person)obj;
            if (target.getFirst().equals(getFirst())
                && target.getLast().equals(getLast()))
            {
                return true;
            }
        }
        return false;
    }

    //重写hashCode方法,根据first、last计算hashCode值
    public int hashCode()
    {
        return getFirst().hashCode() * 13 + getLast().hashCode();
    }
}

Address.java

public class Address
{
    //标识属性
    private int addressId;
    //地址详细信息
    private String addressDetail;
    //记录关联实体的person属性
    private Person person;

    //无参数的构造器
    public Address()
    {
    }
    //初始化全部属性的构造器
    public Address(String addressDetail)
    {
        this.addressDetail = addressDetail;
    }
    ...
    //person属性的setter和getter方法
    public void setPerson(Person person)
    {
        this.person = person;
    }
    public Person getPerson()
    {
        return this.person;
    }
}

这里写图片描述

Person.hbm.xml

<hibernate-mapping package="org.crazyit.app.domain">
    <class name="Person" table="person_inf">
        <!-- 直接使用composite-id映射多列联合主键 -->
        <composite-id>
            <!-- 映射组件主键里的各属性 -->
            <key-property name="first" type="string"/>
            <key-property name="last" type="string"/>
        </composite-id>
        <!-- 映射普通属性 -->
        <property name="age" type="int"/>
        <!-- 映射关联实体 -->
        <set name="addresses" inverse="true"
            cascade="all">
            <key>
                <!-- 映射两个外键列 -->
                <column name="first"/>
                <column name="last"/>
            </key>
            <one-to-many class="Address"/>
        </set>
    </class>
</hibernate-mapping>

Address.hbm.xml

<hibernate-mapping package="org.crazyit.app.domain">
    <!-- Address持久化类-->
    <class name="Address" table="address_inf">
        <!-- 映射标识属性addressId -->
        <id name="addressId" column="address_id">
            <!-- 指定主键生成器策略 -->
            <generator class="identity"/>
        </id>
        <!-- 映射普通属性addressdetail -->
        <property name="addressDetail"/>
        <!-- 映射关联实体 -->
        <many-to-one name="person" class="Person"
            not-null="true">
            <!-- 映射外键列 -->
            <column name="first"/>
            <column name="last"/>
        </many-to-one>
    </class>
</hibernate-mapping>

1-10 复合主键的成员属性为关联实体(不推荐)

这里写图片描述

Order.java

public class Order
{   
    //标识属性
    private Integer orderId;
    //订单日期
    private Date orderDate;
    //关联的的订单项
    private Set<OrderItem> items = new HashSet<OrderItem>();

    //无参数的构造器
    public Order()
    {
    }
    //初始化全部属性的构造器
    public Order(Date orderDate)
    {
        this.orderDate = orderDate;
    }
    ...
    //items属性的setter和getter方法
    public void setItems(Set<OrderItem> items)
    {
        this.items = items;
    }
    public Set<OrderItem> getItems()
    {
        return this.items;
    }
}

Product.java

public class Product
{
    //标识属性
    private Integer productId;
    //产品名
    private String name;

    //无参数的构造器
    public Product()
    {
    }
    //初始化全部属性的构造器
    public Product(String name)
    {
        this.name = name;
    }
    ...
}

OrderItem.java

public class OrderItem implements java.io.Serializable
{
    //下面3个属性将作为联合主键
    //定义关联的Order实体
    private Order order;
    //定义关联的Product实体
    private Product product;
    //该订单项订购的产品数量
    private int count;

    //无参数的构造器
    public OrderItem()
    {
    }
    //初始化全部属性的构造器
    public OrderItem(Order order , Product product , int count)
    {
        this.order = order;
        this.product = product;
        this.count = count;
    }
    ...
}

这里写图片描述

OrderItem.hbm.xml

<hibernate-mapping package="org.crazyit.app.domain">
    <class name="OrderItem" table="order_item_inf">
        <!-- 映射复合主键 -->
        <composite-id>
            <key-many-to-one name="product" 
                class="Product" column="product_id" />
            <key-many-to-one name="order"
                class="Order" column="order_id" />
            <key-property name="count" type="int"/>
        </composite-id>
    </class>
</hibernate-mapping>

1-11 持久化的传播性

这里写图片描述

<!-- 指定 persist() 操作将级联到关联实体 -->
<one-to-many name="person" cascade="persist" />

<!-- 指定persist()、delete()和lock()操作将级联到关联实体 -->
<one-to-one name="person" cascade="persist, delete, lock" />

这里写图片描述
这里写图片描述

2 继承映射

Person.java

public class Person
{
    //标识属性
    private Integer id;
    //定义该Person实体的名字属性
    private String name;
    //定义该Person实体的性别属性
    private char gender;
    //定义该Person实体的组件属性:address
    private Address address;
    ...
}

Address.java

public class Address
{
    //定义该Address的详细信息
    private String detail;
    //定义该Address的邮编信息
    private String zip;
    //定义该Address的国家信息
    private String country; //无参数的构造器
    public Address()
    {
    }
    //初始化全部属性的构造器
    public Address(String detail , String zip , String country)
    {
        this.detail = detail;
        this.zip = zip;
        this.country = country;
    }
    ...
}

Customer.java

//顾客类继承了Person类
public class Customer extends Person
{
    //顾客的评论信息
    private String comments;
    //和员工保持关联关系的属性
    private Employee employee;
    //无参数的构造器
    public Customer()
    {
    }
    //初始化comments属性的构造器
    public Customer(String comments)
    {
        this.comments = comments;
    }
    ...
}

Employee.java

//员工类继承了Person类
public class Employee extends Person
{
    //定义该员工的职位属性
    private String title;
    //定义该员工的工资属性
    private double salary;
    //和顾客保持关联关系的属性
    private Set<Customer> customers = new HashSet<Customer>();
    //和经理保持关联关系的属性
    private Manager manager;

    //无参数的构造器
    public Employee()
    {
    }
    //初始化全部属性的构造器
    public Employee(String title , double salary)
    {
        this.title = title;
        this.salary = salary;
    }
    ...
}

Manager.java

//经理类继承员工类
public class  Manager extends Employee
{
    //定义经理管辖部门的属性
    private String department;
    //和员工保持关联关系的属性
    private Set<Employee> employees = new HashSet<Employee>();

    //无参数的构造器
    public Manager()
    {
    }
    //初始化全部属性的构造器
    public Manager(String department)
    {
        this.department = department;
    }
    ...
}

这里写图片描述

2-1 采用 subclass 元素的继承映射

  • 整个继承树的所有实例都保存在同一个表中。
  • 表中需要增加一个辨别性列(discriminator)来区分每行记录归属于哪个类的实例。
  • 使用< subclass…/ >来映射子持久化类。
  • 每个类映射中都需要指定辨别者列的值。
  • 子类增加的属性不可以有非空约束。

Person.hbm.xml

<hibernate-mapping package="org.crazyit.app.domain">
    <!-- 映射Person类 -->
    <class name="Person" table="person_inf" discriminator-value="普通人">
        <!-- 映射标识属性 -->
        <id name="id" column="person_id">
            <!-- 使用identity的主键生成器策略 -->
            <generator class="identity"/>
        </id>
        <!-- 映射辨别者列 -->
        <discriminator column="wawa" type="string"/>
        <!-- 如下映射两个基本属性 -->
        <property name="name" length="80"/>
        <property name="gender"/>
        <!-- 下面映射了一个组件属性 -->
        <component name="address">
            <!-- 映射组件属性的三个成员属性 -->
            <property name="detail"/>
            <property name="zip"/>
            <property name="country"/>
        </component>
        <!-- 使用subclass元素映射Person类的子类Employee -->
        <subclass name="Employee" discriminator-value="雇员">
            <!-- 映射两个基本属性 -->
            <property name="title" />
            <property name="salary" />
            <!-- 映射N-1的关联映射 -->
            <many-to-one name="manager" column="manager_id"/>
            <!-- 映射与Customer类之间的1-N关联 -->
            <set name="customers" inverse="true">
                <key column="empoyee_id"/>
                <one-to-many class="Customer"/>
            </set>
            <!-- 使用subclass元素映射Employee类的子类Manager -->
            <subclass name="Manager" discriminator-value="经理">
                <!-- 映射Manager类的基本属性department -->
                <property name="department"/>
                <!-- 映射Manager类的关联实体:Employee -->
                <set name="employees" inverse="true">
                    <key column="manager_id"/>
                    <one-to-many class="Employee"/>
                </set>
            </subclass>
        </subclass>
        <!-- 使用subclass映射Person的Customer子类 -->
        <subclass name="Customer" discriminator-value="顾客">
            <!-- 映射Customer类的comments属性 -->
            <property name="comments"/>
            <!-- 映射Customer和Employee的关联关系 -->
            <many-to-one name="employee" column="empoyee_id"/>
        </subclass>
    </class>
</hibernate-mapping>

这里写图片描述
这里写图片描述

2-2 采用 joined-subclass 元素的继承映射(纵向分割映射)

  • 父类实例保存在父类表中,子类实例由父类表和子类表共同存储。
  • 子类和父类共有属性保存在父类表中,子类新增加的元素保存在子类表中。
  • 无需使用辨别者列。但需要为每个子类使用元素映射共同主键-这个主键列还将参照父类表的主键列。
  • 子类增加的属性可以有非空约束。

这里写图片描述

Person.hbm.xml

<hibernate-mapping package="org.crazyit.app.domain">
    <!-- 映射Person类 -->  
    <class name="Person" table="person_inf">
        <!-- 映射标识属性 -->
        <id name="id" column="person_id">
            <!-- 使用identity的主键生成器策略 -->
            <generator class="identity"/>
        </id>
        <!-- 如下映射两个基本属性 -->
        <property name="name" length="80"/>
        <property name="gender"/>
        <!-- 下面映射了一个组件属性 -->
        <component name="address">
            <!-- 映射组件属性的三个成员属性 -->
            <property name="detail"/>
            <property name="zip"/>
            <property name="country"/>
        </component>
        <!-- 使用joined-subclass元素映射Person类的Employee子类 -->
        <joined-subclass name="Employee" table="employee_inf">
            <!-- 必须使用key元素映射父子类的共有主键 -->
            <key column="employee_id"/>
            <!-- 映射Employee类的两个普通属性 -->
            <property name="title" not-null="true"/>
            <property name="salary" not-null="true"/>
            <!-- 映射Employee类与Manager类之间的N-1关联-->
            <many-to-one name="manager" column="manager_id"/>
            <!-- 映射Employee类与Customer类之间的1-N关联-->
            <set name="customers" inverse="true">
                <key column="empoyee_id"/>
                <one-to-many class="Customer"/>
            </set>
            <!-- 使用joined-subclass元素映射Employee类的Manager子类 -->
            <joined-subclass name="Manager" table="manager_inf">
                <!-- 必须使用key元素映射父子类的共有主键 -->
                <key column="manager_id"/>
                <!-- 映射Manager类的department属性 -->
                <property name="department"/>
                <!-- 映射Employee类与Manager类之间的1-N关联-->
                <set name="employees" inverse="true">
                    <key column="manager_id"/>
                    <one-to-many class="Employee"/>
                </set>
            </joined-subclass>
        </joined-subclass>
        <!-- 使用joined-subclass元素映射Person类的Customer子类 -->
        <joined-subclass name="Customer" table="customer_inf">
            <!-- 必须使用key元素映射父子类的共有主键 -->
            <key column="customer_id"/>
            <property name="comments" not-null="true"/>
            <!-- 映射Employee类与Customer类之间的1-N关联-->
            <many-to-one name="employee" column="empoyee_id"
                not-null="true"/>
        </joined-subclass>
    </class>
</hibernate-mapping>

这里写图片描述
这里写图片描述

2-3 采用 union-subclass 元素的继承映射(横向分割映射)

  • 子类实例的数据仅保存在子类表中。
  • 既不需要辨别者列,也不需要< key…/ >元素来映射共有主键。
  • 子类增加的属性可以有非空约束。
  • 比较简洁,推荐使用。

Person.hbm.xml

<hibernate-mapping package="org.crazyit.app.domain">
    <!-- 映射Person类 -->  
    <class name="Person" table="person_inf">
        <!-- 映射标识属性 -->
        <id name="id" column="person_id">
            <!-- 不能使用identity的主键生成器策略
                ,所以使用hilo主键生成器策略-->
            <generator class="hilo"/>
        </id>
        <!-- 如下映射两个基本属性 -->
        <property name="name" length="80"/>
        <property name="gender"/>
        <!-- 下面映射了一个组件属性 -->
        <component name="address">
            <!-- 映射组件属性的三个成员属性 -->
            <property name="detail"/>
            <property name="zip"/>
            <property name="country"/>
        </component>
        <!-- 使用union-subclass元素映射Person类的Employee子类 -->
        <union-subclass name="Employee" table="employee_inf">
            <!-- 映射Employee类的两个普通属性 -->
            <property name="title" not-null="true"/>
            <property name="salary" not-null="true"/>
            <!-- 映射Employee类与Manager类之间的N-1关联-->
            <many-to-one name="manager" column="manager_id"/>
            <!-- 映射Employee类与Customer类之间的1-N关联-->
            <set name="customers" inverse="true">
                <key column="empoyee_id"/>
                <one-to-many class="Customer"/>
            </set>
            <!-- 使用union-subclass元素映射Employee类的Manager子类 -->
            <union-subclass name="Manager" table="manager_inf">
                <!-- 映射Manager类的department属性 -->
                <property name="department"/>
                <!-- 映射Employee类与Manager类之间的1-N关联-->
                <set name="employees" inverse="true">
                    <key column="manager_id"/>
                    <one-to-many class="Employee"/>
                </set>
            </union-subclass>
        </union-subclass>
        <!-- 使用union-subclass元素映射Person类的Customer子类 -->
        <union-subclass name="Customer" table="customer_inf">
            <property name="comments" not-null="true"/>
            <!-- 映射Employee类与Customer类之间的1-N关联-->
            <many-to-one name="employee" column="empoyee_id"
                not-null="true"/>
        </union-subclass>
    </class>
</hibernate-mapping>

这里写图片描述
这里写图片描述
这里写图片描述

这里写图片描述
这里写图片描述

3 Hibernate 的批量处理

User.java

public class User
{
    //定义标识属性
    private int id;
    //定义User实例的名称
    private String name;
    //定义User实例的年龄
    private int age;
    //定义User实例的国别
    private String nationality;

    //无参数的构造器
    public User()
    {
    }
    //初始化全部属性的构造器
    public User(int id , String name , int age , String nationality)
    {
        this.id = id;
        this.name = name;
        this.age = age;
        this.nationality = nationality;
    }
    ...
}

HibernateUtil.java

public class HibernateUtil
{
    public static final SessionFactory sessionFactory;

    static
    {
        try
        {
            //采用默认的hibernate.cfg.xml来启动一个Configuration的实例
            Configuration configuration=new Configuration().configure();
            //由Configuration的实例来创建一个SessionFactory实例
            sessionFactory = configuration.buildSessionFactory();
        }
        catch (Throwable ex)
        {
            // Make sure you log the exception, as it might be swallowed
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    //ThreadLocal并不是线程本地化的实现,而是线程局部变量。也就是说每个使用该变量的线程都必须为
    //该变量提供一个副本,每个线程改变该变量的值仅仅是改变该副本的值,而不会影响其他线程的该变量
    //的值.

    //ThreadLocal是隔离多个线程的数据共享,不存在多个线程之间共享资源,因此不再需要对线程同步    
    public static final ThreadLocal session = new ThreadLocal();

    public static Session currentSession() throws HibernateException
    {
        Session s = (Session) session.get();
        //如果该线程还没有Session,则创建一个新的Session
        if (s == null)
        {
            s = sessionFactory.openSession();
            //将获得的Session变量存储在ThreadLocal变量session里
            session.set(s);
        }
        return s;
    }

    public static void closeSession() throws HibernateException 
    {
        Session s = (Session) session.get();
        if (s != null)
        s.close();
        session.set(null);
    }
}

3-1 批量插入

如果要将100000条记录插入数据库,Hibernate 通常会很低效。

一个简单的策略可以设置一个定时器,定时将 Session 缓存的数据刷入数据库,而不是一直在 Session 层缓存。
UserManager.java

public class UserManager
{
    public static void main(String[] args)throws Exception
    {
        UserManager mgr = new UserManager();
        mgr.testUser();
        HibernateUtil.sessionFactory.close();
    }

    private void testUser()throws Exception
    {
        //打开Session
        Session session = HibernateUtil.currentSession();
        //开始事务
        Transaction tx = session.beginTransaction();
        //循环100000次,插入100000条记录
        for (int i = 0 ; i < 100000 ; i++ )
        {
            //创建User实例
            User u1 = new User();
            u1.setName("xxxxx" + i);
            u1.setAge(i);
            u1.setNationality("china");
            //在Session级别缓存User实例
            session.save(u1);
            //每当累加器是20的倍数时,将Session中数据刷入数据库,
            //并清空Session缓存。
            if (i % 20 == 0)
            {
                session.flush();
                session.clear();
            }
        }
        //提交事务
        tx.commit();
        //关闭事务
        HibernateUtil.closeSession();
    }
}

除了 Session 级别的缓存,还应关闭 SessionFactory 的二级缓存:

hibernate.cache.use_second_level_cache false

这里写图片描述

3-2 批量更新

UserManager.java

public class UserManager
{
    public static void main(String[] args)
        throws Exception
    {
        UserManager mgr = new UserManager();
        mgr.testUser();
        HibernateUtil.sessionFactory.close();
    }

    private void testUser()throws Exception
    {
        //打开Session
        Session session = HibernateUtil.currentSession();
        //开始事务
        Transaction tx = session.beginTransaction();
        //查询出User表中的所有记录
        ScrollableResults users = session.createQuery("from User")
            .setCacheMode(CacheMode.IGNORE)
            .scroll(ScrollMode.FORWARD_ONLY);
        int count=0;
        //遍历User表中的全部记录
        while ( users.next() )
        {
            User u = (User) users.get(0);
            u.setName("新用户名" + count);
            //当count为20的倍数时,
            //将更新的结果从Session中flush到数据库。
            if ( ++count % 20 == 0 ) 
            {
                session.flush();
                session.clear();
            }
        }
        tx.commit();
        HibernateUtil.closeSession();
    }
}

通常这种更新效果不是很好,效率不是很高。除了先执行查询后更新数据,每行记录也是逐步更新的。

3-3 DML 风格的批量更新/删除

批量 UPDATE 和 DELETE 语句的语法格式如下:

UPDATE | DELETE FROM? <ClassName> [WHERE WHERE_CONDITIONS]

这里写图片描述

3-3-1 HQL UPDATE

UserManager.java

public class UserManager
{
    public static void main(String[] args)throws Exception
    {
        UserManager mgr = new UserManager();
        mgr.testUser();
        HibernateUtil.sessionFactory.close();
    }

private void testUser()throws Exception
{
    //打开Session
    Session session = HibernateUtil.currentSession();
    //开始事务
    Transaction tx = session.beginTransaction();
    //定义批量更新的HQL语句
    String hqlUpdate = "update User set name = :newName";
    //执行更新
    int updatedEntities = session.createQuery( hqlUpdate )
        .setString( "newName", "新名字" )
        .executeUpdate();
    //提交事务
    tx.commit();
    HibernateUtil.closeSession();
}
}

3-3-2 HQL DELETE

UserManager.java

public class UserManager
{
    public static void main(String[] args)throws Exception
    {
        UserManager mgr = new UserManager();
        mgr.testUser();
        HibernateUtil.sessionFactory.close();
    }

    private void testUser()throws Exception
    {
        //打开Session实例
        Session session = HibernateUtil.currentSession();
        //开始事务
        Transaction tx = session.beginTransaction();
        //定义批量删除的HQL语句
        String hqlUpdate = "delete User";
        //执行批量删除
        int updatedEntities = session.createQuery(hqlUpdate)
            .executeUpdate();
        //提交事务
        tx.commit();
        //关闭Session
        HibernateUtil.closeSession();
    }
}
  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值