Monday, June 15, 2015

Spring JMS with ActiveMQ – hello world example – receive message using annotation

Since spring 4.1, using spring jms  to receive message can even easier than before. Just add one annotation @JmsListener before any POJO’s method. In this article, a hello world example will show how easy it can be done.

There are other ways to  receive message from ActiveMQ broker using Spring JMS, examples are provided here

The example on how to send out message with Spring JMS and ActiveMQ is in this article.

0. What you need

  • JDK 1.7+
  • Maven 3.2.1
  • ActiveMQ 5.10.0
  • Spring 4.1.0.RELEASE

In this example  we'll run the ActiveMQ broker on a machine of IP 192.168.203.143 with default port 61616.

1. Configure maven pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.springframework.samples</groupId>
<artifactId>spring-jms-activemq-receive-annotation</artifactId>
<version>0.0.1-SNAPSHOT</version>

<properties>
<!-- Spring version -->
<spring-framework.version>4.1.0.RELEASE</spring-framework.version>
<!-- ActiveMQ version -->
<activemq.version>5.10.0</activemq.version>
</properties>

<dependencies>
<!-- Spring Artifacts -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>${spring-framework.version}</version>
</dependency>

<!-- ActiveMQ Artifact -->
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-spring</artifactId>
<version>${activemq.version}</version>
</dependency>
</dependencies>

<!-- Use Jave 1.7 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

There are 2 dependencies, spring-jms and activemq-spring. JDK version is specified to 1.7


2. Define Java class


There are 2 classes in this demo. The first one is a simple spring bean.

package com.shengwang.demo;

import org.springframework.jms.annotation.JmsListener;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Service;

@Service
public class JmsMessageListener {

@JmsListener(destination="SendToRecv")
@SendTo("RecvToSend")
public String processMessage(String text) {
System.out.println("Received: " + text);
return "ACK from handleMessage";
}
}

The only thing make this class different from a common spring bean is the @JmsListener and @SendTo annotations before the method. The @JmsListener tell spring context the following method will be used whenever a message is coming from the destination "SendToRecv" asynchronously. The @SendTo annotation specify the destination for return message from the method.


The second class is the main class.

package com.shengwang.demo;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class DemoMain {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("app-context.xml");
}
}

There's only one line in the main to create the spring context. Imagine what will happen when running it. Will the main exit immediately after the line? No, Because the control is in Spring context, which is waiting for incoming messages from JMS. That's the feeling of Inversion of Control (IoC).  Let the context choose what to do.


3. Spring configuration


In the Spring configuration app-context.xml, there are 3 beans need to define:


  • ActiveMQ connection factory
  • Spring connection factory (use the above one)
  • Spring jms listener container factory (use the above one)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:jms="http://www.springframework.org/schema/jms"
xsi:schemaLocation="http://www.springframework.org/schema/jms
http://www.springframework.org/schema/jms/spring-jms-4.1.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">


<context:component-scan base-package="com.shengwang.demo" />

<!-- enable the configuration of jms on annotations -->
<jms:annotation-driven/>

<!-- =============================================== -->
<!-- JMS Common, define JMS connectionFactory -->
<!-- =============================================== -->
<!-- Activemq connection factory -->
<bean id="amqConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<!-- brokerURL -->
<constructor-arg index="0" value="tcp://192.168.202.168:61616" />
</bean>

<!-- Pooled Spring connection factory -->
<bean id="connectionFactory"
class="org.springframework.jms.connection.CachingConnectionFactory">
<constructor-arg ref="amqConnectionFactory" />
</bean>

<!-- =============================================== -->
<!-- JMS receive, define JmsListenerContainerFactory -->
<!-- =============================================== -->
<bean id="jmsListenerContainerFactory"
class="org.springframework.jms.config.DefaultJmsListenerContainerFactory">
<property name="connectionFactory" ref="connectionFactory" />
<property name="concurrency" value="3-10"/>
</bean>

</beans>

There's a concurrency parameter for the bean jmsListenerContainerFactory, which is 3-10 here. It means at lease 3 beans instances are waiting for the incoming message. It can process 10 incoming messages in parallel.


Everything is almost done now. Let's review the directory structure for our maven project.


image


4. Run the code


