Java – Why this class should implement java.io.Serializable when I using hibernate

hibernatejava

This exception only happened when Tweet Class was used. I cannot find the reason why I should use Serializable. I did the mapping myself in GeneticMessage.hbm.xml. All the types (long & Date) in Tweet Class are basic type in Hibernate (I think so).

Actually, the problem can be solved by just implementing Serializable for Tweet as mentioned in the Exception. But I still want to know the reason.

Method

Domain domain = (Domain) objects[0];
Query q = session.createQuery("FROM PreprocessedMessage WHERE domain = ?");
q.setEntity(0, domain);
return q.list(); // this line

Exception:

java.lang.ClassCastException: idv.petrie.prtm.model.Tweet cannot be cast to java.io.Serializable
    org.hibernate.type.CollectionType.getKeyOfOwner(CollectionType.java:381)
    org.hibernate.type.CollectionType.resolve(CollectionType.java:425)
    org.hibernate.engine.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:139)
    org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:982)
    org.hibernate.loader.Loader.doQuery(Loader.java:857)
    org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:274)
    org.hibernate.loader.Loader.doList(Loader.java:2533)
    org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2276)
    org.hibernate.loader.Loader.list(Loader.java:2271)
    org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:452)
    org.hibernate.hql.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:363)
    org.hibernate.engine.query.HQLQueryPlan.performList(HQLQueryPlan.java:196)
    org.hibernate.impl.SessionImpl.list(SessionImpl.java:1268)
    org.hibernate.impl.QueryImpl.list(QueryImpl.java:102)
    idv.petrie.prtm.model.helper.PreprocessedMessageHelper$3.execute(PreprocessedMessageHelper.java:66)
    idv.petrie.prtm.util.ModelHelper.execute(ModelHelper.java:36)
    idv.petrie.prtm.model.helper.PreprocessedMessageHelper.findMessageByDomain(PreprocessedMessageHelper.java:69)
    idv.petrie.prtm.servlet.MessageEvaluationServlet.doGet(MessageEvaluationServlet.java:44)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:621)
    javax.servlet.http.HttpServlet.service(HttpServlet.java:722)

PreprocessedMessage.java

package idv.petrie.prtm.model;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

public class PreprocessedMessage extends GeneticMessage {

    private GeneticMessage message;

    private Set dependencies;

    private Set tokens;

    public PreprocessedMessage() {
        super();
    }

    public PreprocessedMessage(GeneticMessage message, String content) {
        this();
        this.setMessage(message);
        this.setContent(content);
        this.setDomain(message.getDomain());
    }

    public PreprocessedMessage(GeneticMessage message) {
        this(message, message.getContent());
    }

    public PreprocessedMessage(GeneticMessage message,
            Set dependencies) {
        this(message);
        this.dependencies = dependencies;
    }

    public static Collection convertToCollection(
            Collection messages) {
        Collection result = new HashSet();
        for (GeneticMessage message : messages) {
            result.add(new PreprocessedMessage(message));
        }
        return result;

    }

    public void setMessage(GeneticMessage message) {
        this.message = message;
    }

    public GeneticMessage getMessage() {
        return message;
    }

    public Set getDependencies() {
        return dependencies;
    }

    public void setDependencies(Set dependencies) {
        for (Dependency d : dependencies) {
            d.setMessage(this);
        }
        this.dependencies = dependencies;
    }

    public Collection getTokens() {
        return tokens;
    }

    public void setTokens(Set tokens) {
        for (Token t : tokens) {
            t.setMessage(this);
        }
        this.tokens = tokens;
    }

}

Tweet.java

package idv.petrie.prtm.model;

import java.util.Date;

public class Tweet extends GeneticMessage {

    private long tweetId;

    private Date createdAt;

    private long fromUserId;

    public Tweet() {
        super();
    }

    public Tweet(String content) {
        this();
        setContent(content);
    }

    public Date getCreatedAt() {
        return createdAt;
    }

    public void setCreatedAt(Date createdAt) {
        this.createdAt = createdAt;
    }

    public long getFromUserId() {
        return fromUserId;
    }

    public void setFromUserId(long fromUserId) {
        this.fromUserId = fromUserId;
    }

    public void setTweetId(long tweetId) {
        this.tweetId = tweetId;
    }

    public long getTweetId() {
        return tweetId;
    }

}

GeneticMessage.java


package idv.petrie.prtm.model;

import java.util.Date;

public class GeneticMessage implements Comparable {

    public enum Status {
        NEW(0), PREPROCESSED(1);
        private int id;

        private Status(int id) {
            this.id = id;
        }

        public int getId() {
            return id;
        }
    }

    private long id;

    private Date modifiedAt;

    private String content;

    private Status status;

    private Domain domain;

    public GeneticMessage() {
        setModifiedAt();
        setStatus(Status.NEW);
    }

    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public void setModifiedAt() {
        this.modifiedAt = new Date();
    }

    public Date getModifiedAt() {
        return modifiedAt;
    }

    public void setStatus(Status status) {
        this.status = status;
    }

    public Status getStatus() {
        return status;
    }

    public void setDomain(Domain domain) {
        this.domain = domain;
    }

    public Domain getDomain() {
        return domain;
    }

    public int compareTo(GeneticMessage o) {
        String content = this.getContent();
        String anotherContent = o.getContent();
        return content.compareTo(anotherContent);
    }

    public void setModifiedAt(Date modifiedAt) {
        this.modifiedAt = modifiedAt;
    }
}

GeneticMessage.hbm.xml

<code>
<?xml version="1.0" encoding="utf-8"?> 
<!DOCTYPE hibernate-mapping 
 PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN" 
 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

    <typedef class="idv.petrie.prtm.model.GeneticMessage.Status"
        name="Status">
        <param name="enumClassName">idv.petrie.prtm.model.GeneticMessage.Status</param>
        <param name="identifierMethod">getId</param>
    </typedef>

    <class name="idv.petrie.prtm.model.GeneticMessage">
        <id name="id">
            <generator class="native" />
        </id>
        <property name="modifiedAt" />
        <property name="content" />
        <property name="status" />
        <many-to-one name="domain" class="idv.petrie.prtm.model.Domain"
            cascade="all" outer-join="true" />

        <joined-subclass name="idv.petrie.prtm.model.PreprocessedMessage">
            <key />
            <many-to-one name="message" class="idv.petrie.prtm.model.GeneticMessage"
                outer-join="true" />
            <set name="dependencies" cascade="save-update" inverse="true">
                <key property-ref="message" />
                <one-to-many class="idv.petrie.prtm.model.Dependency" />
            </set>
            <set name="tokens" cascade="save-update" inverse="true">
                <key property-ref="message" />
                <one-to-many class="idv.petrie.prtm.model.Token" />
            </set>
        </joined-subclass>

        <joined-subclass name="idv.petrie.prtm.model.Tweet">
            <key />
            <property name="tweetId" unique="true" />
            <property name="createdAt" />
            <property name="fromUserId" />
        </joined-subclass>
    </class>
</hibernate-mapping>
</code>

Best Solution

6 years has passed since this question, but here's my 5 cents for those who stumble upon this problem:

It's possible (although I cannot tell it from the config xmls given by the OP) that tweetId is not the primary key of Tweet and that's why it's the only class that requires to be Serializable. It is reported as a bug in Hibernate. (https://hibernate.atlassian.net/browse/HHH-7668).

In Summary: if you want to make a relation between Hibernate objects, just use the primary key as the link column. Otherwise, make the linked object Serializable and that'll work as well. :-)