Hibernate: @OneToOne, @OneToMany, @ManyToMnay relationship explanation

1. One to One relationship.

a) Baeldung. 

Let's have a look at the following ER diagram, which represents a foreign key-based one-to-one mapping:

An ER Diagram mapping Users to Addresses via an address_id foreign key

In this example, the address_id column in users is the foreign key to address.

package com.example.relationships.entity;

import jakarta.persistence.*;
import lombok.*;
import org.hibernate.Hibernate;

import java.util.Objects;

@Getter
@Setter
@ToString
@RequiredArgsConstructor
@Entity
@Table(name = "users")
public class User {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;

@OneToOne(cascade = CascadeType.ALL)
@JoinColumn(name = "address_id", referencedColumnName = "id")
private Address address;

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false;
User user = (User) o;
return id != null && Objects.equals(id, user.id);
}

@Override
public int hashCode() {
return getClass().hashCode();
}
}


package com.example.relationships.entity;

import jakarta.persistence.*;
import lombok.*;
import org.hibernate.Hibernate;

import java.util.Objects;

@Getter
@Setter
@ToString
@RequiredArgsConstructor
@Entity
public class Address {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String city;
private String street;

@OneToOne(mappedBy = "address")
private User user;

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false;
Address address = (Address) o;
return id != null && Objects.equals(id, address.id);
}

@Override
public int hashCode() {
return getClass().hashCode();
}
}
package com.example.relationships;

import com.example.relationships.entity.Address;
import com.example.relationships.entity.Book;
import com.example.relationships.entity.Manuscript;
import com.example.relationships.entity.User;
import com.example.relationships.repository.AddressRepository;
import com.example.relationships.repository.BookRepository;
import com.example.relationships.repository.ManuscriptRepository;
import com.example.relationships.repository.UserRepository;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@RequiredArgsConstructor
public class RelationshipsApplication implements CommandLineRunner {
private final UserRepository userRepository;

public static void main(String[] args) {
SpringApplication.run(RelationshipsApplication.class, args);
}

@Override
// @Transactional
public void run(String... args) throws Exception {
System.out.println("Start--------------------------------------------------");

User parvin = new User();
parvin.setName("Parvin");

Address address = new Address();
address.setCity("Baku");
address.setStreet("R.Rza98");
parvin.setAddress(address);

userRepository.save(parvin);

System.out.println("Finish--------------------------------------------------");
}
}

Start--------------------------------------------------

Hibernate: insert into address (city,street) values (?,?)

Hibernate: insert into users (address_id,name) values (?,?)

Finish--------------------------------------------------


Note that we place the @OneToOne annotation on the related entity field, Address.

Also, we need to place the @JoinColumn annotation to configure the name of the column in the users table that maps to the primary key in the address table. If we don't provide a name, Hibernate will follow some rules to select a default one.

Finally, note in the next entity that we won't use the @JoinColumn annotation there. This is because we only need it on the owning side of the foreign key relationship. Simply put, whoever owns the foreign key column gets the @JoinColumn annotation.


We also need to place the @OneToOne annotation here too. That's because this is a bidirectional relationshipThe address side of the relationship is called the non-owning side. 


*** MappedBy signals hibernate that the key for the relationship is on the other side.

This means that although you link 2 tables together, only 1 of those tables has a foreign key constraint to the other one. MappedBy allows you to still link from the table not containing the constraint to the other table.



*** Yuxarida gorduyunuz numunede @JoinColumn annotation user table dadir. Bu o demekdir ki, eger biz bazaya baxsaq user table da address_id adli bir column var ve bu column foreign key xarakteri dashiyir ve address table in id column a birleshib.



--- Indi ise biraz ekperiment aparaq ve @JoinColumn annotation u yerleshdirek addresss entity sine, mappedBy ise user entity sine.

package com.example.relationships.entity;

import jakarta.persistence.*;
import lombok.*;
import org.hibernate.Hibernate;

import java.util.Objects;

