February 25th, 2023
@ManyToOne Mapping

OneToOne Unidirectional Student - Passport

  • @OneToMany – Lazy Fetching
  • @ManyToOne – Eager Fetching
  • @OneToOne – Eager Fetching
Annotations ending with ToOne are always Eager Fetching.
				
					<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.0.2</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.brains.jpa.hibernate</groupId>
	<artifactId>jpa-hibernate-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>jpa-hibernate-demo</name>
	<description>Demo project for Spring Boot</description>
	<properties>
		<java.version>17</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-data-jpa</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>com.h2database</groupId>
			<artifactId>h2</artifactId>
			<scope>runtime</scope>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
				<configuration>
					<excludes>
						<exclude>
							<groupId>org.projectlombok</groupId>
							<artifactId>lombok</artifactId>
						</exclude>
					</excludes>
				</configuration>
			</plugin>
		</plugins>
	</build>

</project>

				
			
				
					spring.datasource.url=jdbc:h2:mem:testdb;NON_KEYWORDS=USER;DB_CLOSE_ON_EXIT=FALSE
spring.h2.console.enabled=true
spring.jpa.defer-datasource-initialization=true

# Turn Statistics ON
#spring.jpa.properties.hibernate.generate_statistics=true
logging.level.org.hibernate.stat=debug
#logging.level.root=trace
# Show all queries
spring.jpa.show-sql=true

# Format the queries
spring.jpa.properties.hibernate.format_sql=true
				
			

Many reviews for 1 Course, here Course 10001 has 5 reviews.


				
					package com.brains.jpa.hibernate.jpahibernatedemo.entity;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;

import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import lombok.AccessLevel;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Entity
@Data
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Course {
	
	@Id
	@GeneratedValue
	private Long id;
	
	private String name;
	
	@Setter(AccessLevel.NONE)
	@OneToMany(mappedBy = "course")
	private List<Review> reviews = new ArrayList<>();
	
	public void addReview(Review review) {
		this.reviews.add(review);
	}
	
	public void removeReview(Review review) {
		this.reviews.remove(review);
	}
	
	@UpdateTimestamp
	private LocalDateTime lastUpdatedDate;

	@CreationTimestamp
	private LocalDateTime createdDate;
	
	public Course(String name) {
		this.name = name;
	}
}

				
			
				
					package com.brains.jpa.hibernate.jpahibernatedemo.entity;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.ManyToOne;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.ToString;

@Entity
@Data @NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@RequiredArgsConstructor
public class Review {

	@Id
	@GeneratedValue
	private Long id;
	
	@NonNull
	private String rating;
	
	@NonNull
	private String description;
	
	@ToString.Exclude
	@ManyToOne
	private Course course;
}

				
			
				
					package com.brains.jpa.hibernate.jpahibernatedemo.entity;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.OneToOne;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.ToString;

@Entity
@Data @NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@RequiredArgsConstructor
public class Passport {

	@Id
	@GeneratedValue
	private Long id;
	
	@NonNull
	@Column(nullable = false)
	private String number;
	
	@ToString.Exclude
	@OneToOne(fetch = FetchType.LAZY, mappedBy = "passport")
	private Student student;
}
				
			
				
					package com.brains.jpa.hibernate.jpahibernatedemo.entity;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import jakarta.persistence.OneToOne;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;

@Entity
@Data @NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
@RequiredArgsConstructor
public class Student {

	@Id
	@GeneratedValue
	private Long id;
	
	@NonNull
	@Column(nullable = false)
	private String name;
	
	@OneToOne(fetch = FetchType.LAZY)
	private Passport passport;
}
				
			
				
					insert into course(id, name, created_date, last_updated_date) 
values(10001, 'JPA in 5 steps', LOCALTIMESTAMP, LOCALTIMESTAMP);
insert into course(id, name, created_date, last_updated_date) 
values(10002, 'JDBC in 10 steps', LOCALTIMESTAMP, LOCALTIMESTAMP);
insert into course(id, name, created_date, last_updated_date) 
values(10003, 'JPQL in 50 steps', LOCALTIMESTAMP, LOCALTIMESTAMP);