Before you can run the code, you need to make sure the ActiveMQ broker is running. So our jave code can connect to it and receive message from it.   Make sure the broker IP and port are correct in your spring configuration file "app-context.xml". Run the ActiveMQ broker like with this command.

ACTIVEMQ_INSTALL_DIR/bin/activemq start

Now you can run you main class. you can run it from IDE such as eclipse, or you can run it direct in command line by using maven.

cd spring-jms-activemq-receive-annotation
# run mvn from project directory
mvn exec:java -Dexec.mainClass="com.shengwang.demo.DemoMain"

After running the demo, Let's check from ActiveMQ Web Console. (http://activemqIp:8161)


image


Now send a message to the Queue "SendToRecv", and refresh the browser.


image


Check the reply message. The return of the listener method will be send back as JMS message automatically.


image

Sunday, June 14, 2015

Build a spring project into an executable standalone jar in maven

To create an executable standalone jar, which can be run by command  like java –jar myExecutable.jar, there are 2 conditions need to be fulfilled:

  • Specify the entry main class in META-INFO/MANIFEST.MF file
  • Include all dependences  in the final jar file

The second condition is not needed theoretically, but in practical will always be necessary.

If the project uses Spring with xml configuration, then there’s one more condition to fulfill.

  • Handle spring schemas for different spring packages. (see below for explanation)

The best maven plugin to handle all there three requirements are maven-shade-plugin. (some other plugin such as maven-assembly-plugin can deal with the first two requirements, but not the third one).

In this article, a hello world spring project will be package to an executable standalone jar file using maven. The way used in this article can also perfectly create executable standalone jar package, even if the project don’t use spring.

0. What you need

  • JDK
  • Spring
  • Maven 3.2.1

1. Define the class

There are 2 classes. The first one the a spring bean.

package com.shengwang.demo;

import org.springframework.stereotype.Component;

@Component
public class HelloWorldBean {
public void sayHello(String name) {
System.out.println("Hello "+name);
}
}

The second one is the main class, which get the hello world bean and call its sayHello method.

package com.shengwang.demo;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class DemoMain {

public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("application-context.xml");
HelloWorldBean bean = ctx.getBean(HelloWorldBean.class);

bean.sayHello("Spring");

((ClassPathXmlApplicationContext) (ctx)).close();
}
}

2. Spring configuration


The spring xml configuration file application-context.xml is also very simple.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.1.xsd">

<context:component-scan base-package="com.shengwang.demo" />

</beans>

3. Maven pom


Plugin maven-shade-plugin in the pom file is the key of this demo.

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.shengwang.demo</groupId>
<artifactId>spring-executable-standalone</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>spring-executable-standalone</name>
<url>http://maven.apache.org</url>

<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.1.0.RELEASE</version>
</dependency>
</dependencies>

<build>

<plugins>
<!-- Use JDK 7 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>

<!-- =============================================================== -->
<!-- use shade plugin to package spring project into executable jar -->
<!-- =============================================================== -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<!-- =================================================== -->
<!-- define the main entry for the output jar file -->
<!-- =================================================== -->
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.shengwang.demo.DemoMain</mainClass>
</transformer>
<!-- =================================================== -->
<!-- append all spring.handlers instead of overwriting -->
<!-- =================================================== -->
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.handlers</resource>
</transformer>
<!-- =================================================== -->
<!-- append all spring.schemas instead of overwriting -->
<!-- =================================================== -->
<transformer
implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>META-INF/spring.schemas</resource>
</transformer>
</transformers>
<!-- ============================================================ -->
<!-- exclude any digital signiture file from third party jar file -->
<!-- ============================================================ -->
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
</configuration>
</execution>
</executions>
</plugin>

</plugins>
</build>
</project>

In the plugin, 3 transformers and 1 filter are used. Let’s explain them one by one.


The first transformer is ManifestResourceTransformer, which will modify the META-INFO/MANIFEST.MF file in the final  output jar file. This transformer is used to add Main-Class entry in the MANIFEST.MF file. If unzip the output jar, open the META-INFO/MANIFEST.MF file, we can see the added line.


image


The second and third transformers are AppendingTransformer. Why these two transformers are needed? Because every Spring package store xsd mapping file in the same direcotry hierarchy, /META-INF/spring.schemas and /META-INF/spring.handlers, see example below.


image image


