Search code examples
javaxmlhibernategwthibernate-mapping

GWT + Hibernate many-to-one xml O/R Mapping: SerializationException


I'm currently working on a GWT + Hibernate project which should work together on an already defined and filled database. I receive an

com.google.gwt.user.client.rpc.SerializationException

when I query the database.

Here are my objects...

Database:

-- Table: asset
CREATE TABLE IF NOT EXISTS asset
(
isin VARCHAR(12) NOT NULL,
mic_code VARCHAR(4) NOT NULL DEFAULT 'n.a.',
name VARCHAR(255) NOT NULL,
type_id VARCHAR(36) NOT NULL,
PRIMARY KEY (isin, mic_code),
INDEX(isin, mic_code),
FOREIGN KEY (type_id) REFERENCES asset_type(id)
)ENGINE=InnoDB;

-- Table: asset_type
CREATE TABLE IF NOT EXISTS asset_type
(
id VARCHAR(36) NOT NULL,
type VARCHAR(40) NOT NULL,
PRIMARY KEY (id)
)ENGINE=InnoDB;

Asset.java:

public class Asset implements Serializable {

    private String isin;
    private String mic_code;
    private String name;
    private AssetType assetType;

    public Asset() {
        super();
    }
...

AssetType.java

public class AssetType implements Serializable {

    private String id;
    private String type;

    public AssetType() {
    }

and finally the hibernate xml files: Asset.hbm.xml

<hibernate-mapping>
    <class name="com.mygwtproject.shared.model.Asset" table="ASSET">
        <id name="isin" type="java.lang.String" access="field">
            <column name="ISIN" />
            <generator class="native" />
        </id>
        <property name="mic_code" type="java.lang.String" access="field">
            <column name="MIC_CODE" />
        </property>
        <property name="name" type="java.lang.String" access="field">
            <column name="NAME" />
        </property>
        <many-to-one name="assetType" class="com.mygwtproject.shared.model.types.AssetType" column="TYPE_ID" cascade="all" not-null="true"/>
    </class>
</hibernate-mapping>

AssetType.hbm.xml

<hibernate-mapping>
    <class name="com.mygwtproject.shared.model.types.AssetType" table="ASSET_TYPE">
        <id name="id" type="java.lang.String" column="ID"> 
            <generator class="native" />
        </id>
        <property name="type" type="java.lang.String" column ="TYPE" />
    </class>
</hibernate-mapping>

hibernate.cfg.xml

<hibernate-configuration>
    <session-factory>
        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.password">*****</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost/asset_db</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.current_session_context_class">thread</property>
        <property name="hibernate.query.factory_class">org.hibernate.hql.internal.classic.ClassicQueryTranslatorFactory</property>
        <property name="show_sql">true</property>
        <property name="format_sql">true</property>
        <property name="use_sql_comments">true</property>

        <mapping resource="com/mygwtproject/shared/model/Asset.hbm.xml" />
        <mapping resource="com/mygwtproject/shared/model/types/AssetType.hbm.xml" />
    </session-factory>
</hibernate-configuration>

from log:

Hibernate: 
    select
        assettype0_.ID as ID1_1_0_,
        assettype0_.TYPE as TYPE2_1_0_ 
    from
        ASSET_TYPE assettype0_ 
    where
        assettype0_.ID=?
09:43:09,139 TRACE BasicBinder:81 - binding parameter [1] as [VARCHAR] - [ee5bb49a-dc95-403a-9f77-864a9c342f25]
09:43:09,142 TRACE BasicExtractor:78 - extracted value ([TYPE2_1_0_] : [VARCHAR]) - [Stock]
Starting Jetty on port 8888
   [WARN] Exception while dispatching incoming RPC call
com.google.gwt.user.client.rpc.SerializationException: Type 'com.mygwtproject.shared.model.types.AssetType_$$_jvst77c_0' was not included in the set of types which can be serialized by this SerializationPolicy or its Class object could not be loaded. For security purposes, this type will not be serialized.: instance = com.mygwtproject.shared.model.types.AssetType@7d617ac9

so there is something wrong with my Asset -> AssetType mapping, but i cant find it. Any help is appreciated. thanks

edit:

mysql> select * from asset_type where id = 'ee5bb49a-dc95-403a-9f77-864a9c342f25';

returns

+--------------------------------------+-------+
| id                                   | type  |
+--------------------------------------+-------+
| ee5bb49a-dc95-403a-9f77-864a9c342f25 | Stock |
+--------------------------------------+-------+
1 row in set (0.00 sec)

Solution

  • Solution from here:

    1. Create counterpart Data Transfer Objects and replace the hibernate objects.

    AssetDTO.java

    public class AssetDTO implements Serializable {
    
        private String isin;
        private String mic_code;
        private String name;
        private AssetTypeDTO assetType;
    
        public AssetDTO() {
            super();
        }
    
        public AssetDTO(String isin, String mic_code, String name,
                AssetTypeDTO assetType) {
            super();
            this.isin = isin;
            this.mic_code = mic_code;
            this.name = name;
            this.assetType = assetType;
        }
    
    //incl. Getter + Setter
    }
    

    AssetTypeDTO.java

    public class AssetTypeDTO implements Serializable {
    
        private String id;
        private String type;
    
        public AssetTypeDTO() {
            super();
        }
    
        public AssetTypeDTO(String id, String type) {
            super();
            this.id = id;
            this.type = type;
        }
    
       //incl. Getter + Setter
    }
    

    2. Add a new constructor to the hibernate objects.

    Asset.java

    ...
    public Asset(AssetDTO dto) {
            this.isin = dto.getIsin();
            this.mic_code = dto.getMic_code();
            this.name = dto.getName();
            AssetTypeDTO assetTypeDTO = dto.getAssetType();
            if (assetTypeDTO != null) {
                this.assetType = new AssetType(assetTypeDTO.getId(),
                        assetTypeDTO.getType());
            }
     }
    ...
    

    AssetType.java

    public AssetType(AssetTypeDTO dto) {
            this.id = dto.getId();
            this.type = dto.getType();
        }
    

    3. Modify your GWT RPC components. Replace the Hibernate objects in

    IService.java

    public List<Asset> getAssets();
    

    with the DTOs.

    public List<AssetDTO> getAssets();
    

    IServiceAsync.java

    public void getAssets(AsyncCallback<List<Asset>> callback);
    

    with

    public void getAssets(AsyncCallback<List<AssetDTO>> callback);
    

    4. Modify your service implementation.

    ServiceImpl.java

    ...
    @Override
    public List<AssetDTO> getAssets() {
        ...
        Query q = session.createQuery("from Asset");
        List<Asset> assets = new ArrayList<Asset>(q.list());
        List<AssetDTO> assetsDto = new ArrayList<AssetDTO>();
        if (assets != null) {
            for (Asset asset : assets) {
                assetsDto.add(createAssetDTO(asset));
            }
        }
        session.getTransaction().commit();
        return assetsDto;
    }
    
    public AssetDTO createAssetDTO(Asset asset) {
        AssetTypeDTO assetTypeDto = new AssetTypeDTO(asset.getAssetType()
                    .getId(), asset.getAssetType().getType());
        AssetDTO result = new AssetDTO(asset.getIsin(), asset.getMicCode(),
                    asset.getName(), assetTypeDto);
        return result;
    }
    ...
    

    5. Move the Hibernate objects (Asset, AssetType) to the server package, move the DTOs (AssetDTO, AssetTypeDTO) to the shared package and update the path in your Hibernate xml files.