1.1Hibernate的持久化类状态

1.1.1Hibernate的持久化类状态

  • 持久化类:就是一个实体类和数据库表建立了映射关系。

    • Hibernate为了方便的管理持久化类,将持久化类分成了三种状态。
  • 瞬时态(临时态)  transient
  • 持久态  persistent
  • 脱管态(游离态)  detached  

1.1.2 三种持久化对象的状态

  • 瞬时态  Transient

    • 不存在持久化标识OID,尚未与Hibernate的session关联,被认为处于瞬时态,失去引用将被JVM回收。换句话说,持久化对象没有唯一标识OID,没有纳入Session的管理。
  • 持久态  Persistent
    • 存在持久化标识OID,与当前session有关联,并且相关联的session没有关闭,并且事务没有提交。换句话说,持久化对象有唯一标识OID,已经纳入Session的管理,并且持久化持久态对象具有自动更新数据库的能力。
  • 脱管态 Detached
    • 存在持久化标识OID,但是没有与当前的session关联,脱管状态改变Hibernate不能检测到。换句话说,持久化对象有唯一的标识OID,没有纳入到session管理。

1.1.3区分三种持久化对象的状态

新建cn.hibernate3.demo1.Book.java类

package cn.hibernate3.demo1;
/**
 * 实体类
 * @author love
 */
public class Book {
    private Integer id;
    private String name;
    private Double price;
    private String author;
    public Integer getId() {
        return id;
    }
    public void setId(Integer id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Double getPrice() {
        return price;
    }
    public void setPrice(Double price) {
        this.price = price;
    }
    public String getAuthor() {
        return author;
    }
    public void setAuthor(String author) {
        this.author = author;
    }

}

配置Book.hbm.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<!-- 引入约束 -->
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="cn.hibernate3.demo1.Book" table="book">
        <id name="id" column="id">
            <generator class="native"/>
        </id>
        <property name="name" column="name" type="java.lang.String"/>
        <property name="price" column="price" type="java.lang.Double"/>
        <property name="author" column="author" type="java.lang.String"/>
    </class>
</hibernate-mapping>

配置核心映射文件(hibernate.cfg.xml文件)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
    <!-- 配置数据库的基本信息 -->
    <!-- 驱动的名称 -->
    <property name="hibernate.connection.driver_class">
        com.mysql.jdbc.Driver
    </property>
    <!-- 访问数据库的url -->
    <property name="hibernate.connection.url">
        jdbc:mysql:///hibernate_day02
    </property>
    <!-- 用户名 -->
    <property name="hibernate.connection.username">root</property>
    <!-- 密码 -->
    <property name="hibernate.connection.password">root</property>
    <!-- 方言 -->
    <property name="hibernate.dialect">
        org.hibernate.dialect.MySQLDialect
    </property>
    <!-- C3P0连接池设定-->
    <!-- 使用c3po连接池  配置连接池提供的供应商-->
    <property name="connection.provider_class">
        org.hibernate.connection.C3P0ConnectionProvider
    </property>
    <!--在连接池中可用的数据库连接的最少数目 -->
    <property name="c3p0.min_size">5</property>
    <!--在连接池中所有数据库连接的最大数目  -->
    <property name="c3p0.max_size">20</property>
    <!--设定数据库连接的过期时间,以秒为单位,
        如果连接池中的某个数据库连接处于空闲状态的时间超过了timeout时间,就会从连接池中清除 -->
    <property name="c3p0.timeout">120</property>
    <!--每3000秒检查所有连接池中的空闲连接 以秒为单位-->
    <property name="c3p0.idle_test_period">3000</property>
    <!-- 可选配置 -->
    <!-- 显示SQL -->
    <property name="hibernate.show_sql">true</property>
    <!-- 格式化SQL -->
    <property name="hibernate.format_sql">true</property>
    <!-- hbm:映射 2:to ddl:create drop alter -->
    <property name="hibernate.hbm2ddl.auto">update</property>
    <mapping resource="cn/hibernate3/demo1/Book.hbm.xml" />
</session-factory>
</hibernate-configuration>

新建HibernateUtils.java类

package cn.utils;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

/**
 * Hibernate抽取工具类
 */
public class HibernateUtils {
    private static Configuration configuration;
    private static SessionFactory sessionFactory;
    static{
        configuration = new Configuration().configure();
        sessionFactory = configuration.buildSessionFactory();
    }

    public static Session openSession(){
        return sessionFactory.openSession();
    }