For example two spring artifacts, spring-aop and spring-beans, have the exactly same files in the same directory. When using maven to package jar, these files from different dependency packages are combined in to one file by the transformers.


The filters are used to exclude the digital signature files out of the final output jar file. Some of the third party dependency may be digitally signed. These signature files must be screened out to prevent signature verification exceptions.


For most cases, the main-class entry is the only thing need to change.


4. Package


Run maven to package the project into one jar file.

mvn clean package

5. Run the output jar


Run the output jar file

java -jar spring-executable-standalone-0.0.1-SNAPSHOT.jar

The output looks like:


image

Friday, June 12, 2015

Use Hibernate level two cache with ehcache – hello world example

No doubt that well designed second level cache will greatly increase persistence performance. Hibernate clearly define the usage of 2nd level cache, but leave the cache implementation to other cache providers.  In this article ehcache is used as the second level cache provider.

0. What you need

  • JDK 1.7+
  • Spring 4.1.0.RELEASE
  • Hibernate 4.3.4.Final

1. Configure maven pom

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.shengwang.demo</groupId>
<artifactId>spring-persistence-hibernate-cache</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>spring-persistence-hibernate-cache</name>
<url>http://maven.apache.org</url>

<properties>
<!-- Spring version -->
<spring.version>4.1.0.RELEASE</spring.version>

<!-- Hibernate version -->
<hibernate.version>4.3.4.Final</hibernate.version>
</properties>

<dependencies>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>${spring.version}</version>
</dependency>

<!-- Hibernate -->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>${hibernate.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>${hibernate.version}</version>
</dependency>

<!-- Database driver-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.2.2</version>
</dependency>

</dependencies>

<!-- Use java 1.7 -->
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
</project>

The dependencies in pom file belong to spring, hibernate and database connection driver.  Beside the normal needed dependencies, the  hibernate-ehcache is added to serve as 2nd level cache provider, which has the same version as the hibernate-core.


2. Add @Cache to entity class


In this demo, there are 2 entity classes, Client and PurchaseOrder. One client can has many orders, so it’s a one-to-many relation.


First is the PurchaseOrder entity class.

package com.shengwang.demo.model;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;


@Entity
@Table(name="purchase_order")
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
public class PurchaseOrder {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="order_id")
private int orderId;

@Column(name="order_desc")
private String orderDesc;

@ManyToOne(cascade=CascadeType.PERSIST)
@JoinColumn(name="client_id")
private Client client;

public int getOrderId() {
return orderId;
}

public String getOrderDesc() {
return orderDesc;
}

public Client getClient() {
return client;
}

public void setOrderId(int orderId) {
this.orderId = orderId;
}

public void setOrderDesc(String orderDesc) {
this.orderDesc = orderDesc;
}

public void setClient(Client client) {
this.client = client;
}
}

This is just an ordinary entity class, but with just one extra annotation @Cache(usage=CacheConcurrencyStrategy.READ_ONLY) . That tells the hibernate the entity can be cached.


Then the client entity class.

package com.shengwang.demo.model;

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

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import org.hibernate.annotations.Cache;
import org.hibernate.annotations.CacheConcurrencyStrategy;


@Entity
@Table(name="client")
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
public class Client {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="client_id")
private int clientId;

@Column(name="client_name")
private String clientName;

//------------------------------------
// Caution: need to cache collections
//------------------------------------
@OneToMany(mappedBy="client",cascade=CascadeType.PERSIST)
@Cache(usage=CacheConcurrencyStrategy.READ_ONLY)
private Set orders = new HashSet();


public int getClientId() {
return clientId;
}
public String getClientName() {
return clientName;
}

public Set getPurchaseOrders() {
return orders;
}
public void setClientId(int clientId) {
this.clientId = clientId;
}
public void setClientName(String clientName) {
this.clientName = clientName;
}
public void setPurchaseOrders(Set orders) {
this.orders = orders;
}
}

Compare to the PurchaseOrder entity class, the Client entity not only add @Cache annotation before the class definition like the above one, but also add @Cache annotaion before the collections elements. Because by default the collection fields of the entity are not cached. For most of the time collections do need to be cached to get performance improvement.


3. Configure Spring


In spring configuration file, enable the second level cache and setup the ehcahe as the cache provider.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">

