44

I have a VARCHAR field in my database, and the value of this field is val1,val2,val3.

Is it possible to set this into an ArrayList<String> attribute of an entity using comma as split delimiter?

4 Answers 4

91

If you use JPA 2.1, then you can create an AttributeConverter:

@Converter
public class StringListConverter implements AttributeConverter<List<String>, String> {

  @Override
  public String convertToDatabaseColumn(List<String> list) {
    // Java 8
    return String.join(",", list); 
    // Guava
    return Joiner.on(',').join(list); 
  }

  @Override
  public List<String> convertToEntityAttribute(String joined) {
    return new ArrayList<>(Arrays.asList(joined.split(",")));
  }

}

You can use this converter in your entity:

@Column
@Convert(converter = StringListConverter.class)
private List<String> strings;

For before JPA 2.1 you could do this by hand:

@Entity
private MyEntity {
  ...
  private String strings;

  public List<String> getStrings() {
    return Arrays.asList(strings.split(","));
  }

  public void setStrings(List<String> list) {
    strings = String.join(",", list);
  }
}

I wrap Arrays.asList in an ArrayList in the converter, because the result is stored in the attribute and any change to that list will be written back to the database - thus I need a changeable list (I can't add anything to the result of Arrays.asList). In the before 2.1 solution the result is not connected with the attribute and a changeable list would not be synchronized with the attribute.

To query for an entity that contains a specific item in such an attribute, see my answer here

Sign up to request clarification or add additional context in comments.

2 Comments

but how do you jpql query that for example this list contains a certain value?
As I mentioned in my last sentence, that question is answered here: stackoverflow.com/questions/50355913/…
6

Yes this is possible.

With Hibernate 4.3.x+ you can define an AttributeConverter although I am pretty sure this will not work for early Hibernate versions because of the List type. See this for an example: http://www.thoughts-on-java.org/jpa-21-how-to-implement-type-converter/

The other way to make this work is by implementing a custom UserType and annotating the field/getter with org.hibernate.annotations.Type. Here is a nice write up of this with examples: http://blog.xebia.com/understanding-and-writing-hibernate-user-types/

Yet another way which is JPA compatible is to have two fields, the List annotated with javax.persistence.Transient and the String but then you have manage the state synchronization between these two field in PrePersist and PreUpdate listeners yourself. Here an example for using listeners: http://alexandregama.org/2014/03/23/entity-listeners-and-callback-methods-jpa/

Comments

3

Try to use @ElementCollection and @CollectionTable annotations

4 Comments

you save my day
That is not what the question asks for, though.
Indeed. This creates another table. Not true
Please do not use this. Create your own table instead before using that and a specific entity and manage it with awareness!
1

If you expect a comma in your String, split()/join() won't be enough. For those searching a better/different solution, here it is - made by serialize to JSON and deserialize from JSON. The downside is more characters used in the database, but it works for the most of text you can have.

package ...;

import java.io.Serializable;
import javax.persistence.AttributeConverter;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

public class StringArrayConverter
        implements AttributeConverter<String[], String>, Serializable {

    private static final long serialVersionUID = -2744576922911393664L;

    @Override
    public String convertToDatabaseColumn(final String[] attribute) {
        try {
            return new ObjectMapper().writeValueAsString(attribute);
        }
        catch (@SuppressWarnings("unused") final JsonProcessingException e) {
            // ignore
        }
        return null;
    }

    @Override
    public String[] convertToEntityAttribute(final String dbData) {
        try {
            return new ObjectMapper().readValue(dbData, String[].class);
        }
        catch (@SuppressWarnings("unused") final JsonProcessingException e) {
            // ignore
        }
        return null;
    }

}

The example of usage:

package ...;

import ....StringArrayConverter;

import javax.persistence.Convert;
import javax.persistence.Entity;
import javax.persistence.EnumType;
import javax.persistence.Enumerated;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.validation.constraints.NotNull;
import com.fasterxml.jackson.annotation.JsonView;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

@Entity
@Getter @Setter @NoArgsConstructor
public class PersonWithCars {

    @Id @NotNull @GeneratedValue(strategy = GenerationType.SEQUENCE)
    private Integer id;
    
    private String name;

    @Convert(converter = StringArrayConverter.class)
    private String[] cars = new String[0];

}

1 Comment

problem with converter is the possibility to use JPA repositories...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.