Hibernate框架学习总结(三) 一对多关系

配置一对多关系

  • hibernate框架只要配置过就不需要手动创建表格,重点和难点都在配置映射文件
    在这里,我选择Customer(客户)和Linkman(联系人)作为演示的模型

在这个模型中,一个联系人专门负责一个客户,一个客户对应多个联系人

Customer字段如下

  private Long cust_id;  //客户ID
  private  String cust_name;  //客户公司名
  private Set linkmens = new HashSet<>();  //多个联系人

Linkman字段如下

    private Long lkm_id;  //联系人ID
    private String lkm_name;  //联系人姓名
    private Customer customer;  //负责的公司名

两个实体化类都各自生成set/get方法,重写toString方法


两个实体化类对应的映射配置文件

Customer.hbm.xml
其他字段的配置和一对一相同,在这里不写了


 <set name="linkmens" >
     
     <key column="lkm_cust_id">key>
         
     <one-to-many class="com.cuixiaoming.domain.Linkman"/>
 set>

Linkman.hbm.xml
所有字段都和一对一相同,不在展示
需要注意的是,实体类中封装了Customer对象,但是和上一个映射文件不同,只需在对象关系中体现就可以了


<many-to-one name="customer" class="com.itheima.domain.Customer" column="lkm_cust_id">many-to-one>

至此,最基本的一对多关系就被创建好了,
因为核心配置文件中添加了

  <property name="hibernate.hbm2ddl.auto">updateproperty>

所以只需要运行下面代码就可以自动创建表
工具类见上一篇文章

@Test
  public void test(){
      Session session = HibernateUtils.getCurrentSession();
      Transaction transaction = session.beginTransaction();
      transaction.commit();
  }

能创建的原因是,一旦获取的Session,核心配置文件就会被加载,两个映射文件也会被加载,所以就会把数据库中与映射文件不同的列创建出来,因为此时数据库为空,所以完全创建两个新表;
这里写图片描述

这里写图片描述

1.普通方法

下面添加一组一对多的数据

 @Test
    //存一组一对多数据
    public void test01(){
        Session session = HibernateUtils.getCurrentSession();
        Transaction transaction = session.beginTransaction();

        //创建Customer,并设置名字
        Customer customer = new Customer();
        customer.setCust_name("某公司1");

        //创建Linkman,并设置名字
        Linkman man1 = new Linkman();
        man1.setLkm_name("联系人1-1");
        Linkman man2 = new Linkman();
        man2.setLkm_name("联系人1-2");

        //维护Customer和Linkman的关系
        customer.getLinkmens().add(man1);
        customer.getLinkmens().add(man2);

        //维护Linkman和Customer的关系
        man1.setCustomer(customer);
        man2.setCustomer(customer);

        //分别将三条数据存入数据库
        session.save(customer);
        session.save(man1);
        session.save(man2);

        transaction.commit();
    }

三条数据成功添加到了数据库中
这里写图片描述

这里写图片描述

但是,我们发现,这一组一对多的数据需要Session save三次才可以,太麻烦了

2.级联保存

我们想,两个Linkman的信息,已经封装到了Customer的集合中了,我们能不能只对Customer进行操作而对所有数据进行操作呢?

我们可以在Customer.hbm.xml中的set标签中配置set标签上配置 cascade=”save-update” ,即 “层叠的存储和更新”
于是,我们就可以

@Test
    //存数据
    public void test02(){
        Session session = HibernateUtils.getCurrentSession();
        Transaction transaction = session.beginTransaction();

        //创建Customer
        Customer customer = new Customer();
        customer.setCust_name("某公司2");
        //创建Linkman
        Linkman man1 = new Linkman();
        man1.setLkm_name("联系人2-1");
        Linkman man2 = new Linkman();
        man2.setLkm_name("联系人2-2");
        //维护Customer和Linkman的关系
        customer.getLinkmens().add(man1);
        customer.getLinkmens().add(man2);
        man1.setCustomer(customer);
        man2.setCustomer(customer);

        session.save(customer);
        //session.save(man1);
        //session.save(man2);

        transaction.commit();



    }


Hibernate框架学习总结(三) 一对多关系_第1张图片
测试成功

类似的,我们也可以Linkman中的many-to-one标签中配置 cascade=”save-update”,从而在只操作Linkman的时候,一起将Customer数据也一并操作

