获得具有持久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: 
> ...

当前回答

好吧,我知道有点晚了。但对于那些勇敢的灵魂来说,随着时间的推移,他们会看到这一点。

如文件中所写:

@Basic: 到数据库列的最简单的映射类型。基本注释可以应用于以下类型中的任意一种的持久属性或实例变量:]、枚举和任何其他实现java.io.Serializable的类型。

重要的部分是实现Serializable的类型

所以到目前为止,最简单和最容易使用的解决方案是简单地使用ArrayList而不是List(或任何可序列化的容器):

@Basic
ArrayList<Color> lovedColors;

@Basic
ArrayList<String> catNames;

但是请记住,这将使用系统序列化,因此它将带来一些代价,例如:

如果序列化对象模型将改变,你可能无法恢复数据 存储的每个元素都会增加少量开销。

简而言之

存储标志或少数元素非常简单,但我不这么做 建议它存储可能变大的数据。

其他回答

这个答案是在JPA2之前实现的,如果你正在使用JPA2,请参阅上面的ElementCollection答案:

模型对象内的对象列表通常被认为是与另一个对象的“OneToMany”关系。但是,String本身并不是一对多关系的允许客户端,因为它没有ID。

因此,您应该将字符串列表转换为包含ID和String的参数类JPA对象列表。您可以潜在地使用String作为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.

我还是建议做第一种情况。这对以后的恋爱很有帮助。

根据Java持久化与Hibernate

用注释映射值类型集合[…]。在撰写本文时,它还不是Java持久性标准的一部分

如果你正在使用Hibernate,你可以这样做:

@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中可用。

我们也可以用这个。

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

好吧,我知道有点晚了。但对于那些勇敢的灵魂来说,随着时间的推移,他们会看到这一点。

如文件中所写:

@Basic: 到数据库列的最简单的映射类型。基本注释可以应用于以下类型中的任意一种的持久属性或实例变量:]、枚举和任何其他实现java.io.Serializable的类型。

重要的部分是实现Serializable的类型

所以到目前为止,最简单和最容易使用的解决方案是简单地使用ArrayList而不是List(或任何可序列化的容器):

@Basic
ArrayList<Color> lovedColors;

@Basic
ArrayList<String> catNames;

但是请记住,这将使用系统序列化,因此它将带来一些代价,例如:

如果序列化对象模型将改变,你可能无法恢复数据 存储的每个元素都会增加少量开销。

简而言之

存储标志或少数元素非常简单,但我不这么做 建议它存储可能变大的数据。

如果有人正在寻找一个替代的解决方案,你存储你的字符串列表作为一个字段在你的数据库,这里是我如何解决这个问题。创建一个像这样的转换器:

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对象中,您将获得一个包含这些字符串的列表。