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-2022



Answer 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.

Answered by: Daisy175 | Posted: 22-02-2022



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;
    
}
  1. The basic @ElementCollection annotation (where you can define the known fetch and targetClass preferences)
  2. The @CollectionTable annotation is very useful when it comes to giving a name to the table that'll be generated, as well as definitions like joinColumns, foreignKey's, indexes, uniqueConstraints, etc.
  3. @Column is important to define the name of the column that'll store the varchar 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-2022



Answer 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-2022



Answer 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-2022



Answer 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-2022



Answer 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-2022



Answer 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:

  1. 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.
  2. 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.

Answered by: Elise760 | Posted: 22-02-2022



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&lt;KEY, Object&gt; values = new HashMap&lt;KEY, Object&gt;(); 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&lt;E&gt; getList(KEY key) { return (List&lt;E&gt;) 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&lt;String&gt; fieldOne; public List&lt;String&gt; 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&lt;String&gt; 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&lt;String&gt; to List&lt;Object&gt;. One of the existing methods is returning List&lt;String&gt; and I want to convert it to List&lt;Object&gt;. 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&lt;String&gt; 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&lt;String&gt; selectedPriorities; Nothing odd or fancy on the getter/setter: public void setSelectedPriorities(List&lt;String&gt; selectedPriorities) { this.selectedPriorities = selectedPriorities; } public List&lt;String&gt; 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)



top