Как сохранить свойство типа 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:
> ...
1136   10  

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

    Ничего не найдено.