@Getter
@Setter
@ToString
@RequiredArgsConstructor
@Entity
@Table(name = "users")
public class User {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;

@OneToOne(mappedBy = "user", cascade = CascadeType.ALL)
private Address address;

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false;
User user = (User) o;
return id != null && Objects.equals(id, user.id);
}

@Override
public int hashCode() {
return getClass().hashCode();
}
}

package com.example.relationships.entity;

import jakarta.persistence.*;
import lombok.*;
import org.hibernate.Hibernate;

import java.util.Objects;

@Getter
@Setter
@ToString
@RequiredArgsConstructor
@Entity
public class Address {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String city;
private String street;

@OneToOne()
@JoinColumn(name = "user_id", referencedColumnName = "id")
private User user;

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false;
Address address = (Address) o;
return id != null && Objects.equals(id, address.id);
}

@Override
public int hashCode() {
return getClass().hashCode();
}
}

package com.example.relationships;

import com.example.relationships.entity.Address;
import com.example.relationships.entity.Book;
import com.example.relationships.entity.Manuscript;
import com.example.relationships.entity.User;
import com.example.relationships.repository.AddressRepository;
import com.example.relationships.repository.BookRepository;
import com.example.relationships.repository.ManuscriptRepository;
import com.example.relationships.repository.UserRepository;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@RequiredArgsConstructor
public class RelationshipsApplication implements CommandLineRunner {

private final UserRepository userRepository;
private final AddressRepository addressRepository;

public static void main(String[] args) {
SpringApplication.run(RelationshipsApplication.class, args);
}

@Override
// @Transactional
public void run(String... args) throws Exception {
System.out.println("Start--------------------------------------------------");

User parvin = new User();
parvin.setName("Parvin");

Address address = new Address();
address.setCity("Baku");
address.setStreet("R.Rza98");

address.setUser(parvin);
parvin.setAddress(address);
userRepository.save(parvin);

System.out.println("Finish--------------------------------------------------");
}
}

Start--------------------------------------------------

Hibernate: insert into users (name) values (?)

Hibernate: insert into address (city,street,user_id) values (?,?,?)

Finish--------------------------------------------------


Bu zaman bazaya baxsaq user_id column u address table yerleshib.


2. One to Many relationship

a) Baeldung

Simply put, one-to-many mapping means that one row in a table is mapped to multiple rows in another table.

Let’s look at the following entity-relationship diagram to see a one-to-many association:

C-1

For this example, we'll implement a cart system where we have a table for each cart and another table for each item. One cart can have many items, so here we have a one-to-many mapping.

The way this works at the database level is we have a cart_id as a primary key in the cart table and also a cart_id as a foreign key in items.

The way we do it in code is with @OneToMany.

Let's map the Cart class to the collection of Item objects in a way that reflects the relationship in the database:


package com.example.relationships.entity;

import jakarta.persistence.*;
import lombok.*;
import org.hibernate.Hibernate;

import java.util.Objects;
import java.util.Set;

@Getter
@Setter
@ToString
@RequiredArgsConstructor
@Entity
public class Cart {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@OneToMany(mappedBy = "cart", cascade = CascadeType.ALL)
private Set<Item> items;

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false;
Cart cart = (Cart) o;
return id != null && Objects.equals(id, cart.id);
}

@Override
public int hashCode() {
return getClass().hashCode();
}
}


package com.example.relationships.entity;

import jakarta.persistence.*;
import lombok.*;
import org.hibernate.Hibernate;

import java.util.Objects;
import java.util.Set;

@Getter
@Setter
@ToString
@RequiredArgsConstructor
@Entity
public class Item {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;

@ManyToOne
@JoinColumn(name = "cart_id", nullable = false)
private Cart cart;

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false;
Item item = (Item) o;
return id != null && Objects.equals(id, item.id);
}

@Override
public int hashCode() {
return getClass().hashCode();
}
}


package com.example.relationships;

import com.example.relationships.entity.Cart;
import com.example.relationships.entity.Item;
import com.example.relationships.repository.CartRepository;
import com.example.relationships.repository.ItemRepository;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.util.Set;