<!-- scan for all spring beans -->
<context:component-scan base-package="com.shengwang.demo" />

<!-- enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="transactionManager"/>

<!-- persistence -->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/spring" />
<property name="username" value="root" />
<property name="password" value="IHave1Dream!" />
</bean>

<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>

<!-- enable 2nd level cache -->
<prop key="hibernate.cache.use_second_level_cache">true</prop>

<!-- setup 2nd level cache -->
<prop key="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory </prop>
<prop key="net.sf.ehcache.configurationResourceName">/ehcache.xml</prop>

</props>
</property>
<!-- set auto scan the Entity, otherwise you will get 'Unknown entiy' error -->
<property name="packagesToScan" value="com.shengwang.demo.model" />
</bean>

<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager"
p:sessionFactory-ref="sessionFactory">
</bean>

</beans>

In the session factory bean configuration, hibernate.cache.use_second_level_cache set to true to enable 2nd level cache. The cache providers set to ehcache using hibernate.cache.region.factory_class property. Also the ehcache configuration file set to ehcache.xml, which will be shown next.


4. Configure ehcache


The ehcache.xml is ehcache configuration file, which looks like this.

<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="ehcache.xsd" updateCheck="true" monitoring="autodetect" dynamicConfig="true">

<cache name="demoEntityCache"
maxEntriesLocalHeap="10000"
maxEntriesLocalDisk="1000"
eternal="false"
diskSpoolBufferSizeMB="20"
timeToIdleSeconds="300" timeToLiveSeconds="600"
transactionalMode="off">
<persistence strategy="localTempSwap" />
</cache>

<defaultCache maxEntriesLocalHeap="0" eternal="false" timeToIdleSeconds="1200" timeToLiveSeconds="1200">
</defaultCache>
</ehcache>

Now the hibernate level 2 cache is ready to serve.  In the next article here, several persistence operation will be made to verify the level two cache’s effect.

Thursday, June 11, 2015

Ehcache Issue : Element <cache> does not allow attribute "maxEntriesLocalHeap"

When using Ehcache as Hibernate second level cache provider, You may face the org.xml.sax.SAXException like this:

Caused by: org.xml.sax.SAXException: null:25: Element <cache> does not allow attribute "maxEntriesLocalHeap".

Reason

This is because in “maxEntriesLocalHeap” is used in ehcahe configuration file ehcache.xml. “maxEntriesLocalHeap” is introducted to ehcache from 2.5, so if the actually ehcache-core version in you project is less then 2.5, the excpetion above comes out.

Unfortunately, Hibernate 4.3 release still uses ehcache 2.4.3. So if the project pom.xml just like this, the exception occures.

<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>4.3.4.Final</version>
</dependency>

image


Solution


To solve this, just add explicit dependency to you project.

<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-ehcache</artifactId>
<version>4.3.4.Final</version>
</dependency>

<dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache-core</artifactId>
<version>2.6.5</version>
</dependency>

Now the dependency looks like this, the SAXException is gone.


image

Tuesday, June 9, 2015

Use JMX to monitor Hibernate 4.3 statistics (with Spring)

To find out hibernate cache’s performance, like cache hit/miss ratio, JMX can be convenient. Before Hibernate 4.3, there is a StatisticsService class can be used. But Hibernate 4.3 removed this class.  If the project also uses Spring framework, it can still be done. 

The original solution came from Marcel Stor from here. This article try to compliment it with details.

0. What you need

  • JDK1.7 +
  • Maven 3.2.1+
  • Spring 4.1.0.RELEASE
  • Hibernate 4.3.4.Final

1. Define Java class

One Jave class is needed. Add the following class to the project.  The class is defined as a normal Spring bean with @Component annotation.

package com.shengwang.demo.statistics;

import org.hibernate.SessionFactory;
import org.hibernate.stat.Statistics;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class HibernateStatisticsFactoryBean implements FactoryBean<Statistics> {

@Autowired
private SessionFactory sessionFactory;

@Override
public Statistics getObject() throws Exception {
return sessionFactory.getStatistics();
}

@Override
public Class<?> getObjectType() {
return Statistics.class;
}

@Override
public boolean isSingleton() {
return true;
}
}

2. Configure Spring


Configure Spring, add following to the spring configuration xml file.

