Core Java

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.

Jpa Optional Field Project
Figure 1 Jpa Optional Field Project

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 with nullable=false attribute for a mandatory field.
  • Line 26: annotates @Column with nullable=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 projection IdName interface.
  • finalAllNames: similar to finalAllIdName but returns a list of the Names projection.
  • finalAllNamesWithoutUsedName: annotates @Query with raw SQL query and returns results as a list of OptionalFieldsEntity.

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 a null value.

Ran the Junit test and verified tests were passed.

Jpa Optional Field Tests
Figure 2 Jpa Optional Field Tests

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.

Download
You can download the full source code of this example here: Optional Fields in JPA Entity Example

Do you want to know how to develop your skillset to become a Java Rockstar?
Subscribe to our newsletter to start Rocking right now!
To get you started we give you our best selling eBooks for FREE!
1. JPA Mini Book
2. JVM Troubleshooting Guide
3. JUnit Tutorial for Unit Testing
4. Java Annotations Tutorial
5. Java Interview Questions
6. Spring Interview Questions
7. Android UI Design
and many more ....
I agree to the Terms and Privacy Policy

Mary Zheng

Mary graduated from the Mechanical Engineering department at ShangHai JiaoTong University. She also holds a Master degree in Computer Science from Webster University. During her studies she has been involved with a large number of projects ranging from programming and software engineering. She worked as a lead Software Engineer where she led and worked with others to design, implement, and monitor the software solution.
Subscribe
Notify of
guest


This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
Back to top button