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

当前回答

Thiago的答案是正确的,添加了更具体的示例问题,@ElementCollection将在您的数据库中创建新表,但没有映射两个表,这意味着集合不是实体的集合,而是简单类型的集合(字符串等)或可嵌入元素的集合(类注释@Embeddable)。

下面是持久化String列表的示例

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

下面是持久化自定义对象列表的示例

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

在这种情况下,我们需要使类Embeddable

@Embeddable
public class Car {
}

其他回答

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

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

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

我想要的是在一个表列中持久化一组string的简单方法。

我最终使用JSON,因为MySQL 5.7+有原生支持。 这是我的解决方案

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

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

如文件中所写:

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

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

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

@Basic
ArrayList<Color> lovedColors;

@Basic
ArrayList<String> catNames;

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

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

简而言之

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

我解决这个问题的方法是将主键和外键分开。如果您正在使用eclipse并进行了上述更改,请记得刷新数据库资源管理器。然后从表中重新创建实体。