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

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,实体类

<span style="font-size:18px;">package com.anlw.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

import org.hibernate.annotations.Cascade;
import org.hibernate.annotations.CascadeType;

@Entity
@Table(name="person_inf")
public class Person {
	//标识属性
	@Id
	@Column(name="person_id11")
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private Integer id;
	private String name1;
	private int age;
	//定义该Person实体关联的Address实体
	@ManyToOne(targetEntity=Address.class)
	//映射外键列,指定外键列的的列名为address_id,不允许为空
	@JoinColumn(name="address_id",nullable=false)
	@Cascade(CascadeType.ALL)
	private Address address;
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName() {
		return name1;
	}
	public void setName(String name1) {
		this.name1 = name1;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public Address getAddress() {
		return address;
	}
	public void setAddress(Address address) {
		this.address = address;
	}
	
}
</span>
上面程序使用了Hibernate本身提供的@Cascade注解,该注解用于指定级联操作策略,此处指定的级联操作策略的CascadeType.ALL---这表明对Person实体的所有持久化操作都会级联到它关联的Address实体;
2,实体类:
<span style="font-size:18px;">package com.anlw.entity;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name="address_inf")
public class Address {
	//标识属性
	@Id
	@Column(name="address_id1")
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private Integer addressId;
	//定义地址详细的成员变量
	private String addressDetail;
	//无参构造器
	public Address(){}
	//初始化全部成员变量的构造器
	public Address(String addressDetail) {
		this.addressDetail = addressDetail;
	}
	public Integer getAddressId() {
		return addressId;
	}
	public void setAddressId(Integer addressId) {
		this.addressId = addressId;
	}
	public String getAddressDetail() {
		return addressDetail;
	}
	public void setAddressDetail(String addressDetail) {
		this.addressDetail = addressDetail;
	}
	
}
</span>

3,核心代码:
<span style="font-size:18px;">package com.anlw.test;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;

import com.anlw.entity.Address;
import com.anlw.entity.Person;

public class Test {
	public static void main(String[] args) {
		Configuration conf = new Configuration().configure();
		ServiceRegistry st = new StandardServiceRegistryBuilder().applySettings(conf.getProperties()).build();
		SessionFactory sf = conf.buildSessionFactory(st);
		Session sess = sf.openSession();
		Transaction tx = sess.beginTransaction();
		Person p = new Person();
		Address a = new Address("guangzhou");
		p.setName("crazyit.org");
		p.setAge(11);
		p.setAddress(a);
		sess.persist(p);
		Address a2 = new Address("shanghai");
		p.setAddress(a2);
		tx.commit();
		sess.close();
		sf.close();

	}
}
</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实体类和核心代码一样的;

<span style="font-size:18px;">package com.anlw.entity;


import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

@Entity
@Table(name="person_inf11")
public class Person {
	//标识属性
	@Id
	@Column(name="person_id11")
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private Integer id;
	private String name2;
	private int age;
	//定义该Person实体关联的Address实体
	@ManyToOne(cascade={CascadeType.ALL},targetEntity=Address.class)
	//显示使用@JoinTable映射连接表
	@JoinTable(name="person_address",//指定连接表的表名为person_address
	//指定连接表中person_id外键列,参照到当前实体对应表的主键列
	joinColumns=@JoinColumn(name="person_idss"
	,referencedColumnName="person_id11",unique=true),
	//指定连接表中address_id外键列,参照到当前实体的关联实体对应表的主键列
	inverseJoinColumns=@JoinColumn(name="address_idff"
	,referencedColumnName="address_id")
	)
	//@Cascade(CascadeType.ALL)
	private Address address;
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getName2() {
		return name2;
	}
	public void setName2(String name2) {
		this.name2 = name2;
	}
	public int getAge() {
		return age;
	}
	public void setAge(int age) {
		this.age = age;
	}
	public Address getAddress() {
		return address;
	}
	public void setAddress(Address address) {
		this.address = address;
	}
	
}
</span>
这个要注意,
<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:(非必须属性)该属性用于为连接表增加唯一约束;

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值