Unidirectional @ManyToMany

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.ManyToMany;
import javax.persistence.Persistence;

public class Test {
    public static void main(String[] args) throws Exception {
        EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
        EntityManager entityManager = factory.createEntityManager();

        Person person1 = new Person();
        Person person2 = new Person();

        Address address1 = new Address("12th Avenue", "12A");
        Address address2 = new Address("18th Avenue", "18B");

        person1.getAddresses().add(address1);
        person1.getAddresses().add(address2);

        person2.getAddresses().add(address1);

        entityManager.getTransaction().begin();
        entityManager.persist(person1);
        entityManager.persist(person2);
        entityManager.flush();
        person1.getAddresses().remove(address1);
        entityManager.getTransaction().commit();

        entityManager.close();
        factory.close();
    }

    @Entity(name = "Person")
    public static class Person {
        @Id
        @GeneratedValue
        private Long id;
        @ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE })
        private List<Address> addresses = new ArrayList<>();

        public Person() {
        }

        public List<Address> getAddresses() {
            return addresses;
        }
    }

    @Entity(name = "Address")
    public static class Address {
        @Id
        @GeneratedValue
        private Long id;
        String street;
        @Column(name = "`number`")
        private String number;

        public Address() {
        }

        public Address(String street, String number) {
            this.street = street;
            this.number = number;
        }
    }
}

生成的 SQL

alter table Person_Address drop constraint FKm7j0bnabh2yr0pe99il1d066u
alter table Person_Address drop constraint FKba7rc9qe2vh44u93u0p2auwti
drop table if exists Address cascade
drop table if exists Person cascade
drop table if exists Person_Address cascade
drop sequence if exists hibernate_sequence

create sequence hibernate_sequence start 1 increment 1
create table Address (id int8 not null, "number" varchar(255), street varchar(255), primary key (id))
create table Person (id int8 not null, primary key (id))
create table Person_Address (Person_id int8 not null, addresses_id int8 not null)
alter table Person_Address add constraint FKm7j0bnabh2yr0pe99il1d066u foreign key (addresses_id) references Address
alter table Person_Address add constraint FKba7rc9qe2vh44u93u0p2auwti foreign key (Person_id) references Person
select nextval ('hibernate_sequence')
select nextval ('hibernate_sequence')
select nextval ('hibernate_sequence')
select nextval ('hibernate_sequence')
insert into Person (id) values (1)
insert into Address ("number", street, id) values ('12A', '12th Avenue', 2)
insert into Address ("number", street, id) values ('18B', '18th Avenue', 3)
insert into Person (id) values (4)
insert into Person_Address (Person_id, addresses_id) values (1, 2)
insert into Person_Address (Person_id, addresses_id) values (1, 3)
insert into Person_Address (Person_id, addresses_id) values (4, 2)
delete from Person_Address where Person_id=1
insert into Person_Address (Person_id, addresses_id) values (1, 3)

注意 person1.getAddresses().remove(address1); 实际导致 delete from Person_Address where Person_id=1insert into Person_Address (Person_id, addresses_id) values (1, 3)

@ManyToMany and CascadeType.ALL

For @ManyToMany associations, the REMOVE entity state transition doesn’t make sense to be cascaded because it will propagate beyond the link table. Since the other side might be referenced by other entities on the parent-side, the automatic removal might end up in a ConstraintViolationException.

但实测不是如此

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.ManyToMany;
import javax.persistence.Persistence;

public class Test {
    public static void main(String[] args) throws Exception {
        EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
        EntityManager entityManager = factory.createEntityManager();

        Person person1 = new Person();
        Person person2 = new Person();

        Address address1 = new Address("12th Avenue", "12A");
        Address address2 = new Address("18th Avenue", "18B");

        person1.getAddresses().add(address1);
        person1.getAddresses().add(address2);

        person2.getAddresses().add(address1);

        entityManager.getTransaction().begin();
        entityManager.persist(person1);
        entityManager.persist(person2);
        entityManager.flush();
        entityManager.remove(person1);
        entityManager.getTransaction().commit();

        entityManager.close();
        factory.close();
    }

    @Entity(name = "Person")
    public static class Person {
        @Id
        @GeneratedValue
        private Long id;
        @ManyToMany(cascade = { CascadeType.ALL })
        private List<Address> addresses = new ArrayList<>();

        public Person() {
        }

        public List<Address> getAddresses() {
            return addresses;
        }
    }

    @Entity(name = "Address")
    public static class Address {
        @Id
        @GeneratedValue
        private Long id;
        String street;
        @Column(name = "`number`")
        private String number;

        public Address() {
        }