<!-- ==================================== -->
<!-- Hibernate 4.3 Statistics for JMX -->
<!-- ==================================== -->
<bean id="jmxExporter" class="org.springframework.jmx.export.MBeanExporter">
<property name="beans">
<map>
<entry key="Hibernate:type=statistics">
<ref bean="hibernateStatisticsFactoryBean"/>
</entry>
</map>
</property>
</bean>

The bean hibernateStatisticsFactoryBean used is the Class defined above. 


All done!


3. Check from JConsole


Run JConsole, the JMX tool shipped with JDK, connect to the running project. Now hibernate statistics can be monitored!


Connect to the Java application process.

image

Now the Hibernate performance can be monitored by JMX, for example the 2nd level cache hit ratio in the following snapshot.


image

Monday, May 25, 2015

Spring Scheduler hello world example - run task in the future

Spring framework now support scheduler, so for most usage there is no need to involve other library, such as Quartz to do this.  The usage is very simple: add annotation @Scheduled(...) to any bean's method that need to be run in the future. The Spring context will create a thread to run it at the right time you specified in the future.

0. What you need

  • JDK 1.7
  • Maven 3.2.1
  • Spring 4.1.0.RELEASE

1. Configure the maven pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>
<modelversion>4.0.0</modelversion>
<groupid>com.shengwang.demo</groupid>
<artifactid>spring-scheduler-hello-world</artifactid>
<version>0.0.1-SNAPSHOT</version>

<dependencies>

<!-- Spring Context aritifact-->
<dependency>
<groupid>org.springframework</groupid>
<artifactid>spring-context</artifactid>
<version>4.1.0.RELEASE</version>
</dependency>

</dependencies>

<!-- Using JDK 1.7 for compiling -->
<build>
<plugins>
<plugin>
<groupid>org.apache.maven.plugins</groupid>
<artifactid>maven-compiler-plugin</artifactid>
<version>2.5.1</version>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
</project>


2. Define the Java Class



In the example there are 2 classes. The first one is a simple spring bean, but with annotation @Scheduled for method.

package com.shengwang.demo;

import java.util.Date;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class Task {
/*
* The method will run on 13:40:00 everyday.
* cron format: sec min hour day mon week
*/
@Scheduled(cron="0 40 13 * * *")
public void runTask() {
System.out.println("Runing at " + new Date());
}
}


The second class is the main class.

package com.shengwang.demo;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class DemoMain {

public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext("app-context.xml");
}
}
The only line in the main is used to create the spring application context. If you run the main function, the program will not never finish since the spring context is not closed. so it will wait for the scheduled task to run in the future. The app-context.xml is the spring configuration file.

 



3. Spring configuration



The spring configuration file is named "app-context.xml" here. It stays in the main resources path /src/main/resources.

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task.xsd">

<!-- enable scheduling annotations -->
<task:annotation-driven scheduler="scheduler"/>
<task:scheduler id="scheduler" pool-size="15"/>

<!-- register spring beans -->
<context:component-scan base-package="com.shengwang.demo" />

</beans>


There are 2 lines start with <task:... /> used to configure spring scheduling. One thing need to mention is that (from spring official document):  If you do not provide a pool-size attribute, the default thread pool will only have a single thread. So in real practical, the pool-size is should always be set  to a number bigger than one to prevent any long task delaying all other scheduled tasks.

Monday, May 18, 2015

Understand all about hibernate fetch - WHEN and HOW

As Hibernate official document says:

Hibernate have two orthogonal notions here: when is the association fetched and how is it fetched.

There are many words on different document to describe ORM fetch like fetch strategy, fetch type, fetch mode, but in essence just the concept of WHEN and HOW.

In this article, a One-To-Many example will be used to demonstrate the concept.  A Client entity and a PurchaseOrder Entity are used in the demo code. One client can have many purchase orders. There are 5 clients in database and every client has 10 orders.

image

0. Environment and version

  • Java 7 (JPA2.0)
  • Hibernate 4.x

The following example and discuss are based on this environment.

1. Concept in short

1.1 WHEN to fetch

  • Eager : Assemble SQL and access database immediately when an entity is created. JPA compatible
  • Lazy  -  A wrapper class called 'proxy' is used for the entity, when an entity is created. The database access only happens when entity's value gets used. JPA compatible. Default behavior
  • Extra Lazy  -  Try not to fetch the whole collection into memory unless absolutely needed. Not JPA compatible

