How to persist a property of type List<String> in JPA?
What is the smartest way to get an entity with a field of type List persisted?
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);
}
}
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:
> ...
Asked by: Paul609 | Posted: 21-01-2022
Answer 1
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.
Edit
As mentioned in the comments below, the correct JPA 2 implementation is
javax.persistence.ElementCollection
@ElementCollection
Map<Key, Value> collection;
See: http://docs.oracle.com/javaee/6/api/javax/persistence/ElementCollection.html
Answered by: Jack428 | Posted: 22-02-2022Answer 2
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;
import static java.util.Collections.*;
@Converter
public class StringListConverter implements AttributeConverter<List<String>, String> {
private static final String SPLIT_CHAR = ";";
@Override
public String convertToDatabaseColumn(List<String> stringList) {
return stringList != null ? String.join(SPLIT_CHAR, stringList) : "";
}
@Override
public List<String> convertToEntityAttribute(String string) {
return string != null ? Arrays.asList(string.split(SPLIT_CHAR)) : emptyList();
}
}
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.
Answer 3
It seems none of the answers explored the most important settings for an @ElementCollection
mapping.
When you map a list with this annotation and let JPA/Hibernate auto-generate the tables, columns, etc., it'll use auto-generated names as well.
So, let's analyze a basic example:
@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;
}
- The basic
@ElementCollection
annotation (where you can define the knownfetch
andtargetClass
preferences) - The
@CollectionTable
annotation is very useful when it comes to giving a name to the table that'll be generated, as well as definitions likejoinColumns
,foreignKey
's,indexes
,uniqueConstraints
, etc. @Column
is important to define the name of the column that'll store thevarchar
value of the list.
The generated DDL would be:
-- 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)
);
Answered by: Melissa423 | Posted: 22-02-2022
Answer 4
This answer was made pre-JPA2 implementations, if you're using JPA2, see the ElementCollection answer above:
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.
So, you should convert 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).
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.
I still suggest doing the first case. It's good practice for real relationships later.
Answered by: Lenny504 | Posted: 22-02-2022Answer 5
We can also use this.
@Column(name="arguments")
@ElementCollection(targetClass=String.class)
private List<String> arguments;
Answered by: Kelvin181 | Posted: 22-02-2022
Answer 6
According to 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
If you were using Hibernate, you could do something like:
@CollectionOfElements(targetElement = String.class)
@JoinTable(name = "foo", joinColumns = @JoinColumn(name = "foo_id"))
@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.
Answered by: Catherine492 | Posted: 22-02-2022Answer 7
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.
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>();
}
Answered by: Max193 | Posted: 22-02-2022
Answer 8
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;
Answered by: Emma825 | Posted: 22-02-2022Answer 9
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.
The important part is type that implements Serializable
So by far the most simple and easiest to use solution is simply using ArrayList instead of List (or any serializable container):
@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.
Answered by: Haris439 | Posted: 22-02-2022Answer 10
Thiago answer is correct, adding sample more specific to question, @ElementCollection will 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).
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
public class Car {
}
Answered by: Kellan937 | Posted: 22-02-2022
Answer 11
Here is the solution for storing a Set using @Converter and StringTokenizer. A bit more checks against @jonck-van-der-kogel solution.
In your Entity class:
@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;
}
}
Answered by: Miller647 | Posted: 22-02-2022
Answer 12
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.
Answered by: Grace466 | Posted: 22-02-2022Answer 13
What I wanted was a simple way of persisting a set of Strings, in a table column.
I ended up using JSON, as MySQL 5.7+, has native support. Here's my solution
@Column(name = "eligible_approvers", columnDefinition = "json")
@Convert(converter = ArrayJsonConverter.class)
private Set<String> eligibleApprovers;
And then write a very basic converter
@Converter(autoApply = true)
public class ArrayJsonConverter implements AttributeConverter<Set, String> {
static final ObjectMapper mapper = new ObjectMapper();
@Override
public String convertToDatabaseColumn(Set list) {
if (list == null)
return null;
try {
return mapper.writeValueAsString(list);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
@Override
public Set convertToEntityAttribute(String dbJson) {
if (dbJson == null)
return null;
try {
return mapper.readValue(dbJson, new TypeReference<Set<String>>() {
});
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
}
Answered by: Rubie565 | Posted: 22-02-2022
Answer 14
As my reputation is not enough yet to comment on the much underrated answer written by @razvang:
As this question was asked over a decade ago, keep in mind much of the world has changed in the time since. We now have databases with native JSON column support and can use this functionality instead of using separate entities, joins or custom String-to-List converters, which are used by the other answers.
Let me suggest two purely optional changes to @razvang's superb answer though, which might be interesting depending on your specific situation:
- You could omit the
auto_apply = true
and add@Convert(converter = <CONVERTER_CLASS_NAME>.class)
to the entity field to keep control over when your converter is used. - Instead of throwing a
RuntimeException
whenever a conversion fails, you could handle the error right there (for example pass an empty list and write a log message) to make it fail somewhat gracefully.
Similar questions
java - How do I return List<String> from a method where I sent to put(Object) into a map (got example)
I have the following code
private Map<KEY, Object> values = new HashMap<KEY, Object>();
public void set(KEY key, Object value) {
values.put(key, value);
}
private Object getObj(KEY key) {
return values.get(key) == null ? key.getDefaultValue() : values.get(key);
}
public List<E> getList(KEY key) {
return (List<E>) getObj(key);
}
The idea to be able to ...
java - iBatis mapping: map a string field into a List<String>
is it possible to map a string field with a particular format like:
aaa,bbb,ccc,ddd
into a List having elements [aaa, bbb, ccc, ddd] using iBatis?
What I need is to have in my model something like:
public class Request{
private List<String> fieldOne;
public List<String> getFieldOne(){....}
public void setFieldOne(){....}
}
even if in my table...
java - Fastest way to check if a List<String> contains a unique String
Basically I have about 1,000,000 strings, for each request I have to check if a String belongs to the list or not.
I'm worried about the performance, so what's the best method? ArrayList? Hash?
java - Problem in saving List<String> through JPA?
I have already seen
How to persist a property of type List<String> in JPA?
and
Map a list of strings with JPA/Hibernate annotations
====================================...
java - How to Convert List<String> to List<Object>
I want to convert List<String> to List<Object>.
One of the existing methods is returning List<String> and I want to convert it to List<Object>.
Is there a direct way in Java other then iterating over and converting element by element?
Text file into Java List<String> using Commons or Guava
What is the most elegant way to put each line of text (from the text file) into LinkedList (as String object) or some other collection, using Commons or Guava libraries.
java - List<String> array in JComboBox
I have a JComboBox already created but I would like to update the information with data from a List Array.
I currently have the code:
java.util.List<String> listNames = empPort.getEmployeeInfo("username");
int itemSize = listNames.size();
the empPort is directed to a web service operation which returned a set of values in a List Array.
When using;
for(...
java - Iteration of List<String> with modyfing String
I can't modyfing element of List this way:
for (String s : list)
{
s = "x" + s;
}
After execution this code elements of this list are unchanged
How to achieve iteration with modyfing through List in the simplest way.
java - How to test List<String> for empty or nullness?
It seems no matter what I do I get the wrong result.
My list is defined as follows:
private List<String> selectedPriorities;
Nothing odd or fancy on the getter/setter:
public void setSelectedPriorities(List<String> selectedPriorities) {
this.selectedPriorities = selectedPriorities;
}
public List<String> getSelectedPriorities() {
return sel...
java - Populate List<String> in struts2 from form data
I feel this should be exceedingly obvious, but so far I've failed to find an answer.
I want to have a list of strings (or an array of strings, I really don't care) get populated by form data in Struts2.
I've seen several examples of how to do indexed properties with beans, but wrapping a single string i...
Still can't find your answer? Check out these amazing Java communities for help...
Java Reddit Community | Java Help Reddit Community | Dev.to Java Community | Java Discord | Java Programmers (Facebook) | Java developers (Facebook)