Mapping and querying a list of primitive types with JPA 2.0

Sometimes you just want to map a list of primitive types. For example, a Book entity has a list of tags and tags are just strings. So you want to do the following :

@Entity
public class Book {
  @Id
  @GeneratedValue
  private Long id;
  private String title;
  private Float price;
  private String description;
  private String isbn;
  private Integer nbOfPage;
  private List<String> tags = new ArrayList<String>();
  ...
}

What happens when you do this with JPA 1.0 ? The ArrayList implements Serializable so the entire list gets serialized into a blob column. What are the other choices ? Well, because JPA maps entities, you can create a Tag entity (with an ID and a value) and have a list of tags :

Entity
public class Book {
  @Id
  @GeneratedValue
  private Long id;
  ...
  private List<Tag> tags = new ArrayList<Tag>();
  ...
}


@Entity
public class Tag {
  @Id
  @GeneratedValue
  private Long id;
  private String value;
  ...
}

That’s a shame to create a separate entity when what you want is just a String. Now with JPA 2.0 you can use the @ElementCollection annotation on a list :

@Entity
public class Book {
  @Id
  @GeneratedValue
  private Long id;
  ...
  @ElementCollection
  @CollectionTable(name ="tags")
  private List<String> tags = new ArrayList<String>();
  ...
}

jpa2The @ElementCollection annotation will automatically map the list of strings into a table named BOOK_TAGS (<name of the entity>_<name of the attribute>). If you need to change the table’s name, just use the @CollectionTable annotation. The result is shown on the figure. The Book table contains all the Book entity attributes, and the TAGS table contains the BOOK_ID foreign key and the value of the tags.

If you then want to find all the books that have the tag ‘scifi’, you can just write the following JPQL query :

SELECT b FROM Book b WHERE b.tags = 'scifi'

I didn’t manage to make this query work with Hibernate 3.6. So I had to rewrite the JPQL statement usgin a JOIN. This way, the query works on both EclipseLink and Hibnerate

SELECT b FROM Book b JOIN b.tags t WHERE t = 'scifi'

Download the code of this example. It uses EclipseLink 2.0-M9 and the Derby database.

Categories: Java

Tagged as:

4 Comments »

  1. Thans a lot.
    What do you suggest if I need to store an Entity attribute that is an array of int?
    For instance if I have a “BingoCard” @Entity with a int[] numbers attribute?
    Is the separate table generated by @ElementCollection still suitable? Or there are other solutions, better from a disk space/performance point of view?

    Many thanks, Giovanni

  2. How would to write queries using Join with static metamodel having your approach? I think it’s not possible and you’ll end up with a separate entity.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s