Hibernate的关联映射(1)-单向N-1关联

拼搏现实的明天。 2022-07-20 16:18 259阅读 0赞

Hibernate的关联映射

1,单向关系:只需单向访问关联端,例如:只能通过老师访问学生,或者只能通过学生访问老师;

2,双向关系:关联的两端可以互相访问,例如老师和学生之间可以互相访问;

单向关联可以分:

单向1-1

单向1-N

单向N-1

单向N-N

双向关联可以分:

双向1-1

双向1-N

双向N-N

双向关系里没有N-1,因为双向N-1和1-N是完全一样的;

=================================================

单向N-1关联

对于N-1关联,无论是单向还是双向,都需要在N的一端使用@ManyToOne修饰代表关联实体的属性,使用manyToOne注解可以使用下面的属性:

cascase:(非必须属性)指定Hibernate对关联实体采用怎样的级联策略,该级联策略支持如下五个属性:

1.CascadeType.ALL:指定Hibernate将多有的持久化操作都级联到关联实体;

2.CascadeType.MERGE:指定Hibernate将merge操作级联到关联实体;

3.CascadeType.PERSIST:指定Hibernate将persist操作级联到关联实体;

4.CascadeType.REFRESH:指定Hibernate将refresh操作级联到关联实体;

5.CascadeType.REMOVE:指定Hibernate将remove操作级联到关联实体;

fetch:(非必须属性)指定抓取关联实体时的抓取策略,该属性支持如下两个属性值:

1.FetchType.EAGER:抓取实体时,立即抓取关联实体,这是默认值;

2.FetchType.LAZY:抓取实体时,延迟抓取关联实体,等到正真用到关联实体时采取抓取;

optional:(非必须属性)该属性指定关联关系是否可选

targetEntity:(非必须属性)该属性指定关联实体的类名,在默认情况下,Hibernate将通过反射来判断关联实体的类名;

(cascade={CascadeType.ALL})这个必须有

对于大部分的关联关系,Hibernate都可以通过反射来确定关联实体的类型,因此可以无需指定targetEntity属性,但在一些特殊情况下,例如使用@OneToMany,@ManyToMany修饰的1-N,N-N关联,如果用于表示关联实体的Set集合不带泛型信息,那就必须指定targetEntity属性;

1>无连接表的N-1关联

对于无连接表的N-1关联而言,程序只要在N的一端增加一列外键,让外键值记录该对象所属的实体即可,Hiberante可以使用@JoinColumn来修饰代表关联实体的属性,@JoinColumn用于映射底层的外键列;

直接使用@JoinColumn注解来映射N-1关联时,hibernate将无需使用连接表,直接使用外键关联策略来处理这种关联映射;

下面的两个持久化类描述了这种关联关系,Person类中增加了一个Address类型的属性,引用关联的Address实体,为了让Hibernate理解该Address类型的属性是关联实体,程序需要使用@ManyToOne,@JoinColumn修饰该属性;

1,实体类

  1. <span style="font-size:18px;">package com.anlw.entity;
  2. import javax.persistence.Column;
  3. import javax.persistence.Entity;
  4. import javax.persistence.GeneratedValue;
  5. import javax.persistence.GenerationType;
  6. import javax.persistence.Id;
  7. import javax.persistence.JoinColumn;
  8. import javax.persistence.ManyToOne;
  9. import javax.persistence.Table;
  10. import org.hibernate.annotations.Cascade;
  11. import org.hibernate.annotations.CascadeType;
  12. @Entity
  13. @Table(name="person_inf")
  14. public class Person {
  15. //标识属性
  16. @Id
  17. @Column(name="person_id11")
  18. @GeneratedValue(strategy=GenerationType.IDENTITY)
  19. private Integer id;
  20. private String name1;
  21. private int age;
  22. //定义该Person实体关联的Address实体
  23. @ManyToOne(targetEntity=Address.class)
  24. //映射外键列,指定外键列的的列名为address_id,不允许为空
  25. @JoinColumn(name="address_id",nullable=false)
  26. @Cascade(CascadeType.ALL)
  27. private Address address;
  28. public Integer getId() {
  29. return id;
  30. }
  31. public void setId(Integer id) {
  32. this.id = id;
  33. }
  34. public String getName() {
  35. return name1;
  36. }
  37. public void setName(String name1) {
  38. this.name1 = name1;
  39. }
  40. public int getAge() {
  41. return age;
  42. }
  43. public void setAge(int age) {
  44. this.age = age;
  45. }
  46. public Address getAddress() {
  47. return address;
  48. }
  49. public void setAddress(Address address) {
  50. this.address = address;
  51. }
  52. }
  53. </span>

