In any application there would be a need for pre-defined ordered set of constants. Enum is a data type good for such cases. Java has enum since 1.5. Databases do support enum type and PostgreSQL has a special support for this since release 8.3. JPA and Spring Data is a good match to use in modern Java applications for persistence, especially in Spring Boot applications.
Environment: Java 17, Spring Boot 2.6,7 on macOS Catalina 10.15.7
Example Scenario - A persistable entity object in a Spring Boot micro-service application with JPA and PostgreSQL DB.
DDL Script
-- create enum type genders
CREATE TYPE genders AS ENUM(
'MALE', 'FEMALE'
);
-- create people table
CREATE TABLE people(
id VARCHAR(36) PRIMARY KEY,
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
gender genders NOT NULL
);
-- Unique Constraints
ALTER TABLE people
ADD CONSTRAINT people_fname_lname_uk UNIQUE (first_name, last_name);
Maven dependencies: pom.xml
...
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>com.vladmihalcea</groupId>
<artifactId>hibernate-types-55</artifactId>
<version>2.16.2</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
<scope>provided</scope>
</dependency>
...
</dependencies>
...
Enum: Gender.java
import lombok.AllArgsConstructor;
@AllArgsConstructor
public enum Gender {
MALE("Male"), FEMALE("Female");
String genderName;
}
Domain Object: Person.java
import com.vladmihalcea.hibernate.type.basic.PostgreSQLEnumType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.UniqueConstraint;
import javax.validation.constraints.NotNull;
import java.util.UUID;
@Entity
@Table(
name = "people",
uniqueConstraints = {
@UniqueConstraint(
columnNames = {"firstName", "lastName"},
name = "people_fname_lname_uk"
)
}
)
@TypeDef(
name = "pgsql_enum",
typeClass = PostgreSQLEnumType.class
)
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class Person {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(length = 36, nullable = false, updatable = false)
@Type(type="org.hibernate.type.UUIDCharType")
private UUID id;
@NotNull
String firstName;
@NotNull
String lastName;
@NotNull
@Enumerated(EnumType.STRING)
@Type(type = "pgsql_enum")
Gender gender;
}
JPA Repository: PersonRepository.java
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.UUID;
public interface PersonRepository extends JpaRepository<Person, UUID> {
Person findByFirstNameAndLastNameAndGender(String firstName, String lastName, Gender gender);
@Query(value = "SELECT * FROM people WHERE first_name = :firstName AND last_name = :lastName AND gender = CAST(:#{#gender.name()} as genders)", nativeQuery = true)
Person findByFirstNameAndLastNameAndGenderNQ(String firstName, String lastName, Gender gender);
}
JPA Repository Test: PersonRepositoryIT.java
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
import static org.assertj.core.api.Assertions.assertThat;
@ActiveProfiles("test")
@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class PersonRepositoryIT {
@Autowired
TestEntityManager testEntityManager;
@Autowired
PersonRepository personRepository;
@Test
public void findBy_returns_expected() {
// given: data in DB
List.of(
Person.builder().firstName("Giri").lastName("Potte").gender(Gender.MALE),
Person.builder().firstName("Boo").lastName("Potte").gender(Gender.MALE)
).forEach(testEntityManager::persist);
testEntityManager.flush();
// expect:
assertThat(
personRepository.findByFirstNameAndLastNameAndGender("Giri", "Potte", Gender.MALE)
).isNotNull()
.extracting("firstName", "lastName", "gender")
.isEqualTo(List.of("Giri", "Potte", Gender.MALE));
// expect:
assertThat(
personRepository.findByFirstNameAndLastNameAndGenderNQ("Boo", "Potte", Gender.MALE)
).isNotNull()
.extracting("firstName", "lastName", "gender")
.isEqualTo(List.of("Boo", "Potte", Gender.MALE));
}
}
TIP
From Database side, sometimes, it's handy to have some SQLs dealing with enum type to create, drop, list values, add a new value, modify existing value etc.
I was just fiddling with PostgreSQL enum type and the following is the link: