OneToOne Unidirectional Student - Passport
@OneToOne
mapping is by default Eager Fetching means it would automatically call the record of the rows of other table which is mapped.
Here Student class owns the mapping of the Passport and hence @OneToOne mapping is defined in the Student class.
@OneToOne(fetch = FetchType.LAZY)
Lazy will call the database only when it is required like student.getPassport();
If LAZY fetch is defined, we must use @Transactional with the method.
If the relationship is bidirectional, the non-owning side must use the mappedBy
element of the OneToOne
annotation to specify the relationship field or property of the owning side.
4.0.0
org.springframework.boot
spring-boot-starter-parent
3.0.2
com.brains.jpa.hibernate
jpa-hibernate-demo
0.0.1-SNAPSHOT
jpa-hibernate-demo
Demo project for Spring Boot
17
org.springframework.boot
spring-boot-starter-data-jpa
org.springframework.boot
spring-boot-starter-web
com.h2database
h2
runtime
org.projectlombok
lombok
true
org.springframework.boot
spring-boot-starter-test
test
org.springframework.boot
spring-boot-maven-plugin
org.projectlombok
lombok
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
package com.brains.jpa.hibernate.jpahibernatedemo.entity;
import java.time.LocalDateTime;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.UpdateTimestamp;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import lombok.AccessLevel;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Data
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Course {
@Id
@GeneratedValue
private Long id;
private String name;
@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.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import lombok.AccessLevel;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
@Entity
@Data @NoArgsConstructor(access = AccessLevel.PROTECTED)
@RequiredArgsConstructor
public class Passport {
@Id
@GeneratedValue
private Long id;
@NonNull
@Column(nullable = false)
private String number;
}
package com.brains.jpa.hibernate.jpahibernatedemo.entity;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.Id;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Data @NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor
public class Review {
@Id
@GeneratedValue
private Long id;
private String rating;
private String description;
}
package com.brains.jpa.hibernate.jpahibernatedemo.entity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
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
@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)
values(50001,'1', 'Great course');
insert into review(id, rating, description)
values(50002,'3', 'Nice course');
insert into review(id, rating, description)
values(50004,'5', 'Good you are writing');
package com.brains.jpa.hibernate.jpahibernatedemo.repository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import com.brains.jpa.hibernate.jpahibernatedemo.entity.Passport;
import com.brains.jpa.hibernate.jpahibernatedemo.entity.Student;
import jakarta.persistence.EntityManager;
import jakarta.transaction.Transactional;
@Repository
@Transactional
public class StudentRepository {
@Autowired
EntityManager em;
public Student findById(Long id) {
return em.find(Student.class, id);
}
public void saveStudentWithPassport() {
Passport passport = new Passport("Z12345");
em.persist(passport);
Student student1 = new Student("Mike");
student1.setPassport(passport);
em.persist(student1);
}
}
package com.brains.jpa.hibernate.jpahibernatedemo;
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.entity.Passport;
import com.brains.jpa.hibernate.jpahibernatedemo.entity.Student;
import com.brains.jpa.hibernate.jpahibernatedemo.repository.StudentRepository;
import jakarta.persistence.EntityManager;
@SpringBootTest(classes = JpaHibernateDemoApplication.class)
class StudentRepositoryTest {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
StudentRepository studentRepository;
@Autowired
EntityManager em;
// Use Transactional when Lazy fetch is defined
@Transactional
@Test
void retrieveStudentAndPassport() {
Student student = em.find(Student.class, 20001L);
logger.info("Student -> {}", student);
Passport passport = student.getPassport();
logger.info("Passport -> {}", passport);
}
}
package com.brains.jpa.hibernate.jpahibernatedemo;
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.repository.CourseRepository;
import com.brains.jpa.hibernate.jpahibernatedemo.repository.StudentRepository;
@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;
@Autowired
private StudentRepository studentRepository;
@Override
public void run(String... args) throws Exception {
studentRepository.saveStudentWithPassport();
}
}
OneToOne Bidirectional Student - Passport
In Birectional mapping, mappedBy is added on the non-owning side(Passport) and mapped with the owning side(Student).
mappedBy ensures that the student_id column is not created in the Passport class.
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;
}
@ToString.Exclude
is added in the Passport class because OneToOne mapping is available in both the Student and Passport classes, and to avoid the duplication of toString methods, one of the class need to include ToString.Exclude.
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;
}
package com.brains.jpa.hibernate.jpahibernatedemo;
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.entity.Passport;
import com.brains.jpa.hibernate.jpahibernatedemo.entity.Student;
import jakarta.persistence.EntityManager;
import jakarta.transaction.Transactional;
@SpringBootTest(classes = JpaHibernateDemoApplication.class)
class StudentRepositoryTest {
private Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
EntityManager em;
@Test
@Transactional
void retrievePassportAndStudent() {
Passport passport = em.find(Passport.class, 40001L);
logger.info("Passport -> {}", passport);
Student student = passport.getStudent();
logger.info("Student -> {}", student);
}
}
2023-02-25T10:48:27.022+05:30 INFO 5022 --- [ main] c.b.j.h.j.StudentRepositoryTest : Passport -> Passport(id=40001, number=E12345, student=Student(id=20001, name=Ranga))
2023-02-25T10:48:27.022+05:30 INFO 5022 --- [ main] c.b.j.h.j.StudentRepositoryTest : Student -> Student(id=20001, name=Ranga)
Leave a Reply