Java 如何在 JPA 中保留 List<String> 类型的属性?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/287201/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to persist a property of type List<String> in JPA?
提问by Andrea Francia
What is the smartest way to get an entity with a field of type List persisted?
获取具有 List 类型字段的实体的最聪明方法是什么?
Command.java
命令.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);
}
}
This code produces:
此代码产生:
> 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:
> ...
采纳答案by Thiago H. de Paula Figueiredo
Use some JPA 2 implementation: it adds a @ElementCollection annotation, similar to the Hibernate one, that does exactly what you need. There's one example here.
使用一些 JPA 2 实现:它添加了一个 @ElementCollection 注释,类似于 Hibernate 注释,这正是您所需要的。有一个例子在这里。
Edit
编辑
As mentioned in the comments below, the correct JPA 2 implementation is
正如下面的评论中提到的,正确的 JPA 2 实现是
javax.persistence.ElementCollection
@ElementCollection
Map<Key, Value> collection;
See: http://docs.oracle.com/javaee/6/api/javax/persistence/ElementCollection.html
请参阅:http: //docs.oracle.com/javaee/6/api/javax/persistence/ElementCollection.html
回答by toolkit
According to Java Persistence with Hibernate
根据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
使用注释映射值类型的集合 [...]。在撰写本文时,它不是 Java Persistence 标准的一部分
If you were using Hibernate, you could do something like:
如果您使用的是 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>();
Update: Note, this is now available in JPA2.
更新:注意,这现在在 JPA2 中可用。
回答by billjamesdev
This answer was made pre-JPA2 implementations, if you're using JPA2, see the ElementCollection answer above:
这个答案是在 JPA2 之前实现的,如果您使用的是 JPA2,请参阅上面的 ElementCollection 答案:
Lists of objects inside a model object are generally considered "OneToMany" relationships with another object. However, a String is not (by itself) an allowable client of a One-to-Many relationship, as it doesn't have an ID.
模型对象内的对象列表通常被视为与另一个对象的“一对多”关系。但是,字符串本身不是一对多关系的允许客户端,因为它没有 ID。
So, you shouldconvert your list of Strings to a list of Argument-class JPA objects containing an ID and a String. You could potentially use the String as the ID, which would save a little space in your table both from removing the ID field and by consolidating rows where the Strings are equal, but you would lose the ability to order the arguments back into their original order (as you didn't store any ordering information).
因此,您应该将字符串列表转换为包含 ID 和字符串的 Argument 类 JPA 对象列表。您可能会使用字符串作为 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.
或者,您可以将列表转换为 @Transient 并将另一个字段 (argStorage) 添加到您的类中,该字段是 VARCHAR() 或 CLOB。然后,您需要添加 3 个函数:其中 2 个是相同的,并且应该将您的字符串列表转换为单个字符串(在 argStorage 中),以您可以轻松地将它们分开的方式分隔。用@PrePersist 和@PreUpdate 注释这两个函数(每个函数都做同样的事情)。最后,再次添加将 argStorage 拆分为 Strings 列表的第三个函数,并将其注释为 @PostLoad。每当您去存储命令时,这将使您的 CLOB 与字符串保持更新,并在将其存储到数据库之前保持更新 argStorage 字段。
I still suggest doing the first case. It's good practice for real relationships later.
我仍然建议做第一种情况。这是以后建立真正关系的好习惯。
回答by billjamesdev
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.
当使用 JPA 的 Hibernate 实现时,我发现简单地将类型声明为 ArrayList 而不是 List 允许 hibernate 存储数据列表。
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>();
}
回答by Anthony
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;
这样,列表在数据库表中很容易阅读/编辑;
回答by powers meat
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.
我对此问题的解决方法是将主键与外键分开。如果您正在使用 eclipse 并进行了上述更改,请记住刷新数据库资源管理器。然后从表中重新创建实体。
回答by Inverce
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.
@Basic:映射到数据库列的最简单类型。Basic 注释可以应用于以下任何类型的持久属性或实例变量:Java 原始类型、[...]、枚举和任何其他实现 java.io.Serializable 的类型。
The important part is type that implements Serializable
重要的部分是实现 Serializable 的类型
So by far the most simple and easiest to use solution is simply using ArrayList instead of List (or any serializable container):
所以到目前为止,最简单和最容易使用的解决方案就是使用 ArrayList 而不是 List(或任何可序列化的容器):
@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.
存储标志或少量元素非常简单,但我不建议它存储可能会变大的数据。
回答by Zia
Thiago answer is correct, adding sample more specific to question, @ElementCollectionwill 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).
蒂亚戈的答案是正确的,添加更具体问题的示例,@ ElementCollection将在您的数据库中创建新表,但不映射两个表,这意味着该集合不是实体集合,而是简单类型(字符串等)的集合.) 或可嵌入元素的集合(用@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
@Embeddable
public class Car {
}
回答by Jaimin Patel
We can also use this.
我们也可以使用这个。
@Column(name="arguments")
@ElementCollection(targetClass=String.class)
private List<String> arguments;
回答by Jonck van der Kogel
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.
在数据库中,您的列表将存储为 foo;bar;foobar,而在您的 Java 对象中,您将获得一个包含这些字符串的列表。
Hope this is helpful to someone.
希望这对某人有帮助。