Как сохранить свойство типа List в JPA?
каков самый умный способ получить объект с сохраненным полем типа List?
.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:
> ...
10 ответов:
используйте некоторую реализацию JPA 2: она добавляет аннотацию @ElementCollection, аналогичную Hibernate, которая делает именно то, что вам нужно. Есть один пример здесь.
Edit
Как уже упоминалось в комментариях ниже, правильная реализация 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" с другим объектом. Однако строка не является (сама по себе) допустимым клиентом отношения "один ко многим", поскольку у нее нет идентификатора.
Итак, вы должны преобразуйте список строк в список объектов JPA класса аргументов, содержащих идентификатор и веревочка. Вы можете потенциально использовать строку в качестве идентификатора, что сэкономит немного места в вашей таблице как от удаления поля ID, так и от консолидации строк, где строки равны, но вы потеряете возможность упорядочить аргументы обратно в их исходный порядок (поскольку вы не сохранили никакой информации о порядке).
кроме того, вы можете преобразовать свой список в @Transient и добавить другое поле (argStorage) в свой класс, который является либо VARCHAR (), либо CLOB. Вы затем нужно добавить 3 функции: 2 из них одинаковы и должны преобразовать ваш список строк в одну строку (в argStorage), разделенную таким образом, что вы можете легко разделить их. Аннотируйте эти две функции (каждая из которых делает то же самое) с помощью @PrePersist и @PreUpdate. Наконец, добавьте третью функцию, которая снова разбивает argStorage на список строк и аннотирует его @PostLoad. Это будет держать ваш CLOB обновляется со строками всякий раз, когда вы идете, чтобы сохранить команду, и сохранить поле argStorage обновлены, прежде чем хранить его в БД.
Я все же предлагаю сделать первый случай. Это хорошая практика для настоящих отношений позже.
по данным Java Persistence с Hibernate
сопоставление коллекций типов значений с аннотациями [...]. На момент написания статьи он не является частью стандарта персистентности 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.
У меня была та же проблема, поэтому я вложил возможное решение, но в конце я решил реализовать свой ';' разделенный список строк.
У меня есть
// a ; separated list of arguments String arguments; public List<String> getArguments() { return Arrays.asList(arguments.split(";")); }таким образом, список легко читается / редактируется в таблице базы данных;
при использовании реализации Hibernate JPA я обнаружил, что простое объявление типа как ArrayList вместо List позволяет hibernate хранить список данных.
очевидно, что это имеет ряд недостатков по сравнению с созданием списка объектов. Нет ленивой загрузки, нет возможности ссылаться на объекты в списке из других объектов, возможно, больше трудностей в построении запросов к базе данных. Однако, когда вы имеете дело со списками довольно примитивных типов то, что вы всегда будете хотеть охотно принести вместе с сущностью, тогда этот подход кажется мне прекрасным.
@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;
хорошо, я знаю, что это немного поздно. Но для тех смельчаков,которые увидят это со временем.
Как написано в документация:
@Basic: Самый простой тип сопоставления со столбцом базы данных. Основная аннотация может быть применена к постоянному свойству или переменной экземпляра любого из следующих типов: Java primitive types, [...], перечисления и любой другой тип, который реализует 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; @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)); } }теперь используйте его на своих объектах следующим образом:
@Convert(converter = StringListConverter.class) private List<String> yourList;в базе данных ваш список будет храниться как foo; bar; foobar и в вашем объекте Java вы получите список с этими строками.
надеюсь, что это полезно для кого-то.
мое исправление для этой проблемы состояло в том, чтобы отделить первичный ключ от внешнего ключа. Если вы используете eclipse и внесли вышеуказанные изменения, не забудьте обновить проводник базы данных. Затем воссоздайте объекты из таблиц.
Тьяго ответ правильный, добавив образец более конкретный вопрос,@ElementCollection будет создать новую таблицу в базе данных, но без сопоставления двух таблиц, это означает, что коллекция не коллекция, но коллекция простые типы (строки, и т. д.) или набор встраиваемых элементов (класс с аннотацией @Embeddable).
вот пример для сохранения списка строка
@ElementCollection private Collection<String> options = new ArrayList<String>();здесь образец для сохранения списка настраиваемого объекта
@Embedded @ElementCollection private Collection<Car> carList = new ArrayList<Car>();для этого случая нам нужно сделать класс встраиваемые
@Embeddable public class Car { }
Comments