JPA에서 List 유형의 속성을 유지하려면 어떻게 해야 합니까?
List 유형의 필드를 가진 엔티티를 지속시키는 가장 현명한 방법은 무엇입니까?
Command.java
package persistlistofstring;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.Basic;
import javax.persistence.Entity;
import javax.persistence.EntityManager;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Persistence;
@Entity
public class Command implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
Long id;
@Basic
List<String> arguments = new ArrayList<String>();
public static void main(String[] args) {
Command command = new Command();
EntityManager em = Persistence
.createEntityManagerFactory("pu")
.createEntityManager();
em.getTransaction().begin();
em.persist(command);
em.getTransaction().commit();
em.close();
System.out.println("Persisted with id=" + command.id);
}
}
이 코드는 다음을 생성합니다.
> Exception in thread "main" javax.persistence.PersistenceException: No Persistence provider for EntityManager named pu: Provider named oracle.toplink.essentials.PersistenceProvider threw unexpected exception at create EntityManagerFactory:
> oracle.toplink.essentials.exceptions.PersistenceUnitLoadingException
> Local Exception Stack:
> Exception [TOPLINK-30005] (Oracle TopLink Essentials - 2.0.1 (Build b09d-fcs (12/06/2007))): oracle.toplink.essentials.exceptions.PersistenceUnitLoadingException
> Exception Description: An exception was thrown while searching for persistence archives with ClassLoader: sun.misc.Launcher$AppClassLoader@11b86e7
> Internal Exception: javax.persistence.PersistenceException: Exception [TOPLINK-28018] (Oracle TopLink Essentials - 2.0.1 (Build b09d-fcs (12/06/2007))): oracle.toplink.essentials.exceptions.EntityManagerSetupException
> Exception Description: predeploy for PersistenceUnit [pu] failed.
> Internal Exception: Exception [TOPLINK-7155] (Oracle TopLink Essentials - 2.0.1 (Build b09d-fcs (12/06/2007))): oracle.toplink.essentials.exceptions.ValidationException
> Exception Description: The type [interface java.util.List] for the attribute [arguments] on the entity class [class persistlistofstring.Command] is not a valid type for a serialized mapping. The attribute type must implement the Serializable interface.
> at oracle.toplink.essentials.exceptions.PersistenceUnitLoadingException.exceptionSearchingForPersistenceResources(PersistenceUnitLoadingException.java:143)
> at oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider.createEntityManagerFactory(EntityManagerFactoryProvider.java:169)
> at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:110)
> at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:83)
> at persistlistofstring.Command.main(Command.java:30)
> Caused by:
> ...
JPA 2의 실장에서는, Hibernate 와 같은 @ElementCollection 의 주석을 추가해, 필요한 것을 정확하게 실행할 수 있습니다.여기 한 가지 예가 있습니다.
편집
이하의 코멘트에 기재되어 있듯이, 올바른 JPA 2의 실장은 다음과 같습니다.
javax.persistence.ElementCollection
@ElementCollection
Map<Key, Value> collection;
참조: http://docs.oracle.com/javaee/6/api/javax/persistence/ElementCollection.html
문자열 목록을 데이터베이스에 하나의 필드로 저장할 수 있는 대체 솔루션을 찾는 사용자가 있다면 다음과 같이 해결합니다.다음과 같이 Converter를 만듭니다.
import java.util.Arrays;
import java.util.List;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import static java.util.Collections.*;
@Converter
public class StringListConverter implements AttributeConverter<List<String>, String> {
private static final String SPLIT_CHAR = ";";
@Override
public String convertToDatabaseColumn(List<String> stringList) {
return stringList != null ? String.join(SPLIT_CHAR, stringList) : "";
}
@Override
public List<String> convertToEntityAttribute(String string) {
return string != null ? Arrays.asList(string.split(SPLIT_CHAR)) : emptyList();
}
}
이제 엔티티에서 다음과 같이 사용합니다.
@Convert(converter = StringListConverter.class)
private List<String> yourList;
된다.foo;bar;foobar
Java 객체에는 이러한 문자열이 포함된 목록이 표시됩니다.
매핑의 가장 중요한 설정을 탐색한 답은 없는 것 같습니다.
이 주석을 사용하여 목록을 매핑하고 JPA/Hibernate가 테이블, 열 등을 자동으로 생성하도록 하면 자동으로 생성된 이름도 사용됩니다.
그럼 기본적인 예를 분석하겠습니다.
@Entity
@Table(name = "sample")
public class MySample {
@Id
@GeneratedValue
private Long id;
@ElementCollection // 1
@CollectionTable(name = "my_list", joinColumns = @JoinColumn(name = "id")) // 2
@Column(name = "list") // 3
private List<String> list;
}
- ★★
@ElementCollection
알려진 주석()을 할 수 .)fetch
★★★★★★★★★★★★★★★★★」targetClass
□□□□□□□□★ - 주석 기능은 다음과 같은 정의뿐만 아니라 생성되는 테이블에 이름을 지정할 때 매우 유용합니다.
joinColumns
,foreignKey
indexes
,uniqueConstraints
등등. @Column
이렇게 '을을 정의하는 것은 합니다.varchar
리스트의 값
생성되는 DDL은 다음과 같습니다.
-- table sample
CREATE TABLE sample (
id bigint(20) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (id)
);
-- table my_list
CREATE TABLE IF NOT EXISTS my_list (
id bigint(20) NOT NULL,
list varchar(255) DEFAULT NULL,
FOREIGN KEY (id) REFERENCES sample (id)
);
이 답변은 JPA2 구현 전에 작성되었습니다.JPA2를 사용하는 경우 위의 ElementCollection 답변을 참조하십시오.
모델 오브젝트 내의 오브젝트 리스트는 일반적으로 다른 오브젝트와의 "OneToMany"관계로 간주됩니다.그러나 ID가 없으므로 문자열 자체는 일대다 관계의 허용 클라이언트가 아닙니다.
따라서 Strings 목록을 ID와 String을 포함하는 Argument-class JPA 개체 목록으로 변환해야 합니다.ID로 String을 사용할 수 있으므로 ID 필드를 삭제하거나 문자열이 동일한 행을 통합하여 테이블 내의 공간을 조금 절약할 수 있습니다.다만, 순서 정보를 보존하지 않았기 때문에, 인수의 순서를 원래의 순서로 되돌릴 수 없게 됩니다.
또는 목록을 @Transient로 변환하고 VARCHAR() 또는 CLOB인 다른 필드(argStorage)를 클래스에 추가할 수도 있습니다.그 후 3개의 함수를 추가해야 합니다.그 중 2개는 동일하며 스트링 목록을 쉽게 구분할 수 있도록 구분된 단일 문자열(argStorage)로 변환해야 합니다.@PrePersist 및 @PreUpdate를 사용하여 이들 2개의 함수(각각 동일한 작업을 수행)에 주석을 추가합니다.마지막으로 argStorage를 Strings 목록에 다시 분할하는 세 번째 함수를 추가하고 @PostLoad에 주석을 추가합니다.그러면 명령어를 저장할 때마다 CLOB가 문자열로 업데이트되고 argStorage 필드가 DB에 저장되기 전에 업데이트됩니다.
난 여전히 첫 번째 사건을 할 것을 제안합니다.나중에 진짜 연애를 할 때 좋은 연습이에요.
이것도 쓸 수 있어요.
@Column(name="arguments")
@ElementCollection(targetClass=String.class)
private List<String> arguments;
최대 절전 모드에서의 Java 지속성 준수
값 유형의 집합을 주석과 매핑 [...].작성 시점에서는 Java Persistence 표준에 포함되지 않습니다.
휴지 상태를 사용하는 경우 다음과 같은 작업을 수행할 수 있습니다.
@CollectionOfElements(targetElement = String.class)
@JoinTable(name = "foo", joinColumns = @JoinColumn(name = "foo_id"))
@IndexColumn(name = "POSITION", base = 1)
@Column(name = "baz", nullable = false)
private List<String> arguments = new ArrayList<String>();
업데이트: JPA2에서 사용할 수 있게 되었습니다.
JPA의 Hibernate 구현을 사용하면 List가 아닌 ArrayList로 유형을 선언하는 것만으로 데이터 목록을 저장할 수 있습니다.
분명히 엔티티 오브젝트 목록을 작성하는 것에 비해 많은 단점이 있습니다.로딩이 느리거나 목록 내의 엔티티를 다른 오브젝트에서 참조할 수 없거나 데이터베이스 쿼리를 구성하는 데 어려움이 있을 수 있습니다.그러나 엔티티와 항상 함께 가져오고 싶어할 상당히 원시적인 유형의 목록을 다룰 때는 이 접근법이 괜찮다고 생각합니다.
@Entity
public class Command implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
Long id;
ArrayList<String> arguments = new ArrayList<String>();
}
저도 같은 문제가 있어서 주어진 해결책을 투자했는데, 결국 String의 ';' 구분 목록을 구현하기로 했습니다.
그래서 나는
// a ; separated list of arguments
String arguments;
public List<String> getArguments() {
return Arrays.asList(arguments.split(";"));
}
이렇게 하면 데이터베이스 테이블에서 목록을 쉽게 읽거나 편집할 수 있습니다.
그래, 좀 늦은 거 알아.하지만 시간이 흐르면서 보게 될 용감한 영혼들을 위해.
매뉴얼에 기재된 바와 같이:
@Basic: 데이터베이스 열에 매핑하는 가장 단순한 유형입니다.기본 주석은 Java primitive type, [...], enums 및 java.io을 구현하는 기타 유형의 영구 속성 또는 인스턴스 변수에 적용할 수 있습니다.시리얼 가능.
중요한 것은 Serialable을 실장하는 타입입니다.
따라서 지금까지 가장 간단하고 사용하기 쉬운 솔루션은 목록(또는 시리얼 가능한 컨테이너) 대신 ArrayList를 사용하는 것입니다.
@Basic
ArrayList<Color> lovedColors;
@Basic
ArrayList<String> catNames;
단, 이 경우 시스템시리얼라이제이션이 사용되므로 다음과 같은 비용이 발생합니다.
직렬 개체 모델이 변경되면 데이터를 복원하지 못할 수 있습니다.
저장된 각 요소에 대해 작은 오버헤드가 추가됩니다.
요컨대
플래그나 소수의 요소를 저장하는 것은 매우 간단하지만, 커질 수 있는 데이터를 저장하는 데는 권장하지 않습니다.
Tiago의 답변은 정확합니다.문제의 구체적인 예제를 추가하면 @ElementCollection은 데이터베이스에 새로운 테이블을 만듭니다만, 2개의 테이블을 매핑하지 않는 경우, 그 컬렉션은 엔티티의 컬렉션이 아니라, 심플한 타입(Strings 등) 또는 임베디드 가능한 요소의 컬렉션(@Embeddable로 주석을 단 클래스)임을 의미합니다.
다음은 String의 지속 목록 샘플입니다.
@ElementCollection
private Collection<String> options = new ArrayList<String>();
다음은 지속할 사용자 지정 개체의 샘플 목록입니다.
@Embedded
@ElementCollection
private Collection<Car> carList = new ArrayList<Car>();
이 경우 클래스를 Embeddable로 만들어야 합니다.
@Embeddable
public class Car {
}
다음은 @Converter 및 StringTokenizer를 사용하여 세트를 저장하는 솔루션입니다.@jonck-van-der-kogel 솔루션에 대한 체크가 조금 더 필요합니다.
엔티티 클래스의 경우:
@Convert(converter = StringSetConverter.class)
@Column
private Set<String> washSaleTickers;
String Set Converter:
package com.model.domain.converters;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import java.util.HashSet;
import java.util.Set;
import java.util.StringTokenizer;
@Converter
public class StringSetConverter implements AttributeConverter<Set<String>, String> {
private final String GROUP_DELIMITER = "=IWILLNEVERHAPPEN=";
@Override
public String convertToDatabaseColumn(Set<String> stringList) {
if (stringList == null) {
return new String();
}
return String.join(GROUP_DELIMITER, stringList);
}
@Override
public Set<String> convertToEntityAttribute(String string) {
Set<String> resultingSet = new HashSet<>();
StringTokenizer st = new StringTokenizer(string, GROUP_DELIMITER);
while (st.hasMoreTokens())
resultingSet.add(st.nextToken());
return resultingSet;
}
}
이 문제에 대한 수정은 프라이머리 키와 외부 키를 분리하는 것이었습니다.이클립스를 사용하고 있고 위의 내용을 변경한 경우 데이터베이스 탐색기를 새로 고쳐야 합니다.그런 다음 테이블에서 엔티티를 다시 만듭니다.
내가 원했던 것은 일련의 문자열을 테이블 열에 유지하는 간단한 방법이었다.
MySQL 5.7+는 네이티브 지원이기 때문에 JSON을 사용하게 되었습니다.여기 해결책이 있습니다.
@Column(name = "eligible_approvers", columnDefinition = "json")
@Convert(converter = ArrayJsonConverter.class)
private Set<String> eligibleApprovers;
그리고 아주 기본적인 변환기를 써라.
@Converter(autoApply = true)
public class ArrayJsonConverter implements AttributeConverter<Set, String> {
static final ObjectMapper mapper = new ObjectMapper();
@Override
public String convertToDatabaseColumn(Set list) {
if (list == null)
return null;
try {
return mapper.writeValueAsString(list);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
@Override
public Set convertToEntityAttribute(String dbJson) {
if (dbJson == null)
return null;
try {
return mapper.readValue(dbJson, new TypeReference<Set<String>>() {
});
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
}
@razvang이 쓴 매우 과소평가된 답변에 대해 코멘트를 하기에는 아직 내 평판이 충분하지 않기 때문에:
이 질문은 10여 년 전에 제기되었듯이, 그 이후로 세계의 많은 부분이 변했다는 것을 명심하세요.네이티브 JSON 컬럼을 지원하는 데이터베이스가 있어 다른 답변에서 사용되는 개별 엔티티, 조인 또는 커스텀 String-to-List 변환기를 사용하는 대신 이 기능을 사용할 수 있습니다.
다만, @razvang의 훌륭한 답변에 대해서, 다음의 2개의 완전한 옵션 변경을 제안합니다.이것은, 고객의 상황에 따라서는 흥미로울지도 모릅니다.
- 때 빼도 요.
auto_apply = true
@Convert(converter = <CONVERTER_CLASS_NAME>.class)
엔티티 필드로 이동하여 컨버터 사용 시기를 제어할 수 있습니다. - a a a a를 대신
RuntimeException
변환이 실패할 때마다 오류를 바로 처리할 수 있습니다(예를 들어 빈 목록을 전달하고 로그 메시지를 쓰는 등). 어느 정도 정상적으로 실패할 수 있습니다.
언급URL : https://stackoverflow.com/questions/287201/how-to-persist-a-property-of-type-liststring-in-jpa
'source' 카테고리의 다른 글
삭제된 하위 구성 요소에서 이벤트 수신 중지 (0) | 2022.08.21 |
---|---|
이것을 사용하는 것의 단점은 무엇입니까?vue 컴포넌트의 $root? (0) | 2022.08.21 |
새로고침 시 Vue 라우터가 홈 페이지로 리디렉션됨 (0) | 2022.08.21 |
도트(".") 연산자와 화살표("->") 연산자가 C와목표-C (0) | 2022.08.21 |
하위에서 스토어 변경을 트리거해도 스토어가 업데이트되지 않습니다. (0) | 2022.08.19 |