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
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
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
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
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
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
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