資料表中常有欄位用來儲存資料的類型(type)、種類(category)、狀態(state)、情況(status),這些欄位值通常對應到Java的enum。JPA 2.1可使用AttributeConverter
轉換資料表欄位與entity的enum資料型態屬性。
範例環境:
- Spring Boot 2.3.2.RELEASE
- Spring Data JPA (Hibernate 5.4.x, JPA 2.2)
- H2 Database
- Lombok
例如下面的enum State
代表員工狀態,每種狀態有對應的數值代碼(code
)。
State
package com.abc.demo.entity.enumeration;
import lombok.Getter;
/** 員工狀態 */
public enum State {
/** 正常 */
NORMAL(0),
/** 缺席 */
LEAVE(1),
/** 離職 */
QUIT(2),
/** 其他 */
OTHERS(3),
;
@Getter
private final int code;
State(int code) {
this.code = code;
}
public static State of(int code) {
for (State state : State.values()) {
if (state.getCode() == code) {
return state;
}
}
return State.OTHERS;
}
}
Employee
為對應EMPLOYEE
資料表的entity。屬性state
型態為上面的State
,而資料表的EMPLOYEE.STATE
欄位的型態為數值,例如Oracle的NUMBER
或MySQL的INT
。所以需要把State
的值轉為對應的數值存到欄位中,在屬性前加上@Convert
,並將converter
設為自訂的AttributeConverter
,也就是下面的StateConverter
。
Employee
package com.abc.demo.entity;
import com.abc.demo.entity.enumeration.State;
import com.abc.demo.entity.enumeration.converter.StateConverter;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.io.Serializable;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@Entity
public class Employee implements Serializable {
private static final Long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
@Convert(converter = StateConverter.class)
private State state;
}
StateConverter
實作AttributeConverter
介面,類別名稱前標註@Converter
,為轉換enum為欄位型態的實作。
覆寫convertToDatabaseColumn()
把enum屬性值轉為資料表欄位值;
覆寫convertToEntityAttribute()
把資料庫欄位值轉為enum屬性值。
StateConverter
package com.abc.demo.entity.enumeration.converter;
import com.abc.demo.entity.enumeration.State;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
@Converter
public class StateConverter implements AttributeConverter<State, Integer> {
@Override
public Integer convertToDatabaseColumn(State attribute) {
return attribute.getCode();
}
@Override
public State convertToEntityAttribute(Integer dbData) {
if (dbData == null) {
return State.OTHERS;
}
return State.of(dbData);
}
}
存取Employee
的EmployeeRepository
。
EmployeeRepository
package com.abc.demo.repository;
import com.abc.demo.entity.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface EmployeeRepository extends JpaRepository<Employee, Long> {
}
使用EmployeeRepositoryTests
測試是否有成功轉換。
EmployeeRepositoryTests
package com.abc.demo.repository;
import com.abc.demo.entity.Employee;
import com.abc.demo.entity.enumeration.State;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.Optional;
@SpringBootTest
class EmployeeRepositoryTests {
@Autowired
private EmployeeRepository employeeRepository;
/**
* 測試資料表欄位值轉entity屬性值
*/
@Test
void testStateConverter_tableColumnToEntityAttribute() {
State state = employeeRepository.findById(1L)
.map(Employee::getState).orElse(State.OTHERS);
Assertions.assertEquals(State.NORMAL, state);
}
/**
* 測試entity屬性值轉資料表欄位值
*/
@Test
void testStateConverter_entityAttributeToTableColumn() {
Employee employee = Employee.builder()
.name("Mike")
.state(State.LEAVE)
.build();
State state = Optional.of(employeeRepository.save(employee))
.map(Employee::getState).orElse(State.OTHERS);
Assertions.assertEquals(State.LEAVE, state);
}
}
參考github。
沒有留言:
張貼留言