上面程序使用了Hibernate本身提供的@Cascade注解,该注解用于指定级联操作策略,此处指定的级联操作策略的CascadeType.ALL—-这表明对Person实体的所有持久化操作都会级联到它关联的Address实体;
2,实体类:

  1. <span style="font-size:18px;">package com.anlw.entity;
  2. import javax.persistence.Column;
  3. import javax.persistence.Entity;
  4. import javax.persistence.GeneratedValue;
  5. import javax.persistence.GenerationType;
  6. import javax.persistence.Id;
  7. import javax.persistence.Table;
  8. @Entity
  9. @Table(name="address_inf")
  10. public class Address {
  11. //标识属性
  12. @Id
  13. @Column(name="address_id1")
  14. @GeneratedValue(strategy=GenerationType.IDENTITY)
  15. private Integer addressId;
  16. //定义地址详细的成员变量
  17. private String addressDetail;
  18. //无参构造器
  19. public Address(){}
  20. //初始化全部成员变量的构造器
  21. public Address(String addressDetail) {
  22. this.addressDetail = addressDetail;
  23. }
  24. public Integer getAddressId() {
  25. return addressId;
  26. }
  27. public void setAddressId(Integer addressId) {
  28. this.addressId = addressId;
  29. }
  30. public String getAddressDetail() {
  31. return addressDetail;
  32. }
  33. public void setAddressDetail(String addressDetail) {
  34. this.addressDetail = addressDetail;
  35. }
  36. }
  37. </span>

3,核心代码:

  1. <span style="font-size:18px;">package com.anlw.test;
  2. import org.hibernate.Session;
  3. import org.hibernate.SessionFactory;
  4. import org.hibernate.Transaction;
  5. import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
  6. import org.hibernate.cfg.Configuration;
  7. import org.hibernate.service.ServiceRegistry;
  8. import com.anlw.entity.Address;
  9. import com.anlw.entity.Person;
  10. public class Test {
  11. public static void main(String[] args) {
  12. Configuration conf = new Configuration().configure();
  13. ServiceRegistry st = new StandardServiceRegistryBuilder().applySettings(conf.getProperties()).build();
  14. SessionFactory sf = conf.buildSessionFactory(st);
  15. Session sess = sf.openSession();
  16. Transaction tx = sess.beginTransaction();
  17. Person p = new Person();
  18. Address a = new Address("guangzhou");
  19. p.setName("crazyit.org");
  20. p.setAge(11);
  21. p.setAddress(a);
  22. sess.persist(p);
  23. Address a2 = new Address("shanghai");
  24. p.setAddress(a2);
  25. tx.commit();
  26. sess.close();
  27. sf.close();
  28. }
  29. }
  30. </span>

上面的程序创建了三个持久化实体,即一个Pserson对象,两个Address对象,程序只保存了一次Person对象,从来没有保存过Address对象,程序第一次创建了一个瞬态的Address对象,当程序执行到persist()处时,系统准备保存person对象,系统要向person_inf数据表插入一条记录,但是该记录参照的主表记录(Address,被参照的Address实体还处于瞬态)还不曾保存,这时可能出现下面两种情况:

(1),系统抛出TransientObjectException异常:object references an unsaved transient instance,因为主表记录不曾插入,所以参照该记录的从表记录无法插入;

(2),系统先自动级联插入主表记录,再插入从表记录;
因为上面的实体类中使用了@Cascade(CascadeType.All)注解,修饰代表关联实体的属性,这意味着系统将自动级联插入主表记录,也就是先持久化Address对象,再持久化Person对象,也就是说,Hibernate在执行persist()方法时,先执行一条insert into address…语句,再执行一条insert into person…语句;从另一个角度讲,如果上面的实体类缺少了@Cascade(CascadeType.All)注解,则程序在运行到persist()时会抛出TransientObjectException异常;

程序第二次创建瞬态的Address对象,但当程序执行到p.setAddress(a2)时,程序将瞬态的Address对象关联到持久化状态下的Person对象,类似的,系统也会自动持久化Address对象,再建立Address和Person对象之间的关联,也就是说,Hibernate在p.setAddress(a2)时先执行一条insert into …语句插入记录,再执行update person…语句修改该Person记录的外键值;