When means at what time will hibernate assemble a SQL clause to access the database.

1.2 How to fetch

If a entity have associations, how the associations of the entity get loaded.  In our Client-To-PurchaseOrder(One-To-Many) demo, when a client entity is loaded, how to get his purchase orders. 

  • Join  - Use SQL outer join to get associations. Be careful about the return list, it may not like what you expect,  see the demo below.   JPA compatible. Default behavior for Eager.
  • Select  - Use a separate select to get only 1 entity's association,  will cause the 'N+1' problem.   JPA compatible. Default behavior FOR Lazy.  
  • Batch  -  Use a separate select to get a batch size entities' association. Can be treated as a kind of improvement of 'Select' Not JPA compatible
  • Subselect  -  Use a separate select to get all associations for all entities retrieved in a previous query. Not JPA compatible

JPA compatible means in java code, only classes or annotations from javax.persistence.* are used, no need to import any thing  from  org.hibernate.*.  Hibernate can just be used as persistence provider in JPA configure file persistence.xml, which makes the the application not close coupled with Hibernate.

2. Code for demo

2.1 The Entity definitions

The entity PurchaseOrder is plain. Nothing's tricky here.

package com.shengwang.demo.model;

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;


@Entity
@Table(name="purchase_order")
public class PurchaseOrder {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="order_id")
private int orderId;

@Column(name="order_desc")
private String orderDesc;

@ManyToOne(cascade=CascadeType.PERSIST)
@JoinColumn(name="client_id")
private Client client;

public int getOrderId() {
return orderId;
}

public String getOrderDesc() {
return orderDesc;
}


public Client getClient() {
return client;
}

public void setOrderId(int orderId) {
this.orderId = orderId;
}

public void setOrderDesc(String orderDesc) {
this.orderDesc = orderDesc;
}

public void setClient(Client client) {
this.client = client;
}
}

All magic happens in the Entity Client. which has a Set variable orders associated with PurcharseOrder to represent the one-to-many mapping. We will change the annoatation for Set variable orders to get different fetch when-how combinations.

package com.shengwang.demo.model;

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

import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.OneToMany;
import javax.persistence.Table;

@Entity
@Table(name="client")
public class Client {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="client_id")
private int clientId;

@Column(name="client_name")
private String clientName;

//---------------------------------
// This is where the magic happens
//---------------------------------
@OneToMany(fetch=FetchType.LAZY,mappedBy="client")
private Set<PurchaseOrder> orders = new HashSet<PurchaseOrder>();


public int getClientId() {
return clientId;
}
public String getClientName() {
return clientName;
}

public Set<PurchaseOrder> getPurchaseOrders() {
return orders;
}
public void setClientId(int clientId) {
this.clientId = clientId;
}
public void setClientName(String clientName) {
this.clientName = clientName;
}
public void setPurchaseOrders(Set<PurchaseOrder> orders) {
this.orders = orders;
}
}

2.2 demo code snippet


Following code snippet is used to show how the different fetch combinations effect. We'll keep running this code on different when-how fetch combinations to demonstrate differents effects

@Override
@Transactional
public void printOrdersForEveryClient() {
List <Client> clients;
Criteria criteria;
criteria = sessionFactory.getCurrentSession().createCriteria(Client.class);
clients = criteria.list(); // checkpoint-1
System.out.println("clients.size="+clients.size());

for (Client client: clients) {
System.out.println("client : "+ client.getClientId());
System.out.println(""+client.getPurchaseOrders().size()); // checkpoint-2
Set <PurchaseOrder> orders = client.getPurchaseOrders(); // checkpoint-3
for (PurchaseOrder order : orders) {
System.out.println(order.getOrderId()+","+order.getOrderDesc());
}
}
}

The method printOrdersForEveryClient() is supposed to print out purchase orders for all clients one by one. The comments checkpoint1-4 is added as anchors to locate where the database accessing happens.


3. Behaviors of When+How fetch combinations


Let's try different When-How fetch combinations on the one-to-many(client-to-purchase order) mapping,  see what really happens. All the following SQL clauses are NOT the original Hibernate generated ones, but simplified to make them more understandable.


3.1 Eager + Join


