Programming

List 유형의 속성을 유지하는 방법

procodes 2020. 6. 15. 22:09
반응형

List 유형의 속성을 유지하는 방법 JPA에서?


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


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

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

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

Alternatively, you could convert your list to @Transient and add another field (argStorage) to your class that is either a VARCHAR() or a CLOB. You'll then need to add 3 functions: 2 of them are the same and should convert your list of Strings into a single String (in argStorage) delimited in a fashion that you can easily separate them. Annotate these two functions (that each do the same thing) with @PrePersist and @PreUpdate. Finally, add the third function that splits the argStorage into the list of Strings again and annotate it @PostLoad. This will keep your CLOB updated with the strings whenever you go to store the Command, and keep the argStorage field updated before you store it to the DB.

I still suggest doing the first case. It's good practice for real relationships later.


Sorry to revive an old thread but should anyone be looking for an alternative solution where you store your string lists as one field in your database, here's how I solved that. Create a Converter like this:

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));
    }
}

Now use it on your Entities like this:

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

In the database your list will be stored as foo;bar;foobar and in your Java object you will get a list with those strings.

Hope this is helpful to someone.


According to Java Persistence with Hibernate

mapping collections of value types with annotations [...]. At the time of writing it isn't part of the Java Persistence standard

If you were using Hibernate, you could do something like:

@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>();

Update: Note, this is now available in JPA2.


We can also use this.

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

When using the Hibernate implementation of JPA , I've found that simply declaring the type as an ArrayList instead of List allows hibernate to store the list of data.

Clearly this has a number of disadvantages compared to creating a list of Entity objects. No lazy loading, no ability to reference the entities in the list from other objects, perhaps more difficulty in constructing database queries. However when you are dealing with lists of fairly primitive types that you will always want to eagerly fetch along with the entity, then this approach seems fine to me.

@Entity
public class Command implements Serializable {

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

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


}

I had the same problem so I invested the possible solution given but at the end I decided to implement my ';' separated list of String.

so I have

// a ; separated list of arguments
String arguments;

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

This way the list is easily readable/editable in the database table;


Ok i know its bit late. But for those brave souls that will see this as time passes.

As written in documentation:

@Basic: The simplest type of mapping to a database column. The Basic annotation can be applied to a persistent property or instance variable of any of the following types: Java primitive types, [...], enums, and any other type that implements java.io.Serializable.

The important part is type that implements Serializable

So by far the most simple and easiest to use solution is simply using ArrayList instead of List (or any serializable container):

@Basic
ArrayList<Color> lovedColors;

@Basic
ArrayList<String> catNames;

Remember however that this will use system serialization, so it will come with some price, such as:

  • if serialized object model will change, u might not be able to restore data

  • small overhead is added for each element stored.

In short

it is quite simple to store flags or few elements, but i would not recomend it to store data that might grow big.


My fix for this issue was to separate the primary key with the foreign key. If you are using eclipse and made the above changes please remember to refresh the database explorer. Then recreate the entities from the tables.


Thiago answer is correct, adding sample more specific to question, @ElementCollection will create new table in your database, but without mapping two tables, It means that the collection is not a collection of entities, but a collection of simple types (Strings, etc.) or a collection of embeddable elements (class annotated with @Embeddable).

Here is the sample to persist list of String

@ElementCollection
private Collection<String> options = new ArrayList<String>();

Here is the sample to persist list of Custom object

@Embedded
@ElementCollection
private Collection<Car> carList = new ArrayList<Car>();

For this case we need to make class Embeddable

@Embeddable
public class Car {
}

It seems none of the answers explored the most important settings for a @ElementCollection mapping.

When you map a list with this annotation, and let JPA/Hibernate auto generates the tables, columns, etc., it'll use auto generated names as well.

So, let's analyze a basic example:

@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;

}
  1. The basic @ElementCollection annotation (where you can define the known fetch and targetClass preferences)
  2. The @CollectionTable annotation is very useful when it comes to give a name to the table that'll be generated, as well as definitions like joinColumns, foreignKey's, indexes, uniqueConstraints, etc.
  3. @Column is important to define the name of column that'll store the varchar value of the list.

The generated DDL creation would be:

-- 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)
);

참고URL : https://stackoverflow.com/questions/287201/how-to-persist-a-property-of-type-liststring-in-jpa

반응형