HIbernate 领域模型关联

简介

关联关系,描述了两个及以上的实体基于数据库连接语义,形成关系

  • @ManyToOne
  • @OneToMany
  • @OneToOne
  • @ManyToMany

@ManyToOne

image-20210907221008727

@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 ,不需要关联表,删除子类很方便

单向连接

image-20210908110550573

@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)

双向关联

image-20210908123149628

@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

单向连接

image-20210908135312799

@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;

}

双向连接

image-20210908135947786

@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 也可以是双向或者单向的

双向连接,有一个拥有者,和一个映射方

单向连接

image-20210908141749746

@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();
}

双向连接

image-20210908151231397

@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 有效

image-20210908151600707

@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里的对象也会保存。对应EntityManagerpresist 方法。

    例子:只有A类新增时,会级联B对象新增。若B对象在数据库存(跟新)在则抛异常(让B变为持久态)

  • CascadeType.MERGE:级联合并(级联更新):若items属性修改了那么order对象保存时同时修改items里的对象。对应 EntityManagermerge 方法 。
    例子:指A类新增或者变化,会级联B对象(新增或者变化)

  • CascadeType.REMOVE:级联删除:对order对象删除也对items里的对象也会删除。对应 EntityManagerremove 方法。

    例子:REMOVE只有A类删除时,会级联删除B类;

  • CascadeType.REFRESH:级联刷新:获取order对象里也同时也重新获取最新的items时的对象。对应 EntityManagerrefresh(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();
}

与外键之间的关系

如果一个实体的某个字段指向另一个实体的主键,就称为 外键
被指向的实体,称之为主实体(主表),也叫父实体(父表)。
负责指向的实体,称之为从实体(从表),也叫子实体(子表)

image-20210908123149628

表之间的关系

  • 一对一
  • 一对多
  • 多对多

级联操作

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

原文地址:https://www.cnblogs.com/Kevin-QAQ/p/15246293.html