Enterprise Java

Adding Ehcache to Openxava application

Introduction

This article shows how to quickly enable Ehcache on Openxava applications and thus improving performance.

When viewing an entity and its graph, relationships are loaded. Adding a second-level cache fasten the retrieval of associated elements, since already loaded elements are retrieved from cache and not database.

Eventually this page explains how minuteproject treats this aspect with keeping its promise: write nothing.
As an example, we will take the Lazuly minuteproject showcase.

Openxava-Ehcache integration

In Openxava, you describe your model in the manner of Java annotated POJO.The annotations come from the standard JPA2 ORM and Openxava specific ones.
But nothing prevents you to add others. This is what is done to add caching. There are also couple of configurations to undertake to enable caching.

List of actions

  1. Add ehcache.xml config file at the root of your sources
  2. Modify persistence.xml to include second level cache
  3. Add caching annotation (alongside JPA2)

Remark:

Openxava comes with the ehcache.jar so there is no need to add a dependency.

    Detailed actions

    Add ehcache.xml

    In /persistence place ehcache.xml file

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    <ehcache>
        <defaultCache
                maxElementsInMemory="1000"
                eternal="false"
                timeToIdleSeconds="300"
                timeToLiveSeconds="300"
                overflowToDisk="false"
                diskPersistent="false"
                diskExpiryThreadIntervalSeconds="300"
                memoryStoreEvictionPolicy="LRU"
                />
       <cache
        name="your.domain.object"
        maxElementsInMemory="5000"
        eternal="false"
        timeToIdleSeconds="300"
        timeToLiveSeconds="600"
        overflowToDisk="false"
       />
    </ehcache>

    Modify persistence.xml

    Persistence.xml file contains information related to the persitence unit such as connection pool info,
    class or configuration to load. ‘persistence.xml’ is located in /persistence/META-INF

    We will append properties for L2 cache.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <properties>
                <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
                <property name="hibernate.cache.provider_class" value="net.sf.ehcache.hibernate.SingletonEhCacheProvider" />
                <property name="net.sf.ehcache.configurationResourceName" value="/ehcache.xml" />
                <property name="hibernate.cache.use_query_cache" value="true" />
                <property name="hibernate.cache.use_second_level_cache" value="true" />
                <property name="hibernate.generate_statistics" value="true" />  
     
            </properties>

    Add cache annotation

    Here the hibernate annotation is used instead of the standard one (Cacheable in fact seems not to work)
    Place Cache annotation at class level of your domain object.

    1
    @org.hibernate.annotations.Cache(usage = org.hibernate.annotations.CacheConcurrencyStrategy.READ_WRITE)

    Example

    Lazuly application

    Lazuly is a sample database holding conference information used for MinuteProject showcase purpose.
    Minuteproject generates a comprehensive set of artefacts to speedup the release of OX application.
    Further information can be found in Minuteproject 4 Openxava Lazuly showcase.
    On this part we focus on the artefacts generated for the caching specific.
    Minuteproject for the generation base itself on a configuration file, where we define the datamodel to reverse engineer. In this configuration there is an enrichement part where you can add information.
    One of this information deals with the type of content is held in an entity. There are 4 possibilities (reference-data, master-data, pseudo-static-data, live-business-data)
    If you enrich your entity with the content-type=”master-data” or “reference-data” MinuteProject 4 Openxava will generate associated caching.

    This is done here for the entity Country.

    1
    <entity name="COUNTRY" content-type="reference-data">

    Here are the cache related artefacts

    ehcache.xml

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    <ehcache>
     
      <!--
        Sets the path to the directory where cache files are created.
     
        If the path is a Java System Property it is replaced by its value in the
        running VM.
     
        The following properties are translated:
        * user.home - User's home directory
        * user.dir - User's current working directory
        * java.io.tmpdir - Default temp file path
     
        Subdirectories can be specified below the property e.g. java.io.tmpdir/one
        -->
    <!--MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @ehcache-main-config-conference@-->
        <diskStore path="java.io.tmpdir"/>
     
     <!--
        Mandatory Default Cache configuration. These settings will be applied to caches
        created programmtically using CacheManager.add(String cacheName)
        -->
        <defaultCache
                maxElementsInMemory="1000"
                eternal="false"
                timeToIdleSeconds="300"
                timeToLiveSeconds="300"
                overflowToDisk="false"
                diskPersistent="false"
                diskExpiryThreadIntervalSeconds="300"
                memoryStoreEvictionPolicy="LRU"
                />
    <!-- The unnamed query cache -->
       <cache
        name="org.hibernate.cache.StandardQueryCache"
        maxElementsInMemory="1000"
        eternal="false"
        timeToLiveSeconds="300"
        overflowToDisk="false"
       />
    <!--MP-MANAGED-UPDATABLE-ENDING-->
     
    <!--MP-MANAGED-UPDATABLE-BEGINNING-DISABLE @cache-entity-country-conference@-->
       <cache
        name="net.sf.mp.demo.conference.domain.admin.Country"
        maxElementsInMemory="5000"
        eternal="false"
        timeToIdleSeconds="300"
        timeToLiveSeconds="600"
        overflowToDisk="false"
       />
    <!--MP-MANAGED-UPDATABLE-ENDING-->
     
    <!--MP-MANAGED-ADDED-AREA-BEGINNING @custom-cache-definition@-->
    <!--MP-MANAGED-ADDED-AREA-ENDING @custom-cache-definition@-->
     
    </ehcache>

    Persistence.xml

    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
                 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                 version="1.0">
                  
        <!-- Tomcat + Hypersonic -->
        <persistence-unit name="default">
         <non-jta-data-source>java:comp/env/jdbc/conferenceDS</non-jta-data-source>
         <class>org.openxava.session.GalleryImage</class>
            <properties>
                <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
                <property name="hibernate.cache.provider_class" value="net.sf.ehcache.hibernate.SingletonEhCacheProvider" />
                <property name="net.sf.ehcache.configurationResourceName" value="/ehcache.xml" />
                <property name="hibernate.cache.use_query_cache" value="true" />
                <property name="hibernate.cache.use_second_level_cache" value="true" />
                <property name="hibernate.generate_statistics" value="true" />  
    <!--MP-MANAGED-ADDED-AREA-BEGINNING @properties@-->
    <!--MP-MANAGED-ADDED-AREA-ENDING @properties@-->
            </properties>
    <!--MP-MANAGED-ADDED-AREA-BEGINNING @persistence-unit@-->
    <!--MP-MANAGED-ADDED-AREA-ENDING @persistence-unit@-->
        </persistence-unit>      
     
    <!--MP-MANAGED-ADDED-AREA-BEGINNING @persistence@-->
    <!--MP-MANAGED-ADDED-AREA-ENDING @persistence@-->
     
    </persistence>

    Class annotation

    1
    2
    3
    4
    5
    6
    7
    8
    9
    @org.hibernate.annotations.Cache(usage = org.hibernate.annotations.CacheConcurrencyStrategy.READ_WRITE)
    //MP-MANAGED-ADDED-AREA-BEGINNING @class-annotation@
    //MP-MANAGED-ADDED-AREA-ENDING @class-annotation@
    public class Country {
     
        @Hidden @Id @Column(name="id" )
        @GeneratedValue(strategy = GenerationType.AUTO)
        private Integer id;
    ...

    Generated code remark

    The generated code has markers inside file extension comment.

    Within MP-MANAGED-ADDED-AREA-BEGINNING and  MP-MANAGED-ADDED-AREA-ENDING you can place customized code
    Within MP-MANAGED-UPDATABLE-BEGINNING-DISABLE and  MP-MANAGED-UPDATABLE-ENDING you can alter the code. To keep your modifications please change MP-MANAGED-UPDATABLE-BEGINNING-DISABLE into MP-MANAGED-UPDATABLE-BEGINNING-ENABLE.
    Updatable code prevent you to lose your customisation over consecutive generations.

    For more information on updatable code see Minuteproject updatable code.

    Generation

    • Place the following file mp-config-LAZULY-OPENXAVA.xml in /mywork/config
    • on a prompt execute mp-model-generation(.sh/cmd) mp-config-LAZULY-OPENXAVA.xml 
    • the resulting artefacts in /DEV/output/openxava/conference 

    To generate use the updated version of mp-config-LAZULY-OPENXAVA.xml

    001
    002
    003
    004
    005
    006
    007
    008
    009
    010
    011
    012
    013
    014
    015
    016
    017
    018
    019
    020
    021
    022
    023
    024
    025
    026
    027
    028
    029
    030
    031
    032
    033
    034
    035
    036
    037
    038
    039
    040
    041
    042
    043
    044
    045
    046
    047
    048
    049
    050
    051
    052
    053
    054
    055
    056
    057
    058
    059
    060
    061
    062
    063
    064
    065
    066
    067
    068
    069
    070
    071
    072
    073
    074
    075
    076
    077
    078
    079
    080
    081
    082
    083
    084
    085
    086
    087
    088
    089
    090
    091
    092
    093
    094
    095
    096
    097
    098
    099
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    <!DOCTYPE root>
    <generator-config>
     <configuration>
      <conventions>
       <target-convention type="enable-updatable-code-feature" />
      </conventions
      <model name="conference" version="1.0" package-root="net.sf.mp.demo">
       <data-model>
        <driver name="mysql" version="5.1.16" groupId="mysql" artifactId="mysql-connector-java"></driver>
        <dataSource>
         <driverClassName>org.gjt.mm.mysql.Driver</driverClassName>
         <url>jdbc:mysql://127.0.0.1:3306/conference</url>
         <username>root</username>
         <password>mysql</password>
        </dataSource>
        <!--
         for Oracle and DB2 please set the schema <schema> </schema>
        -->
        <primaryKeyPolicy oneGlobal="true">
         <primaryKeyPolicyPattern name="autoincrementPattern"></primaryKeyPolicyPattern>
        </primaryKeyPolicy>
       </data-model>
       <business-model>
        <!--
         <generation-condition> <condition type="exclude"
         startsWith="DUAL"></condition> </generation-condition>
        -->
        <business-package default="conference">
            <condition type="package" startsWith="STAT" result="statistics"></condition>
            <condition type="package" startsWith="COUNTRY" result="admin"></condition>
            <condition type="package" startsWith="ROLE" result="admin"></condition>   
        </business-package>
        <enrichment>
         <conventions>
          <column-naming-convention type="apply-strip-column-name-suffix"
           pattern-to-strip="_ID" />
          <reference-naming-convention
           type="apply-referenced-alias-when-no-ambiguity" is-to-plurialize="true" />
         </conventions>
     
         <entity name="COUNTRY" content-type="reference-data">
          <semantic-reference>
           <sql-path path="NAME" />
          </semantic-reference>
         </entity>
         <entity name="CONFERENCE_MEMBER">
          <semantic-reference>
           <sql-path path="FIRST_NAME" />
           <sql-path path="LAST_NAME" />
          </semantic-reference>
          <field name="STATUS">
           <property tag="checkconstraint" alias="conference_member_status">
            <property name="PENDING" value="PENDING" />
            <property name="ACTIVE" value="ACTIVE" />
           </property>
          </field>
          <field name="EMAIL">
           <stereotype stereotype="EMAIL" />
          </field>
         </entity>
         <entity name="SPEAKER">
          <field name="BIO">
           <stereotype stereotype="HTML_TEXT" />
          </field>
          <field name="PHOTO">
           <stereotype stereotype="PHOTO" />
          </field>
          <field name="WEB_SITE_URL">
           <stereotype stereotype="WEBURL" />
          </field>
         </entity>
         <entity name="PRESENTATION">
          <field name="STATUS">
           <property tag="checkconstraint" alias="presentation_status">
            <property name="PROPOSAL" value="PROPOSAL" />
            <property name="ACTIVE" value="ACTIVE" />
           </property>
          </field>
         </entity>
         <entity name="SPONSOR">
          <field name="STATUS">
           <property tag="checkconstraint" alias="sponsor_status">
            <property name="PENDING" value="PENDING" />
            <property name="ACTIVE" value="ACTIVE" />
           </property>
          </field>
          <field name="PRIVILEGE_TYPE">
           <property tag="checkconstraint" alias="sponsor_privilege">
            <property name="GOLDEN" value="Golden" />
            <property name="SILVER" value="Silver" />
            <property name="BRONZE" value="Bronze" />
           </property>
          </field>
         </entity>
         <!-- views -->
         <entity name="stat_mb_per_ctry_conf" alias="MEMBER_PER_COUNTRY_AND_CONFERENCE">
          <virtual-primary-key isRealPrimaryKey="true">
           <property name="virtualPrimaryKey" value="ID" />
          </virtual-primary-key>
         </entity>
         <entity name="stat_mb_by_role" alias="MEMBER_PER_ROLE_COUNTRY_AND_CONFERENCE">
          <virtual-primary-key isRealPrimaryKey="true">
           <property name="virtualPrimaryKey" value="id" />
          </virtual-primary-key>
          <field name="stat_mb_per_ctry_conf_ID" linkToTargetEntity="stat_mb_per_ctry_conf"
           linkToTargetField="id"></field>
         </entity>
        </enrichment>
       </business-model>
      </model>
      <targets>
       <!-- openxava -->
       <target refname="OpenXava" name="OpenXava"
        fileName="mp-template-config-openxava-last-features.xml"
        outputdir-root="../../DEV/output/openxava/conference"
        templatedir-root="../../template/framework/openxava">
       </target>
     
       <target refname="JPA2-LIB" fileName="mp-template-config-JPA2-LIB.xml"
        templatedir-root="../../template/framework/jpa">
       </target>
        
       <target refname="BSLA-LIB" fileName="mp-template-config-bsla-LIB-features.xml"
        templatedir-root="../../template/framework/bsla">
       </target>
     
       <target refname="CACHE-LIB"
          fileName="mp-template-config-CACHE-LIB.xml"
          templatedir-root="../../template/framework/cache">
       </target>
                    
      </targets>
     </configuration>
    </generator-config>

    Test
    To ensure that the caching is working properly:

    • Enable hibernate logging. Add the following snippet as extra properties in persistence.xml.
    1
    2
    <property name="hibernate.show_sql" value="true" />
                <property name="hibernate.format_sql" value="true" />
    • navigate to an entity that reference country (example Address)
    • When you view the detail of this entity you will notice that there is a load of the associated entity ‘country’
    • But the second time you access to the details of this entity (or another entity referencing the same country instance), the country is not loaded twice from the database.

    Reference: Adding Ehcache to Openxava application from our JCG partner Florian Adler at the minuteproject blog.

    Do you want to know how to develop your skillset to become a Java Rockstar?
    Subscribe to our newsletter to start Rocking right now!
    To get you started we give you our best selling eBooks for FREE!
    1. JPA Mini Book
    2. JVM Troubleshooting Guide
    3. JUnit Tutorial for Unit Testing
    4. Java Annotations Tutorial
    5. Java Interview Questions
    6. Spring Interview Questions
    7. Android UI Design
    and many more ....
    I agree to the Terms and Privacy Policy
    Subscribe
    Notify of
    guest


    This site uses Akismet to reduce spam. Learn how your comment data is processed.

    0 Comments
    Oldest
    Newest Most Voted
    Inline Feedbacks
    View all comments
    Back to top button