@SpringBootApplication
@RequiredArgsConstructor
public class RelationshipsApplication implements CommandLineRunner {

private final CartRepository cartRepository;
private final ItemRepository itemRepository;

public static void main(String[] args) {
SpringApplication.run(RelationshipsApplication.class, args);
}

@Override
// @Transactional
public void run(String... args) throws Exception {
System.out.println("Start--------------------------------------------------");

Item one = new Item();
one.setName("potato");

Item two = new Item();
two.setName("onion");

Cart cart = new Cart();
cart.setItems(Set.of(one, two));

one.setCart(cart);
two.setCart(cart);

cartRepository.save(cart);

System.out.println("Finish--------------------------------------------------");
}
}

Start--------------------------------------------------

Hibernate: insert into cart default values

Hibernate: insert into item (cart_id,name) values (?,?)

Hibernate: insert into item (cart_id,name) values (?,?)

Finish--------------------------------------------------


As stated in the JPA specification under section 2.9, it's a good practice to mark the many-to-one side as the owning side.

In other words, Item would be the owning side and Cart the inverse side, which is exactly what we did earlier.

So how did we achieve this?

By including the mappedBy attribute in the Cart class, we mark it as the inverse side.


3. Many to Many relationship.

a) Baeldung

package com.example.relationships.entity;

import jakarta.persistence.*;
import lombok.*;
import org.hibernate.Hibernate;

import java.util.Objects;
import java.util.Set;

@Getter
@Setter
@ToString
@RequiredArgsConstructor
@Entity
public class Student {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String surname;


@ManyToMany
@JoinTable(name = "student_course",
joinColumns = @JoinColumn(name = "student_id"),
inverseJoinColumns = @JoinColumn(name = "course_id"))
private Set<Course> courses;

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false;
Student student = (Student) o;
return id != null && Objects.equals(id, student.id);
}

@Override
public int hashCode() {
return getClass().hashCode();
}
}


package com.example.relationships.entity;

import jakarta.persistence.*;
import lombok.*;
import org.hibernate.Hibernate;

import java.util.Objects;
import java.util.Set;

@Getter
@Setter
@ToString
@RequiredArgsConstructor
@Entity
public class Course {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;

@ManyToMany(mappedBy = "courses")
private Set<Student> students;

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || Hibernate.getClass(this) != Hibernate.getClass(o)) return false;
Course course = (Course) o;
return id != null && Objects.equals(id, course.id);
}

@Override
public int hashCode() {
return getClass().hashCode();
}
}


package com.example.relationships;

import com.example.relationships.entity.Course;
import com.example.relationships.entity.Student;
import com.example.relationships.repository.CourseRepository;
import com.example.relationships.repository.StudentRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import java.util.Set;

@SpringBootApplication
@RequiredArgsConstructor
public class RelationshipsApplication implements CommandLineRunner {

private final StudentRepository studentRepository;
private final CourseRepository courseRepository;

public static void main(String[] args) {
SpringApplication.run(RelationshipsApplication.class, args);
}

@Override
// @Transactional
public void run(String... args) throws Exception {
System.out.println("Start--------------------------------------------------");

Student parvin = new Student();
parvin.setName("Parvin");
parvin.setSurname("Etibarli");

Student nizam = new Student();
nizam.setName("Nizam");
nizam.setSurname("Novruzov");

Course se = new Course();
se.setName("Java SE");
se.setStudents(Set.of(parvin, nizam));

Course ms = new Course();
ms.setName("Microservices");
ms.setStudents(Set.of(parvin, nizam));

parvin.setCourses(Set.of(se, ms));
nizam.setCourses(Set.of(se, ms));

courseRepository.save(se);
courseRepository.save(ms);
studentRepository.save(parvin);
studentRepository.save(nizam);

System.out.println("Finish--------------------------------------------------");
}
}
































Комментарии

Популярные сообщения из этого блога

Lesson1: JDK, JVM, JRE

SE_21_Lesson_11: Inheritance, Polymorphism

SE_21_Lesson_9: Initialization Blocks, Wrapper types, String class