Changing the definition of Client entity, set when to eager by "fetch=FetchType.EAGER", set how to join by @Fetch(FetchMode.JOIN). Since join is the default behavior for eager, annotation  @Fetch(FetchMode.JOIN) can be omitted.

@Entity
@Table(name="client")
public class Client {
// omit other fields

//---------------------------------
// This is where the magic happens
//---------------------------------
@OneToMany(fetch=FetchType.EAGER,mappedBy="client",cascade=CascadeType.PERSIST)
private Set<PurchaseOrder> orders = new HashSet<PurchaseOrder>();

// omit getters & setters
}

The database accessing happens only in 1 place:
select a.*,  b.* from client a left outer join purchase_order b on a.client_id=b.client_id      at checkpoint-1


Suppose there are 5 rows in client table and every client have 10 purchase orders in purchase_order table. The result list size at checkpoint-1 is not 5, but 5x10 = 50. This will make the method printOrdersForEveryClient() doesn't work as design. 


3.2 Lazy + Select (default behavior)


The default when is lazy, and the default how for lazy is select.


The database accessing happens in  2 places:
select *  from client   at checkpoint-1
select * from purchase_order  where client_id=?     at checkpoint-2


Since the checkpoint-2 is in the for loop, every loop will issue a sql query to database, perfectly demonstrate the so-called 'N+1' problems.
You may also notice that although lazy fetch is used, the database accessing immediately happens when querying the clients.  That's because the lazy fetch only works for query entity by primary key.


3.3 Eager+ Select


Changing the definition of Client entity, set when to eager by "fetch=FetchType.EAGER", set how to join by @Fetch(FetchMode.SELECT)

@Entity
@Table(name="client")
public class Client {
// omit other fields

//---------------------------------
// This is where the magic happens
//---------------------------------
@OneToMany(fetch=FetchType.EAGER,mappedBy="client",cascade=CascadeType.PERSIST)
@Fetch(FetchMode.SELECT)
private Set<PurchaseOrder> orders = new HashSet<PurchaseOrder>();

// omit getters & setters
}

The database accessing happens only in 1 place, but N+1 SQL clauses:


select * from client                                                                             at checkpoint-1
select * from purchase_order where client_id=?                at checkpoint-1               
select * from purchase_order where client_id=?               
at checkpoint-1
select * from purchase_order where client_id=?                at checkpoint-1
select * from purchase_order where client_id=?                at checkpoint-1
select * from purchase_order where client_id=?                at checkpoint-1

The query in java also creates N+1 SQL clauses, but all at same time. One to get all clients and 5 to get orders for each client.  (There are 5 clients in the client table) 


3.4 Lazy + Batch


Changing the definition of Client entity, set when to lazy by "fetch=FetchType.LAZY", set how to batch by @Fetch(FetchMode.SELECT) and @BatchSize(size=3) . Since select is the default behavior for lazy, annotation  @Fetch(FetchMode.SELECT) can be omitted.

@Entity
@Table(name="client")
public class Client {
// omit other fields

//---------------------------------
// This is where the magic happens
//---------------------------------
@OneToMany(fetch=FetchType.LAZY,mappedBy="client",cascade=CascadeType.PERSIST)
@BatchSize(size=3)
private Set<PurchaseOrder> orders = new HashSet<PurchaseOrder>();

// omit getters & setters
}

The database accessing happens in 2 places:
select * from client                                                                             at checkpoint-1
select * from purchase_order where client_id in (?, ?, ?)             at checkpoint-2               


Unlike the select, which accesses database N+1 times,  batch fetch accesses database (N/batchSize +1 ) times. In our demo, there are 5 clients in the table,  batch size is 3, total database accessing counts = celling(5/3)+1=3. 


3.5 Eager + Batch


Changing the definition of Client entity, set when to eager by "fetch=FetchType.EAGER", set how to batch by @Fetch(FetchMode.SELECT) and @BatchSize(size=3)

@Entity
@Table(name="client")
public class Client {
// omit other fields

//---------------------------------
// This is where the magic happens
//---------------------------------
@OneToMany(fetch=FetchType.LAZY,mappedBy="client",cascade=CascadeType.PERSIST)
@Fetch(FetchMode.SELECT)
@BatchSize(size=3)
private Set<PurchaseOrder> orders = new HashSet<PurchaseOrder>();

// omit getters & setters
}