        public Address(String street, String number) {
            this.street = street;
            this.number = number;
        }
    }
}

生成 SQL

alter table Person_Address drop constraint FKm7j0bnabh2yr0pe99il1d066u
alter table Person_Address drop constraint FKba7rc9qe2vh44u93u0p2auwti
drop table if exists Address cascade
drop table if exists Person cascade
drop table if exists Person_Address cascade
drop sequence if exists hibernate_sequence

create sequence hibernate_sequence start 1 increment 1
create table Address (id int8 not null, "number" varchar(255), street varchar(255), primary key (id))
create table Person (id int8 not null, primary key (id))
create table Person_Address (Person_id int8 not null, addresses_id int8 not null)
alter table Person_Address add constraint FKm7j0bnabh2yr0pe99il1d066u foreign key (addresses_id) references Address
alter table Person_Address add constraint FKba7rc9qe2vh44u93u0p2auwti foreign key (Person_id) references Person
select nextval ('hibernate_sequence')
select nextval ('hibernate_sequence')
select nextval ('hibernate_sequence')
select nextval ('hibernate_sequence')
insert into Person (id) values (1)
insert into Address ("number", street, id) values ('12A', '12th Avenue', 2)
insert into Address ("number", street, id) values ('18B', '18th Avenue', 3)
insert into Person (id) values (4)
insert into Person_Address (Person_id, addresses_id) values (1, 2)
insert into Person_Address (Person_id, addresses_id) values (1, 3)
insert into Person_Address (Person_id, addresses_id) values (4, 2)
delete from Person_Address where Person_id=1
delete from Address where id=3
delete from Person where id=1

可以看到 address(id=2) 因为还被 person(id=4) 关联,所以不会被 cascade remove 。而 address(id=3) 确实被 cascade remove 了。并没有抛异常 ConstraintViolationException

Bidirectional @ManyToMany

package org.example.demo.hibernate;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

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.ManyToMany;
import javax.persistence.Persistence;

import org.hibernate.annotations.NaturalId;

public class Test {
    public static void main(String[] args) throws Exception {
        EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
        EntityManager entityManager = factory.createEntityManager();

        Person person1 = new Person("ABC-123");
        Person person2 = new Person("DEF-456");

        Address address1 = new Address("12th Avenue", "12A", "4005A");
        Address address2 = new Address("18th Avenue", "18B", "4007B");

        person1.addAddress(address1);
        person1.addAddress(address2);

        person2.addAddress(address1);

        entityManager.getTransaction().begin();
        entityManager.persist(person1);
        entityManager.persist(person2);
        entityManager.flush();
        person1.removeAddress(address1);
        entityManager.getTransaction().commit();

        entityManager.close();
        factory.close();
    }

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

        public Person(String registrationNumber) {
            this.registrationNumber = registrationNumber;
        }

        public List<Address> getAddresses() {
            return addresses;
        }

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

        public Address() {
        }

        public Address(String street, String number, String postalCode) {
            this.street = street;
            this.number = number;
            this.postalCode = postalCode;
        }

        public Long getId() {
            return id;
        }

        public String getStreet() {
            return street;
        }

        public String getNumber() {
            return number;
        }

        public String getPostalCode() {
            return postalCode;
        }

