Unidirectional @OneToMany
When using a unidirectional @OneToMany association, Hibernate resorts to using a link table between the two joining entities.
package org.example.demo.hibernate;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Persistence;
public class Test {
public static void main(String[] args) throws Exception {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
EntityManager em = factory.createEntityManager();
Person person = new Person();
Phone phone1 = new Phone();
phone1.number = "123-456-7890";
Phone phone2 = new Phone();
phone2.number = "321-654-0987";
person.phones.add(phone1);
person.phones.add(phone2);
em.getTransaction().begin();
em.persist(person);
em.flush();
person.phones.remove(phone1);
em.getTransaction().commit();
em.close();
factory.close();
}
@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;
}
}
生成 SQL
alter table Person_Phone drop constraint FKr38us2n8g5p9rj0b494sd3391
alter table Person_Phone drop constraint FK2ex4e4p7w1cj310kg2woisjl2
drop table if exists Person cascade
drop table if exists Person_Phone cascade
drop table if exists Phone cascade
drop sequence if exists hibernate_sequence
create sequence hibernate_sequence start 1 increment 1
create table Person (id int8 not null, primary key (id))
create table Person_Phone (Person_id int8 not null, phones_id int8 not null)
create table Phone (id int8 not null, "number" varchar(255), primary key (id))
alter table Person_Phone add constraint UK_9uhc5itwc9h5gcng944pcaslf unique (phones_id)
alter table Person_Phone add constraint FKr38us2n8g5p9rj0b494sd3391 foreign key (phones_id) references Phone
alter table Person_Phone add constraint FK2ex4e4p7w1cj310kg2woisjl2 foreign key (Person_id) references Person
select nextval ('hibernate_sequence')
select nextval ('hibernate_sequence')
select nextval ('hibernate_sequence')
insert into Person (id) values (1)
insert into Phone ("number", id) values ('123-456-7890', 2)
insert into Phone ("number", id) values ('321-654-0987', 3)
insert into Person_Phone (Person_id, phones_id) values (1, 2)
insert into Person_Phone (Person_id, phones_id) values (1, 3)
delete from Person_Phone where Person_id=1
insert into Person_Phone (Person_id, phones_id) values (1, 3)
delete from Phone where id=2
注意
- 默认使用关联表 Person_Phone
- Person_Phone 中创建了 UK(phones_id) ,这是对的,因为是 OneToMany 而不是 ManyToMany
- 从 person.phones 中删除一个 phone 会导致全部删除,再新增未删除的 phones
如果没有 orphanRemoval = true
即第 47 行改为 @OneToMany(cascade = CascadeType.ALL)
则生成 SQL
alter table Person_Phone drop constraint FKr38us2n8g5p9rj0b494sd3391
alter table Person_Phone drop constraint FK2ex4e4p7w1cj310kg2woisjl2
drop table if exists Person cascade
drop table if exists Person_Phone cascade
drop table if exists Phone cascade
drop sequence if exists hibernate_sequence
create sequence hibernate_sequence start 1 increment 1
create table Person (id int8 not null, primary key (id))
create table Person_Phone (Person_id int8 not null, phones_id int8 not null)
create table Phone (id int8 not null, "number" varchar(255), primary key (id))
alter table Person_Phone add constraint UK_9uhc5itwc9h5gcng944pcaslf unique (phones_id)
alter table Person_Phone add constraint FKr38us2n8g5p9rj0b494sd3391 foreign key (phones_id) references Phone
alter table Person_Phone add constraint FK2ex4e4p7w1cj310kg2woisjl2 foreign key (Person_id) references Person
select nextval ('hibernate_sequence')
select nextval ('hibernate_sequence')
select nextval ('hibernate_sequence')
insert into Person (id) values (1)
insert into Phone ("number", id) values ('123-456-7890', 2)
insert into Phone ("number", id) values ('321-654-0987', 3)
insert into Person_Phone (Person_id, phones_id) values (1, 2)
insert into Person_Phone (Person_id, phones_id) values (1, 3)
delete from Person_Phone where Person_id=1
insert into Person_Phone (Person_id, phones_id) values (1, 3)
只缺少 delete from Phone where id=2
语句,其它语句都相同。
如果第 47 行后加上 @JoinColumn
package org.example.demo.hibernate;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Persistence;
public class Test {
public static void main(String[] args) throws Exception {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
EntityManager em = factory.createEntityManager();
Person person = new Person();
Phone phone1 = new Phone();
phone1.number = "123-456-7890";
Phone phone2 = new Phone();
phone2.number = "321-654-0987";
person.phones.add(phone1);
person.phones.add(phone2);
em.getTransaction().begin();
em.persist(person);
em.flush();
person.phones.remove(phone1);
em.getTransaction().commit();
em.close();
factory.close();
}
@Entity(name = "Person")
public static class Person {
@Id
@GeneratedValue
private Long id;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn
private List<Phone> phones = new ArrayList<>();
}
@Entity(name = "Phone")
public static class Phone {
@Id
@GeneratedValue
private Long id;
@Column(name = "`number`")
private String number;
}
}
则生成 SQL
alter table Phone drop constraint FKewtbuqvkbuyqht4j6q9gye645
drop table if exists Person cascade
drop table if exists Phone cascade
drop sequence if exists hibernate_sequence
create sequence hibernate_sequence start 1 increment 1
create table Person (id int8 not null, primary key (id))
create table Phone (id int8 not null, "number" varchar(255), phones_id int8, primary key (id))
alter table Phone add constraint FKewtbuqvkbuyqht4j6q9gye645 foreign key (phones_id) references Person
select nextval ('hibernate_sequence')
select nextval ('hibernate_sequence')
select nextval ('hibernate_sequence')
insert into Person (id) values (1)
insert into Phone ("number", id) values ('123-456-7890', 2)
insert into Phone ("number", id) values ('321-654-0987', 3)
update Phone set phones_id=1 where id=2
update Phone set phones_id=1 where id=3
update Phone set phones_id=null where phones_id=1 and id=2
delete from Phone where id=2
这时就不是使用 link table 了,而是使用 foreign key ,只是因为 @JoinColumn 没有指定列名,生成的列名怪怪的。如果指定列名
package org.example.demo.hibernate;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.Persistence;
public class Test {
public static void main(String[] args) throws Exception {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
EntityManager em = factory.createEntityManager();
Person person = new Person();
Phone phone1 = new Phone();
phone1.number = "123-456-7890";
Phone phone2 = new Phone();
phone2.number = "321-654-0987";
person.phones.add(phone1);
person.phones.add(phone2);
em.getTransaction().begin();
em.persist(person);
em.flush();
person.phones.remove(phone1);
em.getTransaction().commit();
em.close();
factory.close();
}
@Entity(name = "Person")
public static class Person {
@Id
@GeneratedValue
private Long id;
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "person_id")
private List<Phone> phones = new ArrayList<>();
}
@Entity(name = "Phone")
public static class Phone {
@Id
@GeneratedValue
private Long id;
@Column(name = "`number`")
private String number;
}
}
生成 SQL
alter table Phone drop constraint FKmw13yfsjypiiq0i1osdkaeqpg
drop table if exists Person cascade
drop table if exists Phone cascade
drop sequence if exists hibernate_sequence
create sequence hibernate_sequence start 1 increment 1
create table Person (id int8 not null, primary key (id))
create table Phone (id int8 not null, "number" varchar(255), person_id int8, primary key (id))
alter table Phone add constraint FKmw13yfsjypiiq0i1osdkaeqpg foreign key (person_id) references Person
select nextval ('hibernate_sequence')
select nextval ('hibernate_sequence')
select nextval ('hibernate_sequence')
insert into Person (id) values (1)
insert into Phone ("number", id) values ('123-456-7890', 2)
insert into Phone ("number", id) values ('321-654-0987', 3)
update Phone set person_id=1 where id=2
update Phone set person_id=1 where id=3
update Phone set person_id=null where person_id=1 and id=2
delete from Phone where id=2
还是有点怪: persist Phone 时先 insert Phone 再 update set person_id ,删除 Phone 时,先 update person_id=null 再删除
Bidirectional @OneToMany
Although the Domain Model exposes two sides to navigate this association, behind the scenes, the relational database has only one foreign key for this relationship.
Every bidirectional association must have one owning side only (the child side), the other one being referred to as the inverse (or the mappedBy) side.
package org.example.demo.hibernate;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Persistence;
public class Test {
public static void main(String[] args) throws Exception {
EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
EntityManager em = factory.createEntityManager();
Person person = new Person();
Phone phone1 = new Phone();
phone1.number = "123-456-7890";
Phone phone2 = new Phone();
phone2.number = "321-654-0987";
person.phones.add(phone1);
phone1.person = person;
person.phones.add(phone2);
phone2.person = person;
em.getTransaction().begin();
em.persist(person);
em.flush();
person.phones.remove(phone1);
em.getTransaction().commit();
em.close();
factory.close();
}
@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<>();
}
@Entity(name = "Phone")
public static class Phone {
@Id
@GeneratedValue
private Long id;
@Column(name = "`number`")
private String number;
@ManyToOne
private Person person;
}
}
生成 SQL
alter table Phone drop constraint FKmw13yfsjypiiq0i1osdkaeqpg
drop table if exists Person cascade
drop table if exists Phone cascade
drop sequence if exists hibernate_sequence
create sequence hibernate_sequence start 1 increment 1
create table Person (id int8 not null, primary key (id))
create table Phone (id int8 not null, "number" varchar(255), person_id int8, primary key (id))
alter table Phone add constraint FKmw13yfsjypiiq0i1osdkaeqpg foreign key (person_id) references Person
select nextval ('hibernate_sequence')
select nextval ('hibernate_sequence')
select nextval ('hibernate_sequence')
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)
delete from Phone where id=2
如果将第 50 行改为 @OneToMany(mappedBy = "person", cascade = CascadeType.ALL)
即去掉 orphanRemoval = true
则生成 SQL
alter table Phone drop constraint FKmw13yfsjypiiq0i1osdkaeqpg
drop table if exists Person cascade
drop table if exists Phone cascade
drop sequence if exists hibernate_sequence
create sequence hibernate_sequence start 1 increment 1
create table Person (id int8 not null, primary key (id))
create table Phone (id int8 not null, "number" varchar(255), person_id int8, primary key (id))
alter table Phone add constraint FKmw13yfsjypiiq0i1osdkaeqpg foreign key (person_id) references Person
select nextval ('hibernate_sequence')
select nextval ('hibernate_sequence')
select nextval ('hibernate_sequence')
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)
即没有最后的 delete from Phone where id=2
语句