3.级联删除

如果我们在Customer中set标签中加入cascade=”delete” 标签,我们就可以进行级联删除,在只删除Customer的情况下,对相关联的Linkman数据也进行删除

@Test
//测试删除操作
public void test4(){

    Session session = HibernateUtils.getCurrentSession();
    Transaction transaction = session.beginTransaction();

    //先查询在删除
    //因为在映射文件种配置了customer与linkman的关系 所以查询出的customer本身就与linkman有关系的
    Customer customer = session.get(Customer.class, 2L);
    //删除2号customer与3号linkman和号4linkman存在关系
    session.delete(customer);

    transaction.commit();
}

4.双向操作冗余SQL指令的优化

当我们给Customer和Linkman都配置了cascade之后,运行下面这个代码

@Test
    public void test6(){

        Session session = HibernateUtils.getCurrentSession();
        Transaction transaction = session.beginTransaction();

        //创建Customer
        Customer customer = new Customer();
        customer.setCust_name("某公司3");
        //创建Linkman
        Linkman man1 = new Linkman();
        man1.setLkm_name("联系人3-1");
        Linkman man2 = new Linkman();
        man2.setLkm_name("联系人3-2");

        //级联保存的关系
        customer.getLinkmens().add(man1);
        customer.getLinkmens().add(man2);
        //维护外键
        man1.setCustomer(customer);
        man2.setCustomer(customer);

        session.save(customer);//保存customer的同时 保存man1和man2

        transaction.commit();
    }

我们打开控制台,发现它向数据库发送了5条SQL指令

Hibernate: insert into cst_customer (cust_name) values (?)
Hibernate: insert into cst_linkman (lkm_name, lkm_cust_id) values (?, ?)
Hibernate: insert into cst_linkman (lkm_name, lkm_cust_id) values (?, ?)
Hibernate: update cst_linkman set lkm_cust_id=? where lkm_id=?
Hibernate: update cst_linkman set lkm_cust_id=? where lkm_id=?

我通过多种方式进行了测试,发现前三条语句已经可以将三条数据完整地添加到数据库了,后两条完全是多余的
说一下它这样做的原因吧

  • 因为配置了级联保存 session.save(customer) 运行的同时,也保存了man1和man2,这就产生了前三条sql语句
  • 但是,session中的集合中还有两个man的关系,这两个关系是在cst_linkman表中体现的,这就是后面两条update语句
解决办法

在Customer中的Set标签里配置inverse=true,中断它的外键维护权限

5.对象导航查询

因为两个实例化对象都封装了另一个实例化对象,
所以,我们在查询到其中一个对象的时候,可以通过get方法,获取另一个对象

@Test
    //对象导航查询操作
    public void test8(){

        Session session = HibernateUtils.openSession();
        Transaction transaction = session.beginTransaction();

        Linkman linkman = session.get(Linkman.class, 2L);//第一条查询语句
        System.out.println(linkman);
        Customer customer = linkman.getCustomer();//第二条查询语句
        System.out.println(customer);

        transaction.commit();
        session.close();

    }

6.延迟查询

在上面导航查询的时候,我们使用debug模式运行,发现在我们获取打印Linkman对象的时候,只发送了一条查询Linkman的SQL,并没有同一时间获取Customer对象

在获取打印Customer的时候,才会发送SQL查询Customer数据

这种现象称为延迟查询,如果我们想关闭延迟查询,从而使在查询第一个对象的时候就获取所有关联对象的信息的话

我们可以通过在映射配置中中添加 lazy=”false”

3月17日更新:在一对多多对多关系中,最好都配置lazy=”false”,可以避免发生HHH000142: Javassist Enhancement failed

7.修改操作

这里的修改说的是修改映射关系,
修改都是先查后改,获取想要修改的对象后,替换要更改的映射的对象,这个对象可以是已经存在的(需要获取),或者以前不存在的(需要创建)


    @Test
    //修改操作
    public void test9(){

        Session session = HibernateUtils.openSession();
        Transaction transaction = session.beginTransaction();

        //获得1号的Linkman
        Linkman linkman = session.get(Linkman.class, 1L);
        //获得8号的Customer
        Customer customer = session.get(Customer.class, 8L);
        //修改
        linkman.setCustomer(customer);

        transaction.commit();
        session.close();

    }

你可能感兴趣的:(框架,Hibernate)