        public List<Person> getOwners() {
            return owners;
        }

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

生成 SQL

alter table Person_Address drop constraint FKm7j0bnabh2yr0pe99il1d066u
alter table Person_Address drop constraint FKbn86l24gmxdv2vmekayqcsgup
drop table if exists Address cascade
drop table if exists Person cascade
drop table if exists Person_Address cascade
drop sequence if exists hibernate_sequence

create sequence hibernate_sequence start 1 increment 1
create table Address (id int8 not null, "number" varchar(255), postalCode varchar(255), street varchar(255), primary key (id))
create table Person (id int8 not null, registrationNumber varchar(255), primary key (id))
create table Person_Address (owners_id int8 not null, addresses_id int8 not null)
alter table Person add constraint UK_23enodonj49jm8uwec4i7y37f unique (registrationNumber)
alter table Person_Address add constraint FKm7j0bnabh2yr0pe99il1d066u foreign key (addresses_id) references Address
alter table Person_Address add constraint FKbn86l24gmxdv2vmekayqcsgup foreign key (owners_id) references Person
select nextval ('hibernate_sequence')
select nextval ('hibernate_sequence')
select nextval ('hibernate_sequence')
select nextval ('hibernate_sequence')
insert into Person (registrationNumber, id) values ('ABC-123', 1)
insert into Address ("number", postalCode, street, id) values ('12A', '4005A', '12th Avenue', 2)
insert into Address ("number", postalCode, street, id) values ('18B', '4007B', '18th Avenue', 3)
insert into Person (registrationNumber, id) values ('DEF-456', 4)
insert into Person_Address (owners_id, addresses_id) values (1, 2)
insert into Person_Address (owners_id, addresses_id) values (1, 3)
insert into Person_Address (owners_id, addresses_id) values (4, 2)
delete from Person_Address where owners_id=1
insert into Person_Address (owners_id, addresses_id) values (1, 3)

Bidirectional many-to-many with a link entity

package org.example.demo.hibernate;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

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;

import org.hibernate.annotations.NaturalId;

public class Test {
    public static void main(String[] args) throws Exception {
        EntityManagerFactory factory = Persistence.createEntityManagerFactory("test");
        EntityManager entityManager = factory.createEntityManager();

        Person person1 = new Person("ABC-123");
        Person person2 = new Person("DEF-456");

        Address address1 = new Address("12th Avenue", "12A", "4005A");
        Address address2 = new Address("18th Avenue", "18B", "4007B");

        entityManager.getTransaction().begin();
        entityManager.persist(person1);
        entityManager.persist(person2);
        entityManager.persist(address1);
        entityManager.persist(address2);

        person1.addAddress(address1);
        person1.addAddress(address2);
        person2.addAddress(address1);

        entityManager.flush();

        person1.removeAddress(address1);
        entityManager.getTransaction().commit();

        entityManager.close();
        factory.close();
    }

    @Entity(name = "Person")
    public static class Person {
        @Id
        @GeneratedValue
        private Long id;

        @NaturalId
        private String registrationNumber;
        @OneToMany(mappedBy = "person", cascade = CascadeType.ALL, orphanRemoval = true)
        private List<PersonAddress> addresses = new ArrayList<>();

        public Person() {
        }

        public Person(String registrationNumber) {
            this.registrationNumber = registrationNumber;
        }

        public Long getId() {
            return id;
        }

        public List<PersonAddress> getAddresses() {
            return addresses;
        }

        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;

        public PersonAddress() {
        }

        public PersonAddress(Person person, Address address) {
            this.person = person;
            this.address = address;
        }

        public Person getPerson() {
            return person;
        }

        public void setPerson(Person person) {
            this.person = person;
        }

        public Address getAddress() {
            return address;
        }

        public void setAddress(Address address) {
            this.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 {
        @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<>();

        public Address() {
        }

        public Address(String street, String number, String postalCode) {
            this.street = street;
            this.number = number;
            this.postalCode = postalCode;
        }

        public Long getId() {
            return id;
        }

        public String getStreet() {
            return street;
        }

        public String getNumber() {
            return number;
        }

        public String getPostalCode() {
            return postalCode;
        }

        public List<PersonAddress> getOwners() {
            return owners;
        }

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

生成 SQL

alter table PersonAddress drop constraint FK8b3lru5fyej1aarjflamwghqq
alter table PersonAddress drop constraint FK7p69mgialumhegyl4byrh65jk
drop table if exists Address cascade
drop table if exists Person cascade
drop table if exists PersonAddress cascade
drop sequence if exists hibernate_sequence

create sequence hibernate_sequence start 1 increment 1
create table Address (id int8 not null, "number" varchar(255), postalCode varchar(255), street varchar(255), primary key (id))
create table Person (id int8 not null, registrationNumber varchar(255), primary key (id))
create table PersonAddress (person_id int8 not null, address_id int8 not null, primary key (person_id, address_id))
alter table Person add constraint UK_23enodonj49jm8uwec4i7y37f unique (registrationNumber)
alter table PersonAddress add constraint FK8b3lru5fyej1aarjflamwghqq foreign key (person_id) references Person
alter table PersonAddress add constraint FK7p69mgialumhegyl4byrh65jk foreign key (address_id) references Address
select nextval ('hibernate_sequence')
select nextval ('hibernate_sequence')
select nextval ('hibernate_sequence')
select nextval ('hibernate_sequence')
insert into Person (registrationNumber, id) values ('ABC-123', 1)
insert into Person (registrationNumber, id) values ('DEF-456', 2)
insert into Address ("number", postalCode, street, id) values ('12A', '4005A', '12th Avenue', 3)
insert into Address ("number", postalCode, street, id) values ('18B', '4007B', '18th Avenue', 4)
insert into PersonAddress (person_id, address_id) values (1, 3)
insert into PersonAddress (person_id, address_id) values (1, 4)
insert into PersonAddress (person_id, address_id) values (2, 3)
delete from PersonAddress where person_id=1 and address_id=3

results matching ""

    No results matching ""