The database accessing happens in 1 place:
select * from client                                                                             at checkpoint-1
select * from purchase_order where client_id in (?, ?, ?)             at checkpoint-1               


Compare to Lazy+Batch, Eager+Batch  create same amount SQL and exactly same SQL clauses. but will no wait for the first time accessing contents of client entity, but immediately load all clients' associations when query client entity. 


3.6 Lazy + Subselect


Changing the definition of Client entity, set when to lazy by "fetch=FetchType.LAZY", set how to subselect by @Fetch(FetchMode.SUBSELECT)

@Entity
@Table(name="client")
public class Client {
// omit other fields

//---------------------------------
// This is where the magic happens
//---------------------------------
@OneToMany(fetch=FetchType.LAZY,mappedBy="client",cascade=CascadeType.PERSIST)
@Fetch(FetchMode.SUBSELECT)
private Set<PurchaseOrder> orders = new HashSet<PurchaseOrder>();

// omit getters & setters
}

The database accessing happens in 2 places:
select * from client                                                                             at checkpoint-1
select a.* from purchase_order a where a.client_id in (select b.client_id from client b)        at checkpoint-2


Unlike the select(N+1) or batch (N/batchsize+1), subselect only access database once to get all associations for the previous query.  The first time in the loop cause the second SQL  and rest of the loop won't access database anymore.  Totally the code accesses database twice.


3.7 Eager+ Subselect


Changing the definition of Client entity, set when to eager by "fetch=FetchType.EAGER", set how to subselect by @Fetch(FetchMode.SUBSELECT)

@Entity
@Table(name="client")
public class Client {
// omit other fields

//---------------------------------
// This is where the magic happens
//---------------------------------
@OneToMany(fetch=FetchType.EAGER,mappedBy="client",cascade=CascadeType.PERSIST)
@Fetch(FetchMode.SUBSELECT)
private Set<PurchaseOrder> orders = new HashSet<PurchaseOrder>();

// omit getters & setters
}

The database accessing happens in 1 place:
select * from client                                                                             at checkpoint-1
select a.* from purchase_order a where a.client_id in (select b.client_id from client b)        at checkpoint-1


Compare to Lazy+Subselect, Eager+Subselect  create same amount SQL and exactly same SQL clauses. but will no wait for the first time accessing contents of client entity, but immediately load all clients' associations when query client entity.


3.8 Extra Lazy+ Batch


Changing the definition of Client entity, set when to extra lazy by "fetch=FetchType.LAZY" and @LazyCollection(LazyCollectionOption.EXTRA), set how to batch by @Fetch(FetchMode.SUBSELECT)  and @BatchSize(size=3)

@Entity
@Table(name="client")
public class Client {
// omit other fields

//---------------------------------
// This is where the magic happens
//---------------------------------
@OneToMany(fetch=FetchType.LAZY,mappedBy="client",cascade=CascadeType.PERSIST)
@LazyCollection(LazyCollectionOption.EXTRA)
@Fetch(FetchMode.SELECT)
@BatchSize(size=3)
private Set<PurchaseOrder> orders = new HashSet<PurchaseOrder>();

// omit getters & setters
}

The database accessing happens in 3 places:
select * from client                                                                             at checkpoint-1
select count(order_id) from purchase_order where client_id= ?                         at checkpoint-2
select * from purchase_order where client_id in (?, ?, ?)              at checkpoint-3               


Compare to Lazy+Batch, Extra Lazy+Batch  create one more SQL clause to avoid getting  all collections contents back when possible.  Extra Lazy is not supported by other persistence providers like EclipseLink. In EclipseLink developer has to manually define a query to get the collection's size without get all contents back.


4. More to notice


The batch size doesn't mean how many rows will be fetched from the associations, but means how many associations to be fetched.  For example using batch fetch,  if there are 1000 purchase orders for each client, batch size = 3, every batch SQL will fetch 3 clients' associations, totally 3000 rows, from purchase_order table.  If only part of the collections are needed, pagination is the solution.

Powered by Blogger.

About The Author

My Photo
Has been a senior software developer, project manager for 10+ years. Dedicate himself to Alcatel-Lucent and China Telecom for delivering software solutions.

Pages

Unordered List