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:作用于关联数据的外键