注意:在所有既有的基于外键约束的关联关系中,都必须牢记:要么总是先持久化主表记录对应的实体,要么设置级联操作,否则当Hibernate试图插入从表记录时,如果发现该从表记录参照的主表记录不存在,那么一定会抛出异常;

==============================================================================================================================================================================

2>有连接表的N-1关联

与无连接表的代码只是Person实体类的注解不同,其他address实体类和核心代码一样的;

  1. <span style="font-size:18px;">package com.anlw.entity;
  2. import javax.persistence.CascadeType;
  3. import javax.persistence.Column;
  4. import javax.persistence.Entity;
  5. import javax.persistence.GeneratedValue;
  6. import javax.persistence.GenerationType;
  7. import javax.persistence.Id;
  8. import javax.persistence.JoinColumn;
  9. import javax.persistence.JoinTable;
  10. import javax.persistence.ManyToOne;
  11. import javax.persistence.Table;
  12. @Entity
  13. @Table(name="person_inf11")
  14. public class Person {
  15. //标识属性
  16. @Id
  17. @Column(name="person_id11")
  18. @GeneratedValue(strategy=GenerationType.IDENTITY)
  19. private Integer id;
  20. private String name2;
  21. private int age;
  22. //定义该Person实体关联的Address实体
  23. @ManyToOne(cascade={CascadeType.ALL},targetEntity=Address.class)
  24. //显示使用@JoinTable映射连接表
  25. @JoinTable(name="person_address",//指定连接表的表名为person_address
  26. //指定连接表中person_id外键列,参照到当前实体对应表的主键列
  27. joinColumns=@JoinColumn(name="person_idss"
  28. ,referencedColumnName="person_id11",unique=true),
  29. //指定连接表中address_id外键列,参照到当前实体的关联实体对应表的主键列
  30. inverseJoinColumns=@JoinColumn(name="address_idff"
  31. ,referencedColumnName="address_id")
  32. )
  33. //@Cascade(CascadeType.ALL)
  34. private Address address;
  35. public Integer getId() {
  36. return id;
  37. }
  38. public void setId(Integer id) {
  39. this.id = id;
  40. }
  41. public String getName2() {
  42. return name2;
  43. }
  44. public void setName2(String name2) {
  45. this.name2 = name2;
  46. }
  47. public int getAge() {
  48. return age;
  49. }
  50. public void setAge(int age) {
  51. this.age = age;
  52. }
  53. public Address getAddress() {
  54. return address;
  55. }
  56. public void setAddress(Address address) {
  57. this.address = address;
  58. }
  59. }
  60. </span>

这个要注意,

  1. <span style="font-size:18px;">@ManyToOne(cascade={CascadeType.ALL},targetEntity=Address.class)</span>

一定要添加级联注解,否则会抛异常;

对于使用连接表的N-1关联而言,由于两个实体对应的数据表都无需增加外键列,因此两个实体对应的数据表不存在主从关系,程序完全可以想先持久化哪个实体,就先持久化哪个实体,无论先持久化哪个实体,程序都不会引发性能问题;

@JoinTable专门用于映射底层连接表的信息,使用@JoinTable注解时可以指定下面的属性:

name:(非必须属性)指定该连接表的表名;

catalog:(非必须属性)设置将该连接表放入指定的catalog中,如果没有指定该属性,连接表将放入默认的catalog中;

schema:(非必须属性)设置将该连接表放入指定的schema中,如果没有指定该属性,连接表将放入默认的schema中;

targetEntity:(非必须属性)该属性指定关联实体的类名,在默认情况下,hibernate将通过反射来判断关联实体的类名;

indexes:(非必须属性)该属性值为@Index注解数组,用于为该连接表定义多个索引;

joinColumns:(非必须属性)该属性值可以接受多个@JoinColumn,用于配置连接表中的外键列信息,这些外键列参照当前实体对应表的主键列;

inverseJoinColumns:(非必须属性)该属性值可以接受多个@JoinColumn,用于配置连接表中外键列的列信息,这些外键列参照当前实体的关联实体对应表的主键列;

uniqueConstraints:(非必须属性)该属性用于为连接表增加唯一约束;

发表评论

表情:
评论列表 (有 0 条评论,259人围观)

还没有评论,来说两句吧...

相关阅读