insert into passport(id, number)
values(40001,'E12345');
insert into passport(id, number)
values(40002,'F212345');
insert into passport(id, number)
values(40003,'G12345');

insert into student(id, name, passport_id)
values(20001,'Ranga',40001);
insert into student(id, name, passport_id)
values(20002,'Adam',40002);
insert into student(id, name, passport_id)
values(20003,'Jane',40003);

insert into review(id, rating, description, course_id)
values(50001,'1', 'Great course', 10001);
insert into review(id, rating, description, course_id)
values(50002,'3', 'Nice course', 10001);
insert into review(id, rating, description, course_id)
values(50004,'5', 'Good you are writing', 10003);
				
			
				
					package com.brains.jpa.hibernate.jpahibernatedemo.repository;

import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import com.brains.jpa.hibernate.jpahibernatedemo.entity.Course;
import com.brains.jpa.hibernate.jpahibernatedemo.entity.Review;

import jakarta.persistence.EntityManager;
import jakarta.transaction.Transactional;

@Repository
@Transactional
public class CourseRepository {

	private Logger logger = LoggerFactory.getLogger(this.getClass());
	
	@Autowired
	EntityManager em;
	
	public Course findById(Long id) {
		return em.find(Course.class, id);
	}
	
	public void deleteById(Long id) {
		Course course = findById(id);
		em.remove(course);
	}
	
	public Course save(Course course) {
		if(course.getId() == null) {
			//insert
			em.persist(course);
		}else {
			//update
			em.merge(course);
		}
		return course;
	}
	
	public void playWithEntityManager() {
		Course course1 = new Course("AngularJs in 100 Steps");
		em.persist(course1);
		
		Course course2 = findById(10001L);
		course2.setName("JPA in 5 steps -- updated");
	}

	public void addHardcodedReviewsForCourse() {
		Course course = findById(10003L);
		logger.info("course Reviews -> {}", course.getReviews());
		Review review1 = new Review("3", "Better");
		Review review2 = new Review("5", "Superb");
		
		course.getReviews().add(review1);
		review1.setCourse(course);
		
		course.getReviews().add(review2);
		review2.setCourse(course);

		em.persist(review1);
		em.persist(review2);
		logger.info("course Reviews -> {}", course.getReviews());
	}
	
	public void addDynamicReviewsForCourse(Long courseId, List<Review> reviews) {
		Course course = findById(courseId);
		logger.info("course Reviews -> {}", course.getReviews());
		
		for(Review review : reviews) {
			course.getReviews().add(review);
			review.setCourse(course);
			em.persist(review);
		}
	}
}

				
			
				
					package com.brains.jpa.hibernate.jpahibernatedemo.repository;

import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import com.brains.jpa.hibernate.jpahibernatedemo.JpaHibernateDemoApplication;
import com.brains.jpa.hibernate.jpahibernatedemo.entity.Course;
import com.brains.jpa.hibernate.jpahibernatedemo.entity.Review;

import jakarta.persistence.EntityManager;
import jakarta.transaction.Transactional;

@SpringBootTest(classes = JpaHibernateDemoApplication.class)
class CourseRepositoryTest {

	private Logger logger = LoggerFactory.getLogger(this.getClass());
	
	@Autowired
	CourseRepository courseRepository;
	
	@Autowired
	EntityManager em;
	
	@Test
	@Transactional
	void retrieveReviewsForCourse() {
		Course course = courseRepository.findById(10001L);
		logger.info("Reviews: -> {}", course.getReviews());
	}
	
	@Test
	@Transactional
	void retrieveCourseForReview() {
		Review review = em.find(Review.class, 50001L);
		logger.info("Reviews: -> {}", review);
		Course course = review.getCourse();
		logger.info("Course: -> {}", course);
	}

}

				
			
				
					package com.brains.jpa.hibernate.jpahibernatedemo;

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

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

