双向1-N关联

对于1-N关联,Hibernate推荐使用双向关联,而且不要让1的一端控制关联关系,而使用N端控制。双向的1-N关联与N-1关联完全相同,两端都需要增加对关联属性的访问,N的一端增加引用到关联实体的属性,1的一端增加集合属性,集合元素为关联实体。


无连接表的双向1-N关联

N端使用@ManyToOne来修饰代表关联实体的属性,1端使用OneToMany来修饰。由于不希望1端控制关联关系,所以需要为1端的OneToMany指定mappedBy属性来接触这一端的控制权。放弃了控制权,就没法使用JoinColumn或者@JoinTable修饰代表的关联实体的属性了。

@Entity
@Table(name = "personb_inf")
public class PersonB {
	@Id
	@Column(name = "person_id")
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Integer id;
	private String name;
	private int age;

	@OneToMany(targetEntity = Address.class, mappedBy = "person")
	private Set<Address> address = new HashSet<>();

}

这个Person类中使用了OneToMany并且设定了mappedBy属性修饰Set集合,该集合用于记录关联的一系列Address实体。这个实体不控制关联关系。

@Entity
@Table(name = "address_inf")
public class Address {
	@Id
	@Column(name = "addrress_id")
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private int addressId;
	private String addressDetail;

	@ManyToOne(targetEntity = Person.class)
	@JoinColumn(name = "personB_id", referencedColumnName = "person_id", nullable = false)
	private PersonB person;

	public Address() {
	}

	public Address(String addressDetail) {
		this.addressDetail = addressDetail;
	}
}

Address端需要增加一个Person属性。哪一端不控制,哪一端就使用这个属性,表明Address与Person存在N-1的关系。

private static void createAndStorePersons() {
		Session session = HibernateUtil.getSession();
		Transaction tx = session.beginTransaction();
		
		PersonB p=new PersonB();
		p.setName("alvin");
		p.setAge(23);
		session.save(p);
		Address a=new Address("beijing");
		a.setPerson(p);
		session.persist(a);
		Address a1=new Address("shanghai");
		a1.setPerson(p);
		session.persist(a1);
		
		
		tx.commit();
		session.close();

	}

mysql> select * from persone_inf;

+-----------+-----+-------+

| person_id | age | name  |

+-----------+-----+-------+

|         1 |  23 | alvin |

+-----------+-----+-------+


mysql> select * from addresse_inf;

+-------------+---------------+-----------+

| addrress_id | addressDetail | person_id |

+-------------+---------------+-----------+

|           1 | beijing       |         1 |

|           2 | shanghai      |         1 |

+-------------+---------------+-----------+


Address中使用JoinColumn映射外键列,在address_inf中增加名为person_id的外键列,也就意味着address_inf作为从表。测试代码保存的p对象相当于向person_inf中插入一条。之后又保存了两个address对象,并建立person与address的关联关系。这里我们要注意最好先持久化Person对象,因为程序希望持久化address对象时,address的外键已经存在。先设定person与address的关系再保存address对象,否则address中的外键没有值。等到设置关联关系,还得再使用一次update语句更新address表中的外键值。不要通过person设置address,而是要通过address来设置person,因为person没有控制权。


有连接表的双线1-N关联

此时1端无需任何改变,只需要在N端使用@JoinTable指定连接表。address中代码如下

@OneToMany(targetEntity = PersonB.class)
@JoinTable(name = "person_address", 
joinColumns = @JoinColumn(name = "addredd_id", referencedColumnName = "address_id", unique = true), 
inverseJoinColumns = @JoinColumn(name = "person_id", referencedColumnName = "person_id"))
	
private Set<Address> address = new HashSet<>();

person类的代码与不使用连接表完全相同。如果希望person也得到控制权,则需要在person中也适用JoinTable,指定同一张表。由于使用了连接表管理关联关系,无所谓主表从表。持久化顺序没有下影响。


双向N-N关联

要求两端都使用Set集合属性,两端都增加对集合属性的访问。这种方式只能通过连接表来实现。两边分别使用ManyToMany标注Set属性也都是用JoinTable指定连接表。


双向1-1关联

两端都奥增加引用相关实体的属性。


基于外键的双向1-1关联

两端都使用@OneToOne注解。基于外键时,外键可以存放在任意一端,存放外键的一端需要使用JoinColumn注解来映射外键,并且制定它的unique=true来标识这一段是1。本来平等的两个表,由于有一端增加了外键,因此就变成了从表。主表应该使用mappedBy解除控制权。


基于连接表的双线1-1关联

这种情况相当罕见。两端都要使用@JoinTable,也都要使用@OneToOne。双方的注解是对等的,双方的映射表是一样的,两者都有控制权。