java - How to persist a property of type List<String> in JPA?

ID : 20381

viewed : 12

Tags : javaormjpajava

Top 5 Answer for java - How to persist a property of type List<String> in JPA?

vote vote

92

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

vote vote

89

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.

vote vote

79

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) ); 
vote vote

68

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.

vote vote

54

We can also use this.

@Column(name="arguments") @ElementCollection(targetClass=String.class) private List<String> arguments; 

Top 3 video Explaining java - How to persist a property of type List<String> in JPA?

Related QUESTION?