A basic quick Spring Data JDBC example on how to map One-to-One database tables relation in entities.
1. Project Configuration
1.1. Used Technologies :
- Spring Boot 2.3.0.RELEASE
- Spring Data JDBC 2.0.0.RELEASE
- Spring Framework 5.2.6.RELEASE
- H2 / MySql DB
- Lombok 1.18.12
- JUnit 5
1.2. Maven Dependencies :
To start working with Spring Boot with Spring Data JDBC you need dependency spring-boot-starter-data-jdbc
. Use Lombok to avoid boiler plate code. Here is complete dependency list used in the example application.
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</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>
1.3. Database Tables :
Here are the database tables CUSTOMER and ADDRESS. Assume Customer
is associated with Address
and each customer have only one address.
CREATE TABLE `CUSTOMER` ( `ID` INT NOT NULL AUTO_INCREMENT, `NAME` varchar(45) NOT NULL, `MEMBERSHIP` varchar(100) NOT NULL, PRIMARY KEY (`ID`) ); CREATE TABLE `ADDRESS` ( `CUSTOMER` INT NOT NULL, `BUILDING` VARCHAR(45) NOT NULL, `STREET` VARCHAR(100) NOT NULL, `STATE` VARCHAR(45) NOT NULL, `COUNTRY` VARCHAR(45) NOT NULL, `ZIPCODE` VARCHAR(45) NOT NULL, CONSTRAINT `BK_PUB_ID_FK` FOREIGN KEY (`CUSTOMER`) REFERENCES `CUSTOMER` (`ID`) );
2. Mapping
Since Spring Data JDBC 1.1 supports annotation based configuration for entity relationships using Spring Data JDBC. Let’s have a look into following code snippets to understand entity mappings.
2.1. Address Entity :
@Table("ADDRESS") @Data // lombok @AllArgsConstructor @NoArgsConstructor public class Address { //@Id //private Long id; private String building; private String street; private String state; private String country; private String zipcode; }
2.2. Customer Entity :
@Data @AllArgsConstructor @NoArgsConstructor @Table("CUSTOMER") public class Customer { @Id private Long id; private String name; private String membership; private Address address; }
The above entities we can call Customer aggregation and Customer
is the root entity of the aggregation and it consists of Address
entity. Spring Data JDBC uses the ID to identify entities. The ID of an entity must be annotated with Spring Data’s @Id
annotation. When your data base has an auto-increment column for the ID column, the generated value gets set in the entity after inserting it into the database. Not required any additional mapping to associate One-to-One mapping for Address
entity within Customer
, presence of Address reference only required unless the associated foreign key name and entity name is different ( See next example in section 4).
Spring Data JDBC leave Id generation to the database, means we need to handle inserting Id in database by using auto-increment or other strategy in the database. Relations inside an aggregate need to be unidirectional.
You can notice that only aggregate root entity annotated with @Id annotation. If you ignore including ‘id’ field in Address entity, the insertion of Id value handled by database. If you include id field in Address entity, then the id field must be annotated with @Id or has to set the value to the property before calling repository method for insert data.
3. Test the mapping
3.1. A test Repository
@Repository public interface CustomerOneToOneTestRepository extends CrudRepository<Customer, Long>{ }
3.2. Test Case for Test the mapping
@SpringBootTest public class OneToOneMappingTest { @Autowired private CustomerOneToOneTestRepository customerOneToOneTestRepository; @Test @DisplayName("One-to-One Mapping Test1") void oneToOne_MappingTest1() { Address address = new Address(); address.setBuilding("102, North Wing, Lotus Avenue"); address.setStreet("Parklane, Los Angeles"); address.setState("California"); address.setCountry("United States"); address.setZipcode("90011"); Customer customer = new Customer(); customer.setName("Peter"); customer.setMembership("FREE"); customer.setAddress(address); Customer saved = customerOneToOneTestRepository.save(customer); System.err.println(saved); Assertions.assertTrue(saved != null); Optional<Customer> loaded = customerOneToOneTestRepository.findById(1L); System.err.println(loaded.get()); Assertions.assertTrue(loaded != null); } }
4.Another One-to-One Example
4.1. To understand bit more about entity mappings, I would like to provide one more example. First let’s have look into tables design for another example.
CREATE TABLE `USER_DETAILS` ( `ID` INT NOT NULL AUTO_INCREMENT, `CREATED_TIME` datetime NOT NULL, `UPDATED_TIME` datetime DEFAULT NULL, `USER_TYPE` varchar(45) NOT NULL, `DOB` date NOT NULL, PRIMARY KEY (`ID`) ); CREATE TABLE `USER_CREDENTIALS` ( `ID` INT NOT NULL AUTO_INCREMENT, `USER_NAME` VARCHAR(20) NOT NULL, `PASSWORD` VARCHAR(45) NOT NULL, `USER_DETAIL` INT NOT NULL, UNIQUE (`USER_NAME`), PRIMARY KEY (`ID`), CONSTRAINT `UD_CREDS_ID_FK` FOREIGN KEY (`USER_DETAIL`) REFERENCES `USER_DETAILS` (`ID`) );
4.2. Entity mappings for USER_DETAILS and USER_CREDENTIALS tables.
@Table("USER_CREDENTIALS") @Data // lombok @AllArgsConstructor @NoArgsConstructor public class Credentials { @Id private Long id; private String userName; private String password; }
@Data @AllArgsConstructor @NoArgsConstructor @Table("USER_DETAILS") public class User { @Id private Long id; private Date createdTime; private Date updatedTime; @Column("DOB") private Date dateofBirth; private UserType userType; @Column("USER_DETAIL") private Credentials credentials; }
From the above example and database tables, you can notice that associated foreign key name(USER_DETAIL) in USER_CREDENTIALS table and the corresponding entity name is different. So that we need to map the association using @Column annotation in this case like showed in above User entity mapping.
5. Conclusion
We demonstrated a Spring Data JDBC example on how to map One-to-One database tables relation in entities.
Checkout source code at Git Hub.
Other Spring Data JDBC Examples :
- Spring Data JDBC – Embedded Entities
- Spring Data JDBC – One-to-Many
- Spring Data JDBC – Many-to-Many
- Spring Data JDBC – Pagination
- Spring Data JDBC – Query Derivation
- Spring Boot – Loading Initial data
5. References
- Spring Documentation
- Lombok Data
- Hibernate One-to-One Unidirectional
- Spring Boot Data JDBC Example
- Spring Boot H2 Example