The Spring Boot JPA One to One Foreign Key Relationship Mapping Example shows you the process of mapping an one-to-one relationship using Spring JPA and Spring Boot. A OneToOne relationship in Java is where the source object has an attribute that references another target object and (if) that target object had the inverse relationship back to the source object it would also be a OneToOne relationship. All relationships in Java and JPA are unidirectional, in that if a source object references a target object there is no guarantee that the target object also has a relationship to the source object. This is different than a relational database, in which relationships are defined through foreign keys and querying such that the inverse query always exists.
Other interesting posts you may like
Let’s begin:
Project structure
Our classic mvn project in this Spring Boot JPA One to One Foreign Key Relationship Mapping Example.
Maven dependencies
Our Spring Boot JPA One to One Foreign Key Relationship Mapping Example will use JPA, MySQL, so that we must add these dependencies in the pom.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.javabycode</groupId> <artifactId>springboot-jpa-onetoone-foreignkey-example</artifactId> <version>1.0.0-SNAPSHOT</version> <packaging>jar</packaging> <name>SpringBoot JPA One-To-One Foreignkey Example</name> <description>SpringBoot JPA One-To-One Foreignkey Example</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.2.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency> <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> <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> </plugin> </plugins> </build> </project> |
One-To-One Relationship
We are using a database named example_onetoone and two student and student_detail tables . The student and student_detail tables have a one-to-one relationship via student.id and student_detail.student_id.
Here is the sql script
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
-- -- Table structure for table `student_detail` -- DROP TABLE IF EXISTS `student_detail`; CREATE TABLE `student_detail` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `age` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; -- -- Table structure for table `student` -- DROP TABLE IF EXISTS `student`; CREATE TABLE `student` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(255) DEFAULT NULL, `student_detail_id` int(11) unsigned DEFAULT NULL, PRIMARY KEY (`id`), KEY `fk_student_studentdetail` (`student_detail_id`), CONSTRAINT `fk_student_studentdetail` FOREIGN KEY (`student_detail_id`) REFERENCES `student_detail` (`id`) ON DELETE CASCADE ON UPDATE CASCADE ) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8; |
Create JPA Entities
Entity class Student and StudentDetail are simple POJO class. Here we are using class Student and StudentDetail with JPA @Entity annotation to map them to a database tables (these tables were created in above step).
Student Entity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
package com.javabycode.jpa.model; import javax.persistence.*; @Entity public class Student { private int id; private String name; private StudentDetail studentDetail; public Student(){ } public Student(String name){ this.name = name; } public Student(String name, StudentDetail studentDetail){ this.name = name; this.studentDetail = studentDetail; } @Id @GeneratedValue(strategy = GenerationType.AUTO) public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @OneToOne(cascade = CascadeType.ALL) @JoinColumn(name = "student_detail_id") public StudentDetail getStudentDetail() { return studentDetail; } public void setStudentDetail(StudentDetail studentDetail) { this.studentDetail = studentDetail; } @Override public String toString() { return String.format( "student[id=%d, name='%s', age='%d']", id, name, studentDetail.getAge()); } } |
StudentDetail Entity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
package com.javabycode.jpa.model; import javax.persistence.*; @Entity @Table(name = "student_detail") public class StudentDetail { private Integer id; private Integer age; private Student student; public StudentDetail(){ } public StudentDetail(Integer age){ this.age = age; } @Id @GeneratedValue(strategy = GenerationType.AUTO) public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } @Column(name = "age") public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } @OneToOne(mappedBy = "studentDetail") public Student getStudent() { return student; } public void setStudent(Student student) { this.student = student; } } |
Let’s dig deeper:
@Table annotation maps the entity with the table. If no @Table is omitted, the default value is used. It means that the class name of the entity maps with the table.
@Id annotation marks the identifier property of the entity.
@Column application maps the entity’s field with the table’s column. If @Column is omitted, the default value is used. It means that the field name of the entity maps with the table’s column.
@OneToOne annotation defines a one-to-one relationship with another entity. The annotation @JoinColumn indicates that this entity is the owner of the relationship (that is: the corresponding table has a column with a foreign key to the referenced table), whereas the attribute mappedBy indicates that the entity in this side is the inverse of the relationship, and the owner resides in the “other” entity.
Spring Data JPA Repository
In this example, we all need to extend the JpaRepository. This is a built-in Repository implemented some common functions to work with database: findOne, findAll, save,etc.
1 2 3 4 5 6 7 |
package com.javabycode.jpa.repository; import com.javabycode.jpa.model.Student; import org.springframework.data.jpa.repository.JpaRepository; public interface StudentRepository extends JpaRepository<Student, Integer>{ } |
Properties Configuration
1 2 3 4 5 6 7 8 |
spring.datasource.url=jdbc:mysql://localhost:3306/example_onetoone spring.datasource.username=javabycode spring.datasource.password=mypassword spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.jpa.hibernate.ddl-auto=create-drop spring.jpa.show-sql=false spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL5Dialect |
Create SpringBootApplication class
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
package com.javabycode.jpa; import com.javabycode.jpa.model.Student; import com.javabycode.jpa.model.StudentDetail; import com.javabycode.jpa.repository.StudentRepository; 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 java.util.ArrayList; import java.util.List; @SpringBootApplication public class MyJPAApplication implements CommandLineRunner { private static final Logger logger = LoggerFactory.getLogger(MyJPAApplication.class); @Autowired private StudentRepository studentRepository; public static void main(String[] args) { SpringApplication.run(MyJPAApplication.class, args); } @Override public void run(String... strings) throws Exception { // fetch all students before update logger.info("============ Fetch all students before update ============"); for (Student student : studentRepository.findAll()) { logger.info(student.toString()); } // save a couple of students logger.info("============ Save a couple of students ============"); List<Student> students = new ArrayList<>(); students.add(new Student("Student A", new StudentDetail(10))); students.add(new Student("Student B", new StudentDetail(11))); studentRepository.save(students); // fetch all students logger.info("============ Fetch all students after update ============"); for (Student student : studentRepository.findAll()) { logger.info(student.toString()); } } } |
DEMO
You can go to the project directory on the console screen and run the command line
1 |
mvn spring-boot:run |
Or create the unit test class, for example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
package com.javabycode.jpa; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @SpringBootTest(classes=MyJPAApplication.class) public class JpaApplicationTests { @Test public void contextLoads() { } } |
Run the above test class and see the output like below
1 2 3 4 5 6 |
2017-04-19 15:23:51.081 INFO 6588 --- [ main] com.javabycode.jpa.MyJPAApplication : ============ Fetch all students before update ============ 2017-04-19 15:23:51.147 INFO 6588 --- [ main] o.h.h.i.QueryTranslatorFactoryInitiator : HHH000397: Using ASTQueryTranslatorFactory 2017-04-19 15:23:51.598 INFO 6588 --- [ main] com.javabycode.jpa.MyJPAApplication : ============ Save a couple of students ============ 2017-04-19 15:23:51.870 INFO 6588 --- [ main] com.javabycode.jpa.MyJPAApplication : ============ Fetch all students after update ============ 2017-04-19 15:23:51.904 INFO 6588 --- [ main] com.javabycode.jpa.MyJPAApplication : student[id=1, name='Student A', age='10'] 2017-04-19 15:23:51.904 INFO 6588 --- [ main] com.javabycode.jpa.MyJPAApplication : student[id=2, name='Student B', age='11'] |
That’s all on the Spring Boot JPA One to One Foreign Key Relationship Mapping Example
References
One-to-One Relationships
Spring Boot – Database initialization
Download source complete, click link below
springboot-jpa-onetoone-foreignkey-example.zip (366 downloads)