JavaWeb学习:Hibernate一对多关系简单实列

1、创建一对多表SQLServer的Sql语句

CREATE TABLE [dbo].[cst_customer](
    [cust_id] [bigint] IDENTITY(1,1) NOT NULL PRIMARY Key,
    [cust_name] [varchar](32) NOT NULL,
    [cust_source] [varchar](32) NULL,
    [cust_industry] [varchar](32) NULL,
    [cust_level] [varchar](32) NULL,
    [cust_phone] [varchar](64) NULL,
    [cust_mobile] [varchar](16) NULL
)
CREATE TABLE [dbo].[cst_linkman](
    [lkm_id] [bigint] IDENTITY(1,1) NOT NULL PRIMARY Key,
    [lkm_name] [varchar](16) NULL,
    [lkm_cust_id] [bigint] NOT NULL FOREIGN KEY([lkm_cust_id]) REFERENCES [cst_customer]([cust_id]) ,
    [lkm_gender] [char](1) NULL,
    [lkm_phone] [varchar](16) NULL,
    [lkm_mobile] [varchar](16) NULL,
    [lkm_email] [varchar](64) NULL,
    [lkm_qq] [varchar](16) NULL,
    [lkm_position] [varchar](16) NULL,
    [lkm_memo] [varchar](512) NULL
)

2、引入jar包

 3、配置log4j.properties

### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.err
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

### direct messages to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=c:mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

### set log levels - for more verbose logging change 'info' to 'debug' ###
# error warn info debug trace
log4j.rootLogger= info, stdout

4、创建实体类

public class Customer {
    private Long cust_id;
    private String cust_name;

    private String cust_source;
    private String cust_industry;
    private String cust_level;
    private String cust_phone;
    private String cust_mobile;

    // 通过ORM方式表示:一个客户对应多个联系人。
    // 放置的多的一方的集合。Hibernate默认使用的是Set集合。
    private Set<LinkMan> linkMans = new HashSet<LinkMan>();
public class LinkMan {
    private Long lkm_id;
    private String lkm_name;
    private String lkm_gender;
    private String lkm_phone;
    private String lkm_mobile;
    private String lkm_email;
    private String lkm_qq;
    private String lkm_position;
    private String lkm_memo;
    // 通过ORM方式表示:一个联系人只能属于某一个客户。
    // 放置的是一的一方的对象。
    private Customer customer;

5、配置持久化类的映射文件

  5.1、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="com.hibernate.demo.Customer" table="cst_customer">
        <!-- 建立类中的属性与表中的主键对应 -->
        <id name="cust_id" column="cust_id">
            <generator class="native"></generator>
        </id>

        <!-- 建立类中的普通属性与表中的字段对应 -->
        <property name="cust_name" column="cust_name"></property>
        <property name="cust_source" column="cust_source"></property>
        <property name="cust_industry" column="cust_industry"></property>
        <property name="cust_level" column="cust_level"></property>
        <property name="cust_phone" column="cust_phone"></property>
        <property name="cust_mobile" column="cust_mobile"></property>
        <!-- 主要配置一对多的映射:放置的多的一方的集合 -->
        <!-- set标签 :
         * name :多的一方的对象集合的属性名称。 
         * cascade:级联
         * inverse:放弃外键维护权。 -->
        <set name="linkMans" inverse="true" cascade="save-update">
            <!--key标签 
            * column:多的一方的外键的名称。 -->
            <key column="lkm_cust_id" />
            <!-- one-to-many标签
             * class :多的一方的类的全路径 -->
            <one-to-many class="com.hibernate.demo.LinkMan" />
        </set>
    </class>
</hibernate-mapping>

  5.2、LinkMan.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 table="cst_linkman"    name="com.itheima.hibernate.domain.LinkMan">
        <!-- 建立OID与主键映射 -->
        <id name="lkm_id" column="lkm_id">
            <generator class="native" />
        </id>
        <!-- 建立普通属性与表字段映射 -->
        <property name="lkm_name" />
        <property name="lkm_gender" />
        <property name="lkm_phone" />
        <property name="lkm_mobile" />
        <property name="lkm_email" />
        <property name="lkm_qq" />
        <property name="lkm_position" />
        <property name="lkm_memo" />
        <!-- 主要配置多对一的关系:放置的是一的一方的对象 -->
        <!-- many-to-one标签 
        * name :一的一方的对象的属性名称。 
        * class :一的一方的类的全路径。
        * column :在多的一方的表的外键的名称。 -->
        <many-to-one name="customer" column="lkm_cust_id"
            class="com.hibernate.demo.Customer" />
    </class>
</hibernate-mapping>

6、核心配置文件的映射关系

<mapping resource="com/hibernate/demo/Customer.hbm.xml"/>
<mapping resource="com/hibernate/demo/LinkMan.hbm.xml"/>

7、测试代码:

