Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Play Framework: Solution for storing a Map, key - value using JPA

I am struggling for almost a week of how to store a HashMap using JPA in Play. All the other attributes are stored, only the HashMap has zero elements (is empty).

public class ImageModel extends Model {

 @Id
 private String id;
 private String url;

 @ElementCollection(targetClass = java.lang.String.class)
 @MapKeyClass(java.lang.String.class)
 private Map<String,String> tags = new HashMap<>();

 // the method for adding keys and values to the HashMap
 public void add(String key, String value){
    tags.put(key,value);
    this.save();
}

}

I also tried to annotate with:

@ElementCollection(fetch=FetchType.LAZY)

So, my only guess for now is that I have not configured well Play. (I must say that I am using Play2 and haven't configured anything, except deleting "#" (comment mark) in front of database settings).

On Play documentation says: "There is no built-in JPA implementation in Play 2.0". Could be that the case?

Update: I have made some research and said that could be because I'm using the default database from Play - H2. I tried with MySQL - video how to do it , but again it saves only the id and url. I looked in MySQL database and table with the name of the class had only 2 attribute (id and url), no trace of my HashMap.

Update2: Added a print screen - with database query.

Update3: The sample "computer-database-jpa" from Play is working fine, so the JPA should be fine, but that sample didn't used a Map (HashMap). I even changed hibernate with EclipseLink, but still not working. :( (although, all the basic types attributes are in database)

like image 831
Alin Ciocan Avatar asked May 29 '13 15:05

Alin Ciocan


2 Answers

Here's how I implemented a HashMap using your models.

@Entity
public class ImageModel extends Model {
    //All your fields, etc.

    @ManyToMany(mappedBy = "imagesWithTag")
    @MapKey(name = "name")
    public Map<String,Tag> tags = new HashMap<String,Tag>();
}

@Entity
public class Tag extends Model {
    // All your fields, etc.

    public String name;
    public String value;

    @ManyToMany
    public List<ImageModel> imagesWithTag = new ArrayList<ImageModel>();
}

I decided to go with Eric's suggestion of creating an entire model for Tag, as to make queries easily, but there are times where being able to use the functionalities of a Map will help a lot. This should work for EnumMaps too.

public void addTag(String tagName, String value) {
    Tag tag = Tag.finderTag.where().eq("name", tagName).eq("value", value).findUnique();
    if(tag == null) {
        tag = Tag.newTag(tagName, value);
    }
    this.tags.put(tag.name, tag);
    this.saveManyToManyAssociations("tags");
}

public static List<ImageModel> getImagesWithTag(String tagName, String tagValue) {

    Tag tag = finderTag.where().eq("name", tagName).eq("value", tagValue).findUnique();

    List<ImageModel> images = new ArrayList<ImageModel>();
    for(ImageModel model : tag.imagesWithTag) {
        images.add(model);
    }
    return images;
}

I have seen barely anything about using Maps in Play Framework models, so I decided to create a small sample project (Play 2.1.1) to toy around with and further demonstrate map use.

https://github.com/Raymond26/play-framework-demos/tree/master/PlayMap

like image 107
Raymond26 Avatar answered Nov 16 '22 03:11

Raymond26


I think the real question here is:
Why store these tags in a Map? I guess it will be some kind of key->value pair like "author"->"John Smith", "theme"->"Animals", etc. right?

IMHO, you should have a type Tag:

@Entity
class Tag {
    @Id
    private Long id;
    private String name;
    private String value;
    // getters, setters, hashCode, equals, etc.
    // ...
}

and have a relationship in ImageModel:

public class ImageModel extends Model {
    // ...
    @ManyToMany
    private Set<Tag> tags;
    // ...
}

Here you have a strong typed collection which make really explicit what the relation is.

This way, you could add some interesting stuff on Tag like a unique constraint, some validation (JSR303), additionnal fields, Javadoc, unit tests, etc.

Also, this allow you to do some useful queries easily, such as:

  • search images by tag
  • list existing tags
  • list tags with name "author"
  • ...
like image 20
Eric Citaire Avatar answered Nov 16 '22 03:11

Eric Citaire