hibernate 多对一 一对多 出现死循环
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hibernate 多对一 一对多 出现死循环相关的知识,希望对你有一定的参考价值。
参考技术A https://blog.csdn.net/ludengji/article/details/11584281环境:springmvc+hibernate+json
在controller返回数据到统一json转换的时候,出现了json infinite recursion stackoverflowerror的错误,即json在将对象转换为json格式的数据的时候,出现了无限递归调用的情况。
具体的情况如下:
A类中,有个属性:List b, A与B的关系为 OneToMany;在B类中,有属性A a,引用到A中的字段id,并作为外键。hibernate查询结果正常,可以看到返回的A对象中,有b参数值,但在json转换的时候就出现了无限递归的情况。个人分析,应该是json在序列化A中的b属性的时候,找到了B类,然后序列化B类,而B类中有a属性,因此,为了序列化a属性,json又得去序列化A类,如此递归反复,造成该问题。
解决:
在B类中a的getter setter方法上加注解@JsonBackReference,其实自己试过只在setter方法上加@JsonBackReference也够了。
参考文章:
http://my.oschina.net/u/943437/blog/145246
hibernate的一对多和多对一关联
一对一的关联就不写了,一般项目也用不到,如果可以一对一就直接合成一个表了,也不会出现一对一的关系。
本文主要研究一对多的关系。
1.一对多的关系研究:
(1)RDB中关系表达: 多的一方创建外键指向一的一方的主键
(2)Java实体中的表达: 在一的一方创建Set集合维护多的一方,在多的一方创建单个一的一方的对象的引用。
也可以更深入的理解为:
(3)ORM元数据中表达: (XXX.hbm.xml中的配置)
- 一的一方的配置(Customer)
- 多的一方的配置(LinkMan)
2.关联操作
顾客和联系人是一对多的关系,一个顾客可以有多个联系人,一个联系人对应一个顾客。
1.配置文件
Customer.java
package cn.qlq.domain; import java.util.HashSet; import java.util.Set; 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_linkman; private String cust_phone; private String cust_mobile; //使用set集合,表达一对多关系 private Set<LinkMan> linkMens = new HashSet<LinkMan>(); public Set<LinkMan> getLinkMens() { return linkMens; } public void setLinkMens(Set<LinkMan> linkMens) { this.linkMens = linkMens; } public Long getCust_id() { return cust_id; } public void setCust_id(Long cust_id) { this.cust_id = cust_id; } public String getCust_name() { return cust_name; } public void setCust_name(String cust_name) { this.cust_name = cust_name; } public String getCust_source() { return cust_source; } public void setCust_source(String cust_source) { this.cust_source = cust_source; } public String getCust_industry() { return cust_industry; } public void setCust_industry(String cust_industry) { this.cust_industry = cust_industry; } public String getCust_level() { return cust_level; } public void setCust_level(String cust_level) { this.cust_level = cust_level; } public String getCust_linkman() { return cust_linkman; } public void setCust_linkman(String cust_linkman) { this.cust_linkman = cust_linkman; } public String getCust_phone() { return cust_phone; } public void setCust_phone(String cust_phone) { this.cust_phone = cust_phone; } public String getCust_mobile() { return cust_mobile; } public void setCust_mobile(String cust_mobile) { this.cust_mobile = cust_mobile; } @Override public String toString() { return "Customer [cust_id=" + cust_id + ", cust_name=" + cust_name + "]"; } }
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"> <!-- 配置表与实体对象的关系 --> <!-- package属性:填写一个包名.在元素内部凡是需要书写完整类名的属性,可以直接写简答类名了. --> <hibernate-mapping package="cn.qlq.domain" > <!-- class元素: 配置实体与表的对应关系的 name: 完整类名 table:数据库表名 --> <class name="Customer" table="cst_customer" > <!-- id元素:配置主键映射的属性 name: 填写主键对应属性名 column(可选): 填写表中的主键列名.默认值:列名会默认使用属性名 type(可选):填写列(属性)的类型.hibernate会自动检测实体的属性类型. 每个类型有三种填法: java类型|hibernate类型|数据库类型 not-null(可选):配置该属性(列)是否不能为空. 默认值:false length(可选):配置数据库中列的长度. 默认值:使用数据库类型的最大长度 --> <id name="cust_id" > <!-- generator:主键生成策略 --> <!--identity : 主键自增.由数据库来维护主键值.录入时不需要指定主键. --> <generator class="native"></generator> </id> <!-- property元素:除id之外的普通属性映射 name: 填写属性名 column(可选): 填写列名 type(可选):填写列(属性)的类型.hibernate会自动检测实体的属性类型. 每个类型有三种填法: java类型|hibernate类型|数据库类型 not-null(可选):配置该属性(列)是否不能为空. 默认值:false length(可选):配置数据库中列的长度. 默认值:使用数据库类型的最大长度 --> <property name="cust_name" column="cust_name" > <!-- <column name="cust_name" sql-type="varchar" ></column> --> </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_linkman" column="cust_linkman" ></property> <property name="cust_phone" column="cust_phone" ></property> <property name="cust_mobile" column="cust_mobile" ></property> <!-- 集合,一对多关系,在配置文件中配置 --> <!-- name属性:集合属性名 column属性: 外键列名 class属性: 与我关联的对象完整类名 --> <set name="linkMens"> <key column="lkm_cust_id" ></key> <one-to-many class="LinkMan" /> </set> </class> </hibernate-mapping>
LinkMan.java
package cn.qlq.domain; //联系人实体 public class LinkMan { private Long lkm_id; private Character lkm_gender; private String lkm_name; private String lkm_phone; private String lkm_email; private String lkm_qq; private String lkm_mobile; private String lkm_memo; private String lkm_position; // 表达多对一关系 private Customer customer; public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer; } public Long getLkm_id() { return lkm_id; } public void setLkm_id(Long lkm_id) { this.lkm_id = lkm_id; } public Character getLkm_gender() { return lkm_gender; } public void setLkm_gender(Character lkm_gender) { this.lkm_gender = lkm_gender; } public String getLkm_name() { return lkm_name; } public void setLkm_name(String lkm_name) { this.lkm_name = lkm_name; } public String getLkm_phone() { return lkm_phone; } public void setLkm_phone(String lkm_phone) { this.lkm_phone = lkm_phone; } public String getLkm_email() { return lkm_email; } public void setLkm_email(String lkm_email) { this.lkm_email = lkm_email; } public String getLkm_qq() { return lkm_qq; } public void setLkm_qq(String lkm_qq) { this.lkm_qq = lkm_qq; } public String getLkm_mobile() { return lkm_mobile; } public void setLkm_mobile(String lkm_mobile) { this.lkm_mobile = lkm_mobile; } public String getLkm_memo() { return lkm_memo; } public void setLkm_memo(String lkm_memo) { this.lkm_memo = lkm_memo; } public String getLkm_position() { return lkm_position; } public void setLkm_position(String lkm_position) { this.lkm_position = lkm_position; } }
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 package="cn.qlq.domain" > <class name="LinkMan" table="cst_linkman" > <id name="lkm_id" > <generator class="native"></generator> </id> <property name="lkm_gender" ></property> <property name="lkm_name" ></property> <property name="lkm_phone" ></property> <property name="lkm_email" ></property> <property name="lkm_qq" ></property> <property name="lkm_mobile" ></property> <property name="lkm_memo" ></property> <property name="lkm_position" ></property> <!-- 多对一 --> <!-- name属性:引用属性名 column属性: 外键列名 class属性: 与我关联的对象完整类名 --> <many-to-one name="customer" column="lkm_cust_id" class="Customer"></many-to-one> </class> </hibernate-mapping>
2.测试代码
1.简单的添加一个顾客张三
/** * 添加一个顾客张三 */ @Test public void fun1() { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); //------------------S业务逻辑------------ Customer c = new Customer(); c.setCust_name("张三"); c.setCust_industry("金融行业"); c.setCust_phone("18545869586"); //------------------E业务逻辑------------ session.save(c); tx.commit(); }
SQL:
Hibernate: insert into cst_customer (cust_name, cust_source, cust_industry, cust_level, cust_linkman, cust_phone, cust_mobile) values (?, ?, ?, ?, ?, ?, ?)
2.给张三增加1个联系人
/** * 给张三增加1个联系人 */ @Test public void fun2(){ Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); //------------------S业务逻辑------------// Customer c = session.get(Customer.class, 1l);//项目中可能是根据顾客姓名等信息获取到顾客实体(持久态) LinkMan m1 = new LinkMan(); m1.setLkm_name("后来给张三添加的联系人"); //维护二者的关系,下面两种方式二选一 // c.getLinkMans().add(m1); m1.setCustomer(c); session.save(m1);//将m1设置为持久态,不用执行session.update((c)是因为c对象本来处于持久态) //------------------E业务逻辑------------// tx.commit(); }
SQL:
Hibernate: select customer0_.cust_id as cust_id1_0_0_, customer0_.cust_name as cust_nam2_0_0_, customer0_.cust_source as cust_sou3_0_0_, customer0_.cust_industry as cust_ind4_0_0_, customer0_.cust_level as cust_lev5_0_0_, customer0_.cust_linkman as cust_lin6_0_0_, customer0_.cust_phone as cust_pho7_0_0_, customer0_.cust_mobile as cust_mob8_0_0_ from cst_customer customer0_ where customer0_.cust_id=? Hibernate: insert into cst_linkman (lkm_gender, lkm_name, lkm_phone, lkm_email, lkm_qq, lkm_mobile, lkm_memo, lkm_position, lk_cust_id) values (?, ?, ?, ?, ?, ?, ?, ?, ?)
3.给上面新增加的联系人修改信息
/** * 给上面新增加的联系人修改信息 */ @Test public void fun3(){ Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); /***------------------S业务逻辑------------**/ LinkMan linkMan = session.get(LinkMan.class, 1l); linkMan.setLkm_name("修改过的名字"); // session.update(linkMan);//不用执行这句话是因为获取出来的对象本来处于持久态 /***------------------E业务逻辑------------**/ tx.commit(); }
SQL:
Hibernate: select linkman0_.lkm_id as lkm_id1_1_0_, linkman0_.lkm_gender as lkm_gend2_1_0_, linkman0_.lkm_name as lkm_name3_1_0_, linkman0_.lkm_phone as lkm_phon4_1_0_, linkman0_.lkm_email as lkm_emai5_1_0_, linkman0_.lkm_qq as lkm_qq6_1_0_, linkman0_.lkm_mobile as lkm_mobi7_1_0_, linkman0_.lkm_memo as lkm_memo8_1_0_, linkman0_.lkm_position as lkm_posi9_1_0_, linkman0_.lk_cust_id as lk_cust10_1_0_ from cst_linkman linkman0_ where linkman0_.lkm_id=? Hibernate: update cst_linkman set lkm_gender=?, lkm_name=?, lkm_phone=?, lkm_email=?, lkm_qq=?, lkm_mobile=?, lkm_memo=?, lkm_position=?, lk_cust_id=? where lkm_id=?
4.删除一个联系人(分为两种:第一种是简单的去掉二者的外键关系---linkman表外键的值设为null,第二种是彻底从库中删除数据----linkman表数据删除)
/** * 删除一个联系人(分为两种:第一种是简单的去掉二者的外键关系,第二种是彻底从库中删除数据) */ @Test public void fun4(){ Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); /***------------------S业务逻辑------------**/ //第一种:去掉二者的关系但是不删除数据 // Customer c = session.get(Customer.class, 1l);//项目中可能是根据顾客姓名等信息获取到顾客实体(持久态) // LinkMan linkMan = session.get(LinkMan.class, 3l); // c.getLinkMans().remove(linkMan); //第二种:彻底的删除数据 LinkMan linkMan = session.get(LinkMan.class, 3l); session.delete(linkMan); /***------------------E业务逻辑------------**/ tx.commit(); }
5.添加一个顾客,同时添加多个联系人
/** * 添加一个顾客,同时添加多个联系人 */ @Test public void fun5() { Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); Customer c = new Customer(); c.setCust_name("李四"); c.setCust_industry("IT行业"); c.setCust_phone("18545869587"); LinkMan m1 = new LinkMan(); LinkMan m2 = new LinkMan(); m1.setLkm_name("联系人1"); m2.setLkm_name("联系人2"); //维护关系 // c.getLinkMans().add(m1); // c.getLinkMans().add(m2); m1.setCustomer(c); m2.setCustomer(c); session.save(c); session.save(m1); session.save(m2); tx.commit(); }
SQL:
Hibernate: insert into cst_customer (cust_name, cust_source, cust_industry, cust_level, cust_linkman, cust_phone, cust_mobile) values (?, ?, ?, ?, ?, ?, ?) Hibernate: insert into cst_linkman (lkm_gender, lkm_name, lkm_phone, lkm_email, lkm_qq, lkm_mobile, lkm_memo, lkm_position, lk_cust_id) values (?, ?, ?, ?, ?, ?, ?, ?, ?) Hibernate: insert into cst_linkman (lkm_gender, lkm_name, lkm_phone, lkm_email, lkm_qq, lkm_mobile, lkm_memo, lkm_position, lk_cust_id) values (?, ?, ?, ?, ?, ?, ?, ?, ?)
6.删除一个顾客,级联删除联系人
/** * 删除一个顾客,级联删除联系人 */ @Test public void fun6(){ Session session = HibernateUtils.openSession(); Transaction tx = session.beginTransaction(); /***------------------S业务逻辑------------**/ //第一种:去掉二者的关系但是不删除数据 Customer c = session.get(Customer.class, 2l);//项目中可能是根据顾客姓名等信息获取到顾客实体(get方法获取出来的实体处于持久态) for(LinkMan man : c.getLinkMans()){ session.delete(man); } //第二种:彻底的删除数据 session.delete(c); /***------------------E业务逻辑------------**/ tx.commit(); }
SQL:
Hibernate: select customer0_.cust_id as cust_id1_0_0_, customer0_.cust_name as cust_nam2_0_0_, customer0_.cust_source as cust_sou3_0_0_, customer0_.cust_industry as cust_ind4_0_0_, customer0_.cust_level as cust_lev5_0_0_, customer0_.cust_linkman as cust_lin6_0_0_, customer0_.cust_phone as cust_pho7_0_0_, customer0_.cust_mobile as cust_mob8_0_0_ from cst_customer customer0_ where customer0_.cust_id=? Hibernate: select linkmans0_.lk_cust_id as lk_cust10_1_0_, linkmans0_.lkm_id as lkm_id1_1_0_, linkmans0_.lkm_id as lkm_id1_1_1_, linkmans0_.lkm_gender as lkm_gend2_1_1_, linkmans0_.lkm_name as lkm_name3_1_1_, linkmans0_.lkm_phone as lkm_phon4_1_1_, linkmans0_.lkm_email as lkm_emai5_1_1_, linkmans0_.lkm_qq as lkm_qq6_1_1_, linkmans0_.lkm_mobile as lkm_mobi7_1_1_, linkmans0_.lkm_memo as lkm_memo8_1_1_, linkmans0_.lkm_position as lkm_posi9_1_1_, linkmans0_.lk_cust_id as lk_cust10_1_1_ from cst_linkman linkmans0_ where linkmans0_.lk_cust_id=? Hibernate: update cst_linkman set lk_cust_id=null where lk_cust_id=? Hibernate: delete from cst_linkman where lkm_id=? Hibernate: delete from cst_linkman where lkm_id=? Hibernate: delete from cst_customer where cust_id=?
3.进阶操作
1.级联操作 配置级联更新、保存和级联删除(开发中设置级联保存和更新即可,一般是业务中手动删除)-----------当前对象中有变动的另一方的时候会级联更新另一方
<!-- cascade:级联操作 简化操作.目的就是为了少些两行代码. 值为save-update表示级联保存、更新 值为delete表示级联删除 值为all:save-update+delete --> <set name="linkMens" cascade="all"> <key column="lkm_cust_id" ></key> <one-to-many class="LinkMan" /> </set>
1.测试级联更新和保存:
修改Customer.hbm.xml配置:
<!-- cascade:级联操作 简化操作.目的就是为了少些两行代码. 值为save-update表示级联保存、更新 值为delete表示级联删除 值为all:save-update+delete --> <set name="linkMens" cascade="save-update"> <key column="lkm_cust_id" ></key> <one-to-many class="LinkMan" /> </set>
测试代码
@Test //保存客户 以及客户 下的联系人 //cascade:save-update public void fun1(){ //1 获得session Session session = HibernateUtil.openSession(); //2 开启事务 Transaction tx = session.beginTransaction(); //------------------------------------------------- //3操作 Customer c = new Customer(); c.setCust_name("ZDSOft"); LinkMan lm1 = new LinkMan(); lm1.setLkm_name("田七"); LinkMan lm2 = newHibernate__多对一 一对多 一对多对多