[java] JPA에서 List <String> 유형의 속성을 유지하는 방법은 무엇입니까?

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


답변

오래된 스레드를 되살려 미안하지만 누구나 문자열 목록을 데이터베이스에 하나의 필드로 저장하는 대체 솔루션을 찾고 있어야합니다. 여기에서 해결했습니다. 다음과 같이 변환기를 작성하십시오.

import java.util.Arrays;
import java.util.List;

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;

@Converter
public class StringListConverter implements AttributeConverter<List<String>, String> {
    private static final String SPLIT_CHAR = ";";

    @Override
    public String convertToDatabaseColumn(List<String> stringList) {
        return String.join(SPLIT_CHAR, stringList);
    }

    @Override
    public List<String> convertToEntityAttribute(String string) {
        return Arrays.asList(string.split(SPLIT_CHAR));
    }
}

이제 다음과 같이 엔터티에서 사용하십시오.

@Convert(converter = StringListConverter.class)
private List<String> yourList;

데이터베이스에서 목록은 foo; bar; foobar로 저장되며 Java 객체에는 해당 문자열이 포함 된 목록이 표시됩니다.

이것이 누군가에게 도움이되기를 바랍니다.


답변

이 답변은 JPA2 이전에 구현되었으며 JPA2를 사용하는 경우 위의 ElementCollection 답변을 참조하십시오.

모델 개체 내부의 개체 목록은 일반적으로 다른 개체와의 “OneToMany”관계로 간주됩니다. 그러나 문자열 자체는 ID가 없기 때문에 일대 다 관계의 허용 가능한 클라이언트가 아닙니다.

그래서, 당신은 해야 인수 급 JPA의 목록에 문자열 목록을 변환 ID와 문자열을 포함하는 객체. 문자열을 ID로 사용하면 잠재적으로 ID 필드를 제거하고 문자열이 동일한 행을 통합하여 테이블의 공간을 절약 할 수 있지만 인수를 원래 순서로 다시 정렬하는 기능을 잃을 수 있습니다 (주문 정보를 저장하지 않았으므로).

또는 목록을 @Transient로 변환하고 클래스에 VARCHAR () 또는 CLOB 인 다른 필드 (argStorage)를 추가 할 수 있습니다. 그런 다음 3 개의 함수를 추가해야합니다. 그 중 2 개는 동일하며 문자열 목록을 쉽게 분리 할 수있는 방식으로 구분 된 단일 문자열 (argStorage)로 변환해야합니다. @PrePersist 및 @PreUpdate를 사용하여이 두 가지 기능 (각각 동일한 기능)에 주석을 답니다. 마지막으로 argStorage를 문자열 목록으로 다시 분할하는 세 번째 함수를 추가하고 @PostLoad에 주석을 답니다. 이렇게하면 명령을 저장할 때마다 CLOB가 문자열로 업데이트되고 argStorage 필드가 DB에 저장되기 전에 업데이트됩니다.

나는 여전히 첫 번째 사례를 제안합니다. 나중에 실제 관계를 유지하는 것이 좋습니다.


답변

Hibernate 를 사용한 Java Persistence 에 따르면

주석으로 값 유형의 콜렉션 맵핑 […]. 작성 당시에는 Java Persistence 표준의 일부가 아닙니다.

최대 절전 모드를 사용하는 경우 다음과 같은 작업을 수행 할 수 있습니다.

@org.hibernate.annotations.CollectionOfElements(
    targetElement = java.lang.String.class
)
@JoinTable(
    name = "foo",
    joinColumns = @JoinColumn(name = "foo_id")
)
@org.hibernate.annotations.IndexColumn(
    name = "POSITION", base = 1
)
@Column(name = "baz", nullable = false)
private List<String> arguments = new ArrayList<String>();

업데이트 : 이제 JPA2에서 사용할 수 있습니다.


답변

이것을 사용할 수도 있습니다.

@Column(name="arguments")
@ElementCollection(targetClass=String.class)
private List<String> arguments;


답변

나는 같은 문제가 있었기 때문에 주어진 가능한 해결책에 투자했지만 결국에는 ‘;’ 분리 된 문자열 목록.

그래서 나는 가지고있다

// a ; separated list of arguments
String arguments;

public List<String> getArguments() {
    return Arrays.asList(arguments.split(";"));
}

이런 식으로 데이터베이스 테이블에서 목록을 쉽게 읽고 편집 할 수 있습니다.


답변

JPA의 Hibernate 구현을 사용할 때, 단순히 List 대신 ArrayList로 유형을 선언하면 최대 절전 모드가 데이터 목록을 저장할 수 있다는 것을 알았습니다.

분명히 이것은 Entity 객체 목록을 만드는 것과 비교하여 많은 단점이 있습니다. 게으른 로딩이나 다른 개체에서 목록의 엔터티를 참조 할 수 없으며 데이터베이스 쿼리를 구성하는 데 어려움이있을 수 있습니다. 그러나 엔터티와 함께 ​​항상 열심히 가져오고 싶은 상당히 원시적 인 유형의 목록을 처리 할 때이 접근법은 나에게 잘 보입니다.

@Entity
public class Command implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    Long id;

    ArrayList<String> arguments = new ArrayList<String>();


}