    public static void main(String[] args) {
        openSession();
    }
}

运行HibernateUtils工具类,即可发现创建了book表

测试

package cn.hibernate3.demo1;

import org.hibernate.Session;
import org.hibernate.Transaction;
import org.junit.Test;

import cn.utils.HibernateUtils;

public class HibernateTest1 {
    @Test
    //区分持久化对象的三种装填
    public void demo1(){
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();

        //向数据库中保存一本图书
        Book book = new Book();//瞬时态:没有唯一标识OID,没有与session关联
        book.setName("Hibernate开发");
        book.setAuthor("哈哈");
        book.setPrice(65.0);

        session.save(book);//持久态:有唯一标识OID,与session关联

        tx.commit();
        session.close();

        book.setName("Struts2开发");//脱管态:有唯一标识,没有与session关联

    }
}

 1.1.4三种对象的转换

  • 瞬时态:

    • 获取瞬时态  Book book = new Book();
    • 瞬时态-->持久态  save(book);
    • 瞬时态-->脱管态  book.setId(1);
  • 持久态
    • 获取持久态  Book book = (Book)session.get(Book.class,1);

      • get()、load()、find()、iterate();
    • 持久态-->瞬时态  delete(book);
    • 持久态-->脱管态  
      • session.close();
      • close()/clear()/evict(Object obj)。
  • 脱管态
    • 获取脱管态  Book book = new Book();book.setId(1);

      • 脱管态-->持久态  session.update(book);
      • 脱管态-->瞬时态  book.setId(null);

            

1.1.5持久态对象有自动更新数据库的能力

   @Test
    //测试持久态对象有自动更新数据库的能力
    public void demo2(){
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();

        //获取一个持久态对象
        Book book = (Book) session.get(Book.class, 1);
        book.setName("Struts2开发");

        tx.commit();
        session.close();

    }    

自动更新数据库的能力依赖了Hibernate的一级缓存。

1.2Hibernate的一级缓存

1.2.1Hibernate的一级缓存

  • 什么是缓存?

    • 缓存将数据库/硬盘上文件中数据,加入到缓存中(就是内存中的一块的空间),当再次使用的时候,可以直接从内存中获取。
  • 缓存的好处?
    • 提升程序运行的效率,缓存技术是Hibernate的一个优化的手段。
  • Hibernate分为两个基本的缓存?
    • 一级缓存:session级别的缓存。一级缓存和session的生命周期一直。自带的,不可卸载。
    • 二级缓存:sessionFactory级别的缓存。不是自带的。

1.2.2证明Hibernate一级缓存的存在

  @Test
    //证明一级缓存的存在
    public void demo3(){
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();

        //获取一个持久态对象
        Book book = (Book) session.get(Book.class, 1);//发送SQL语句
        System.out.println(book);

        Book book2 = (Book) session.get(Book.class, 1);//不发送SQL语句
        System.out.println(book2);

        tx.commit();
        session.close();

    }

1.2.3理解session缓存(一级缓存)

  • 在session接口的实现中包含了一系列的java集合,这些java集合构成了session缓存,只要session实例没有结束生命周期,存放在它缓存中的对象也不会结束生命周期。
  • 当session的save()方法持久化一个对象的时候,该对象被装载入缓存,以后即使程序中不再引用该对象,只要缓存不清空,该对象仍然处于生命周期中。当试图get()、load()对象时,会判断缓存中是否存在该对象,有则返回,此时不查询数据库,没有再查询数据库。
  • session能够在某些时间点,按照缓存中对象的变化来执行相关的SQL语句,来同步更新数据库,这一过程被称为刷出缓存(flush)。
  • 默认情况下,session在以下时间点刷出缓存:
    • 当应用程序调用Transaction的commit()方法的时候,该方法会先刷出缓存,然后再向数据库提交事务。
    • 当应用程序执行一些查询操作的时候,如果缓存中持久化对象的属性已经发生了变化,会先刷出缓存,以保证查询结果能够反映持久化对象的最新状态。
    • 调用session的flush()方法。

1.2.4Hibernate一级缓存的快照区

  @Test
    //快照区
    public void demo2(){
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();

        //获取一个持久态对象
        Book book = (Book) session.get(Book.class, 1);
        book.setName("Spring开发");

        tx.commit();
        session.close();

    }

分析:

  • 情况一:数据库中的数据和要修改的数据不一致