  7.1、一对多两边都保存

@Test
    public void demo() {
    Session session = HibernateUtils.getCurrentSession();
    Transaction transaction = session.beginTransaction();
    //创建两个客户
    Customer customer1=new Customer();
    customer1.setCust_name("张三");
    Customer customer2=new Customer();
    customer2.setCust_name("李四");
    
    //创建三个联系人
    LinkMan linkMan1=new LinkMan();
    linkMan1.setLkm_name("王二");
    LinkMan linkMan2=new LinkMan();
    linkMan2.setLkm_name("王五");
    LinkMan linkMan3=new LinkMan();
    linkMan3.setLkm_name("赵六");

    //设置关系
    linkMan1.setCustomer(customer1);
    linkMan2.setCustomer(customer1);
    linkMan3.setCustomer(customer2);
    
    customer1.getLinkMans().add(linkMan1);
    customer1.getLinkMans().add(linkMan2);
    customer2.getLinkMans().add(linkMan3);
    
    // 保存
    session.save(linkMan1);
    session.save(linkMan2);
    session.save(linkMan3);
    session.save(customer1);
    session.save(customer2);
    transaction.commit();
    }

7.2、一对多关系只保存一边

@Test
    public void demo1() {
    Session session = HibernateUtils.getCurrentSession();
    Transaction transaction = session.beginTransaction();
    //创建两个客户
    Customer customer1=new Customer();
    customer1.setCust_name("张三");
    
    //创建三个联系人
    LinkMan linkMan1=new LinkMan();
    linkMan1.setLkm_name("王二");

    //设置关系
    linkMan1.setCustomer(customer1);
    
    customer1.getLinkMans().add(linkMan1);
    
    // 保存
    // 报瞬时对象异常:持久态对象关联了一个瞬时态对象
    //session.save(linkMan1);
    session.save(customer1);
    transaction.commit();
    }

结果:java.lang.IllegalStateException: org.hibernate.TransientObjectException(报瞬时对象异常)

  7.2.1、解决方案配置级联

    级联:操作一个对象同时操作其关联对象

    级联方向性:

      操作一的一方,同时操作多的一方

      操作多的一方,同时操作一的一方

    7.2.1.1、保存 客户(一) 级联 联系人(多),(客户映射文件Customer.hbm.xml)

    <!-- set标签 :
         * name :多的一方的对象集合的属性名称。 
         * cascade:级联
         * inverse:放弃外键维护权。 -->
        <set name="linkMans" cascade="save-update">
            <!--key标签 
            * column:多的一方的外键的名称。 -->
            <key column="lkm_cust_id" />
            <!-- one-to-many标签
             * class :多的一方的类的全路径 -->
            <one-to-many class="com.hibernate.demo.LinkMan" />
        </set>
@Test
    public void demo1() {
    Session session = HibernateUtils.getCurrentSession();
    Transaction transaction = session.beginTransaction();
    //创建两个客户
    Customer customer1=new Customer();
    customer1.setCust_name("zhangsan");
    
    //创建三个联系人
    LinkMan linkMan1=new LinkMan();
    linkMan1.setLkm_name("wanger");

    //设置关系
    linkMan1.setCustomer(customer1);
    
    customer1.getLinkMans().add(linkMan1);
    
    // 保存
    session.save(customer1);
    transaction.commit();
    }

    7.2.1.2、保存联系人级联客户(操作多同时操作一:配置LinkMan.hbm.xml)

<!-- 配置多对一的关系:放置的是一的一方的对象 -->
        <!-- many-to-one标签 
        * name :一的一方的对象的属性名称。 
        * class :一的一方的类的全路径。
        * column :在多的一方的表的外键的名称。 -->
        <many-to-one name="customer" cascade="save-update" column="lkm_cust_id"
            class="com.hibernate.demo.Customer" />
@Test
    public void demo1() {
    Session session = HibernateUtils.getCurrentSession();
    Transaction transaction = session.beginTransaction();
    //创建两个客户
    Customer customer1=new Customer();
    customer1.setCust_name("lisi");
    
    //创建三个联系人
    LinkMan linkMan1=new LinkMan();
    linkMan1.setLkm_name("zhaoliu");

    //设置关系
    linkMan1.setCustomer(customer1);
    
    customer1.getLinkMans().add(linkMan1);
    
    // 保存
    session.save(linkMan1);
    transaction.commit();
    }

