5 Replies Latest reply on Nov 9, 2012 4:02 AM by erider

    Standalone hibernate + second level infinispan cluster with composite-id

    erider Newbie

      Hello,

       

      my post will try to cover two questions. First I will describe the problem and the solution I’m thinking of. I want to discuss if my solution is reasonable. But maybe there is a better architectural solution I am not aware of because I am not too familiar neither with Hibernate nor with Infinispan.

      My second issue is an Unmarshalling exception when using composite-id.

       

       

      1. Architectural Discussion:

      The server isn’t using an Application Server, so everything must run standalone. The actual server architecture looks like this:

       

      [database] <-> [backend] <-> [frontend]

       

      The issue to solve is that the frontend should have data locally cached from the database (single table). Only the backend is manipulating the table. The frontend should use the cache read-only.

      My idea was to use Hibernate+Infinispan on the backend. The backend uses Hibernate to do all data manipulations it needs to do. The frontend uses the clustered second level cache from the backend.

       

      [database] <-> [backend] [hibernate+cache]<->[cache] [frontend]

       

      The tricky part is that I don’t want to use Hibernate on the frontend side to access the cache. I want to access the cache directly.

      Is this a reasonable architecture? Is there a better way to solve this?

       

       

      2. Unmarshalling exception when using composite-id:

       

      Infinispan version: Infinispan 'Brahma' 5.1.6.FINAL

      Hibernate version: Hibernate 4.1.7


      I already build a prototype with the setup described above. The prototype works. I can access the cache directly when I create a CacheKey. But this only works if I use the auto generated ID from the database.

       

      This is the config that works:

      cfg.xml

      <hibernate-configuration>
          <session-factory>
      
                 <!-- MySQL -->
       <property name="hibernate.connection.driver_class">org.gjt.mm.mysql.Driver</property>        
       <property name="hibernate.connection.url">jdbc:mysql://somemysqlserver</property>        
                <property name="hibernate.connection.username">name</property>
                <property name="hibernate.connection.password">word</property>        
                <property name="hibernate.dialect">org.hibernate.dialect.MySQL5InnoDBDialect</property>
                <property name="show_sql">false</property>
      
                <!-- Infinispan -->
      <property name="hibernate.cache.use_second_level_cache">true</property>
      <property name="hibernate.cache.use_query_cache">true</property>
      <property name="hibernate.cache.region.factory_class">com.eride.test.hibernate.util.CustomInfinispanRegionFactory</property>
      <property name="hibernate.cache.infinispan.cfg">replication-read-write.xml</property>
      <property name="hibernate.cache.use_structured_entries">true</property>
      <property name="hibernate.generate_statistics">true</property>
      
               <!--  Hibernate -->
               <property name="hibernate.cache.infinispan.entity.cfg">repCache</property>
      
          </session-factory>
      </hibernate-configuration>
      

       

      hbm.xml

      <hibernate-mapping>
                <class name="com.test.hibernate.database.Bean" table="Table" catalog="test">
                          <cache usage="read-only" region="repCache" />
                          <id name="id" type="java.lang.Long">
                                    <column name="ID" />
                                    <generator class="identity" />
                          </id> 
                          <property name="mcc" type="int">
                                    <column name="MCC" not-null="true" />
                          </property>
                          <property name="mnc" type="int">
                                    <column name="MNC" not-null="true" />
                          </property>
                          <property name="count" type="int">
                                    <column name="Count" not-null="true" />
                          </property>
                          <property name="valid" type="int">
                                    <column name="Valid" not-null="true" />
                          </property>
                          <property name="lastUpdate" type="long">
                                    <column name="LastUpdate" not-null="true" />
                          </property>
                </class>
      </hibernate-mapping>
      

       

      replication.xml

      <infinispan xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xsi:schemaLocation="urn:infinispan:config:5.1 http://www.infinispan.org/schemas/infinispan-config-5.1.xsd"
                xmlns="urn:infinispan:config:5.1">
                <global>
                          <transport
                                    transportClass="org.infinispan.remoting.transport.jgroups.JGroupsTransport"
                                    clusterName="infinispan-hibernate-cluster" 
                                    distributedSyncTimeout="50000"
                                    strictPeerToPeer="false">
                                    <properties>
                                              <property name="configurationFile" value="jgroups-udp.xml" />
                                    </properties>
                          </transport>
                </global>
                <default> 
                </default>
                <namedCache name="repCache">
                          <clustering mode="replication">
                                    <sync />
                          </clustering>
                </namedCache>
      </infinispan>
      

       

      jgroup-udp.xml was taken from the infinispan examples

       

      snip from the client code

      EmbeddedCacheManager lCacheManager = lClient.createCacheManagerFromXml();
      Cache<Object, Object> lCache = lCacheManager.getCache("repCache");
      CacheKey lCacheKey = new CacheKey(15350L, org.hibernate.type.LongType.INSTANCE , "com.test.hibernate.database.Bean", null, null);
      Bean lBean = Bean.parseMap((Map)lCache.get(lCacheKey));
      

       

      But I don't want to access the cache with the auto generated ID but with a more usefull key. So I changed to hbm.xml to use a composite-id:

      hbm.xml
      <hibernate-mapping>
                <class name="com.test.hibernate.database.Bean" table="Table" catalog="test">
                          <cache usage="read-only" region="repCache" />                    
                              <composite-id name="myId" class="com.test.hibernate.database.BeanPK">
                                    <key-property name="mcc" type="int">
                                              <column name="MCC" not-null="true" />
                                    </key-property>
                                    <key-property name="mnc" type="int">
                                              <column name="MNC" not-null="true" />
                                    </key-property>
                          </composite-id>
                          <property name="count" type="int">
                                    <column name="Count" not-null="true" />
                          </property>
                          <property name="valid" type="int">
                                    <column name="Valid" not-null="true" />
                          </property>
                          <property name="lastUpdate" type="long">
                                    <column name="LastUpdate" not-null="true" />
                          </property>
                </class>
      </hibernate-mapping>
      

       

      This also works if the second level cache is un-clustered. But when I start the cache cluster I get this exception:

      08:28:47,354 DEBUG SessionFactoryImpl:1692 – Deserialized: a0e4209e-1be8-499d-a67d-3bc7efe73617
      08:28:47,354 DEBUG SessionFactoryRegistry:62 – Initializing SessionFactoryRegistry : org.hibernate.internal.SessionFactoryRegistry@36498fbd
      08:28:47,354 DEBUG SessionFactoryRegistry:139 – Lookup: uid=a0e4209e-1be8-499d-a67d-3bc7efe73617
      08:28:47,354 DEBUG SessionFactoryRegistry:142 – Not found: a0e4209e-1be8-499d-a67d-3bc7efe73617
      08:28:47,354 DEBUG SessionFactoryRegistry:143 – {}
      08:28:47,364  WARN CommandAwareRpcDispatcher:212 – Problems unmarshalling remote command from byte buffer
      java.io.InvalidObjectException: Could not find a SessionFactory [uuid=a0e4209e-1be8-499d-a67d-3bc7efe73617,name=null]
                at org.hibernate.internal.SessionFactoryImpl.locateSessionFactoryOnDeserialization(SessionFactoryImpl.java:1726)
                at org.hibernate.internal.SessionFactoryImpl.readResolve(SessionFactoryImpl.java:1706)
                at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
                at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
                at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
                at java.lang.reflect.Method.invoke(Method.java:601)
                at org.jboss.marshalling.reflect.SerializableClass.callReadResolve(SerializableClass.java:325)
      ...
      

       

      It seems there is problem with the serialization. I already got the hint to overwrite the Externalizer. Is this a the right way to do it? Is there an issue with overwriting the Externalizer id from the Hibernate 2nd level cache?

       

      any comment will be appreciate!

       

      axel