简介
关联关系,描述了两个及以上的实体基于数据库连接语义,形成关系
@ManyToOne
@OneToMany
@OneToOne
@ManyToMany
@ManyToOne
@Entity(name = "Person")
public static class Person {
@Id
@GeneratedValue
private Long id;
}
@Entity(name = "Phone")
public static class Phone {
@Id
@GeneratedValue
private Long id;
@Column(name = "`number`")
private String number;
@ManyToOne
@JoinColumn(name = "person_id",
foreignKey = @ForeignKey(name = "PERSON_ID_FK")
)
private Person person;
}
@Test
public void test() {
Person person = new Person();
// INSERT INTO Person ( id ) VALUES ( 1 )
session.save(person);
Phone phone = new Phone();
phone.setNumber("111-222-333");
phone.setPerson(person);
// INSERT INTO Phone ( number, person_id, id ) VALUES ( '123-456-7890', 1, 2 )
session.save(phone);
transaction.commit();
}
@OneToMany
OneToMany
表示,一个父类实体,有一个或多个子类实体。
- 单向连接:
@OneToMany
在子类中没有一个对应的@ManyToOne
,需要一张关联表,删除子类有些麻烦 - 双向连接:
@OneToMany
在子类中有一个对应的@ManyToOne
,不需要关联表,删除子类很方便
单向连接
@Entity(name = "Person")
public static class Person {
@Id
@GeneratedValue
private Long id;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
private List<Phone> phones = new ArrayList<>();
}
@Entity(name = "Phone")
public static class Phone {
@Id
@GeneratedValue
private Long id;
@Column(name = "`number`")
private String number;
}
@Test
public void save() {
// INSERT INTO Person (id) VALUES (1)
Person person = new Person();
// INSERT INTO Phone (number, id) VALUES ('123-456-7890', 2)
// INSERT INTO Phone (number, id) VALUES ('321-654-0987', 3)
Phone phone1 = new Phone( "123-456-7890" );
Phone phone2 = new Phone( "321-654-0987" );
// INSERT INTO Person_Phone (Person_id, phones_id) VALUES (1, 2)
// INSERT INTO Person_Phone (Person_id, phones_id) VALUES (1, 3)
person.getPhones().add( phone1 );
person.getPhones().add( phone2 );
session.save(person);
transaction.commit();
}
// 删除父类
@Test
public void deleteParent() {
/* select person0_.id as id1_0_0_ from person person0_ where person0_.id=?
select
phones0_.Person_id as Person_i1_1_0_,
phones0_.phones_id as phones_i2_1_0_,
phone1_.id as id1_2_1_,
phone1_.number as number2_2_1_
from person_phone phones0_
inner join phone phone1_
on phones0_.phones_id=phone1_.id
where phones0_.Person_id=? */
// delete from person_phone where Person_id=?
// delete from phone where id=?
// delete from person where id=?
Person person = session.get(Person.class, 1l);
session.delete(person);
transaction.commit();
}
// 不支持删除子类
@Test
public void deleteChild() {
/*
select
phone0_.id as id1_2_0_,
phone0_.number as number2_2_0_
from phone phone0_ where phone0_.id=?
delete from phone where id=?
*/
// org.hibernate.engine.jdbc.spi.SqlExceptionHelper logExceptions ERROR: Cannot delete or update a parent row:
Phone phone = session.get(Phone.class, 25l);
session.delete(phone);
transaction.commit();
}
会自行创建一张实体关系表,并自动进行外键关联
Hibernate:
create table person_phone (
Person_id bigint not null,
phones_id bigint not null
)
Hibernate:
alter table person_phone
drop constraint UK_n18iu022oaxft4wgo5ptpvsq9
Hibernate:
alter table person_phone
add constraint UK_n18iu022oaxft4wgo5ptpvsq9 unique (phones_id)
Hibernate:
alter table person_phone
add constraint FKcr8ypojbtlpbtim9b95m6a968
foreign key (phones_id)
references phone (id)
Hibernate:
alter table person_phone
add constraint FK9pbvaylxtjku5s7dle5wwq2oc
foreign key (Person_id)
references person (id)
双向关联
@Entity(name = "Person")
public static class Person {
@Id
@GeneratedValue
private Long id;
@OneToMany(mappedBy = "person", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Phone> phones = new ArrayList<>();
public void addPhone(Phone phone) {
phones.add( phone );
phone.setPerson( this );
}
public void removePhone(Phone phone) {
phones.remove( phone );
phone.setPerson( null );
}
}
@Entity(name = "Phone")
public static class Phone {
@Id
@GeneratedValue
private Long id;
@NaturalId
@Column(name = "`number`", unique = true)
private String number;
@ManyToOne
private Person person;
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
Phone phone = (Phone) o;
return Objects.equals( number, phone.number );
}
@Override
public int hashCode() {
return Objects.hash( number );
}
}
@Test
public void test() {
/*
INSERT INTO Person ( id ) VALUES ( 1 )
INSERT INTO Phone ( "number", person_id, id ) VALUES ( '123-456-7890', 1, 2 )
INSERT INTO Phone ( "number", person_id, id ) VALUES ( '321-654-0987', 1, 3 )
*/
Person person = new Person();
Phone phone1 = new Phone("123-457890");
Phone phone2 = new Phone("123-456-7890");
person.addPhone(phone1);
person.addPhone(phone2);
session.save(person);
transaction.commit();
}
@Test
public void deleteChild() {
/*
select
phone0_.id as id1_1_0_,
phone0_.`number` as number2_1_0_,
phone0_.person_id as person_i3_1_0_,
person1_.id as id1_0_1_
from Phone phone0_
left outer join Person person1_ on phone0_.person_id=person1_.id
where phone0_.id=?
delete from Phone where id=?
*/
Phone phone = session.get(Phone.class, 5l);
session.delete(phone);
transaction.commit();
}
@Test
public void deleteParent() {
/*
select person0_.id as id1_0_0_ from Person person0_ where person0_.id=?
select
phones0_.person_id as person_i3_1_0_,
phones0_.id as id1_1_0_,
phones0_.id as id1_1_1_,
phones0_.`number` as number2_1_1_,
phones0_.person_id as person_i3_1_1_
from Phone phones0_
where phones0_.person_id=?
delete from Phone where id=?
delete from Person where id=?
*/
Person person = session.get(Person.class, 4l);
session.delete(person);
transaction.commit();
}
@OneToOne
- 单向连接:遵循,关系数据库,外键规则,子端控制连接
- 双向连接:父类会有一个
mappedBy @OneToOne
单向连接
@Entity(name = "Phone")
public static class Phone {
@Id
@GeneratedValue
private Long id;
@Column(name = "`number`")
private String number;
// 与 @ManyToOne 比较类似,遵循关系数据库的外键规则
@OneToOne
@JoinColumn(name = "details_id")
private PhoneDetails details;
}
@Entity(name = "PhoneDetails")
public static class PhoneDetails {
@Id
@GeneratedValue
private Long id;
private String provider;
private String technology;
}
双向连接
@Entity(name = "Phone")
public static class Phone {
@Id
@GeneratedValue
private Long id;
@Column(name = "`number`")
private String number;
// mappedBy 对应的是,子类的中,OneToOne 的对象
@OneToOne(
mappedBy = "phone",
cascade = CascadeType.ALL,
orphanRemoval = true,
fetch = FetchType.LAZY
)
private PhoneDetails details;
public void addDetails(PhoneDetails details) {
details.setPhone( this );
this.details = details;
}
public void removeDetails() {
if ( details != null ) {
details.setPhone( null );
this.details = null;
}
}
}
@Entity(name = "PhoneDetails")
public static class PhoneDetails {
@Id
@GeneratedValue
private Long id;
private String provider;
private String technology;
@OneToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "phone_id")
private Phone phone;
}
@Test
public void test() {
// insert into phone (`number`, id) values (?, ?)
// insert into phonedetails (phone_id, provider, technology, id) values (?, ?, ?, ?)
Phone phone = new Phone("123-456-7890");
Phonedetail detail = new Phonedetail("T-Mobile", "GSM");
phone.addDetails(detail);
session.save(phone);
transaction.commit();
}
@ManyToMany
@ManyToMany
需要一个关联表来表示实体之间的关系,就像 @OneToMany
,同时,@ManyToMany
也可以是双向或者单向的
双向连接,有一个拥有者,和一个映射方
单向连接
@Entity(name = "Person")
public static class Person {
@Id
@GeneratedValue
private Long id;
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private List<Address> addresses = new ArrayList<>();
}
@Entity(name = "Address")
public static class Address {
@Id
@GeneratedValue
private Long id;
private String street;
@Column(name = "`number`")
private String number;
}
单向的 @ManyToMany
跟 单向的 @OneToMany
很相像
// 当 entity 中的集合,删除一个元素后,先将连接表中对应的集合元素全部删除,再重新创建当前的集合信息
@Test
public void deleteParent() {
/*
select person0_.id as id1_1_0_ from Person person0_ where person0_.id=?
select
addresses0_.Person_id as Person_i1_2_0_,
addresses0_.addresses_id as addresse2_2_0_,
address1_.id as id1_0_1_,
address1_.`number` as number2_0_1_,
address1_.street as street3_0_1_
from Person_Address addresses0_
inner join Address address1_ on addresses0_.addresses_id=address1_.id
where addresses0_.Person_id=?
delete from Person_Address where Person_id=?
insert into Person_Address (Person_id, addresses_id) values(?, ?)
*/
Person person = session.get(Person.class, 15l);
person.getAddressList().remove(1);
session.update(person);
transaction.commit();
}
双向连接
@Entity(name = "Person")
public static class Person {
@Id
@GeneratedValue
private Long id;
@NaturalId
private String registrationNumber;
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private List<Address> addresses = new ArrayList<>();
public void addAddress(Address address) {
addresses.add( address );
address.getOwners().add( this );
}
public void removeAddress(Address address) {
addresses.remove( address );
address.getOwners().remove( this );
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
Person person = (Person) o;
return Objects.equals( registrationNumber, person.registrationNumber );
}
@Override
public int hashCode() {
return Objects.hash( registrationNumber );
}
}
@Entity(name = "Address")
public static class Address {
@Id
@GeneratedValue
private Long id;
private String street;
@Column(name = "`number`")
private String number;
private String postalCode;
@ManyToMany(mappedBy = "addresses")
private List<Person> owners = new ArrayList<>();
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
Address address = (Address) o;
return Objects.equals( street, address.street ) &&
Objects.equals( number, address.number ) &&
Objects.equals( postalCode, address.postalCode );
}
@Override
public int hashCode() {
return Objects.hash( street, number, postalCode );
}
}
@OneToMany
转换
有些时候,双向的@ManyToMany
在删除或者更新数据的时候,没有 双向的@OneToMany
有效
@Entity(name = "Person")
public static class Person implements Serializable {
@Id
@GeneratedValue
private Long id;
@NaturalId
private String registrationNumber;
@OneToMany(
mappedBy = "person",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PersonAddress> addresses = new ArrayList<>();
public void addAddress(Address address) {
PersonAddress personAddress = new PersonAddress( this, address );
addresses.add( personAddress );
address.getOwners().add( personAddress );
}
public void removeAddress(Address address) {
PersonAddress personAddress = new PersonAddress( this, address );
address.getOwners().remove( personAddress );
addresses.remove( personAddress );
personAddress.setPerson( null );
personAddress.setAddress( null );
}
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
Person person = (Person) o;
return Objects.equals( registrationNumber, person.registrationNumber );
}
@Override
public int hashCode() {
return Objects.hash( registrationNumber );
}
}
@Entity(name = "PersonAddress")
public static class PersonAddress implements Serializable {
@Id
@ManyToOne
private Person person;
@Id
@ManyToOne
private Address address;
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
PersonAddress that = (PersonAddress) o;
return Objects.equals( person, that.person ) &&
Objects.equals( address, that.address );
}
@Override
public int hashCode() {
return Objects.hash( person, address );
}
}
@Entity(name = "Address")
public static class Address implements Serializable {
@Id
@GeneratedValue
private Long id;
private String street;
@Column(name = "`number`")
private String number;
private String postalCode;
@OneToMany(
mappedBy = "address",
cascade = CascadeType.ALL,
orphanRemoval = true
)
private List<PersonAddress> owners = new ArrayList<>();
@Override
public boolean equals(Object o) {
if ( this == o ) {
return true;
}
if ( o == null || getClass() != o.getClass() ) {
return false;
}
Address address = (Address) o;
return Objects.equals( street, address.street ) &&
Objects.equals( number, address.number ) &&
Objects.equals( postalCode, address.postalCode );
}
@Override
public int hashCode() {
return Objects.hash( street, number, postalCode );
}
}
级联关系 cascade
-
CascadeType.PERSIST
:级联新增(又称级联保存):对order对象保存时也对items里的对象也会保存。对应EntityManager
的presist
方法。例子:只有A类新增时,会级联B对象新增。若B对象在数据库存(跟新)在则抛异常(让B变为持久态)
-
CascadeType.MERGE
:级联合并(级联更新):若items属性修改了那么order对象保存时同时修改items里的对象。对应EntityManager
的merge
方法 。
例子:指A类新增或者变化,会级联B对象(新增或者变化) -
CascadeType.REMOVE
:级联删除:对order对象删除也对items里的对象也会删除。对应EntityManager
的remove
方法。例子:REMOVE只有A类删除时,会级联删除B类;
-
CascadeType.REFRESH
:级联刷新:获取order对象里也同时也重新获取最新的items时的对象。对应EntityManager
的refresh(object)
方法有效。即会重新查询数据库里的最新数据。 -
`CascadeType.ALL:以上四种都是。
@Entity(name = "Person")
public static class Person {
@Id
@GeneratedValue
private Long id;
// 不包括 CascadeType.remove
@ManyToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private List<Address> addresses = new ArrayList<>();
}
// 此时进行删除
@Test
public void deleteParent() {
/*
delete from Person_Address where Person_id=?
delete from Person where id=?
*/
Person person = session.get(Person.class, 26l);
session.delete(person);
transaction.commit();
}
@Entity(name = "Person")
public static class Person {
@Id
@GeneratedValue
private Long id;
// 包括 CascadeType.remove
@ManyToMany(cascade = { CascadeType.remove, CascadeType.PERSIST, CascadeType.MERGE})
private List<Address> addresses = new ArrayList<>();
}
// 此时进行删除
@Test
public void deleteParent() {
/*
delete from Person_Address where Person_id=?
delete from Address where id=?
delete from Person where id=?
*/
Person person = session.get(Person.class, 26l);
session.delete(person);
transaction.commit();
}
与外键之间的关系
如果一个实体的某个字段指向另一个实体的主键,就称为 外键
被指向的实体,称之为主实体(主表),也叫父实体(父表)。
负责指向的实体,称之为从实体(从表),也叫子实体(子表)
表之间的关系
- 一对一
- 一对多
- 多对多
级联操作
on update、on delete
决定了在主表数据发生改变时,与之关联的从表数据应该如何处理
属性:
cascade
:关联操作,如果主表被更新或删除,从表也会执行相应的操作set null
:表示从表数据不指向主表任何记录restrict
:拒绝主表的相关操作(默认情况)
参考网站:
Hibernate 官网
https://docs.jboss.org/hibernate/orm/5.5/userguide/html_single/Hibernate_User_Guide.html#associations