The relation between two entities of bidirectional kind using a table in between allows at database level but at data jpa not allow have the many to one relations that can be possible at database level.
Java Entities
Person Entity
@Entity
@NoArgsConstructor
@Table(name = "person", schema = "public")
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.AUTO, generator="public.person_seq")
private Long id;
private String name;
@OneToOne(cascade = CascadeType.ALL) // fetch default EAGER
@JoinTable( name = "person_personpicture", schema = "public",
joinColumns = { @JoinColumn(name = "person_id")}, // could be omitted
inverseJoinColumns = { @JoinColumn(name = "personpicture_id")}) // could be omitted
private PersonPicture personPicture;
public void addPicture(PersonPicture personPicture){
this.setPersonPicture(personPicture);
personPicture.setPerson(this);
}
public void removePicture(){
this.personPicture.setPerson(null);
this.personPicture = null;
}
PersonPicture Entity
@Entity
@NoArgsConstructor
@Table(name = "person_picture", schema = "public")
public class PersonPicture {
@Id
@GeneratedValue(strategy = GenerationType.AUTO, generator = "public.person_picture_seq")
private Long id;
private String name;
@OneToOne(mappedBy = "personPicture", cascade = CascadeType.ALL)
private Person person;
public void addPerson(Person person){
person.setPersonPicture(this);
this.setPerson(person);
}
public void removePerson(){
this.person.setPersonPicture(null);
this.person = null;
}
Hibernate creates automatically the tables the next way
Hibernate:
create table public.person (
id int8 not null,
name varchar(255),
primary key (id)
)
Hibernate:
create table public.person_personpicture (
personpicture_id int8,
person_id int8 not null,
primary key (person_id)
)
Hibernate:
create table public.person_picture (
id int8 not null,
name varchar(255),
primary key (id)
)
Hibernate:
alter table if exists public.person_personpicture
add constraint FK9nfe8f1mtx9xjvxp5mnqrvhla
foreign key (personpicture_id)
references public.person_picture
Hibernate:
alter table if exists public.person_personpicture
add constraint FKencuxcn11kv9j2vycvuth8575
foreign key (person_id)
references public.person
Test the behaviour
@Transactional
public void createPersonAndPicture(){
Person person1 = new Person("Jose Alberto");
PersonPicture personPicture = new PersonPicture("picture of Person ...");
person1.addPicture(personPicture);
personRepository.save(person1);
}
At the output the queries generated by hibernate on this test.
Saving Person insert also Person_Picture
Hibernate:
select
nextval ('public.person_seq')
Hibernate:
select
nextval ('public.person_picture_seq')
Hibernate:
/* insert com.bext.onetoonebidirectable.entity.PersonPicture
*/ insert
into
public.person_picture
(name, id)
values
(?, ?)
Hibernate:
/* insert com.bext.onetoonebidirectable.entity.Person
*/ insert
into
public.person
(name, id)
values
(?, ?)
Hibernate:
/* insert com.bext.onetoonebidirectable.entity.Person
*/ insert
into
public.person_personpicture
(personpicture_id, person_id)
values
(?, ?)
@Transactional
public void createPersonAndTryMorePictures(){
Person person1 = new Person("Jose Alberto");
PersonPicture personPicture = new PersonPicture("picture of Person ...");
PersonPicture personPicture2 = new PersonPicture("picture of Jose Alberto 2");
person1.addPicture(personPicture);
personRepository.save(person1);
person1.addPicture(personPicture2);
personRepository.save(person1);
}
The queries
Hibernate:
select
nextval ('public.person_seq')
Hibernate:
select
nextval ('public.person_picture_seq')
Hibernate:
/* insert com.bext.onetoonebidirectable.entity.PersonPicture
*/ insert
into
public.person_picture
(name, id)
values
(?, ?)
Hibernate:
/* insert com.bext.onetoonebidirectable.entity.Person
*/ insert
into
public.person
(name, id)
values
(?, ?)
Hibernate:
/* insert com.bext.onetoonebidirectable.entity.Person
*/ insert
into
public.person_personpicture
(personpicture_id, person_id)
values
(?, ?)
Hibernate:
/* load com.bext.onetoonebidirectable.entity.Person */ select
person0_.id as id1_0_2_,
person0_.name as name2_0_2_,
person0_1_.personpicture_id as personpi1_1_2_,
personpict1_.id as id1_2_0_,
personpict1_.name as name2_2_0_,
personpict1_1_.person_id as person_i0_1_0_,
person2_.id as id1_0_1_,
person2_.name as name2_0_1_,
person2_1_.personpicture_id as personpi1_1_1_
from
public.person person0_
left outer join
public.person_personpicture person0_1_
on person0_.id=person0_1_.person_id
left outer join
public.person_picture personpict1_
on person0_1_.personpicture_id=personpict1_.id
left outer join
public.person_personpicture personpict1_1_
on personpict1_.id=personpict1_1_.personpicture_id
left outer join
public.person person2_
on personpict1_1_.person_id=person2_.id
left outer join
public.person_personpicture person2_1_
on person2_.id=person2_1_.person_id
where
person0_.id=?
Hibernate:
select
nextval ('public.person_picture_seq')
Hibernate:
/* insert com.bext.onetoonebidirectable.entity.PersonPicture
*/ insert
into
public.person_picture
(name, id)
values
(?, ?)
Hibernate:
/* update
com.bext.onetoonebidirectable.entity.Person */ update
public.person_personpicture
set
personpicture_id=?
where
person_id=?
@Transactional
public void createPictureAndPerson(){
PersonPicture personPicture = new PersonPicture("picture of Person :)");
Person _person = new Person("Jose Alberto");
personPicture.addPerson( _person);
personPictureRepository.save(personPicture);
}
At the output the queries generated by hibernate on this test.
Saving PersonPicture require previous save Person in order to insert also Person.
Hibernate:
select
nextval ('public.person_picture_seq')
Hibernate:
select
nextval ('public.person_seq')
Hibernate:
/* insert com.bext.onetoonebidirectable.entity.Person
*/ insert
into
public.person
(name, id)
values
(?, ?)
Hibernate:
/* insert com.bext.onetoonebidirectable.entity.PersonPicture
*/ insert
into
public.person_picture
(name, id)
values
(?, ?)
Hibernate:
/* insert com.bext.onetoonebidirectable.entity.Person
*/ insert
into
public.person_personpicture
(personpicture_id, person_id)
values
(?, ?)
@Transactional
public void createPersonAddPicture_checkBidirection(){
createPersonAndPicture();
Person _person = personRepository.findById(1L).get();
log.info("_person: {}", _person);
PersonPicture _personPicture = _person.getPersonPicture();
log.info("_person.getPersonPicture() is Proxy: {}", (_personPicture instanceof HibernateProxy) ? "YES" : "NO"); //YES
log.info("_person.getPersonPicture() in Initialized {}", Hibernate.isInitialized( _personPicture)); //true
log.info("_PersonPicture1: {}", _personPicture);
}
The output create the queries
Hibernate:
select
nextval ('public.person_seq')
Hibernate:
select
nextval ('public.person_picture_seq')
Hibernate:
/* insert com.bext.onetoonebidirectable.entity.PersonPicture
*/ insert
into
public.person_picture
(name, id)
values
(?, ?)
Hibernate:
/* insert com.bext.onetoonebidirectable.entity.Person
*/ insert
into
public.person
(name, id)
values
(?, ?)
Hibernate:
/* insert com.bext.onetoonebidirectable.entity.Person
*/ insert
into
public.person_personpicture
(personpicture_id, person_id)
values
(?, ?)
Hibernate:
select
person0_.id as id1_0_0_,
person0_.name as name2_0_0_,
person0_1_.personpicture_id as personpi1_1_0_,
personpict1_.id as id1_2_1_,
personpict1_.name as name2_2_1_,
personpict1_1_.person_id as person_i0_1_1_,
person2_.id as id1_0_2_,
person2_.name as name2_0_2_,
person2_1_.personpicture_id as personpi1_1_2_
from
public.person person0_
left outer join
public.person_personpicture person0_1_
on person0_.id=person0_1_.person_id
left outer join
public.person_picture personpict1_
on person0_1_.personpicture_id=personpict1_.id
left outer join
public.person_personpicture personpict1_1_
on personpict1_.id=personpict1_1_.personpicture_id
left outer join
public.person person2_
on personpict1_1_.person_id=person2_.id
left outer join
public.person_personpicture person2_1_
on person2_.id=person2_1_.person_id
where
person0_.id=?
2022-06-21 18:49:27.945 INFO 33636 --- [ main] .o.SpringdatajpaentitymappingApplication : _person: Person{id=1, name='Jose Alberto', picture.name=picture of Person ...}
2022-06-21 18:49:27.948 INFO 33636 --- [ main] .o.SpringdatajpaentitymappingApplication : _person.getPersonPicture() is Proxy: NO
2022-06-21 18:49:27.949 INFO 33636 --- [ main] .o.SpringdatajpaentitymappingApplication : _person.getPersonPicture() in Initialized true
2022-06-21 18:49:27.949 INFO 33636 --- [ main] .o.SpringdatajpaentitymappingApplication : _PersonPicture1: PersonPicture{id=1, name='picture of Person ...', person.name=Jose Alberto}
@Transactional
public void createPictureAddPerson_checkBidirection(){
createPictureAndPerson();
PersonPicture _personPicture = personPictureRepository.findById(1L).get();
log.info("_personPicture", _personPicture);
Person _person = _personPicture.getPerson();
log.info("_personPicture.getPerson() is Proxy: {}", (_person instanceof HibernateProxy) ? "YES" : "NO");
log.info("_personPicture.getPerson() is Initialized {}", Hibernate.isInitialized( _person));
log.info("_person: {}", _person);
}
The queries generated
Hibernate:
select
nextval ('public.person_picture_seq')
Hibernate:
select
nextval ('public.person_seq')
Hibernate:
/* insert com.bext.onetoonebidirectable.entity.Person
*/ insert
into
public.person
(name, id)
values
(?, ?)
Hibernate:
/* insert com.bext.onetoonebidirectable.entity.PersonPicture
*/ insert
into
public.person_picture
(name, id)
values
(?, ?)
Hibernate:
/* insert com.bext.onetoonebidirectable.entity.Person
*/ insert
into
public.person_personpicture
(personpicture_id, person_id)
values
(?, ?)
Hibernate:
select
personpict0_.id as id1_2_0_,
personpict0_.name as name2_2_0_,
personpict0_1_.person_id as person_i0_1_0_,
person1_.id as id1_0_1_,
person1_.name as name2_0_1_,
person1_1_.personpicture_id as personpi1_1_1_,
personpict2_.id as id1_2_2_,
personpict2_.name as name2_2_2_,
personpict2_1_.person_id as person_i0_1_2_
from
public.person_picture personpict0_
left outer join
public.person_personpicture personpict0_1_
on personpict0_.id=personpict0_1_.personpicture_id
left outer join
public.person person1_
on personpict0_1_.person_id=person1_.id
left outer join
public.person_personpicture person1_1_
on person1_.id=person1_1_.person_id
left outer join
public.person_picture personpict2_
on person1_1_.personpicture_id=personpict2_.id
left outer join
public.person_personpicture personpict2_1_
on personpict2_.id=personpict2_1_.personpicture_id
where
personpict0_.id=?
2022-06-21 18:53:32.626 INFO 43788 --- [ main] .o.SpringdatajpaentitymappingApplication : _personPicture
2022-06-21 18:53:32.626 INFO 43788 --- [ main] .o.SpringdatajpaentitymappingApplication : _personPicture.getPerson() is Proxy: NO
2022-06-21 18:53:32.626 INFO 43788 --- [ main] .o.SpringdatajpaentitymappingApplication : _personPicture.getPerson() is Initialized true
2022-06-21 18:53:32.626 INFO 43788 --- [ main] .o.SpringdatajpaentitymappingApplication : _person: Person{id=1, name='Jose Alberto', picture.name=picture of Person :)}
@Transactional
public void getPerson_checkPicture(){
createPersonAndPicture();
Person _person = personRepository.findById(1L).get();
PersonPicture personPicture = _person.getPersonPicture();
log.info("Person: {}", _person);
log.info("PersonPicture: {}", personPicture);
}
The queries generated
Hibernate:
select
nextval ('public.person_seq')
Hibernate:
select
nextval ('public.person_picture_seq')
Hibernate:
/* insert com.bext.onetoonebidirectable.entity.PersonPicture
*/ insert
into
public.person_picture
(name, id)
values
(?, ?)
Hibernate:
/* insert com.bext.onetoonebidirectable.entity.Person
*/ insert
into
public.person
(name, id)
values
(?, ?)
Hibernate:
/* insert com.bext.onetoonebidirectable.entity.Person
*/ insert
into
public.person_personpicture
(personpicture_id, person_id)
values
(?, ?)
Hibernate:
select
person0_.id as id1_0_0_,
person0_.name as name2_0_0_,
person0_1_.personpicture_id as personpi1_1_0_,
personpict1_.id as id1_2_1_,
personpict1_.name as name2_2_1_,
personpict1_1_.person_id as person_i0_1_1_,
person2_.id as id1_0_2_,
person2_.name as name2_0_2_,
person2_1_.personpicture_id as personpi1_1_2_
from
public.person person0_
left outer join
public.person_personpicture person0_1_
on person0_.id=person0_1_.person_id
left outer join
public.person_picture personpict1_
on person0_1_.personpicture_id=personpict1_.id
left outer join
public.person_personpicture personpict1_1_
on personpict1_.id=personpict1_1_.personpicture_id
left outer join
public.person person2_
on personpict1_1_.person_id=person2_.id
left outer join
public.person_personpicture person2_1_
on person2_.id=person2_1_.person_id
where
person0_.id=?
2022-06-21 18:56:43.657 INFO 43696 --- [ main] .o.SpringdatajpaentitymappingApplication : Person: Person{id=1, name='Jose Alberto', picture.name=picture of Person ...}
2022-06-21 18:56:43.661 INFO 43696 --- [ main] .o.SpringdatajpaentitymappingApplication : PersonPicture: PersonPicture{id=1, name='picture of Person ...', person.name=Jose Alberto}
@Transactional
public void getPicture_checkPerson(){
createPictureAndPerson();
PersonPicture _personPicture = personPictureRepository.findById(1L).get();
Person _person = _personPicture.getPerson();
log.info("PersonPicture: {}", _personPicture);
log.info("Person: {}", _person);
}
The queries generated
Hibernate:
select
nextval ('public.person_picture_seq')
Hibernate:
select
nextval ('public.person_seq')
Hibernate:
/* insert com.bext.onetoonebidirectable.entity.Person
*/ insert
into
public.person
(name, id)
values
(?, ?)
Hibernate:
/* insert com.bext.onetoonebidirectable.entity.PersonPicture
*/ insert
into
public.person_picture
(name, id)
values
(?, ?)
Hibernate:
/* insert com.bext.onetoonebidirectable.entity.Person
*/ insert
into
public.person_personpicture
(personpicture_id, person_id)
values
(?, ?)
Hibernate:
select
personpict0_.id as id1_2_0_,
personpict0_.name as name2_2_0_,
personpict0_1_.person_id as person_i0_1_0_,
person1_.id as id1_0_1_,
person1_.name as name2_0_1_,
person1_1_.personpicture_id as personpi1_1_1_,
personpict2_.id as id1_2_2_,
personpict2_.name as name2_2_2_,
personpict2_1_.person_id as person_i0_1_2_
from
public.person_picture personpict0_
left outer join
public.person_personpicture personpict0_1_
on personpict0_.id=personpict0_1_.personpicture_id
left outer join
public.person person1_
on personpict0_1_.person_id=person1_.id
left outer join
public.person_personpicture person1_1_
on person1_.id=person1_1_.person_id
left outer join
public.person_picture personpict2_
on person1_1_.personpicture_id=personpict2_.id
left outer join
public.person_personpicture personpict2_1_
on personpict2_.id=personpict2_1_.personpicture_id
where
personpict0_.id=?
2022-06-21 18:58:27.889 INFO 40816 --- [ main] .o.SpringdatajpaentitymappingApplication : PersonPicture: PersonPicture{id=1, name='picture of Person :)', person.name=Jose Alberto}
2022-06-21 18:58:27.893 INFO 40816 --- [ main] .o.SpringdatajpaentitymappingApplication : Person: Person{id=1, name='Jose Alberto', picture.name=picture of Person :)}
@Transactional
public void deletePerson_checkPersonPictureAlsoDeleted(){
createPersonAndPicture();
personRepository.deleteById(1L);
log.info("person exist: {}",personRepository.existsById(1L)); // false
log.info("personPicture exist: {}", personPictureRepository.existsById(1L)); // false
}
The queries executed
Hibernate:
select
nextval ('public.person_seq')
Hibernate:
select
nextval ('public.person_picture_seq')
Hibernate:
/* insert com.bext.onetoonebidirectable.entity.PersonPicture
*/ insert
into
public.person_picture
(name, id)
values
(?, ?)
Hibernate:
/* insert com.bext.onetoonebidirectable.entity.Person
*/ insert
into
public.person
(name, id)
values
(?, ?)
Hibernate:
/* insert com.bext.onetoonebidirectable.entity.Person
*/ insert
into
public.person_personpicture
(personpicture_id, person_id)
values
(?, ?)
Hibernate:
select
person0_.id as id1_0_0_,
person0_.name as name2_0_0_,
person0_1_.personpicture_id as personpi1_1_0_,
personpict1_.id as id1_2_1_,
personpict1_.name as name2_2_1_,
personpict1_1_.person_id as person_i0_1_1_,
person2_.id as id1_0_2_,
person2_.name as name2_0_2_,
person2_1_.personpicture_id as personpi1_1_2_
from
public.person person0_
left outer join
public.person_personpicture person0_1_
on person0_.id=person0_1_.person_id
left outer join
public.person_picture personpict1_
on person0_1_.personpicture_id=personpict1_.id
left outer join
public.person_personpicture personpict1_1_
on personpict1_.id=personpict1_1_.personpicture_id
left outer join
public.person person2_
on personpict1_1_.person_id=person2_.id
left outer join
public.person_personpicture person2_1_
on person2_.id=person2_1_.person_id
where
person0_.id=?
Hibernate:
/* delete com.bext.onetoonebidirectable.entity.Person */ delete
from
public.person_personpicture
where
person_id=?
Hibernate:
/* delete com.bext.onetoonebidirectable.entity.Person */ delete
from
public.person
where
id=?
Hibernate:
/* delete com.bext.onetoonebidirectable.entity.PersonPicture */ delete
from
public.person_picture
where
id=?
Hibernate:
/* select
count(*)
from
Person x
WHERE
x.id = :id */ select
count(*) as col_0_0_
from
public.person person0_
where
person0_.id=?
2022-06-21 19:03:46.476 INFO 58620 --- [ main] .o.SpringdatajpaentitymappingApplication : person exist: false
Hibernate:
/* select
count(*)
from
PersonPicture x
WHERE
x.id = :id */ select
count(*) as col_0_0_
from
public.person_picture personpict0_
where
personpict0_.id=?
2022-06-21 19:03:46.478 INFO 58620 --- [ main] .o.SpringdatajpaentitymappingApplication : personPicture exist: false
@Transactional
public void deletePersonPicture_checkPersonAlsoDeleted(){
createPictureAndPerson();
List<PersonPicture> all = personPictureRepository.findAll();
all.forEach( pp -> log.info("finadAll personPictures: {}",pp) );
//personRepository.deleteById(1L); // This delete Person and PersonPicture
personPictureRepository.deleteById(1L); // This don´t Delete PersonPicture
log.info("personPicture exist: {}", personPictureRepository.existsById(1L)); // true
log.info("person: {}", personRepository.existsById(1L)); // true
}
The queries executed
Hibernate:
select
nextval ('public.person_picture_seq')
Hibernate:
select
nextval ('public.person_seq')
Hibernate:
/* insert com.bext.onetoonebidirectable.entity.Person
*/ insert
into
public.person
(name, id)
values
(?, ?)
Hibernate:
/* insert com.bext.onetoonebidirectable.entity.PersonPicture
*/ insert
into
public.person_picture
(name, id)
values
(?, ?)
Hibernate:
/* insert com.bext.onetoonebidirectable.entity.Person
*/ insert
into
public.person_personpicture
(personpicture_id, person_id)
values
(?, ?)
Hibernate:
/* select
generatedAlias0
from
PersonPicture as generatedAlias0 */ select
personpict0_.id as id1_2_,
personpict0_.name as name2_2_,
personpict0_1_.person_id as person_i0_1_
from
public.person_picture personpict0_
left outer join
public.person_personpicture personpict0_1_
on personpict0_.id=personpict0_1_.personpicture_id
Hibernate:
select
person0_.id as id1_0_0_,
person0_.name as name2_0_0_,
person0_1_.personpicture_id as personpi1_1_0_,
personpict1_.id as id1_2_1_,
personpict1_.name as name2_2_1_,
personpict1_1_.person_id as person_i0_1_1_,
person2_.id as id1_0_2_,
person2_.name as name2_0_2_,
person2_1_.personpicture_id as personpi1_1_2_
from
public.person person0_
left outer join
public.person_personpicture person0_1_
on person0_.id=person0_1_.person_id
left outer join
public.person_picture personpict1_
on person0_1_.personpicture_id=personpict1_.id
left outer join
public.person_personpicture personpict1_1_
on personpict1_.id=personpict1_1_.personpicture_id
left outer join
public.person person2_
on personpict1_1_.person_id=person2_.id
left outer join
public.person_personpicture person2_1_
on person2_.id=person2_1_.person_id
where
person0_.id=?
2022-06-21 19:06:21.852 INFO 55232 --- [ main] .o.SpringdatajpaentitymappingApplication : finadAll personPictures: PersonPicture{id=1, name='picture of Person :)', person.name=Jose Alberto}
Hibernate:
select
personpict0_.id as id1_2_0_,
personpict0_.name as name2_2_0_,
personpict0_1_.person_id as person_i0_1_0_,
person1_.id as id1_0_1_,
person1_.name as name2_0_1_,
person1_1_.personpicture_id as personpi1_1_1_,
personpict2_.id as id1_2_2_,
personpict2_.name as name2_2_2_,
personpict2_1_.person_id as person_i0_1_2_
from
public.person_picture personpict0_
left outer join
public.person_personpicture personpict0_1_
on personpict0_.id=personpict0_1_.personpicture_id
left outer join
public.person person1_
on personpict0_1_.person_id=person1_.id
left outer join
public.person_personpicture person1_1_
on person1_.id=person1_1_.person_id
left outer join
public.person_picture personpict2_
on person1_1_.personpicture_id=personpict2_.id
left outer join
public.person_personpicture personpict2_1_
on personpict2_.id=personpict2_1_.personpicture_id
where
personpict0_.id=?
Hibernate:
/* delete com.bext.onetoonebidirectable.entity.Person */ delete
from
public.person_personpicture
where
person_id=?
Hibernate:
/* delete com.bext.onetoonebidirectable.entity.PersonPicture */ delete
from
public.person_picture
where
id=?
Hibernate:
/* delete com.bext.onetoonebidirectable.entity.Person */ delete
from
public.person_personpicture
where
person_id=?
Hibernate:
/* delete com.bext.onetoonebidirectable.entity.Person */ delete
from
public.person
where
id=?
Hibernate:
/* select
count(*)
from
PersonPicture x
WHERE
x.id = :id */ select
count(*) as col_0_0_
from
public.person_picture personpict0_
where
personpict0_.id=?
2022-06-21 19:06:21.874 INFO 55232 --- [ main] .o.SpringdatajpaentitymappingApplication : personPicture exist: false
Hibernate:
/* select
count(*)
from
Person x
WHERE
x.id = :id */ select
count(*) as col_0_0_
from
public.person person0_
where
person0_.id=?
2022-06-21 19:06:21.877 INFO 55232 --- [ main] .o.SpringdatajpaentitymappingApplication : person: false
eot