如何在JPA中保留List <String>types的属性?

获取带有Listtypes字段的实体的最明智的方法是什么?

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实现:它添加了一个@ElementCollection注释,类似于Hibernate的注释,它完全符合你的需求。 这里有一个例子。

编辑

正如下面的评论中所提到的,正确的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”关系。 然而,一个string本身并不是一对多关系的允许客户端,因为它没有一个ID。

所以,你应该将你的string列表转换为包含一个ID和一个string的Argument-class JPA对象列表。 您可以使用String作为ID,这样可以通过删除ID字段并合并string相等的行来节省表中的一小部分空间,但是您将失去将这些参数sorting回原始顺序的能力(因为您没有存储任何订购信息)。

或者,您可以将列表转换为@Transient,并将另一个字段(argStorage)添加到您的类中,该类是VARCHAR()或CLOB。 然后你需要添加3个函数:其中2个是相同的,并且应该将你的string列表转换成一个单独的string(在argStorage中),以便于分隔的方式进行分隔。 使用@PrePersist和@PreUpdate注释这两个函数(每个函数都执行相同的操作)。 最后,添加第三个函数,将argStorage再次分解到string列表中,并对@PostLoad进行注释。 这将保持您的CLOB更新与string每当你去存储的命令,并保持argStorage领域之前,你把它存储到数据库。

我仍然build议做第一个案子。 以后这对于真正的人际关系很好。

根据Hibernate的Java持久性

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

如果你使用的是Hibernate,你可以这样做:

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

我有同样的问题,所以我投入了可能的解决scheme,但最终我决定实施我的';' string的分隔列表。

所以我有

 // a ; separated list of arguments String arguments; public List<String> getArguments() { return Arrays.asList(arguments.split(";")); } 

这样,列表在数据库表中很容易读取/编辑;

当使用JPA的Hibernate实现时,我发现简单地将types声明为ArrayList而不是List允许hibernate存储数据列表。

显然,与创build实体对象列表相比,这有许多缺点。 没有延迟加载,没有能力从其他对象引用列表中的实体,可能在构build数据库查询时更困难。 然而,当你处理的是相当原始types的列表,你总是希望与实体一起急切地获取,那么这种方法对我来说似乎很好。

 @Entity public class Command implements Serializable { @Id @GeneratedValue(strategy = GenerationType.AUTO) Long id; ArrayList<String> arguments = new ArrayList<String>(); } 

我们也可以使用这个。

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

我对这个问题的解决方法是将主键与外键分开。 如果你使用eclipse并进行了上述更改,请记得刷新数据库浏览器。 然后从表中重新创build实体。

好吧,我知道它晚了一点。 但是对于那些随着时间的推移将会看到的勇敢的灵魂。

正如文件中所写:

@Basic:映射到数据库列的最简单的types。 Basic注释可以应用于以下任何types的持久性属性或实例variables:Java基本types,枚举以及实现java.io.Serializable的任何其他types。

重要的部分是实现Serializable的types

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

 @Basic ArrayList<Color> lovedColors; @Basic ArrayList<String> catNames; 

但请记住,这将使用系统序列化,所以它会带来一些价格,如:

  • 如果序列化的对象模型会改变,你可能无法恢复数据

  • 为每个存储的元素添加小开销。

简而言之

存储标志或很less的元素是相当简单的,但我不会推荐它来存储可能变大的数据。

Thiago的答案是正确的,添加样本更具体的问题,@ ElementCollection会在您的数据库中创build新的表,但没有映射两个表,这意味着该集合不是一个实体的集合,而是一个简单types的集合(string等。)或可embedded元素的集合(用@Embeddable注解的类)。

这里是保存string列表的示例

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

这里是坚持自定义对象列表的示例

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

对于这种情况,我们需要使类Embeddable

 @Embeddable public class Car { }