Nhibernate一对多级联保存_双向映射
- 格式:doc
- 大小:51.00 KB
- 文档页数:4
Hibernate(6)——一对多和多对多关联关系映射(xml和注解)总结涉及的知识点总结如下:∙One to Many 映射关系o多对一单向外键关联(XML/Annotation)o一对多单向外键关联(XML/Annotation)o懒加载和积极加载o一对多双向外键关联(XML/Annotation)∙Many to Many 映射关系o多对多单向外键关联(XML/Annotation)o多对多双向外键关联(XML/Annotation)o set的inverse元素详解∙问题小结∙关联关系的优缺点多对一单向外键关联关系注意多对一关联是多方持有一方的引用。
看一个例子,去淘宝购物,那么一个淘宝用户可以对应多个购物订单,如图所示:多的一方是Orders,持有一方的引用,也就是Users,而在Users中无需作任何定义,从订单到用户的关系是单向多对一关联。
对应数据库就是:还有比如说学生和班级的关系,多个学生可以属于同一个班级,这就是从学生到班级也是典型的单向多对一关系,看代码实现:基于注解的多对一单向外键关联:单向多对一关联中,多方需要持有一方的引用,那么多方(学生类)需要额外配置,需要对持有的一方引用使用注解@ManyToOne (cascade={CascadeType.ALL}, fetch=FetchType.EAGER),设置为级联操作和饥渴的抓取策略,@JoinColumn(name="cid"),而一方(教室类)无需做任何多方的定义。
注意;多方必须保留一个不带参数的构造器!importjavax.persistence.Entity;importjavax.persistence.GeneratedValue;importjavax.persistence.Id;//班级类,在多对一关系中属于一的方,不持有其他多余的配置,反而是被多方持有@Entitypublic class ClassRoom {private intcid;//班级编号private String cname;//班级名称// 自动增长的主键@Id@GeneratedValuepublicintgetCid() {returncid;}public void setCid(intcid) {this.cid = cid;}public String getCname() {returncname;}public void setCname(String cname) {ame = cname;}}View Code一方——班级类无需做多余的定义,下面是多方——学生实体和配置:importjavax.persistence.CascadeType;importjavax.persistence.Entity;importjavax.persistence.FetchType;importjavax.persistence.GeneratedValue;importjavax.persistence.Id;importjavax.persistence.JoinColumn;importjavax.persistence.ManyToOne;//学生实体类,属于多对一的多方,持有班级(一方)的引用@Entitypublic class Students {private intsid; //编号private String sname; //姓名private ClassRoom classroom;//学生班级//注意:多方一定要显式的定义不带参数的构造方法public Students() {}public Students(String sname){this.sname = sname;}// 多方使用注解:@ManyToOne// fetch=FetchType.EAGER,急加载,加载一个实体时,定义急加载的属性会立即从数据库中加载。
hibernate一对多关联映射一对多关联映射映射原理一对多关联映射和多对一关联映射的映射原理是一致的,都是在多的一端加入一个外键,指向一的一端。
关联关系都是由多端维护,只是在写映射时发生了变化。
多对一和一对多的区别多对一和一对多的区别在于维护的关系不同:(1)多对一:多端维护一端的关系,在加载多端时,可以将一端加载上来。
(2)一对多:一端维护多端的关系,在加载一端时,可以将多端加载上来。
分类一对多单向关联映射对象模型从对象模型中,我们可以看出,Group持有User的一个引用。
由于是单向关联,所以数据在加载Group时,会把User 加载上来,但是User并不知道Group的存在。
我们先看一下Group和User的实体,以及映射文件。
GroupUserUser.hbm.xmlGroup.hbm.xml生成的表结构和测试数据缺点1)因为多端User不知道Group的存在(也就是User不维护与Group的关系),所以在保存User时,关系字段groupId 为null,如果该字段设置为非空,则将无法保存数据。
2)因为User不维护关系,而Group维护关系,Group就会发出多余的update语句,保证Group和User有关系,这样加载Group时才把该Users对应的用户加载上来。
一对多双向关联映射对象模型双向关联映射对比单向关联映射,对象的加载方向由单向变成了双向。
我们看一下Group和User的实体,映射文件GroupUserGroup.hbm.xmlUser.hbm.xml生成的表和测试数据一对多双向关联的映射方式:1)在一的一端的集合上采用<key>标签,在多的一端加入一个外键2)在多的一端采用<many-to-one>标签注意:<key>标签和<many-to-one>标签加入的字段保持一直,否则会产生数据混乱。
inverse属性:inverse属性可以用在一对多和多对多双向关联上,inverse属性默认为false,为false表示本端维护关系,如果inverse 为true,则本端不能维护关系,会交给另一端维护关系,本端失效。
Hibernate(6)——一对多和多对多关联关系映射(xml和注解)总结涉及的知识点总结如下:∙One to Many 映射关系o多对一单向外键关联(XML/Annotation)o一对多单向外键关联(XML/Annotation)o懒加载和积极加载o一对多双向外键关联(XML/Annotation)∙Many to Many 映射关系o多对多单向外键关联(XML/Annotation)o多对多双向外键关联(XML/Annotation)o set的inverse元素详解∙问题小结∙关联关系的优缺点多对一单向外键关联关系注意多对一关联是多方持有一方的引用。
看一个例子,去淘宝购物,那么一个淘宝用户可以对应多个购物订单,如图所示:多的一方是Orders,持有一方的引用,也就是Users,而在Users中无需作任何定义,从订单到用户的关系是单向多对一关联。
对应数据库就是:还有比如说学生和班级的关系,多个学生可以属于同一个班级,这就是从学生到班级也是典型的单向多对一关系,看代码实现:基于注解的多对一单向外键关联:单向多对一关联中,多方需要持有一方的引用,那么多方(学生类)需要额外配置,需要对持有的一方引用使用注解@ManyToOne (cascade={CascadeType.ALL}, fetch=FetchType.EAGER),设置为级联操作和饥渴的抓取策略,@JoinColumn(name="cid"),而一方(教室类)无需做任何多方的定义。
注意;多方必须保留一个不带参数的构造器!import ;import ;import ;//班级类,在多对一关系中属于一的方,不持有其他多余的配置,反而是被多方持有public class ClassRoom {private int cid;//班级编号private String cname;//班级名称// 自动增长的主键@Id@GeneratedValuepublic int getCid() {return cid;}public void setCid(int cid) {this.cid = cid;}public String getCname() {return cname;}public void setCname(String cname) {ame = cname;}}View Code一方——班级类无需做多余的定义,下面是多方——学生实体和配置:import ;import ;import ;import ;import ;import ;import ;//学生实体类,属于多对一的多方,持有班级(一方)的引用@Entitypublic class Students {private int sid; //编号private String sname; //姓名private ClassRoom classroom;//学生班级//注意:多方一定要显式的定义不带参数的构造方法public Students() {public Students(String sname){this.sname = sname;}// 多方使用注解:@ManyToOne// fetch=FetchType.EAGER,急加载,加载一个实体时,定义急加载的属性会立即从数据库中加载。
hibernate中双向关联在级联情况下save对象讨论一般在双向关联的情况下,都要在一方设置mappedBy(name="xxx"),由对方主导映射关系。
在多对一的情况下,一般在多的一方设置主导映射的关系(为了方便叙述,就这么叫它了,呵呵)。
所谓主导这种映射关系,如group,user,就是由多的一方(user)里面产生一个外键参考一的一方(group)的主键,这时候user就是主导的一方,写mappedBy是被主导的一方。
在多对多的情况下,随便由那方主导,在数据库表的结构上都是一样的,都会产生一个中间表,中间表有两个字段的联合主键,分别作为外键参考两个多的一方。
在一对多和多对多的双向关联的情况下,并且在cascade=CascadeType.ALL情况下,save 不同方面(如主导关系一方或被主导的一方)在级联的具体表现上是不同的。
分别来讨论一下。
先看一对多的双向关联关系,这里就拿group和user举例。
Group类如下:[java]view plain copy1.package com.chen.hibernate.ormapping;2.3.import java.util.HashSet;4.import java.util.Set;5.6.import javax.persistence.CascadeType;7.import javax.persistence.Entity;8.import javax.persistence.FetchType;9.import javax.persistence.GeneratedValue;10.import javax.persistence.Id;11.import javax.persistence.OneToMany;12.import javax.persistence.Table;13.14.@Entity15.@Table(name = "t_group")16.public class Group {17.private int id;18.private String name;19.private Set<User> users = new HashSet<User>();20.21.@Id22.@GeneratedValue23.public int getId() {24.return id;25. }26.27.public void setId(int id) {28.this.id = id;29. }30.31.public String getName() {32.return name;33. }34.35.public void setName(String name) { = name;37. }38.39.// 设置mappedBy是被主导的一方40.@OneToMany(mappedBy = "group", cascade = { CascadeType.ALL }, fetch = FeZY)41.public Set<User> getUsers() {42.return users;43. }44.45.public void setUsers(Set<User> users) {ers = users;47. }48.}User类如下:[java]view plain copy1.package com.chen.hibernate.ormapping;2.3.import javax.persistence.CascadeType;4.import javax.persistence.Entity;5.import javax.persistence.FetchType;6.import javax.persistence.GeneratedValue;7.import javax.persistence.Id;8.import javax.persistence.JoinColumn;9.import javax.persistence.ManyToOne;10.import javax.persistence.Table;11.12.@Entity13.@Table(name = "t_user")14.public class User {15.private int id;16.private String name;17.private Group group;18.19.@Id20.@GeneratedValue21.public int getId() {22.return id;23. }24.25.public void setId(int id) {26.this.id = id;27. }28.29.public String getName() {30.return name;31. }32.33.public void setName(String name) { = name;35. }36.37.//设置fetch为lazy,多的一方默认问eager38.@ManyToOne(cascade = { CascadeType.ALL }, fetch = FetchType.EAGER)39.@JoinColumn(name = "groupId")40.public Group getGroup() {41.return group;42. }43.44.public void setGroup(Group group) {45.this.group = group;46. }47.}junit测试类[java]view plain copy1.package com.chen.hibernate.ormapping;2.3.import org.hibernate.Session;4.import org.hibernate.SessionFactory;5.import org.hibernate.cfg.AnnotationConfiguration;6.import org.hibernate.tool.hbm2ddl.SchemaExport;7.import org.junit.AfterClass;8.import org.junit.BeforeClass;9.import org.junit.Test;10.11.public class CRUDTest {12.private static SessionFactory sessionFactory = null;13.14.@BeforeClass15.public static void beforeClass() {16.new SchemaExport(new AnnotationConfiguration().configure()).create(17.false, true);18. sessionFactory = new AnnotationConfiguration().configure()19. .buildSessionFactory();20. }21.22.@Test23.public void testSaveUser() {24. User user = new User();25. user.setName("u1");26. Group group = new Group();27. group.setName("g1");28.29.// 这里仅仅把group加到user中,而不需要group.getUsers().add(user);把当前的对象加入到group的set里30. user.setGroup(group);31.32. Session session = sessionFactory.getCurrentSession();33. session.beginTransaction();34. session.save(user);35. session.getTransaction().commit();36. }37.38.// 再看看save被主导的一方会如何。
Java程序员从笨鸟到菜鸟之(五十四)细谈Hibernate(五)Hibernate一对多关系映射前几篇系列博客:细谈Hibernate(一)hibernate基本概念和体系结构细谈Hibernate(二)开发第一个hibernate基本详解细谈Hibernate(三)Hibernate常用API详解及源码分析细谈Hibernate(四)Hibernate常用配置文件详解在前几篇博客,我们初步对Hibernate有了一定的基础性的认知了,也能够简单的用hibernate进行增删改查,但hibernate真正的难度和精髓我们都还没接触到,其中最主要的关联映射就是其中一个,这篇博客,我们就一起来看一下这个hibernate关联映射。
我们大家都知道,在域模型(实体域)中,关联关系是类与类之间最普遍的关系,他是指通过一个对象持有另一个对象的实例根据UML语言,关系是有方向的。
实质上关联映射的本质:将关联关系映射到数据库,所谓的关联关系是对象模型在内存中的一个或多个引用。
搞清关联映射的的关键就在于搞清实体之间的关系。
下面我们首先来看一下具体什么事关联关系:一:关联关系1.关联关系的方向可分为单向关联和双向关联。
单向关联:假设存在两张表person表和address表,如果在应用的业务逻辑中,仅需要每个person实例能够查询得到其对应的Address实例,而Address实例并不需要查询得到其对应的person 实例;或者反之。
双向关联:既需要每个person实例能够查询得到其对应的Address实例,Address实例也需要查询得到其对应的person实例。
2.关联的数量,根据拥有被关联对象的个数确定多对一(many to one):如用户和组学生和班级一对多(one to many):如用户和电子邮件多对多(many to many):如学生选课一对一(one to one):如用户和身份证下面我们就开始讲第一种关联关系:一对多,一对多总共分为:单向一对多,单向多对一,双向一对多。
hibernate关联映射详解一对多关系 1.一对多关系(单向):在一端映射文件中加入:name= student keyumn= classes / one-to-many > /set 2.一对多关系(双向):(1).在一端映射文件中加入: setname= student keycolumn= classesid / one-to-many > /set (2).在多的一端加入: many-to-onename= classes column=classesid /many-to-one 多对一关系在多的一端加入:many-to-one name= classes column= classesid / 一对一关系 1. 一对一外键关联单向:(它其实就是一个多对一映射的一个特例,只是设置ue=”ue”)在维护关系一端的映射文件中加入:many-to-onename= card unique= true cascade= all /many-to-one 双向:在维护关系一端的映射文件中加入: many-to-onename= card unique= true cascade= all /many-to-one 在另一段的映射文件中加入: one-to-onename= users property-ref= card /one-to-one 因为 one-to-one name 标签默认是找关联主键,所以在这里需要property-ref= card 配置关联属性。
Cascade属性是级联的意思。
2. 一对一主键关联 a) 单向在维持关系一端的代码: classname= er_pk idname= id generator > paramname= property card_pk /param /generator /id propertyname= name unique= true /property one-to-onename= card_pk constrain= true /one-to-one /class 设置generator > constrained= true 设置该实体的主键是card_p的外键。
日期作业: 2011年12月8日Hibernate课堂讲课知识点总结:一.hibernate基础知识二.hibernate一对一映射三.hibernate一对多(多对一)映射四.hibernate多对多映射五.hibernate的HQL检索学习一.Hibernate基础知识1.hibernate:hibernate就是一个可以自动的根据xml或annotation完成对象关系映射(orm),并持久化到数据库的开源框架。
是连接java应用程序和关系数据库的中间件,这是对JDBC的封装,主要负责java对象的持久化。
2.ORM(Object Relation Mapping)对象关系映射是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术。
3.hibernate映射文件:它的作用是描述持久层对象以及让他们的属性和数据库中的表和表的字段之间的对应关系。
没有映射文件,Hibernate系统无法完成Java对象和数据库表中的数据的相互转化。
只有通过映射文件,Hibernate才能知道所操作的对象与哪个表相关联。
4.hibernate.cfg.xml文件中包含了hibernate与数据库的基本连接信息。
在Hibernate工作的初始阶段,由Configuration的对象开启hibernate框架,然后将信息加载到SessionFactory实例中。
5.SessionFactory是Hibernate中的一个类,这个类主要负责保存Hibernate的配置信息,以及对Session的操作。
6.手动创建hibernate框架的步骤:a)拷贝所需Jar包到lib目录下,基本包含(antlr.jar,cglib.jsr,asm.jar,commons-collections.jar,commons-logging.jar,jta.jar,dom4j.jar,hibernate3.jar,hibernate-annotations.jar)b)在src目录下创建一个hibernate.cfg.xml文件,该文件包含了hibernate与数据库连接的基本连接信息。
一对多关系示例,Base_Order和Base_OrderDetail是一对多的关系,通过Base_Order.OrderId和Base_O rderDetail.OrderId关联:实体Order代码:[Class(Table = "BASE_Order")]public class Order : Common.BaseEntity{private IList _childList;public Order(){_childList = new ArrayList();}/// <summary>/// 主键/// </summary>[Id(0, Name = "OrderId", Column = "OrderId", TypeType = typeof(int),UnsavedValue="0")][Generator(1, Class = "native")]public virtual int? OrderId { get; set; }/// <summary>/// 订单编号/// </summary>[Property(Name = "OrderCode", Column = "OrderCode", TypeType = typeof(String), Length = 128)]public virtual string OrderCode { get; set; }//订单明细物料,返回一个ArrayList对象[Bag(0, Cascade = "all", Name = "Cascade_OrderDetail",Inverse=true,Lazy=CollectionLazy.False)][Key(1,Column="OrderId")][OneToMany(1, ClassType = typeof(OrderDetail))]public virtual IList Cascade_OrderDetail {get{return _childList;}set{this._childList = value;}}}注意:OrderDetail类中的[KeyManyToOne(1, Column = "OrderId")]声明,是对应Order类中的[Key(1,Column="OrderId")]声明,这两个属性必须成对出现,否则Nhibernate.Mapping.Attributes生成的xml会缺key属性Nhibernate.Mapping.Attributes对应生成的Xml文件:<class table="BASE_Order" name="anization.Domain.Order, anization"><id name="OrderId" column="OrderId" type="Int32" unsaved-value="0"><generator class="native" /></id><property name="OrderCode" type="String" column="OrderCode" length="128" /><bag name="Cascade_OrderDetail" lazy="false" inverse="true" cascade="all"><key column="OrderId" /><one-to-many class="anization.Domain.OrderDetail, anization" /></bag></class>实体OrderDetail代码:[Class(Table = "BASE_OrderDetail")]public class OrderDetail : Common.BaseEntity{/// <summary>/// 主键/// </summary>[Id(0, Name = "OrderDetailId", Column = "OrderDetailId", TypeType = typeof(int), UnsavedValue = "0")][Generator(1, Class = "native")]public virtual int? OrderDetailId { get; set; }/// <summary>/// 物料编号/// </summary>[Property(Name = "ClassCode", Column = "ClassCode", TypeType = typeof(String), Length = 128)]public virtual string ClassCode { get; set; }[ManyToOne(0,Cascade = "all", Name = "Cascade_Order", Column = "OrderId", ClassType = typeof(Order))][KeyManyToOne(1, Column = "OrderId")]public virtual Order Cascade_Order {get;set;}}注意:OrderDetail类中的[KeyManyToOne(1, Column = "OrderId")]声明,是对应Order类中的[Key(1,Column="OrderId")]声明,这两个属性必须成对出现,否则Nhibernate.Mapping.Attributes生成的xml会缺key属性Nhibernate.Mapping.Attributes对应生成的Xml文件:<class table="BASE_OrderDetail" name="anization.Domain.OrderDetail, anization"> <id name="OrderDetailId" column="OrderDetailId" type="Int32" unsaved-value="0"><generator class="native" /></id><property name="ClassCode" type="String" column="ClassCode" length="128" /><many-to-one name="Cascade_Order" class="anization.Domain.Order, anization" col umn="OrderId" cascade="all" /></class>相关业务逻辑:添加Order的同时,向OrderDetail添加一条数据:Order parentObj = new Order();parentObj.OrderCode = "orderCode1523";OrderDetail child1 = new OrderDetail();child1.ClassCode = "classCode1523_1";child1.Cascade_Order = parentObj;parentObj.Cascade_OrderDetail.Add(child1);OrderDetail child2 = new OrderDetail();child2.ClassCode = "classCode1523_2";child2.Cascade_Order = parentObj;parentObj.Cascade_OrderDetail.Add(child2);this.entityBLO.Save(parentObj);Nhibernate自动生成的sql语句:exec sp_executesql N'INSERT INTO BASE_Order (OrderCode) V ALUES (@p0); select SCOPE_IDENTITY()', N'@p0 n varchar(128)', @p0 = N'orderCode1523' goexec sp_executesql N'INSERT INTO BASE_OrderDetail (ClassCode, OrderId) VALUES (@p0, @p1); select SCOPE_ID ENTITY()', N'@p0 nvarchar(128),@p1 int', @p0 = N'classCode1523_1', @p1 = 2 goexec sp_executesql N'INSERT INTO BASE_OrderDetail (ClassCode, OrderId) VALUES (@p0, @p1); select SCOPE_ID ENTITY()', N'@p0 nvarchar(128),@p1 int', @p0 = N'classCode1523_2', @p1 = 2 go修改Order的同时,修改OrderDetail的数据,并且添加一条OrderDetail的数据:Order parentObj = (Order)this.entityBLO.Get(1, typeof(Order));parentObj.OrderCode = "orderCode1523"; //如果新赋值与原值相同,那么Nhibernate不会产生Update语句OrderDetail childObj = (OrderDetail)parentObj.Cascade_OrderDetail[0];childObj.ClassCode = "classCode1523_1"; //直接修改第一个物料,如果新赋值与原值相同,那么Nhibernate不会产生Up date语句OrderDetail childObj3 = new OrderDetail();childObj3 = new OrderDetail();childObj3.ClassCode = "classCode1523_3";childObj3.Cascade_Order = parentObj; //如果不加这句,那么插入的OrderDetail数据的OrderId为NullparentObj.Cascade_OrderDetail.Add(childObj3); //如果不加这句,则无法插入OrderDetail对象this.entityBLO.MergeUpdate(parentObj); //如果是级联保存,则不能用Update方法,Nhibernate会报错:Illegal attempt to associate a collection with two open sessionsNhibernate自动生成的sql语句:INSERT INTO BASE_OrderDetail (ClassCode, OrderId) VALUES (@p0, @p1); select SCOPE_IDENTITY();@p0 = 'cla ssCode1523_3' [Type: String (128)], @p1 = 1 [Type: Int32 (0)]UPDATE BASE_Order SET OrderCode = @p0 WHERE OrderId = @p1;@p0 = 'orderCode1523' [Type: String (128)], @p1 = 1 [Type: Int32 (0)]UPDATE BASE_OrderDetail SET ClassCode = @p0, OrderId = @p1 WHERE OrderDetailId = @p2;@p0 = 'classCode 1523_1' [Type: String (128)], @p1 = 1 [Type: Int32 (0)], @p2 = 1 [Type: Int32 (0)]删除Organization的同时,删除Department的数据:Order parentObj = (Order)this.entityBLO.Get(1, typeof(Order));this.entityBLO.MergeDelete(parentObj);//如果是级联删除,则不能用Delete方法,Nhibernate会报错:Illegal atte mpt to associate a collection with two open sessionsNhibernate自动生成的sql语句:exec sp_executesql N'DELETE FROM BASE_OrderDetail WHERE OrderDetailId = @p0', N'@p0 int', @p0 = 1 go exec sp_executesql N'DELETE FROM BASE_OrderDetail WHERE OrderDetailId = @p0', N'@p0 int', @p0 = 2 go exec sp_executesql N'DELETE FROM BASE_OrderDetail WHERE OrderDetailId = @p0', N'@p0 int', @p0 = 3 go exec sp_executesql N'DELETE FROM BASE_Order WHERE OrderId = @p0', N'@p0 int', @p0 = 1 go注意事项:如果Xml的配置属性Cascade = “all”,表示Order和OrderDetail的数据同时删除如果Xml的配置属性Cascade = “save-update”,表示仅删除Order的数据在NHibernate配置文件中使用<set>, <list>, <map>, <bag>, <array> 和<primitive-array>等元素来定义集合。