获得具有持久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:
> ...
似乎没有一个答案探讨了@ElementCollection映射的最重要的设置。
当您用这个注释映射一个列表并让JPA/Hibernate自动生成表、列等时,它也会使用自动生成的名称。
让我们来分析一个基本的例子:
@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;
}
基本的@ElementCollection注释(您可以在其中定义已知的fetch和targetClass首选项)
@CollectionTable注释在为将要生成的表命名时非常有用,它还可以定义像joinColumns、foreignKey’s、indexes、uniqueConstraints等。
@Column对于定义存储列表varchar值的列的名称很重要。
生成的DDL将是:
-- 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)
);
似乎没有一个答案探讨了@ElementCollection映射的最重要的设置。
当您用这个注释映射一个列表并让JPA/Hibernate自动生成表、列等时,它也会使用自动生成的名称。
让我们来分析一个基本的例子:
@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;
}
基本的@ElementCollection注释(您可以在其中定义已知的fetch和targetClass首选项)
@CollectionTable注释在为将要生成的表命名时非常有用,它还可以定义像joinColumns、foreignKey’s、indexes、uniqueConstraints等。
@Column对于定义存储列表varchar值的列的名称很重要。
生成的DDL将是:
-- 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)
);
好吧,我知道有点晚了。但对于那些勇敢的灵魂来说,随着时间的推移,他们会看到这一点。
如文件中所写:
@Basic:
到数据库列的最简单的映射类型。基本注释可以应用于以下类型中的任意一种的持久属性或实例变量:]、枚举和任何其他实现java.io.Serializable的类型。
重要的部分是实现Serializable的类型
所以到目前为止,最简单和最容易使用的解决方案是简单地使用ArrayList而不是List(或任何可序列化的容器):
@Basic
ArrayList<Color> lovedColors;
@Basic
ArrayList<String> catNames;
但是请记住,这将使用系统序列化,因此它将带来一些代价,例如:
如果序列化对象模型将改变,你可能无法恢复数据
存储的每个元素都会增加少量开销。
简而言之
存储标志或少数元素非常简单,但我不这么做
建议它存储可能变大的数据。
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 {
}
下面是使用@Converter和StringTokenizer存储Set的解决方案。再检查一下@jonck-van-der-kogel的解决方案。
在你的实体类中:
@Convert(converter = StringSetConverter.class)
@Column
private Set<String> washSaleTickers;
StringSetConverter:
package com.model.domain.converters;
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
import java.util.HashSet;
import java.util.Set;
import java.util.StringTokenizer;
@Converter
public class StringSetConverter implements AttributeConverter<Set<String>, String> {
private final String GROUP_DELIMITER = "=IWILLNEVERHAPPEN=";
@Override
public String convertToDatabaseColumn(Set<String> stringList) {
if (stringList == null) {
return new String();
}
return String.join(GROUP_DELIMITER, stringList);
}
@Override
public Set<String> convertToEntityAttribute(String string) {
Set<String> resultingSet = new HashSet<>();
StringTokenizer st = new StringTokenizer(string, GROUP_DELIMITER);
while (st.hasMoreTokens())
resultingSet.add(st.nextToken());
return resultingSet;
}
}