Today we will see JPA annotation and association mapping in Spring Boot JPA.
Before we start if you don't know how to create a Spring boot project and database connection, please see the below blogs to get a clear view.
Create Spring Boot Project in IDE Eclipse
Spring boot JPA integration with SQlite
Spring boot With Lombok library
JPA Annotations
JPA annotations are present in the javax.persistence package.
@Entity - This annotation is to mark a class as an entity in JPA.
@Table - This annotation in combination with @entity. It specifies the table which contains the data for the entity. If @Table is not present, then JPA takes the class name as the table name.
@Column - It specifies the table column to the entity field.
@Id - It's used to define the primary key of the table.
Association Mapping
What is Association Mapping in JPA?
When two entities have a relationship or association with them. In association, an entity can be an owing entity or a non-owing entity.
An entity that contains a reference to another entity is the owning entity. Another entity that is being referred to is the non-owning entity.
If any of the entities contain the reference, then it's a unidirectional association.
If both entities contain a reference to each other then it's called bi-directional association.
Types of Association in JPA
- One-to-one
- One-to-many
- Many-to-one
- Many-to-many
Maven dependencies
Add the following dependency to the pom.xml file.
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<!-- Database library -->
<dependency>
<groupId>com.zsoltfabok</groupId>
<artifactId>sqlite-dialect</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
Configuring DataSource
#Database
spring.datasource.url=jdbc:sqlite:StudentsDB.db
#logging
logging.level.root=info
#logging.level.org.hibernate.SQL=info
#Show sql commands
spring.jpa.show-sql=true
spring.jpa.properties.hibernate.format_sql=true
spring.jpa.properties.hibernate.physical_naming_strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
One-to-one
First, we will see about one-to-one association mapping in JPA Hibernate. An association is one-to-one when there exists a relationship or association between two entities.
Let's create two tables like below, a student and parent table in which student and parent share the one-to-one association. A student can have a parent.
The student table contains the foreign key (parent Id) which refers to the parent table primary key. So here we are trying to map the association based on the foreign key.
Create Entity class
In JPA, the @OneToOne annotation defines a one-to-one association.
@JoinColumn annotation when a foreign key is used to map the association between entities.
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Parent {
@Id
@Column(name ="id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int pid;
private String fatherName;
private String motherName;
}
@PrimaryKeyJoinColumn is used when two entities share the same primary key.
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.OneToOne;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
public class Student {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
private String name;
@OneToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
@JoinColumn(name = "parentid", referencedColumnName = "id")
private Parent parent;
}
Cascade In JPA
Whenever the parent table gets manipulated by DB operations (insert, update, delete) the child table also with reference gets manipulated.The cascade attributes in @JoinColumn tell JPA how the cascade between the entities happens.
Create JPA Repository
Create an interface annotated with @Repository that extends JpaRepository. These repositories contain the methods to handle the database operation.
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.example.relational.entity.Student;
@Repository
public interface StudentRepository extends JpaRepository<Student, Integer> {
}
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import com.example.relational.entity.Parent;
@Repository
public interface ParentRepository extends JpaRepository<Parent, Integer> {
}
Testing DB Operation
Let's create JUnit to test our repository using Spring boot @DataJpaTest annotation. The @DataJpaTest provides support to test the JPA repository.
By default, all DataJpatest cases will be transactional and roll back after completion.
import static org.junit.jupiter.api.Assertions.*;
import java.util.List;
import java.util.Optional;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.test.context.junit4.SpringRunner;
import com.example.relational.entity.Parent;
import com.example.relational.entity.Student;
import com.example.relational.repository.StudentRepository;
@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class StudentRepositoryTest {
@Autowired
StudentRepository studentRepository;
@Test
public void addStudent() {
Parent parent = Parent.builder().fatherName("Bharath1").motherName("Gayu1").build();
Student student = Student.builder().name("JaiKuamr1").parent(parent).build();
int id = studentRepository.save(student).getId();
System.out.println("Student ID = " + id);
Student savedStudent = studentRepository.getById(id);
System.out.println("Student =" + savedStudent);
assertNotNull(savedStudent);
}
@Test
public void getStudentById() {
Optional<Student> student = studentRepository.findById(2);
System.out.println("Student =" + student.get());
assertNotNull(student.orElseGet(null));
}
@Test
public void getAllStudent() {
List<Student> students = studentRepository.findAll();
System.out.println("Total number of students record" + students.size());
assertEquals(4, students.size());
}
}
On executing the above tests we can see below logs on the console after each test the rollback is performed and the JPA query is printed for each DB operation.
we can see JPA one-to-many and many-to-one examples here.