Saturday, June 27, 2009

Hibernate Annotations CollectionOfElements to create a Map

There are times is it very comfortable using Map data structure to easily get elements by some key. For example, suppose we have in our database a “user” table, and we would like easily store and retrieve user properties.

Hibernate gives a nice solution to this issue, by using the annotation @CollectionOfElements. We will see a demonstration of using this annotation with our “user” table. Suppose we have 2 tables in out database:

  • user: Contains users information. The table contains the following fields:
    • user_id
    • first_name
    • last_name
    • password
  • user_property: Stores user properties. The tables contains the following fields:
    • user_id
    • key
    • value

The structure of these 2 tables is quite simple and straight forward, therefore, we will not broaden on it.

We would like that “user_property” will be a Map containing key and value pairs, rather than being a collection in “user”. We will use @CollectionOfElements annotation in order to achieve that. This is how our User class looks like:

package com.bashan.blog.hibernate;
import org.hibernate.annotations.CollectionOfElements;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Map;
@Entity
@Table(name = "user")
public class User implements Serializable {
  @Id
  @Column(name = "user_id")
  private Integer userId;
  @Column(name = "first_name")
  private String firstName;
  @Column(name = "last_name")
  private String last_name;
  @Column(name = "password")
  private String password;
  @CollectionOfElements
  @JoinTable(name = "user_property", joinColumns = @JoinColumn(name = "user_id"))
  @org.hibernate.annotations.MapKey(columns = {@Column(name = "key")})
  @Column(name = "value")
  private Map<String, String> properties;
  public Integer getUserId() {
    return userId;
  }
  public void setUserId(Integer userId) {
    this.userId = userId;
  }
  public String getFirstName() {
    return firstName;
  }
  public void setFirstName(String firstName) {
    this.firstName = firstName;
  }
  public String getLast_name() {
    return last_name;
  }
  public void setLast_name(String last_name) {
    this.last_name = last_name;
  }
  public String getPassword() {
    return password;
  }
  public void setPassword(String password) {
    this.password = password;
  }
  public Map<String, String> getProperties() {
    return properties;
  }
  public void setProperties(Map<String, String> properties) {
    this.properties = properties;
  }
}
Note that no class exists for “user_properties” table. In this case the @CollectionOfElements mapping acts like in @ManyToMany mapping. The link to “user_properties” table is defined by the “name” property of @JoinTable. Also pay attention to the fact the @MapKey annotation that is used here belongs to “org.hibernate.annotations” and not to the “javax.persistence” package.

You can also download this class by pressing this link.

2 comments:

  1. Hi Bashan.. This post really helped me to get started with my project to persist key value pair data. Unfortunately I am unable to retrieve any User info if i pass a map of key value as a hibernate criteria.

    @SuppressWarnings("unchecked")
    public List getEvents(final Event entity){
    return this.hibernateTemplate.executeFind(new HibernateCallback() {

    public Object doInHibernate(Session session) {
    Criteria criteria = session.createCriteria(Event.class);
    criteria.add(Restrictions.between("startdatetime", entity.getStartdatetime(), entity.getEnddatetime()));

    Map map = new HashMap();
    map.put("key1", "value1");
    criteria.add(Restrictions.eq("properties", map.values()));
    return criteria.list();
    }
    });



    }

    ReplyDelete
    Replies
    1. Hi Robin,
      Sorry, I wrote this long time ago, so I won't be able to assist on that issue. I hope you have already managed.

      Delete