  • 情况二:当数据库中的数据和要修改的数据一致。

 

【总结】向一级缓存中存入数据的时候,放入一级缓存区和一级缓存快照区,当更新了一级缓存的数据的时候,事务一旦提交,会对比一级缓存和快照区,如果数据一致,不更新;如果数据不一致,就更新数据库。       

1.2.5 Hibernate管理一级缓存

  • 一级缓存是与session的生命周期相关的,session的生命周期结束,一级缓存就消失了。
  • clear()/evict()/flush()/refresh()管理一级缓存。
  • clear():清空一级缓存的所有的对象。
  • evict(Object obj):清空一级缓存中的某个对象。
  • flush():刷出缓存。
  • refresh(Object obj):将快照区的数据,覆盖了一级缓存的数据。

1.2.6Hibernate一级缓存刷出时机(了解)

  • FlushMode:

    • 常量

      • ALWAYS:每次查询的时候都会刷出,手动调用flush,事务提交的时候。
      • AUTO:默认值,有些查询会刷出,手动调用flush,事务提交的时候。
      • COMMIT:在事务提交的时候,手动调用flush的时候。
      • MANUAL:只有在手动调用flush才会刷出。
    • 严格程序: MANUAL> COMMIT>AUTO>ALWAYS。

1.3操作持久化对象的方法   

  • save()

    • 保存一条记录。
    • 将瞬时态对象变为持久态对象。
  • update()
    • 更新一条记录。
    • 将脱管态对象变为持久态对象。
  • saveOrUpdate()
    • 根据对象的状态的不同执行save()或update()方法。

      • 如果对象是一个瞬时态对象,执行save()方法。
      • 如果对象是一个脱管态对象,执行update()方法。
  • delete()
    • 将持久态对象变为瞬时态对象。
  • get()/load()
    • 获取一个持久化对象。

1.4Hibernate关联关系的映射            

 1.4.1实体之间的关系

  • 一对多:

    • 一个用户,生成多个订单,每一个订单只能属于一个用户。
    • 建表的原则:
      • 在多的一方创建一个字段,作为外键,指向一的一方的主键。  
  • 多对多:
    • 一个学生,可以选择多门课程,一门课程,可以被多个学生选择。
    • 建表的原则:
      • 创建第三张表,中间表至少有2个字段,分别作为外键指向多对多双方的主键。  
  • 一对一:(特殊,应用最少)
    • 一个公司只能有一个注册的地址,一个注册地址只能被一个公司所使用。
    • 建表的原则:
      • 唯一外键:

        • 一对一的双方,假设一方是多的关系,需要在多的一方创建一个字段作为外键,指向一的一方的主键。但是,在外键上添加一个unique。  
      • 主键对应: 
        • 一对一的双方,通过主键进行关联。

1.4.2Hibernate中一对多的配置

  • 创建实体

    • 创建客户实体
package cn.hibernate3.demo2;

import java.io.Serializable;
import java.util.HashSet;
/**
 * 客户实体
 */
import java.util.Set;
public class Customer implements Serializable{
    private Integer cid;
    private String cname;
    //一个客户有多个订单
    private Set<Order> orders = new HashSet<Order>();
    public Integer getCid() {
        return cid;
    }
    public void setCid(Integer cid) {
        this.cid = cid;
    }
    public String getCname() {
        return cname;
    }
    public void setCname(String cname) {
        this.cname = cname;
    }
    public Set<Order> getOrders() {
        return orders;
    }
    public void setOrders(Set<Order> orders) {
        this.orders = orders;
    }

}
    • 订单实体
package cn.hibernate3.demo2;

import java.io.Serializable;
/**
 * 订单实体
 */
public class Order implements Serializable{
    private Integer oid;
    private String addr;
    //订单属于某一个客户
    private Customer customer ;

    public Integer getOid() {
        return oid;
    }
    public void setOid(Integer oid) {
        this.oid = oid;
    }
    public String getAddr() {
        return addr;
    }
    public void setAddr(String addr) {
        this.addr = addr;
    }
    public Customer getCustomer() {
        return customer;
    }
    public void setCustomer(Customer customer) {
        this.customer = customer;
    }

}
  • 配置映射文件

    • Customer.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- 引入约束 -->
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="cn.hibernate3.demo2.Customer" table="customer">
        <!-- 配置唯一标识 -->
        <id name="cid" column="cid">
            <generator class="native"/>
        </id>
        <!-- 配置普通属性 -->
        <property name="cname" column="cname" type="java.lang.String"/>
        <!-- 建立映射 -->
        <!-- 配置集合 -->
        <!--
            set标签中的name表示关联对象的属性名称

