Spring Data JPA provides excellent support for creating Spring-powered applications that communicate with different relational databases to implement JPA-based repositories.
Java Persistence API (JPA) is just a specification that defines an object-relational mapping (ORM) standard for storing, accessing, and managing Java objects in a relational database. Hibernate is the most popular and widely used implementation of JPA specifications. By default, Spring Data JPA uses Hibernate as a JPA provider.
Hibernate provides a framework for mapping relational database tables to entity classes in your application. You can describe the relationships between these entities as you define relationships among the tables in your relational database.
In this article, you'll learn how to create a one-to-one mapping between two entities using Spring Data JPA in a Spring Boot and MySQL application.
Dependencies
You just need spring-data-starter-data-jpa
and mysql-connector-java
dependencies to use Spring Data JPA with the MySQL database.
For a Gradle project, add the following dependencies to your build.gradle
file:
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'mysql:mysql-connector-java'
For a Maven project, include the following dependencies to your pom.xml
file:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
If you want to create a new Spring Boot project from scratch, just use Spring Initializr web tool to bootstrap a new application with the above dependencies.
Configure MySQL Database
By default, Spring Boot automatically configures the DataSource
bean for in-memory databases like H2 database. But for MySQL, we need to manually define the database connection details in a properties file.
Open your application.properties
file and add the following properties:
# MySQL connection properties
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=mypass
spring.datasource.url=jdbc:mysql://localhost:3306/testdb?createDatabaseIfNotExist=true&useSSL=false
# Log JPA queries
# Comment this in production
spring.jpa.show-sql=true
# Drop and create new tables (create, create-drop, validate, update)
# Only for testing purposes - comment this in production
spring.jpa.hibernate.ddl-auto=create
# Hibernate SQL dialect
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5InnoDBDialect
Spring Boot will read the above properties and auto-configure a DataSource
bean for you. Make sure to change the spring.datasource.username
and spring.datasource.password
properties to match your MySQL credentials.
The hibernate property spring.jpa.hibernate.ddl-auto = create
will automatically create database tables based on the entity classes in your application on startup.
We have also included the createDatabaseIfNotExist=true
configuration property in spring.datasource.url
to automatically create the database if it doesn't already exist.
One-To-One Relationship
A one-to-one relationship refers to the relationship between two entities/database tables A and B in which only one element/row of A may be linked to one element/row of B, and vice versa.
Let us consider an application scenario where you want to store users' information along with their addresses. We want to ensure that a user can have just one postal address, and a postal address can only be associated with a single user.
We can map the above requirement as a one-to-one relationship between the user and the address entities, as shown in the following Entity-Relationship (ER) diagram:
The one-to-one relationship is defined using a foreign key called user_id
in the addresses
table.
Create Entities
You need to create the following two entity classes to map the above database tables.
User
Entity
Let us define the below User
entity in your application to map the users
database table:
package com.attacomsian.jpa.one2one.domains;
import javax.persistence.*;
import java.io.Serializable;
@Entity
@Table(name = "users")
public class User implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private String email;
private String password;
@OneToOne(mappedBy = "user", fetch = FetchType.LAZY,
cascade = CascadeType.ALL)
private Address address;
public User() {
}
public User(String name, String email, String password) {
this.name = name;
this.email = email;
this.password = password;
}
// getters and setters, equals(), toString() .... (omitted for brevity)
}
Address
Entity
To map the addresses
table, you need to define the following Address
entity:
package com.attacomsian.jpa.one2one.domains;
import javax.persistence.*;
import java.io.Serializable;
@Entity
@Table(name = "addresses")
public class Address implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String street;
private String city;
private String state;
private String zipCode;
private String country;
@OneToOne(fetch = FetchType.LAZY, optional = false)
@JoinColumn(name = "user_id", nullable = false)
private User user;
public Address() {
}
public Address(String street, String city, String state, String zipCode,
String country) {
this.street = street;
this.city = city;
this.state = state;
this.zipCode = zipCode;
this.country = country;
}
// getters and setters, equals(), toString() .... (omitted for brevity)
}
Both User
and Address
classes are annotated with Entity
to indicate that they are JPA entities.
The @Table
annotation is used to specify the name of the database table that should be mapped to this entity.
The id
attributes are annotated with both @Id
and @GeneratedValue
annotations. The former indicates that they are the primary keys of the entities. The latter defines the primary key generation strategy. In the above case, we have declared that the primary key should be an AUTO INCREMENT
field.
@OneToOne
Annotation
In Spring Data JPA, a one-to-one relationship between two entities is declared using the @OneToOne
annotation. It accepts the following parameters:
fetch
— Defines a strategy for fetching data from the database. By default, it isEAGER
indicating that the data must be eagerly fetched. We have set it toLAZY
to fetch the entities lazily from the database.cascade
— Defines a set of cascadable operations applied to the associated entity.CascadeType.ALL
means to apply all cascading operations to the related entity. The cascading operations are applied when you delete or update the parent entity.mappedBy
— Defines the entity that owns the relationship, which is theAddress
entity in our case.optional
— Defines whether the relationship is optional. If set tofalse
, then a non-null relationship must always exist.
In a bidirectional relationship, we have to specify a @OneToOne
annotation in both entities. But only one entity is the owner of the association. Usually, the child entity owns the relationship, and the parent entity is the inverse side of the relationship.
@JoinColumn
Annotation
The @JoinColumn
annotation is used to specify the foreign key column in the relationship owner. The inverse side of the relationship sets the @OneToOne
's mappedBy
parameter to indicate that the relationship is mapped by the other entity.
The @JoinColumn
accepts the following two parameters, among others:
name
— Defines the name of the foreign key column.nullable
— Defines whether the foreign key column is nullable. By default, it is true.
Create Repositories
Finally, let us create repository interfaces to save and retrieve User
and Address
entities from the database. We'll extend our repositories from Spring Data JPA's CrudRepository
interface, which provides generic CRUD methods out-of-the-box.
UserRepository
Interface
Here is what our UserRepository
interface looks like:
package com.attacomsian.jpa.one2one.repositories;
import com.attacomsian.jpa.one2one.domains.User;
import org.springframework.data.repository.CrudRepository;
public interface UserRepository extends CrudRepository<User, Long> {
}
AddressRepository
Interface
The next step is to create the following AddressRepository
interface:
package com.attacomsian.jpa.one2one.repositories;
import com.attacomsian.jpa.one2one.domains.Address;
import org.springframework.data.repository.CrudRepository;
public interface AddressRepository extends CrudRepository<Address, Long> {
}
That's all you need to do to define a one-to-one mapping in Spring Data JPA. Now, you're ready to perform the CRUD operations on the User
and Address
entities without implementing the above interfaces. This is what makes Spring Data JPA a powerful tool.
Create an Application Class
Let us now create the main application for our Spring Boot console application to test our one-to-one relationship mapping:
Application.java
package com.attacomsian.jpa;
import com.attacomsian.jpa.one2one.domains.Address;
import com.attacomsian.jpa.one2one.domains.User;
import com.attacomsian.jpa.one2one.repositories.AddressRepository;
import com.attacomsian.jpa.one2one.repositories.UserRepository;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public CommandLineRunner mappingDemo(UserRepository userRepository,
AddressRepository addressRepository) {
return args -> {
// create a user instance
User user = new User("John Doe", "john.doe@example.com", "1234abcd");
// create an address instance
Address address = new Address("Lake View 321", "Berlin", "Berlin",
"95781", "DE");
// set child reference
address.setUser(user);
// set parent reference
user.setAddress(address);
// save the parent
// which will save the child (address) as well
userRepository.save(user);
};
}
}
We've defined a CommandLineRunner
interface bean in the main application class. This interface provides a run()
method, which is automatically invoked by Spring Boot after the application context is loaded.
Run the Application
Now is the time to run the application to see the output. If you are using Gradle, execute the following command to start the application:
$ ./gradlew bootRun
For Maven, type the following command to launch the application:
$ ./mvnw spring-boot:run
Once the application is started, you should see the following lines printed on the console:
Hibernate: drop table if exists addresses
Hibernate: drop table if exists users
Hibernate: create table addresses (id bigint not null auto_increment, city varchar(255), country varchar(255), state varchar(255), street varchar(255), zip_code varchar(255), user_id bigint not null, primary key (id)) engine=InnoDB
Hibernate: create table users (id bigint not null auto_increment, email varchar(255), name varchar(255), password varchar(255), primary key (id)) engine=InnoDB
Hibernate: insert into users (email, name, password) values (?, ?, ?)
Hibernate: insert into addresses (city, country, state, street, user_id, zip_code) values (?, ?, ?, ?, ?, ?)
...
Source Code: Download the complete source code from GitHub available under MIT license.
Conclusion
That's all. In this article, you learned how to map and use a one-to-one relationship in Spring Data JPA and Hibernate.
In upcoming articles, I will explain in detail how to use one-to-many and many-to-many relationships in Spring Data JPA. Do not forget to join the newsletter if you want to be the first to know when new tutorials are available.
Further Reading
To learn about Spring Data JPA, check out the following articles:
- Getting Started with Spring Data JPA
- Spring Data JPA with H2 DataBase and Spring Boot
- Accessing Data with Spring Data JPA and MySQL
- Derived Query Methods in Spring Data JPA
- Spring Data JPA Custom Queries using @Query Annotation
- How to Use Spring Data JPA Named Queries
- Sorting Query Results in Spring Data JPA
- Pagination with Spring Data JPA
- Spring Data JPA One To Many Relationship Mapping Example
- Spring Data JPA Many To Many Relationship Mapping Example
✌️ Like this article? Follow me on Twitter and LinkedIn. You can also subscribe to RSS Feed.