import com.brains.jpa.hibernate.jpahibernatedemo.entity.Review;
import com.brains.jpa.hibernate.jpahibernatedemo.repository.CourseRepository;

@SpringBootApplication
public class JpaHibernateDemoApplication implements CommandLineRunner {

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

	private Logger logger = LoggerFactory.getLogger(this.getClass());
	
	@Autowired
	private CourseRepository courseRepository;
	
	@Override
	public void run(String... args) throws Exception {
		List<Review> reviews = new ArrayList<>();
		reviews.add(new Review("3", "Better"));
		reviews.add(new Review("5", "Superb"));
		reviews.add(new Review("4", "Wow"));
		courseRepository.addDynamicReviewsForCourse(10001L, reviews);
	}
}
				
			
				
					Hibernate: 
    select
        c1_0.id,
        c1_0.created_date,
        c1_0.last_updated_date,
        c1_0.name 
    from
        course c1_0 
    where
        c1_0.id=?
Hibernate: 
    select
        r1_0.course_id,
        r1_0.id,
        r1_0.description,
        r1_0.rating 
    from
        review r1_0 
    where
        r1_0.course_id=?
2023-02-25T22:10:56.790+05:30  INFO 69975 --- [           main] c.b.j.h.j.repository.CourseRepository    : course Reviews -> [Review(id=50001, rating=1, description=Great course), Review(id=50002, rating=3, description=Nice course)]
Hibernate: 
    select
        next value for review_seq
Hibernate: 
    select
        next value for review_seq
Hibernate: 
    insert 
    into
        review
        (course_id, description, rating, id) 
    values
        (?, ?, ?, ?)
Hibernate: 
    insert 
    into
        review
        (course_id, description, rating, id) 
    values
        (?, ?, ?, ?)
Hibernate: 
    insert 
    into
        review
        (course_id, description, rating, id) 
    values
        (?, ?, ?, ?)
Hibernate: 
    select
        c1_0.id,
        c1_0.created_date,
        c1_0.last_updated_date,
        c1_0.name 
    from
        course c1_0 
    where
        c1_0.id=?
Hibernate: 
    select
        r1_0.course_id,
        r1_0.id,
        r1_0.description,
        r1_0.rating 
    from
        review r1_0 
    where
        r1_0.course_id=?
2023-02-25T22:10:56.984+05:30  INFO 69975 --- [           main] c.b.j.h.j.r.CourseRepositoryTest         : Reviews: -> [Review(id=1, rating=3, description=Better), Review(id=2, rating=5, description=Superb), Review(id=3, rating=4, description=Wow), Review(id=50001, rating=1, description=Great course), Review(id=50002, rating=3, description=Nice course)]
Hibernate: 
    select
        r1_0.id,
        c1_0.id,
        c1_0.created_date,
        c1_0.last_updated_date,
        c1_0.name,
        r1_0.description,
        r1_0.rating 
    from
        review r1_0 
    left join
        course c1_0 
            on c1_0.id=r1_0.course_id 
    where
        r1_0.id=?
2023-02-25T22:10:57.005+05:30  INFO 69975 --- [           main] c.b.j.h.j.r.CourseRepositoryTest         : Reviews: -> Review(id=50001, rating=1, description=Great course)
Hibernate: 
    select
        r1_0.course_id,
        r1_0.id,
        r1_0.description,
        r1_0.rating 
    from
        review r1_0 
    where
        r1_0.course_id=?
2023-02-25T22:10:57.005+05:30  INFO 69975 --- [           main] c.b.j.h.j.r.CourseRepositoryTest         : Course: -> Course(id=10001, name=JPA in 5 steps, reviews=[Review(id=1, rating=3, description=Better), Review(id=2, rating=5, description=Superb), Review(id=3, rating=4, description=Wow), Review(id=50001, rating=1, description=Great course), Review(id=50002, rating=3, description=Nice course)], lastUpdatedDate=2023-02-25T22:10:56.658209, createdDate=2023-02-25T22:10:56.658209)

				
			

Related Tutorials

Leave a Reply

Your email address will not be published. Required fields are marked *