         -->
        <set name="orders" >
            <!-- key标签中的column用来一对多的多的一方的外键 -->
            <key column="cno"/>
            <!-- 配置一个one-to-many -->
            <one-to-many class="cn.hibernate3.demo2.Order"/>
        </set>
    </class>
</hibernate-mapping>
    • Order.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- 引入约束 -->
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="cn.hibernate3.demo2.Order" table="orders">
        <!-- 配置唯一标识 -->
        <id name="oid" column="oid">
            <generator class="native"/>
        </id>
        <!-- 配置普通属性 -->
        <property name="addr" column="addr" type="java.lang.String"/>
        <!-- 建立映射 -->
        <!--
            many-to-one标签
                属性:
                    name:关联对象的属性名称。
                    column:表中外键的名称。
                    class:关联对象的全路径。
         -->
        <many-to-one name="customer" column="cno" class="cn.hibernate3.demo2.Customer"></many-to-one>
    </class>
</hibernate-mapping>
  • 将映射文件放到核心配置文件中
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
    <!-- 配置数据库的基本信息 -->
    <!-- 驱动的名称 -->
    <property name="hibernate.connection.driver_class">
        com.mysql.jdbc.Driver
    </property>
    <!-- 访问数据库的url -->
    <property name="hibernate.connection.url">
        jdbc:mysql:///hibernate_day02
    </property>
    <!-- 用户名 -->
    <property name="hibernate.connection.username">root</property>
    <!-- 密码 -->
    <property name="hibernate.connection.password">root</property>
    <!-- 方言 -->
    <property name="hibernate.dialect">
        org.hibernate.dialect.MySQLDialect
    </property>
    <!-- C3P0连接池设定-->
    <!-- 使用c3po连接池  配置连接池提供的供应商-->
    <property name="connection.provider_class">
        org.hibernate.connection.C3P0ConnectionProvider
    </property>
    <!--在连接池中可用的数据库连接的最少数目 -->
    <property name="c3p0.min_size">5</property>
    <!--在连接池中所有数据库连接的最大数目  -->
    <property name="c3p0.max_size">20</property>
    <!--设定数据库连接的过期时间,以秒为单位,
        如果连接池中的某个数据库连接处于空闲状态的时间超过了timeout时间,就会从连接池中清除 -->
    <property name="c3p0.timeout">120</property>
    <!--每3000秒检查所有连接池中的空闲连接 以秒为单位-->
    <property name="c3p0.idle_test_period">3000</property>
    <!-- 可选配置 -->
    <!-- 显示SQL -->
    <property name="hibernate.show_sql">true</property>
    <!-- 格式化SQL -->
    <property name="hibernate.format_sql">true</property>
    <!-- hbm:映射 2:to ddl:create drop alter -->
    <property name="hibernate.hbm2ddl.auto">update</property>
    <mapping resource="cn/hibernate3/demo2/Customer.hbm.xml" />
    <mapping resource="cn/hibernate3/demo2/Order.hbm2.xml" />

</session-factory>
</hibernate-configuration>
  • 运行HibernateUtils.java类

1.4.3Hibernate中一对多级联保存

@Test
    //向客户表插入一个客户,在订单表中插入两个订单
    public void demo1(){
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();
        //定义客户
        Customer customer = new Customer();
        customer.setCname("郭浩");
        //定义订单
        Order order1 = new Order();
        order1.setAddr("新疆");
        Order order2 = new Order();
        order2.setAddr("江苏");
        //建立关系
        customer.getOrders().add(order1);
        customer.getOrders().add(order2);
        order1.setCustomer(customer);
        order2.setCustomer(customer);

        session.save(customer);
        session.save(order2);
        session.save(order1);

        tx.commit();
        session.close();
    }

1.4.4Hibernate中一对多的级联保存

  • 从1.4.3我们让一方和多方对象产生关联关系,并保存一方和多方对象,那么我们是否只保存一方呢?
  @Test
    //向客户表插入一个客户,在订单表中插入两个订单
    public void demo2(){
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();
        //定义客户
        Customer customer = new Customer();
        customer.setCname("郭浩");
        //定义订单
        Order order1 = new Order();
        order1.setAddr("新疆");
        Order order2 = new Order();
        order2.setAddr("江苏");
        //建立关系
        customer.getOrders().add(order1);
        customer.getOrders().add(order2);
        //保存的时候只保存一方
        session.save(customer);

        tx.commit();
        session.close();
    }

此时,会出现异常。

上面的异常的意思就是对象引用了一个没有保存的瞬时态的对象,换句话说,就是customer对象是持久态的,而order1和order2是瞬时态的对象,Hibernate不允许持久态对象关联瞬时态对象。

