In this article, let us see how we can integrate ZK With spring, JPA and Hibernate Entity Manager.
Technologies Used
1. ZK 6.5 CE Version2. Hibernate Entity Manager Version 4.1.9 Final.
3. Hibernate Core Version 4.1.9 Final
4. Spring ORM Version 3.2.0
5. MySQL version 5
Reference
- ZK Integration with Hibernate
- Spring by Example
- http://www.aoiblog.com/step-by-step-configuration-of-spring-hibernate-and-jpa/
- http://hop2croft.wordpress.com/2011/07/06/jpa-basic-example-with-entitymanager-spring-and-maven/
- Not a good idea to have Spring managing the composer
- Warning: Register a Composer (or ViewModel) as a Spring bean
Project Name and Structure
ZKSpringJPAStep 1:
If you are new ZK, then you can setup the Development environment by downloading this document.Step 2:
MySQL Setup. Let us create the following table in MySQL. Let us create the following table and insert two records by default.CREATE TABLE `zkspringjpaexample` ( `ID` bigint(20) NOT NULL auto_increment, `FirstName` varchar(100) default NULL, `LastName` varchar(100) default NULL, `LoginName` varchar(100) default NULL, PRIMARY KEY (`ID`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1
Step 3:
Now let us create ZK Maven Project. By default POM File will point ZK 6.0.1 Version. we will change this to latest version 6.5.0 and also we will include all the dependency. You can copy the below 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>ZKSpringJPA</groupId> <artifactId>ZKSpringJPA</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <zk.version>6.5.0</zk.version> <commons-io>1.3.1</commons-io> <maven.build.timestamp.format>yyyy-MM-dd</maven.build.timestamp.format> <packname>-${project.version}-FL-${maven.build.timestamp}</packname> <jsr330.version>1</jsr330.version> <spring-data-jpa.version>1.0.3.RELEASE</spring-data-jpa.version> <servlet-api.version>2.4</servlet-api.version> <slf4j.version>1.6.1</slf4j.version> </properties> <packaging>war</packaging> <name>The ZKExamples Project</name> <description>The ZKExamples Project</description> <licenses> <license> <name>GNU LESSER GENERAL PUBLIC LICENSE, Version 3</name> <url>http://www.gnu.org/licenses/lgpl.html</url> <distribution>repo</distribution> </license> </licenses> <repositories> <repository> <id>ZK CE</id> <name>ZK CE Repository</name> <url>http://mavensync.zkoss.org/maven2</url> </repository> <repository> <id>ZK EVAL</id> <name>ZK Evaluation Repository</name> <url>http://mavensync.zkoss.org/eval</url> </repository> </repositories> <pluginRepositories> <pluginRepository> <id>zkmaven</id> <name>ZK Maven Plugin Repository</name> <url>http://mavensync.zkoss.org/maven2/</url> </pluginRepository> </pluginRepositories> <dependencies> <dependency> <groupId>org.zkoss.zk</groupId> <artifactId>zkbind</artifactId> <version>${zk.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-simple</artifactId> <version>${slf4j.version}</version> <scope>runtime</scope> </dependency> <dependency> <groupId>org.zkoss.zk</groupId> <artifactId>zul</artifactId> <version>${zk.version}</version> </dependency> <dependency> <groupId>org.zkoss.zk</groupId> <artifactId>zkplus</artifactId> <version>${zk.version}</version> </dependency> <dependency> <groupId>org.zkoss.zk</groupId> <artifactId>zhtml</artifactId> <version>${zk.version}</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>${commons-io}</version> </dependency> <!-- ZK 5 breeze theme <dependency> <groupId>org.zkoss.theme</groupId> <artifactId>breeze</artifactId> <version>${zk.version}</version> <optional>true</optional> </dependency> --> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>2.0</version> </dependency> <dependency> <groupId>commons-beanutils</groupId> <artifactId>commons-beanutils</artifactId> <version>1.8.3</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-core</artifactId> <version>4.1.9.Final</version> </dependency> <dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-entitymanager</artifactId> <version>4.1.9.Final</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>3.2.0.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>3.2.0.RELEASE</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.22</version> </dependency> <dependency> <groupId>org.springframework.data</groupId> <artifactId>spring-data-jpa</artifactId> <version>${spring-data-jpa.version}</version> <exclusions> <exclusion> <artifactId>slf4j-api</artifactId> <groupId>org.slf4j</groupId> </exclusion> </exclusions> </dependency> </dependencies> <build> <finalName>${project.artifactId}</finalName> <plugins> <!-- Run with Jetty --> <plugin> <groupId>org.mortbay.jetty</groupId> <artifactId>maven-jetty-plugin</artifactId> <version>6.1.10</version> <configuration> <scanIntervalSeconds>5</scanIntervalSeconds> <stopKey>foo</stopKey> <stopPort>9999</stopPort> </configuration> <executions> <execution> <id>start-jetty</id> <phase>pre-integration-test</phase> <goals> <goal>run</goal> </goals> <configuration> <scanIntervalSeconds>0</scanIntervalSeconds> <daemon>true</daemon> </configuration> </execution> <execution> <id>stop-jetty</id> <phase>post-integration-test</phase> <goals> <goal>stop</goal> </goals> </execution> </executions> </plugin> <!-- Compile java --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>2.3.2</version> <configuration> <source>1.6</source> <target>1.6</target> </configuration> </plugin> <!-- Build war --> <plugin> <artifactId>maven-war-plugin</artifactId> <groupId>org.apache.maven.plugins</groupId> <version>2.1.1</version> </plugin> <!-- Pack zips --> <plugin> <artifactId>maven-assembly-plugin</artifactId> <version>2.2</version> <executions> <execution> <id>webapp</id> <phase>package</phase> <goals> <goal>single</goal> </goals> <configuration> <finalName>ZKSpringJPA${packname}</finalName> <appendAssemblyId>false</appendAssemblyId> <descriptors> <descriptor>assembly/webapp.xml</descriptor> </descriptors> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>
Step 4:
Now let us create our domain object as followspackage zkMVVM.zKMVVMSpringJPA; import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; @Entity @Table(name = "zkspringjpaexample") public class ZkSpringJpaExample { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private long id; @Column(name = "firstName") private String firstName; @Column(name = "LastName") private String lastName; @Column(name = "loginName") private String loginName; public long getId() { return id; } public void setId(long id) { this.id = id; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } public String getLoginName() { return loginName; } public void setLoginName(String loginName) { this.loginName = loginName; } }
Step 5:
Now let us create Service and DAO Layers as follows.ZkSpringJpaExampleService.java
package zkMVVM.zKMVVMSpringJPA; import java.util.List; public interface ZkSpringJpaExampleService { public List<ZkSpringJpaExample> findAll(); }
package zkMVVM.zKMVVMSpringJPA; import java.util.List; import org.springframework.transaction.annotation.Transactional; public class ZkSpringJpaExampleServiceImpl implements ZkSpringJpaExampleService { private ZkSpringJpaExampleDao ZkSpringJpaExampleDao; @Transactional(readOnly=true) public List<ZkSpringJpaExample> findAll() { return ZkSpringJpaExampleDao.findAll(); } public ZkSpringJpaExampleDao getZkSpringJpaExampleDao() { return ZkSpringJpaExampleDao; } public void setZkSpringJpaExampleDao(ZkSpringJpaExampleDao zkSpringJpaExampleDao) { ZkSpringJpaExampleDao = zkSpringJpaExampleDao; } }
ZkSpringJpaExampleDao.java
package zkMVVM.zKMVVMSpringJPA; import java.util.List; public interface ZkSpringJpaExampleDao { public List<ZkSpringJpaExample> findAll(); }
package zkMVVM.zKMVVMSpringJPA; import java.util.LinkedList; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.Query; import org.springframework.stereotype.Repository; @Repository public class ZkSpringJpaExampleDaoImpl implements ZkSpringJpaExampleDao { @PersistenceContext private EntityManager em; @SuppressWarnings("unchecked") public List<ZkSpringJpaExample> findAll() { List<ZkSpringJpaExample> exList = new LinkedList<ZkSpringJpaExample>(); Query query = em.createQuery("from ZkSpringJpaExample as ex"); exList = query.getResultList(); return exList; } }
Step 6:
Next step to create the ZUL File and its VM File as followszkspringjpaexampleListing.zul
<zk> <window id="zkspringjpaexampleListing" title="Users List" height="90%" border="normal" apply="org.zkoss.bind.BindComposer" viewModel="@id('vm') @init('zkMVVM.zKMVVMSpringJPA.ZKSpringJPAExampleListVM')"> <separator /> <listbox id="" mold="paging" pageSize="11" pagingPosition="top" selectedItem="@bind(vm.selectedItem)" model="@load(vm.examplesListData)"> <listhead sizable="true"> <listheader label="First Name" sort="auto(firstName)" /> <listheader label="Loast Name" sortDirection="ascending" sort="auto(lastName)" /> <listheader label="Login Name" /> </listhead> <template name="model" var="p1"> <listitem> <listcell label="@load(p1.firstName)" /> <listcell label="@load(p1.lastName)" /> <listcell label="@load(p1.loginName)" /> </listitem> </template> </listbox> </window> </zk>
ZKSpringJPAExampleListVM.java
package zkMVVM.zKMVVMSpringJPA; import java.util.List; import org.zkoss.bind.annotation.AfterCompose; import org.zkoss.bind.annotation.ContextParam; import org.zkoss.bind.annotation.ContextType; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.select.Selectors; import org.zkoss.zk.ui.select.annotation.VariableResolver; import org.zkoss.zk.ui.select.annotation.WireVariable; import org.zkoss.zul.ListModelList; @VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver.class) public class ZKSpringJPAExampleListVM { @WireVariable private ZkSpringJpaExampleService ZkSpringJpaExampleService; private ZkSpringJpaExample selectedItem; public ZkSpringJpaExample getSelectedItem() { return selectedItem; } public void setSelectedItem(ZkSpringJpaExample selectedItem) { this.selectedItem = selectedItem; } @AfterCompose public void initSetup(@ContextParam(ContextType.VIEW) Component view) { Selectors.wireComponents(view, this, false); } public List<ZkSpringJpaExample> getexamplesListData() { List<ZkSpringJpaExample> result = ZkSpringJpaExampleService.findAll(); return new ListModelList<ZkSpringJpaExample>(result); } }
Step 7:
Next step to create the persistence.xml file as follows. Here I have created under the resource folder, so make sure that this folder is added in the build path.<?xml version="1.0" encoding="UTF-8"?> <persistence xmlns="http://java.sun.com/xml/ns/persistence" version="2.0"> <persistence-unit name="ZKSpringJPA" transaction-type="RESOURCE_LOCAL"> <provider>org.hibernate.ejb.HibernatePersistence</provider> <properties> <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/> <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver" /> <property name="hibernate.connection.username" value="root" /> <property name="hibernate.connection.password" value="1234" /> <property name="hibernate.show_sql" value="true" /> <property name="hibernate.connection.url" value="jdbc:mysql://localhost:3306/ZKExamples" /> </properties> </persistence-unit> </persistence>
Step 8:
Next step to create the applicationContext.xml in WEB-INF Folder as follows<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://localhost:3306/ZKExamples" /> <property name="username" value="root" /> <property name="password" value="1234" /> </bean> <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalEntityManagerFactoryBean"> <property name="persistenceUnitName" value="ZKSpringJPA" /> </bean> <bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager"> <property name="entityManagerFactory" ref="entityManagerFactory" /> </bean> <bean id="ZkSpringJpaExampleDao" class="zkMVVM.zKMVVMSpringJPA.ZkSpringJpaExampleDaoImpl" /> <bean id="ZkSpringJpaExampleService" class="zkMVVM.zKMVVMSpringJPA.ZkSpringJpaExampleServiceImpl"> <property name="ZkSpringJpaExampleDao" ref="ZkSpringJpaExampleDao" /> </bean> <tx:annotation-driven /> <context:annotation-config /> </beans>
Step 9:
Finally we will refer the applicationContext.xml in the web.xml as follows<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"> <description><![CDATA[My ZK Application]]></description> <display-name>ZKSpringJPA</display-name> <context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/applicationContext.xml </param-value> </context-param> <!-- Loads the Spring web application context --> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener> <!-- Spring filter --> <filter> <filter-name>OpenEntityManagerInViewFilter</filter-name> <filter-class>org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class> </filter> <filter-mapping> <filter-name>OpenEntityManagerInViewFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <!-- //// --> <!-- //// --> <!-- ZK --> <listener> <description>ZK listener for session cleanup</description> <listener-class>org.zkoss.zk.ui.http.HttpSessionListener</listener-class> </listener> <servlet> <description>ZK loader for ZUML pages</description> <servlet-name>zkLoader</servlet-name> <servlet-class>org.zkoss.zk.ui.http.DHtmlLayoutServlet</servlet-class> <!-- Must. Specifies URI of the update engine (DHtmlUpdateServlet). It must be the same as <url-pattern> for the update engine. --> <init-param> <param-name>update-uri</param-name> <param-value>/zkau</param-value> </init-param> <!-- Optional. Specifies whether to compress the output of the ZK loader. It speeds up the transmission over slow Internet. However, if you configure a filter to post-processing the output, you might have to disable it. Default: true <init-param> <param-name>compress</param-name> <param-value>true</param-value> </init-param> --> <!-- [Optional] Specifies the default log level: OFF, ERROR, WARNING, INFO, DEBUG and FINER. If not specified, the system default is used. <init-param> <param-name>log-level</param-name> <param-value>OFF</param-value> </init-param> --> <load-on-startup>1</load-on-startup><!-- Must --> </servlet> <servlet-mapping> <servlet-name>zkLoader</servlet-name> <url-pattern>*.zul</url-pattern> </servlet-mapping> <servlet-mapping> <servlet-name>zkLoader</servlet-name> <url-pattern>*.zhtml</url-pattern> </servlet-mapping> <!-- [Optional] Uncomment it if you want to use richlets. <servlet-mapping> <servlet-name>zkLoader</servlet-name> <url-pattern>/zk/*</url-pattern> </servlet-mapping> --> <servlet> <description>The asynchronous update engine for ZK</description> <servlet-name>auEngine</servlet-name> <servlet-class>org.zkoss.zk.au.http.DHtmlUpdateServlet</servlet-class> <!-- [Optional] Specifies whether to compress the output of the ZK loader. It speeds up the transmission over slow Internet. However, if your server will do the compression, you might have to disable it. Default: true <init-param> <param-name>compress</param-name> <param-value>true</param-value> </init-param> --> <!-- [Optional] Specifies the AU extension for particular prefix. <init-param> <param-name>extension0</param-name> <param-value>/upload=com.my.MyUploader</param-value> </init-param> --> </servlet> <servlet-mapping> <servlet-name>auEngine</servlet-name> <url-pattern>/zkau/*</url-pattern> </servlet-mapping> <!-- [Optional] Uncomment if you want to use the ZK filter to post process the HTML output generated by other technology, such as JSP and velocity. <filter> <filter-name>zkFilter</filter-name> <filter-class>org.zkoss.zk.ui.http.DHtmlLayoutFilter</filter-class> <init-param> <param-name>extension</param-name> <param-value>html</param-value> </init-param> <init-param> <param-name>compress</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>zkFilter</filter-name> <url-pattern>your URI pattern</url-pattern> </filter-mapping> --> <!-- //// --> <!-- ///////////// --> <!-- DSP (optional) Uncomment it if you want to use DSP However, it is turned on since zksandbox uses DSP to generate CSS. <servlet> <servlet-name>dspLoader</servlet-name> <servlet-class>org.zkoss.web.servlet.dsp.InterpreterServlet</servlet-class> <init-param> <param-name>class-resource</param-name> <param-value>true</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>dspLoader</servlet-name> <url-pattern>*.dsp</url-pattern> </servlet-mapping> --> <!-- /////////// --> <!-- [Optional] Session timeout --> <session-config> <session-timeout>60</session-timeout> </session-config> <!-- [Optional] MIME mapping --> <mime-mapping> <extension>doc</extension> <mime-type>application/vnd.ms-word</mime-type> </mime-mapping> <mime-mapping> <extension>gif</extension> <mime-type>image/gif</mime-type> </mime-mapping> <mime-mapping> <extension>htm</extension> <mime-type>text/html</mime-type> </mime-mapping> <mime-mapping> <extension>html</extension> <mime-type>text/html</mime-type> </mime-mapping> <mime-mapping> <extension>jpeg</extension> <mime-type>image/jpeg</mime-type> </mime-mapping> <mime-mapping> <extension>jpg</extension> <mime-type>image/jpeg</mime-type> </mime-mapping> <mime-mapping> <extension>js</extension> <mime-type>text/javascript</mime-type> </mime-mapping> <mime-mapping> <extension>pdf</extension> <mime-type>application/pdf</mime-type> </mime-mapping> <mime-mapping> <extension>png</extension> <mime-type>image/png</mime-type> </mime-mapping> <mime-mapping> <extension>txt</extension> <mime-type>text/plain</mime-type> </mime-mapping> <mime-mapping> <extension>xls</extension> <mime-type>application/vnd.ms-excel</mime-type> </mime-mapping> <mime-mapping> <extension>xml</extension> <mime-type>text/xml</mime-type> </mime-mapping> <mime-mapping> <extension>zhtml</extension> <mime-type>text/html</mime-type> </mime-mapping> <mime-mapping> <extension>zul</extension> <mime-type>text/html</mime-type> </mime-mapping> <welcome-file-list> <welcome-file>index.zul</welcome-file> <welcome-file>index.zhtml</welcome-file> <welcome-file>index.html</welcome-file> <welcome-file>index.htm</welcome-file> <welcome-file>zkspringjpaexampleListing.zul</welcome-file> </welcome-file-list> </web-app>
Finally, you can run and see the output as follows
You can download the source here
Step 10:
In the above example, we have used XML Configuration to initialize our bean. Normally you declare all the beans or components in XML bean configuration file, so that Spring container can detect and register your beans or components. Actually, Spring is able to auto scan, detect and instantiate your beans from pre-defined project package, no more tedious beans declaration in in XML file.This can be achieved by using context:component-scan element in xml configuration file
References
References
- http://www.mkyong.com/spring/spring-auto-scanning-components/
- www.techferry.com/articles/spring-annotations.html
Now let us convert the above example to Spring Annotation. Thanks to gekkio who helped me to achieved this
There is no change in zul file and its VM. And also there is no change in the interface. We need to just add @Service and @Autowired annotation in the ZkSpringJpaExampleServiceImpl.java file as shown here.
import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.zkoss.zul.Messagebox; @Service public class ZkSpringJpaExampleServiceImpl implements ZkSpringJpaExampleService { @Autowired private ZkSpringJpaExampleDao zkSpringJpaExampleDao;
Now we need to change our applicationContext.xml. Remove the manual declaration of the bean and include auto scan feature.
context:component-scan base-package="zkMVVM.zKMVVMSpringJPA">
</context:component-scan>
In the ZKSpringJPAExampleListVM.java, we have declared as follows
@WireVariable
private ZkSpringJpaExampleService zkSpringJpaExampleService;
ZK @WireVariable injects by name, not by type, so the variable name must be an exact match to the bean name. Note that Spring @Autowired by default injects by type, so the @Autowired variable name can be anything and it will work. So additionally we need to match the name in the xml file as follows
<bean id="zkSpringJpaExampleService" class="zkMVVM.zKMVVMSpringJPA.ZkSpringJpaExampleServiceImpl" />
Alternate method as follows
You can customize names by adding them to the annotations. For example:
@Service("zkSpringJpaExampleService")
public class ZkSpringJpaExampleServiceImpl ...
Now the bean gets registered as "zkSpringJpaExampleService", so that should be the variable name:
@WireVariable
private ZkSpringJpaExampleService zkSpringJpaExampleService;
You can download the source here