Optional Fields in JPA Entity Example
1. Introduction
JPA Entity can have optional fields as not every data is needed for every operation. In this example, I will create a simple Spring Data JPA application that shows how to define the optional fields and retrieve the optional data from a database.
2. Setup
In this step, I will create a Spring boot gradle project along with Lombok, Spring Data JPA, Rest, and H2 libraries via Spring Initializer.
Here is the generated build.gradle
file. No modification is needed.
build.gradle
01 02 03 04 05 06 07 08 09 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 | plugins { id 'java' id 'org.springframework.boot' version '3.3.5' id 'io.spring.dependency-management' version '1.1.6' } group = 'com.example' version = '0.0.1-SNAPSHOT' java { toolchain { languageVersion = JavaLanguageVersion.of(17) } } configurations { compileOnly { extendsFrom annotationProcessor } } repositories { mavenCentral() } dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-validation' implementation 'org.springframework.boot:spring-boot-starter-data-rest' compileOnly 'org.projectlombok:lombok' runtimeOnly 'com.h2database:h2' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' testRuntimeOnly 'org.junit.platform:junit-platform-launcher' } tasks.named('test') { useJUnitPlatform() } |
2.1 Generated Spring Boot Application
No modification is needed for the generated DemoOptionalFieldsApplication.java
.
DemoOptionalFieldsApplication.java
01 02 03 04 05 06 07 08 09 10 11 12 13 | package com.example.demo_OptionalFields; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class DemoOptionalFieldsApplication { public static void main(String[] args) { SpringApplication.run(DemoOptionalFieldsApplication. class , args); } } |
2.2 Generated Spring Properties
In this step, I will update the generated application.properties
to enable the logger for SQL
statements.
application.properties
1 2 3 4 | spring.application.name=demo-OptionalFields spring.jpa.properties.hibernate.format_sql=true spring.jpa.show-sql=true |
- Line 3, 4: enable the SQL logging.
3. Jpa Optional Field Entity
In this step, I will create an OptionalFieldsEntity.Java
class that includes an optional field – usedName
and two projections: IdName
and Names
.
OptionalFieldsEntity.java
01 02 03 04 05 06 07 08 09 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 | package com.example.demo_OptionalFields; import org.springframework.data.rest.core.config.Projection; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; import jakarta.persistence.Table; import lombok.Data; import lombok.NoArgsConstructor; @Data @Entity @Table (name = "T_DEMOTABLE" ) @NoArgsConstructor public class OptionalFieldsEntity { @Id @GeneratedValue private Long id; @Column (name = "T_NAME" , nullable = false ) private String name; @Column (name = "T_USED_NAME" , nullable = true ) private String usedName; @Projection (name = "idName" , types = { OptionalFieldsEntity. class }) public interface IdName { Long getId(); String getName(); } @Projection (name = "Names" , types = { OptionalFieldsEntity. class }) public interface Names { String getName(); String getUsedName(); } } |
- Line 14, 15: annotates with
@Entity
and@Table
and the database table name is “T_DEMOTABLE
“. - Line 23: annotates
@Column
withnullable=false
attribute for a mandatory field. - Line 26: annotates
@Column
withnullable=true
attribute for an optional field. - Line 29, 35: annotates @Projection with a different grouping of fields.
4. Jpa Optional Field Repository
In this step, I will create an OptionalFieldsRepo.java
that extends from JpaRepository
. It has three methods:
finalAllIdName
: annotates@Query
with HQL query and returns the result as a list of the projectionIdName
interface.finalAllNames
: similar tofinalAllIdName
but returns a list of theNames
projection.finalAllNamesWithoutUsedName
: annotates@Query
with raw SQL query and returns results as a list ofOptionalFieldsEntity
.
OptionalFieldsRepo.java
01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 | package com.example.demo_OptionalFields; import java.util.List; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; import org.springframework.stereotype.Repository; @Repository public interface OptionalFieldsRepo extends JpaRepository<OptionalFieldsEntity, Long> { @Query (value = "SELECT idName from OptionalFieldsEntity idName" ) List<OptionalFieldsEntity.IdName> finalAllIdName(); @Query (value = "SELECT names from OptionalFieldsEntity names" ) List<OptionalFieldsEntity.Names> finalAllNames(); @Query (value = "SELECT * from T_DEMOTABLE where t_used_name is null" , nativeQuery = true ) List<OptionalFieldsEntity> finalAllNamesWithoutUsedName(); } |
- Line 12, 13: use a native HQL query to return the
IdName
projection. - Line 15, 16: use a native HQL query to return the
Names
projection. - Line 18, 19: use a native raw SQL query to return the
OptionalFieldsEntity
. Note:nativeQuery = true
.
5. JPA Optional Field Test
In this step, I will create an OptionalFieldsRepoTest.java
that has three tests to save and verify the optional field data via findAll
methods.
OptionalFieldsRepoTest.java
01 02 03 04 05 06 07 08 09 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 62 63 64 65 66 67 | package com.example.demo_OptionalFields; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; import java.util.List; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import com.example.demo_OptionalFields.OptionalFieldsEntity.Names; @SpringBootTest class OptionalFieldsRepoTest { @Autowired private OptionalFieldsRepo testClass; private OptionalFieldsEntity buildData(String name, String usedName) { OptionalFieldsEntity user1 = new OptionalFieldsEntity(); user1.setName(name); if (usedName != null ) { user1.setUsedName(usedName); } return user1; } @Test void test_findAll_hql() { testClass.save(buildData( "Mary" , "Shuning" )); testClass.save(buildData( "Zheng" , null )); List<OptionalFieldsEntity> allData = testClass.findAll(); assertEquals( "Shuning" , allData.get( 0 ).getUsedName()); assertNull(allData.get( 1 ).getUsedName()); } @Test void test_findAll_projection() { testClass.save(buildData( "Mary" , "Shuning" )); testClass.save(buildData( "Zheng" , null )); List<OptionalFieldsEntity.IdName> allData = testClass.finalAllIdName(); assertEquals( "Mary" , allData.get( 0 ).getName()); assertEquals( "Zheng" , allData.get( 1 ).getName()); List<Names> names = testClass.finalAllNames(); assertEquals( "Mary" , names.get( 0 ).getName()); assertEquals( "Zheng" , names.get( 1 ).getName()); assertEquals( "Shuning" , names.get( 0 ).getUsedName()); assertNull(names.get( 1 ).getUsedName()); } @Test void test_findAll_raw_sql() { testClass.save(buildData( "Mary" , "Shuning" )); testClass.save(buildData( "Zheng" , null )); List<OptionalFieldsEntity> allData = testClass.finalAllNamesWithoutUsedName(); assertEquals( 1 , allData.size()); assertEquals( "Zheng" , allData.get( 0 ).getName()); assertNull(allData.get( 0 ).getUsedName()); } } |
- Line 35, 36, 51, 52, 63: the optional field –
useName
– can have anull
value.
Ran the Junit test and verified tests were passed.
6. Conclusion
In this example, I created a simple Spring boot application that defined a JPA entity with an optional field and created a repository interface to retrieve the data via both Spring projection
interface and native query with HQL and raw SQL.
7. Download
This was an example of a gradle project that included Jpa optional fields.
You can download the full source code of this example here: Optional Fields in JPA Entity Example