  • 没有办法了吗?不是,我们需要使用Hibernate提供的级联保存。

    • 级联:操作当前对象的时候,关联的对象如何处理。
    • 级联的方向性:
      • 保存客户的时候,选择级联订单。
      • 保存订单的时候,选择级联客户。
    • cascade="save-update"。
  @Test
    //级联保存 保存客户的时候,级联订单
    //<set>标签上是客户的关联订单对象的集合,所以在<set>上配置一个属性:cascade
    public void demo3(){
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();
        //定义客户
        Customer customer = new Customer();
        customer.setCname("郭浩");
        //定义订单
        Order order1 = new Order();
        order1.setAddr("新疆");
        Order order2 = new Order();
        order2.setAddr("江苏");
        //建立关系
        customer.getOrders().add(order1);
        customer.getOrders().add(order2);
        //保存的时候只保存一方
        session.save(customer);

        tx.commit();
        session.close();
    }
<?xml version="1.0" encoding="UTF-8"?>
<!-- 引入约束 -->
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="cn.hibernate3.demo2.Customer" table="customer">
        <!-- 配置唯一标识 -->
        <id name="cid" column="cid">
            <generator class="native"/>
        </id>
        <!-- 配置普通属性 -->
        <property name="cname" column="cname" type="java.lang.String"/>
        <!-- 建立映射 -->
        <!-- 配置集合 -->
        <!--
            set标签中的name表示关联对象的属性名称

         -->
        <set name="orders" cascade="save-update">
            <!-- key标签中的column用来一对多的多的一方的外键 -->
            <key column="cno"/>
            <!-- 配置一个one-to-many -->
            <one-to-many class="cn.hibernate3.demo2.Order"/>
        </set>
    </class>
</hibernate-mapping>

  • 如何我从多的一方呢?

    • 在<many-to-one>上配置cascade="save-update"
  @Test
    public void demo4(){
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();
        //定义客户
        Customer customer = new Customer();
        customer.setCname("郭浩");
        //定义订单
        Order order1 = new Order();
        order1.setAddr("新疆");
        Order order2 = new Order();
        order2.setAddr("江苏");
        //建立关系
        customer.getOrders().add(order1);
        customer.getOrders().add(order2);
        order1.setCustomer(customer);
        order2.setCustomer(customer);

        session.save(order1);
        session.save(order2);

        tx.commit();
        session.close();
    }
<?xml version="1.0" encoding="UTF-8"?>
<!-- 引入约束 -->
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="cn.hibernate3.demo2.Order" table="orders">
        <!-- 配置唯一标识 -->
        <id name="oid" column="oid">
            <generator class="native"/>
        </id>
        <!-- 配置普通属性 -->
        <property name="addr" column="addr" type="java.lang.String"/>
        <!-- 建立映射 -->
        <!--
            many-to-one标签
                属性:
                    name:关联对象的属性名称。
                    column:表中外键的名称。
                    class:关联对象的全路径。
         -->
        <many-to-one cascade="save-update" name="customer" column="cno" class="cn.hibernate3.demo2.Customer"></many-to-one>
    </class>
</hibernate-mapping>

1.4.5Hibernate中一对多级联删除 

  • 默认情况下,将外键设置为null,删除数据记录。

  @Test
    //默认情况下,将外键设置为null,然后删除记录
    public void demo5(){
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();

        Customer customer = (Customer) session.get(Customer.class, 1);
        session.delete(customer);

        tx.commit();
        session.close();
    }
    

  • 添加cascade="delete"。
  @Test
    //级联删除
    public void demo5(){
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();

        Customer customer = (Customer) session.get(Customer.class, 1);
        session.delete(customer);

        tx.commit();
        session.close();
    }
<?xml version="1.0" encoding="UTF-8"?>
<!-- 引入约束 -->
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="cn.hibernate3.demo2.Customer" table="customer">
        <!-- 配置唯一标识 -->
        <id name="cid" column="cid">
            <generator class="native"/>
        </id>
        <!-- 配置普通属性 -->
        <property name="cname" column="cname" type="java.lang.String"/>
        <!-- 建立映射 -->
        <!-- 配置集合 -->
        <!--
            set标签中的name表示关联对象的属性名称

