6

Hello I am very new to the hibernate world and seem to have hit a roadblock. The object I need to store has a hashmap in it.

private Map<String, SentimentFrequencyCounts> modelData = null;

The thing is I will never need to search, sort or do any thing with this map I just need to save it with the object and load it when the object is loaded, so I was hoping there was some way that hibernate could just serializes it and then store it in a CLOB or BLOB field but I can not seem to find any way to do that.

So I next tried to have hibernate save this like so

    @OneToMany(mappedBy="ngram_data", fetch = FetchType.EAGER) 
 @MapKey(name = "attributeName") 
 public Map<String, SentimentFrequencyCounts> getModelData() {
  return modelData;
 }

But this gives me the following exception at runtime org.hibernate.AnnotationException: Use of @OneToMany or @ManyToMany targeting an unmapped class:

The SentimentFrequencyCounts class is an inner class of the one I am trying to persist. So basically I think I really am not understanding how hibernate works for hashmap. Really a shame I can not just get it to serialize this and lump it up in a single column.

Thanks in advance for your help and time.

Sean Patrick Floyd
  • 292,901
  • 67
  • 465
  • 588
JNR
  • 63
  • 1
  • 1
  • 3
  • How have you mapped `SentimentFrequencyCoungs`? Is it a separate entity? A composite? – Jeremy Jan 15 '11 at 15:14
  • I have SentimentFrequencyCount (it is Serializable) is marked as @Embeddable, but I also tried to make it an entity too with no luck – JNR Jan 15 '11 at 15:22

2 Answers2

5
@org.hibernate.annotations.Type(
        type = "org.hibernate.type.SerializableToBlobType", 
        parameters = { @Parameter( name = "classname", value = "java.util.HashMap" ) }
)
public Map<String, SentimentFrequencyCounts> getModelData() {
  return modelData;
}

Or even just this will work in most cases (distributed caching might be a problem):

@org.hibernate.annotations.Type( type = "org.hibernate.type.SerializableType" )
public Map<String, SentimentFrequencyCounts> getModelData() {
  return modelData;
}
Steve Ebersole
  • 9,339
  • 2
  • 48
  • 46
  • I used your first option and it worked! I can persist a Map and retrieve it too. The second one didn't work, the stack trace said it couldn't find org.hibernate.type.Serializable even though the class is in the classpath. Don't know what's up with that. ¯\\_(ツ)_/¯ – geekonablog Dec 08 '16 at 21:13
  • 1
    Oops, thats because the second one contained a typo. Should have been `org.hibernate.type.SerializableType`, not just `org.hibernate.type.Serializable`. I have fixed that above. – Steve Ebersole Dec 11 '16 at 17:20
  • This solution worked flawlessly for me. The first example. Thanks! – bezbos. Apr 27 '20 at 12:02
3

Take off your existing annotations and annotate the list with @Lob - this specifies that a persistent property or field should be persisted as a large object to a database-supported large object type.

If the type of the variable was a subtype of Serializable, you could just leave off the annotations altogether; JPA's rules on default mappings state that types which are Serializable and not primitive or Embeddable are serialized and stored in BLOB columns. However, List is not Serializable, even though ArrayList is.

You can use @Lob with @ElementCollection, but i'm not sure what the result is; i don't know if that serializes the whole list, or creates a table in which each list element is serialized separately. It probably isn't of interest to you either way.

MUCH LATER EDIT: Of course, as a diligent student of the spec will know, this annotation only works for fields of Serializable type, not fields which merely happen to hold objects of a Serializable class. Consequently, to make this work, you will have to engage in shenanigans. I had a look at whether you could do something clever with a generic wildcard bounded with an intersection type, but i don't think you can. You can, however, write a little class like this:

class SerializableInstanceOf<T> implements Serializable {
    public final T instance;

    public SerializableInstanceOf(T instance) {
        Serializable s = (Serializable)instance;
        this.instance = instance;
    }
}

And use that as a holder for the list - the entity has a field of this type marked with @Lob, and keeps a reference to the list in it. Every time you want to work with the list, you go via the instance field, probably via a getList method on the entity.

It's ugly, but it should let you do what you need to do.

Tom Anderson
  • 46,189
  • 17
  • 92
  • 133
  • I think this is the general right solution but I am getting java.util.HashMap cannot be cast to java.sql.Blob exception, HashMap is Serializable as is my key and value classes, so I am not sure why it can not cast it.. but thanks again for the pointer in the right direction – JNR Jan 15 '11 at 15:39
  • @JNR: turns out my answer is wrong. See section 2.1.1 of the EJB 3.0 persistence specification - the paragraph starting "The persistent fields or properties of an entity may be of the following types". That paragraph does not actually allow a field of type Map. You probably need to declare it HashMap. Nasty. I'm not sure what else you can do. – Tom Anderson Jan 17 '11 at 16:24
  • Eclipselink does the trick. I wonder why hibernate is not serializing the object which is the expected behavior. – Ralph Jan 09 '22 at 21:55