    7.2.2、级联删除

      删除客户级联删除联系人    

@Test
    public void demo2() {
    Session session = HibernateUtils.getCurrentSession();
    Transaction transaction = session.beginTransaction();
    // 没有设置级联删除
    Customer customer=session.get(Customer.class, 1l);
    session.delete(customer);
    transaction.commit();
    }

控制台输出结果:

Hibernate: 
    update
        cst_linkman 
    set
        lkm_cust_id=null 
    where
        lkm_cust_id=?
Hibernate: 
    delete 
    from
        cst_customer 
    where
        cust_id=?

      设置级联删除

<set name="linkMans" cascade="save-update,delete">

控制台输出结果

Hibernate: 
    delete 
    from
        cst_linkman 
    where
        lkm_id=?
Hibernate: 
    delete 
    from
        cst_linkman 
    where
        lkm_id=?
Hibernate: 
    delete 
    from
        cst_customer 
    where
        cust_id=?

8、一对多设置了双向关联产生多余的sql语句

    @Test
    public void demo3() {
    Session session = HibernateUtils.getCurrentSession();
    Transaction transaction = session.beginTransaction();
    
    Customer customer=session.get(Customer.class, 2l);
    LinkMan linkMan=session.get(LinkMan.class, 2l);
    // 双向关联
    linkMan.setCustomer(customer);
    customer.getLinkMans().add(linkMan);
    
    transaction.commit();
    }

控制台结果:

Hibernate: 
    update
        cst_linkman 
    set
        lkm_name=?,
        lkm_gender=?,
        lkm_phone=?,
        lkm_mobile=?,
        lkm_email=?,
        lkm_qq=?,
        lkm_position=?,
        lkm_memo=?,
        lkm_cust_id=? 
    where
        lkm_id=?
Hibernate: 
    update
        cst_linkman 
    set
        lkm_cust_id=? 
    where
        lkm_id=?

发现cst_linkman中lkm_cust_id被更新两次了

解决方案一的一方放弃外键维护权:

一、单向维护

    @Test
    public void demo3() {
    Session session = HibernateUtils.getCurrentSession();
    Transaction transaction = session.beginTransaction();
    
    Customer customer=session.get(Customer.class, 1l);
    LinkMan linkMan=session.get(LinkMan.class, 2l);
    linkMan.setCustomer(customer);
    //customer.getLinkMans().add(linkMan);
    
    transaction.commit();
    }

结果:

Hibernate: 
    update
        cst_linkman 
    set
        lkm_name=?,
        lkm_gender=?,
        lkm_phone=?,
        lkm_mobile=?,
        lkm_email=?,
        lkm_qq=?,
        lkm_position=?,
        lkm_memo=?,
        lkm_cust_id=? 
    where
        lkm_id=?

二、放弃外键维护权

<set name="linkMans" cascade="save-update,delete" inverse="true">
    @Test
    public void demo3() {
    Session session = HibernateUtils.getCurrentSession();
    Transaction transaction = session.beginTransaction();
    
    Customer customer=session.get(Customer.class, 1l);
    LinkMan linkMan=session.get(LinkMan.class, 2l);
    // 双向关联
    linkMan.setCustomer(customer);
    customer.getLinkMans().add(linkMan);
    
    transaction.commit();
    }

控制台结果:

Hibernate: 
    update
        cst_linkman 
    set
        lkm_name=?,
        lkm_gender=?,
        lkm_phone=?,
        lkm_mobile=?,
        lkm_email=?,
        lkm_qq=?,
        lkm_position=?,
        lkm_memo=?,
        lkm_cust_id=? 
    where
        lkm_id=?

9、cascade和inverse的区别

<set name="linkMans" cascade="save-update,delete" inverse="true">
@Test
    public void demo4() {
    Session session = HibernateUtils.getCurrentSession();
    Transaction transaction = session.beginTransaction();
    
    Customer customer=new Customer();
    customer.setCust_name("libing");
    
    LinkMan linkMan=new LinkMan();
    linkMan.setLkm_name("fenjie");
    
    customer.getLinkMans().add(linkMan);
    //cascade和inverse区别
    session.save(customer);
    transaction.commit();
    }

cascade:作用于关联数据

inverse:作用于关联数据的外键

原文地址:https://www.cnblogs.com/WarBlog/p/13953728.html