         -->
        <set name="orders" cascade="delete">
            <!-- key标签中的column用来一对多的多的一方的外键 -->
            <key column="cno"/>
            <!-- 配置一个one-to-many -->
            <one-to-many class="cn.hibernate3.demo2.Order"/>
        </set>
    </class>
</hibernate-mapping>

1.4.6Hibernate中级联的取值

  • none:不使用级联。
  • save-update:保存或更新的时候使用级联。
  • delete:删除的时候使用级联。
  • all:除了孤儿删除以外的所有级联。
  • delete-orphan:孤儿删除。
    • 仅限于一对多的情况,只有一对多时候,才有父子表(主从表),认为一的一方是父表,多的一方是子表。
    • 当一个客户与某个订单解除了关系,将外键设置为null。订单没有了所属的客户,相当于一个孩子没有父亲,就相当于一个孤儿,那么Hibernate就会将这类没有外键的记录删除。
  • all-delete-orphan:包含了孤儿删除的所有的级联。  

演示:孤儿删除的情况,默认情况下。

  @Test
    //孤儿删除,默认情况下,是将外键设置为null。
    public void demo6(){
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();

        Customer customer = (Customer) session.get(Customer.class, 1);

        Order order = (Order) session.get(Order.class, 1);

        customer.getOrders().remove(order);

        tx.commit();
        session.close();
    }
<?xml version="1.0" encoding="UTF-8"?>
<!-- 引入约束 -->
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="cn.hibernate3.demo2.Customer" table="customer">
        <!-- 配置唯一标识 -->
        <id name="cid" column="cid">
            <generator class="native"/>
        </id>
        <!-- 配置普通属性 -->
        <property name="cname" column="cname" type="java.lang.String"/>
        <!-- 建立映射 -->
        <!-- 配置集合 -->
        <!--
            set标签中的name表示关联对象的属性名称

         -->
        <set name="orders" >
            <!-- key标签中的column用来一对多的多的一方的外键 -->
            <key column="cno"/>
            <!-- 配置一个one-to-many -->
            <one-to-many class="cn.hibernate3.demo2.Order"/>
        </set>
    </class>
</hibernate-mapping>

  • 配置孤儿删除。

  @Test
    //孤儿删除
    public void demo6(){
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();

        Customer customer = (Customer) session.get(Customer.class, 1);

        Order order = (Order) session.get(Order.class, 1);

        customer.getOrders().remove(order);

        tx.commit();
        session.close();
    }
<?xml version="1.0" encoding="UTF-8"?>
<!-- 引入约束 -->
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="cn.hibernate3.demo2.Customer" table="customer">
        <!-- 配置唯一标识 -->
        <id name="cid" column="cid">
            <generator class="native"/>
        </id>
        <!-- 配置普通属性 -->
        <property name="cname" column="cname" type="java.lang.String"/>
        <!-- 建立映射 -->
        <!-- 配置集合 -->
        <!--
            set标签中的name表示关联对象的属性名称

         -->
        <set name="orders" cascade="delete-orphan">
            <!-- key标签中的column用来一对多的多的一方的外键 -->
            <key column="cno"/>
            <!-- 配置一个one-to-many -->
            <one-to-many class="cn.hibernate3.demo2.Order"/>
        </set>
    </class>
</hibernate-mapping>

1.4.7双向维护产生多余SQL及inverse配置

  @Test
    //双向关联,产生多余的SQL
    public void demo7(){
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();

        Customer customer = (Customer) session.get(Customer.class, 1);

        Order order = (Order) session.get(Order.class, 2);

        customer.getOrders().add(order);
        order.setCustomer(customer);

        tx.commit();
        session.close();
    }

  • 分析

  • 所以,双向维护,意味着双方都有外键的维护能力,那怎么办呢?必须让其中的一方放弃外键的维护权。

    • 添加属性inverse="true"。
    • inverse:是反转的意思,即我不维护,就是我放弃外键的维护权。
    • 一般情况下,是一方放弃的,多方维护的。
<?xml version="1.0" encoding="UTF-8"?>
<!-- 引入约束 -->
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="cn.hibernate3.demo2.Customer" table="customer">
        <!-- 配置唯一标识 -->
        <id name="cid" column="cid">
            <generator class="native"/>
        </id>
        <!-- 配置普通属性 -->
        <property name="cname" column="cname" type="java.lang.String"/>
        <!-- 建立映射 -->
        <!-- 配置集合 -->
        <!--
            set标签中的name表示关联对象的属性名称

