PostgreSQL을 포함한 몇몇 RDB에서는 Array Type의 컬럼 유형을 지원한다.
그러나 hibernate에서 제공해주는 기본 Type들은 Array Type을 지원하지 않는다.
개발자는 Hibernate Type을 Custom 구현해서 사용하거나,
라이브러를 이용해서 PostgreSQL ArrayType을 Spring-data-jpa를 이용해서 hibernate로 매핑할 수 있다.
테스트에 사용된 도메인 엔티티는 다음과 같다.
도메인 엔티티
@Entity public class User { @Id private Long id; private String name; private String[] roles; private List<String> hobbies; }
1. Custom Hibernate Type
Hibernate는 다양한 기본 Type을 지정해 주므로 Custom 을 구현하기는 드물지만, 데이터를 저장, 가져오는 것에 대해 Custom Type을 지정할 수 있다.
org.hibernate.type.Type
인터페이스를 구현하거나
org.hibernate.usertype.UserType
인터페이스를 구현하고
타입을 적용할 컬럼에 @Type(type = "패키지명.커스텀타입명")
어노테이션을 적용해서 구현할 수 있다.
그 중 UserType을 이용해서 구현한 예제이다.
자바의 String[] 배열을 컬럼의 Array 타입으로 변환하는 Custom Type
public class CustomStringArrayType implements UserType { @Override public int[] sqlTypes() { return new int[]{Types.ARRAY}; } @Override public Class returnedClass() { return String[].class; } @Override public Object nullSafeGet(ResultSet rs, String[] names, SharedSessionContractImplementor session, Object owner) throws HibernateException, SQLException { Array array = rs.getArray(names[0]); return array != null ? array.getArray() : null; } @Override public void nullSafeSet(PreparedStatement st, Object value, int index, SharedSessionContractImplementor session) throws HibernateException, SQLException { if (value != null && st != null) { Array array = session.connection().createArrayOf("text", (String[])value); st.setArray(index, array); } else { st.setNull(index, sqlTypes()[0]); } } //implement equals, hashCode, and other methods }
- sqlTypes() 메소드 : 매핑된 컬럼에 대한 SQL Type을 반환해야 한다.
- SQL Type은 java.sql.Types에 정의되어 있다.
- returnedClass() 메소드 : nullSafeGet() 메소드에 의해 반환될 클래스 타입을 지정해야 한다.
- SQL Type과 같아야 한다
- 사용자 지정 커스텀 클래스 (예를들면 Embedded 클래스)도 반환이 가능하다.
- nullSafeGet() 메소드 : DB에서 값을 읽어온 후 자바 데이터 타입으로 처리한다.
- null을 반환할 수 있으므로 null 처리가 필요하다.
- nullSafeSet() 메소드 : DB에 값을 쓸 때, 자바 타입을 어떻게 쓸건지 처리한다.
- 여기서는 PostgreSQL text 타입의 배열을 지정한다.
그런 다음 CustomStringArrayType 클래스를 사용하여 문자열 배열을 PostgreSQL 텍스트 배열에 매핑한다.
@Entity public class User { //... @Column(columnDefinition = "text[]") @Type(type = "com.ys.arraymapping.CustomStringArrayType") private String[] roles; }
2. hibernate-types 라이브러리로 매핑
깃허브 : https://github.com/vladmihalcea/hypersistence-utils
vladmihalcea의 오픈소스 라이브러리를 사용하면 쉽게 구현할 수 있다
여기서 다양한 데이터 타입들을 커스텀해서 지원해준다.
- 라이브러리를 추가하고 필요한것만 사용하면 된다.
의존성 추가
implementation("com.vladmihalcea:hibernate-types-52:2.21.1")
하이버네이트 버전에 따른 지원하는 버전이 다르므로, 깃허브 가서 보고 하이브네이트 버전에 맞는 라이브러리 버전을 선택하면 된다.
List<String>
타입과 String[]
타입을 매핑하려면 다음과 같이 사용하면 된다.
@TypeDefs({ @TypeDef( name = "string-array", typeClass = StringArrayType.class ), @TypeDef( name = "list-array", typeClass = ListArrayType.class ) }) @Entity public class User { @Id private Long id; private String name; @Type(type = "string-array") @Column( name = "roles", columnDefinition = "text[]" ) private String[] roles; @Type(type = "list-array") @Column( name = "hobbies", columnDefinition = "text[]" ) private List<String> hobbies; }
Custom Type을 구현한것과 같이 CustomStringArrayType 과 유사하게,
String 배열 에 대한 매퍼로서 hibernate-types 라이브러리에서 제공하는 StringArrayType
클래스를 사용할 수 있다.
또한, list-array 타입도 지원하므로 List에 대해서도 사용할 수 있다.
- 라이브러리에서 DateArrayType, EnumArrayType, DoubleArrayType 과 같은 몇 가지 편리한 매퍼도 추가적으로 제공한다.
hibernate-types 에서 지원하는 다양한 Array Types 예제
스키마와 도메인 모델
event라는 Table이 있다. 이 Table의 스키마는 다음과 같다.
다음은 event table과 매핑되는 Entity Domain model이다.
다음과 같이 매핑할 수 있다.
@Entity(name = "Event") @Table(name = "event") @TypeDef( name = "list-array", typeClass = ListArrayType.class ) public class Event { @Id private Long id; @Type(type = "list-array") @Column( name = "sensor_ids", columnDefinition = "uuid[]" ) private List<UUID> sensorIds; @Type(type = "list-array") @Column( name = "sensor_names", columnDefinition = "text[]" ) private List<String> sensorNames; @Type(type = "list-array") @Column( name = "sensor_values", columnDefinition = "integer[]" ) private List<Integer> sensorValues; @Type(type = "list-array") @Column( name = "sensor_long_values", columnDefinition = "bigint[]" ) private List<Long> sensorLongValues; @Type( type = "io.hypersistence.utils.hibernate.type.array.ListArrayType", parameters = { @Parameter( name = ListArrayType.SQL_ARRAY_TYPE, value = "sensor_state" ) } ) @Column( name = "sensor_states", columnDefinition = "sensor_state[]" ) private List<SensorState> sensorStates; @Type(type = "list-array") @Column( name = "date_values", columnDefinition = "date[]" ) private List<Date> dateValues; @Type(type = "list-array") @Column( name = "timestamp_values", columnDefinition = "timestamp[]" ) private List<Date> timestampValues; //Getters and setters omitted for brevity }
https://vladmihalcea.com/postgresql-array-java-list/
이 사이트에 사용방법이 자주 업데이트 되므로 필요할때마다 가서 보면 된다.
참조
'Spring > JPA' 카테고리의 다른 글
JPA Hibernate Id 생성 전략과 UUID 전략 (0) | 2023.01.18 |
---|---|
Jpa Hibernate Custom Id Generator (1) | 2023.01.18 |
Cannot call sendError() after the response has been committed - 순환 참조 문제 With JPA Entity (0) | 2022.12.23 |
Jpa 쿼리 파라미터 로그 확인방법 - With DataJpaTest p6spy (1) | 2022.12.17 |
영속성 전이(Cascade) ManyToOne 시 주의할점 - 상위 엔티티 삭제 문제 (1) | 2022.12.15 |