码农小汪-Hibernate学习9-hibernate双向关联关系注解表示@OneToMany mappedBy @ManyToMany @JoinTable
来源:互联网 发布:手机淘宝 降价提醒 编辑:程序博客网 时间:2024/05/23 01:13
之前我学习了单向的关联,现在继续学习,双向的关联。这个关联关系的理解还是有点复杂,慢慢的理解懂了就好啦!这个过程不是一蹴而就的。我们需要不断的积累,才可以有所成绩的。
年轻人,不要怕~慢慢来
对啦,有的时候我们可能会采用逆向工程产生实体哦。所以我们要看得懂,会修改,会改变
双向1-N关联
对于1-N的关联,Hibernate 推荐使用双向的关联,而不要让1的端(也就是有Set这个集合的那一个)控制关联的关系。而是使用N的一端控制关联关系。双向的1-N和N-1关联其实就是一回事。两端都需要增加对关联属性的访问,N的一端增加引用关联实体的属性,1的一端增加集合属性,集合元素为关联的实体.
Hibernate 同样对月双向关联的映射提供了两种技术支持:一种是有连接表的,一种是无连接表的。对于大部分来说,对于1-N的双向关联我们采用无连接表的策略即可。
无连接的双向的1-N关联
无连接的双向的1-N关联,N的一端肯定是使用@ManyToOne 我们肯定不会默生的涩,1的一端使用@OneToMany.
底层的数据库在记录这种1-N的关联关系的时候,其实就是我们的外键而已,只需要在N的一端增加一个外键列就好啦。因此在使用@ManyToOne的同时,还需要使用@JointColumn来映射外键列。
对于双向的1-N关联的映射,通常不会让1(set集合)的端控制关系,这样没次都需要更新我们的set集合,很不方便的说。而是该由我们的N的一端控制关联的关系。因此我们使用@OneToMany的时候中应该指明mappedBy (映射由谁控制) mappedBy属性—–一但为@OneToMany,@ManyToMany指定了这个属性,则表明当前的实体不能控制关联的关系。放弃控制关系之后,就不能指明@JoinColumn 或@JointTable 来修饰关联实体的属性啦(这两个实体都是外键和第三张表啊来维护关系)
例子:下面的@OneToMany在关联实体的set集合,指明了mappedBy那么1 的端放弃控制关联关系,其实这样也好。每次都更新set集合好累啊。
这个所谓的维护关联关系,主要是看底层的表中是否有外键,如果有外键的话,肯定的先有外键生成,才可以产生现在的嘛。不能乱了顺序涩。
比如 person这个一的端 现在不维护关联关系了 这么说嘛person的 底层表中没得关于地址的任何消息所以我可以随便的保存person实体。而不需要任何的条件。 但是address就不行了,因为现在表中有了person_id这个外键。,所以必须等到有了person实体的属性,才可以让我的地址产生并且维护他们之间的关系。 后面有例子的。这个只是我自己的看法。以后忘了再来看看当时的心德。
例子~~
Person 1个人 多个地址
@Entity@Table(name="person_inf")public class Person{ @Id @Column(name="person_id") @GeneratedValue(strategy=GenerationType.IDENTITY) private Integer id; private String name; private int age; // 定义该Person实体所有关联的Address实体 // 指定mappedBy属性表明该Person实体不控制关联关系 //这里的person是address中的person @OneToMany(targetEntity=Address.class , mappedBy="person") private Set<Address> addresses = new HashSet<>();...}
然后Address中只需要增加一个Person person这个属性。需要使用@ManyToOne @JoinColumn 设置外键
继续看例子
@Entity@Table(name="address_inf")public class Address{ @Id @Column(name="address_id") @GeneratedValue(strategy=GenerationType.IDENTITY) private int addressId; private String addressDetail; // 定义该Address实体关联的Person实体 @ManyToOne(targetEntity=Person.class) // 定义名为person_id外键列,该外键列引用person_inf表的person_id列。 @JoinColumn(name="person_id" , referencedColumnName="person_id" , nullable=false) private Person person;...}
不知道你是不是觉得好神奇啊,我们的底层的数据库都是一样的都是增加了外键,只是在person中增加了个set然后呢,我们就双向关联了,任意的获得对方的属性啦。开源的力量特别叼
底层数据和我们的1-N的一样的哦~我是觉得很厉害~不知道你认为呢
Session session = HibernateUtil.currentSession(); Transaction tx = session.beginTransaction(); // 创建一个瞬态的Person对象 Person p = new Person(); p.setName("Test"); p.setAge(29);
你说现在我可以保存到数据库? Person中的set没有赋值哦。可能你忘记啦,我们表的关系是有address维护的,在address中有个外键咯。所以现在我们的person表和address没有关系。我的person当然可以持久化啦。这个 毫无疑问
// 持久化Person对象(对应于插入主表记录) session.save(p);没问题
创建address啦
Address a = new Address("遵义市海龙坝");
现在可以保存?当然不可以,我们必须有外键呢,外键都没得,所以得设置关联关系。并且我们还必须是个持久化的对象,不能是瞬态的对象,上面的person是个持久态的,所以可以。为啥瞬态的不行呢?因为我们没有设置级联关系,插入的时候不会级联的操作,这样的话,外键表Id不存在,会报错的出现异常。你懂了?我也是慢慢的自己分析懂啦
// 先设置Person和Address之间的关联关系 a.setPerson(p); // 再持久化Address对象(对应于插入从表记录) session.persist(a); //a 作为外键必须是持久化对象,不然会报错的 tx.commit(); HibernateUtil.closeSession();
你懂了吧?这个理解很重要哦,Hibernate的难点,持久化对象,关联关系。记得大二的时候觉得框架好特别,没得人带我飞,自己看这个Hibernate,看的那个各种晕,现在好辣,终于可以懂啦。不错,说明在进步。
有连接表的双向1-N关联
我们的关注底层数据库以怎么样的形式呈现给我们呢,因为增加第三张表来维护关系。Person和Address表中都没有外键啦!这样子不需要担心保存我们的Person 信息和Address 信息。只是在建立两者之间的关联关系的时候,放在第三张表中,要保证我们的外键有效,什么是有效呢?address.setPonsen(b) 这种情况下要保证adress 和 b都是有效的,数据库中存在的,才会插入第三张表中没得问题。
对于有连接表的1-N,让N端控制关系的话,1的端无需改变,N的端和单向有连接表的类似
@Id @Column(name="address_id") @GeneratedValue(strategy=GenerationType.IDENTITY) private int addressId; private String addressDetail; // 定义该Address实体关联的Person实体 @ManyToOne(targetEntity=Person.class) // 映射连接表,指定连接表为person_address @JoinTable(name="person_address", // 指定连接表中address_id列参照当前实体对应数据表的address_id主键列 joinColumns=@JoinColumn(name="address_id" , referencedColumnName="address_id", unique=true), // 指定连接表中person_id列参照当前实体的关联实体对应数据表的person_id主键列 inverseJoinColumns=@JoinColumn(name="person_id" , referencedColumnName="person_id") ) private Person person; ...}
person –我们还是不维持关系吧。其实吧,这里谁维持关系都一样。都是插入第三张表中,性能没得影响。person中也可以使用@JoinTableb 表名一样就行啦。不需要指定谁维护,都一样啊!
因为是第三张表中的哈哈。
@Entity@Table(name="person_inf")public class Person{ @Id @Column(name="person_id") @GeneratedValue(strategy=GenerationType.IDENTITY) private Integer id; private String name; private int age; // 定义该Person实体所有关联的Address实体 @OneToMany(targetEntity=Address.class ,mappedBy="person")这两种的区别就是把,维护关系不啊~ /*@OneToMany(targetEntity=Address.class) // 映射连接表,指定连接表为person_address @JoinTable(name="person_address", // 指定连接表中person_id列参照当前实体对应数据表的person_id主键列 joinColumns=@JoinColumn(name="person_id" , referencedColumnName="person_id"), // 指定连接表中address_id列参照当前实体的关联实体对应数据表的address_id主键列 inverseJoinColumns=@JoinColumn(name="address_id" , referencedColumnName="address_id", unique=true) )*/ private Set<Address> addresses = new HashSet<>();
看上面的两种关系都可以,维护维护关系影响不大的,自己看着办就是啦。因为第三张表啦。对于性能没影响,不是更新,是插入第三张表。两者都可以维护关系
看你理解没理解,不需要维护关系是啥子意思呢?
我们来操作就知道啦
// 创建一个瞬态的Person对象 Person p = new Person(); p.setName("Test"); p.setAge(21);
// 创建一个瞬态的Address对象 Address a = new Address("遵义市海龙坝");
现在可以a.setPerson? 答案不可以的,第三张表中有两个外键,作为主键,引用了我们的Person和Address .现在的Person和address对象都不是持久化对象在数据库中不存在。不行吧??
session.save(p);session.save(a)a.setPerson(p).commit()...这样子就行啦
下面的也是可以的
a.setPerson(p);// 持久化Address对象session.persist(a);// 持久化Person对象session.save(p);tx.commit();
为啥子 可以呢,persist并不是立即转换为insert语句的,save是立即的。所以这样子就好像可以啦~两个外键都存在。
双向的N-N关联
双向的N-N 两端都需要set集合,两端都需要增加对Set集合的属性的访问,这里的Set集合是关联关系的,不要和以前的不是关联关系的搞混了。双向的有太多的可能,所以只能采用连接表来操作啦。来表示实体之间的关系。
双向的N-N关联,两端都要set分别使用@ManyToMany 都要使用@JoinTable 显示的连接在一起,和刚刚一对N不同的是(1-N增加了unique约束)
需要说明的是,哪端想放弃控制关联关系,可以使用mappBy。也就不能使用@joinTable啦。其实吧上面都是说过了的。建立第三张表啦。我们两端都可以随意的控制关联关系。重点在于底层的数据库怎么实现的。理解清楚啦,你就知道现在可以持久化?没有控制关系诶!
来来例子,虽然都是差不多,但是还是来啊~
@Entity@Table(name="person_inf")public class Person{ @Id @Column(name="person_id") @GeneratedValue(strategy=GenerationType.IDENTITY) private Integer id; private String name; private int age; @ManyToMany(targetEntity=Address.class) // 映射连接表,指定连接表的表名为person_address @JoinTable(name="person_address", // 映射连接表中名为person_id的外键列, // 该列参照当前实体对应表的person_id主键列 joinColumns=@JoinColumn(name="person_id" , referencedColumnName="person_id"), // 映射连接表中名为address_id的外键列, // 该列参数当前实体的关联实体对应表的address_id主键列 inverseJoinColumns=@JoinColumn(name="address_id" , referencedColumnName="address_id") ) private Set<Address> addresses = new HashSet<>();
一样的继续
@Entity@Table(name="address_inf")public class Address{ // 标识属性 @Id @Column(name="address_id") @GeneratedValue(strategy=GenerationType.IDENTITY) private int addressId; // 定义地址详细信息的成员变量 private String addressDetail; // 定义该Address实体所有关联的Person实体 @ManyToMany(targetEntity=Person.class) // 映射连接表,指定连接表的表名为person_address @JoinTable(name="person_address", // 映射连接表中名为address_id的外键列, // 该列参照当前实体对应表的address_id主键列 joinColumns=@JoinColumn(name="address_id" , referencedColumnName="address_id"), // 映射连接表中名为person_id的外键列, // 该列参照当前实体对应表的person_id主键列 inverseJoinColumns=@JoinColumn(name="person_id" , referencedColumnName="person_id") ) private Set<Person> persons = new HashSet<>();...}
两面都没放弃控制权,哈哈。没必要!就是晓得原理啦,随意来啊~~
我就是任性
// 创建一个Person对象 Person p = new Person();
// 持久化Person对象(对应于插入主表记录) session.save(p);
// 创建一个瞬态的Address对象 Address a = new Address("遵义市海龙坝"); // 先设置Person和Address之间的关联关系 a.getPersons().add(p); // 再持久化Address对象 session.persist(a);
这上面没错的,我先持久化了person,创建关联关系。在持久化地址。地值在持久化的时候,会先创建插入地址,在插入到关系表中的。
也可以这样写
session.save(a);
a.getPersons().add(p);
这样也是可以的,最后提交的时候就插入到了数据库中。多自己分析哈,自然的就懂了。
双向的1-1关联
基于外键的哦
直接的代码。前面的懂了,这点还不是小意思。你维护不维护实体关系呢?一方维护,一方不维护涩
总有一方要先把实体存到数据库中,然后另一方才有外键存在呢。
有mappedBy的不维护,那么低层的数据表中就不会出现啦,所以,可以自己创建并保存到数据库,不用去理会这个属性。
@Entity@Table(name="person_inf")public class Person{ // 标识属性 @Id @Column(name="person_id") @GeneratedValue(strategy=GenerationType.IDENTITY) private Integer id; private String name; private int age; // 定义该Person实体关联的Address实体 @OneToOne(targetEntity=Address.class , mappedBy="person") private Address address; ...}
所以下面
Person p=new Person();
session.save(p)没有压力
…
继续地址,这里没有使用连接表哦。还是有先后顺序的。必须先有person在有address;
@Entity@Table(name="address_inf")public class Address{ // 标识属性 @Id @Column(name="address_id") @GeneratedValue(strategy=GenerationType.IDENTITY) private int addressId; // 定义地址详细信息的成员变量 private String addressDetail; // 定义该Address实体关联的Person实体 @OneToOne(targetEntity=Person.class) // 用于映射person_id外键列,参照person_inf表的person_id列 // 指定了unique=true表明是1-1关联 @JoinColumn(name="person_id" , referencedColumnName="person_id" , unique=true) private Person person;..}
地层的数据库中多了个person_id 外键列
Address address=new Address();
address.addPerson(持久化的Person);
session.save(address)
没有问题涩。
下面继续。有链接表的
可以两端都有JoinTable unique熟悉必须的涩,因为是1-1必须具有唯一性另外一段不想控制关系,可以使用mappedBy涩.简单搞定
@Entity@Table(name="person_inf")public class Person{ @Id @Column(name="person_id") @GeneratedValue(strategy=GenerationType.IDENTITY) private Integer id; private String name; private int age; // 定义该Person实体关联的Address实体 @OneToOne(targetEntity=Address.class) // 映射底层连接表,表名为person_address @JoinTable(name="person_address", // 映射连接表的外键列,增加unique=true表明是1-1关联 joinColumns=@JoinColumn(name="person_id" , referencedColumnName="person_id" , unique=true), // 映射连接表的外键列,增加unique=true表明是1-1关联 inverseJoinColumns=@JoinColumn(name="address_id" , referencedColumnName="address_id", unique=true) ) private Address address;
… 不想贴了
- 码农小汪-Hibernate学习9-hibernate双向关联关系注解表示@OneToMany mappedBy @ManyToMany @JoinTable
- 码农小汪-Hibernate学习8-hibernate关联关系注解表示@OneToMany mappedBy @ManyToMany @JoinTable
- Hibernate学习总结:ManyToMany双向关联
- Hibernate 使用 Annotation 之 OneToMany(双向关联关系)
- Hibernate xml配置方法之OneToMany(双向关联关系)
- Hibernate Annotation注解方式完成表映射OneToOne, OneToMany, ManyToMany
- 【Hibernate】双向关联的oneTomany练习
- Hibernate关系映射(七)多对多双向关联@ManyToMany Annotation方式
- 3 OneToMany ManyToMany MappedBy Cascade
- Hibernate学习总结:ManyToMany单向关联
- Hibernate学习总结:OneToMany/ManyToOne双向
- Hibernate注解之@ManyToMany
- 在Hibernate中@ManyTOMany 单/双向外键关联
- Hibernate @OneToMany的mappedBy和@JoinColumn总结
- Hibernate学习总结:OneToMany单向关联
- hibernate 单向外键关联,一对一双向外间关联凡是双向关联必设mappedBy!!!
- 关于hibernate的onetomany和manytomany
- hibernate中的@OneToMany、@ManyToOne以及@ManyToMany
- 100个直接可以拿来用的JavaScript实用功能代码片段
- 概率论与数量统计(二)3___ 第一章__概率,频率定义
- android开发--android-async-http
- 求最长公共序列长度-递归
- R基础知识
- 码农小汪-Hibernate学习9-hibernate双向关联关系注解表示@OneToMany mappedBy @ManyToMany @JoinTable
- hdu2059龟兔赛跑
- 普通树转化成二叉树
- ES6-let带来了更简单的块级作用域
- 【01背包】Bone Collector
- 希尔排序
- Step by Step into Spring (事务)
- 【BZOJ1030】【Tyvj1806】文本生成器,AC自动机+DP
- Windows7安装U盘 DIY 制作全攻略