         -->
        <set name="orders" inverse="true">
            <!-- key标签中的column用来一对多的多的一方的外键 -->
            <key column="cno"/>
            <!-- 配置一个one-to-many -->
            <one-to-many class="cn.hibernate3.demo2.Order"/>
        </set>
    </class>
</hibernate-mapping>

【注意】

  • cascade:操作关联对象。
  • inverse:控制外键的维护。

    

1.4.8Hibernate中多对多的配置

  • 建立实体类

    • Student.java
package cn.hibernate3.demo3;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

public class Student implements Serializable{
    private Integer sid;
    private String sname;
    private Set<Course> courses = new HashSet<Course>();
    public Integer getSid() {
        return sid;
    }
    public void setSid(Integer sid) {
        this.sid = sid;
    }
    public String getSname() {
        return sname;
    }
    public void setSname(String sname) {
        this.sname = sname;
    }
    public Set<Course> getCourses() {
        return courses;
    }
    public void setCourses(Set<Course> courses) {
        this.courses = courses;
    }

}
    • Course.java
package cn.hibernate3.demo3;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

public class Course implements Serializable{
    private Integer cid;
    private String cname;
    private Set<Student> students = new HashSet<Student>();
    public Integer getCid() {
        return cid;
    }
    public void setCid(Integer cid) {
        this.cid = cid;
    }
    public String getCname() {
        return cname;
    }
    public void setCname(String cname) {
        this.cname = cname;
    }
    public Set<Student> getStudents() {
        return students;
    }
    public void setStudents(Set<Student> students) {
        this.students = students;
    }

}
  • 建立映射

    • Student.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- 引入约束 -->
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="cn.hibernate3.demo3.Student" table="student">
        <!-- 配置唯一标识 -->
        <id name="sid" column="sid">
            <generator class="native"/>
        </id>
        <!-- 配置普通属性 -->
        <property name="sname" column="sname" type="java.lang.String"/>
        <!-- 建立映射 -->
        <!--
            <set>标签中的name:对应学生中课程集合的名称 table对应的是中间表的名称
         -->
        <set name="courses" table="student_course">
            <!--
                <key>标签对应的是中间表中的外键
             -->
            <key column="sid"/>
            <!--
                <many-to-many>对应的是另一方的类的全路径,column:另一方在中间表中外键的名称
             -->
            <many-to-many class="cn.hibernate3.demo3.Course" column="cid" />
        </set>
    </class>
</hibernate-mapping>
    • Course.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- 引入约束 -->
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="cn.hibernate3.demo3.Course" table="course">
        <!-- 配置唯一标识 -->
        <id name="cid" column="cid">
            <generator class="native"/>
        </id>
        <!-- 配置普通属性 -->
        <property name="cname" column="cname" type="java.lang.String"/>
        <!-- 建立映射 -->
        <set name="students" table="student_course">
            <key column="cid"/>
            <many-to-many class="cn.hibernate3.demo3.Student" column="sid"/>
        </set>
    </class>
</hibernate-mapping>
  • 将映射文件加载到核心配置文件中
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
    <!-- 配置数据库的基本信息 -->
    <!-- 驱动的名称 -->
    <property name="hibernate.connection.driver_class">
        com.mysql.jdbc.Driver
    </property>
    <!-- 访问数据库的url -->
    <property name="hibernate.connection.url">
        jdbc:mysql:///hibernate_day02
    </property>
    <!-- 用户名 -->
    <property name="hibernate.connection.username">root</property>
    <!-- 密码 -->
    <property name="hibernate.connection.password">root</property>
    <!-- 方言 -->
    <property name="hibernate.dialect">
        org.hibernate.dialect.MySQLDialect
    </property>
    <!-- C3P0连接池设定-->
    <!-- 使用c3po连接池  配置连接池提供的供应商-->
    <property name="connection.provider_class">
        org.hibernate.connection.C3P0ConnectionProvider
    </property>
    <!--在连接池中可用的数据库连接的最少数目 -->
    <property name="c3p0.min_size">5</property>
    <!--在连接池中所有数据库连接的最大数目  -->
    <property name="c3p0.max_size">20</property>
    <!--设定数据库连接的过期时间,以秒为单位,
        如果连接池中的某个数据库连接处于空闲状态的时间超过了timeout时间,就会从连接池中清除 -->
    <property name="c3p0.timeout">120</property>
    <!--每3000秒检查所有连接池中的空闲连接 以秒为单位-->
    <property name="c3p0.idle_test_period">3000</property>
    <!-- 可选配置 -->
    <!-- 显示SQL -->
    <property name="hibernate.show_sql">true</property>
    <!-- 格式化SQL -->
    <property name="hibernate.format_sql">true</property>
    <!-- hbm:映射 2:to ddl:create drop alter -->
    <property name="hibernate.hbm2ddl.auto">update</property>
    <mapping resource="cn/hibernate3/demo3/Student.hbm.xml" />
    <mapping resource="cn/hibernate3/demo3/Course.hbm.xml" />

</session-factory>
</hibernate-configuration>

【注意】:映射文件的理解

1.4.9Hibernate中多对多的保存

  • 测试类
  @Test
    //保存学生和课程,为学生选择一些课程
    public void demo1(){
        Session session = HibernateUtils.openSession();
        Transaction tx = session.beginTransaction();

        //创建学生
        Student student1 = new Student();
        student1.setSname("张三");
        Student student2 = new Student();
        student2.setSname("李四");
        //创建课程
        Course course1 = new Course();
        course1.setCname("java语言");
        Course course2 = new Course();
        course2.setCname("Android语言");

        //张三选择1号和2号课程
        student1.getCourses().add(course1);
        student1.getCourses().add(course2);
        course1.getStudents().add(student1);
        course2.getStudents().add(student1);

        //李四选择1号课程
        student2.getCourses().add(course1);
        course1.getStudents().add(student2);

        //保存记录
        session.save(student1);
        session.save(student2);
        session.save(course1);
        session.save(course2);

        tx.commit();
        session.close();
    }

但是,这样会抛出异常。

所以,多对多的配置中,必须有一方放弃主键维护。比如学生放弃主键维护的权利。

<?xml version="1.0" encoding="UTF-8"?>
<!-- 引入约束 -->
<!DOCTYPE hibernate-mapping PUBLIC
    "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <class name="cn.hibernate3.demo3.Student" table="student">
        <!-- 配置唯一标识 -->
        <id name="sid" column="sid">
            <generator class="native"/>
        </id>
        <!-- 配置普通属性 -->
        <property name="sname" column="sname" type="java.lang.String"/>
        <!-- 建立映射 -->
        <!--
            <set>标签中的name:对应学生中课程集合的名称 table对应的是中间表的名称
         -->
        <set name="courses" table="student_course" inverse="true">
            <!--
                <key>标签对应的是中间表中的外键
             -->
            <key column="sid"/>
            <!--
                <many-to-many>对应的是另一方的类的全路径,column:另一方在中间表中外键的名称
             -->
            <many-to-many class="cn.hibernate3.demo3.Course" column="cid" />
        </set>
    </class>
</hibernate-mapping>

  

最新文章

  1. 设计模式(七)适配器模式(Adapter Pattern)
  2. (译)详解javascript立即执行函数表达式(IIFE)
  3. 推荐几个的chorme的扩展程序
  4. Android Studio系列教程五--Gradle命令详解与导入第三方包
  5. Human Gene Functions(poj 1080)
  6. 【BZOJ】1049: [HAOI2006]数字序列(lis+特殊的技巧)
  7. php 安装composer
  8. JSOI2008 最小生成树计数
  9. elasticsearch基础概念
  10. 提高java编程质量 - (四)i++ 和 ++i 探究原理
  11. selenium自动化测试配置工具整理
  12. [Codeforces]813F Bipartite Checking
  13. android M Launcher之LauncherModel (一)
  14. Spring Cloud(Dalston.SR5)--Config 集群配置中心-刷新配置
  15. tomcat设置开机自动启动
  16. C#事件の事件聚合器
  17. sort.js
  18. wpf(使用定时器)使用定时器操作UI界面
  19. 峰Spring4学习(6)spring AOP的应用例子
  20. POJ 3481 Double Queue(set实现)

热门文章

  1. SequoiaDB x Spark 新主流架构引领企业级应用
  2. Vivo展柜灯怎样设计才吸引大量客户?
  3. 浅谈关于特征选择算法与Relief的实现
  4. 【Python3之多进程】
  5. laravel5.4+vue+element-ui配置及简单使用
  6. Unity-Shader-镜面高光Phong&amp;BlinnPhong-油腻的师姐在哪里
  7. Unity3D调用摄像头
  8. js获取客户端MAC地址
  9. HDU 5547 Sudoku(DFS)
  10. linux DNS 问题