Next we move onto how we use the generated classes in the post below to develop a hibernate application that demonstrates many to one bidirectional relationship. We have a little bit of spring but the highlight is to demonstrate hibernate capabilities.
First we would need to create a database with the following DDL for mySQL:
script
drop database hibemappings;
create database hibemappings;
use hibemappings;
create table Book (isbn_code bigint(3) not null auto_increment, book_title varchar(30), primary key(isbn_code));
create table Author (author_id bigint(3) not null auto_increment, isbn_code_in_author bigint(3), first_name varchar(30), last_name varchar(30), primary key(author_id), foreign Key (isbn_code_in_author) references Book(isbn_code));
The workspace for the project has the following structure on eclipse:
<project>
<com.mattiz.persistence.beans>
...copy over the generated domain files from previous post
BookWithMultipleAuthors.java
ContributingAuthor.java
<com.mattiz.persistnece.data>
MappingDAO.java
MappingDAOImpl.java
<com.mattiz.service.spring>
AppInvoker.java
<resources>
author.hbm.xml(same mapping file used to generate domain files as in previous post)
book.hbm.xml(same mapping file used to generate domain files as in previous post)
mattiz.xml(spring configuration file)
I have put most of the jars in this example on the classpath with a minor change. In addition I have added \spring-framework-2.5.4-with-dependencies\spring-framework-2.5.4\lib\cglib\cglib-nodep-2.1_3.jar also on the classpath.
Copy over the domain classes generated by the hbm2 java and the hbml.xml files to the location shown above. (See previous post)
Note the lazy=false in both hibernate mapping files at two locations by which we enforce eager fetch.
mattiz.xml is the spring configuration file which provides the Spring's hibernate support to our DAO:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="mattiz.hibernate.dataSource"
class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass" value="com.mysql.jdbc.Driver" />
<property name="jdbcUrl"
value="jdbc:mysql://localhost:3306/hibemappings" />
<property name="user" value="root" />
<property name="password" value="admin" />
</bean>
<bean id="mattiz.abstract.hibernate.sessionfactory"
class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"
abstract="true" lazy-init="true">
<property name="dataSource">
<ref local="mattiz.hibernate.dataSource" />
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</prop>
<prop key="hibernate.show_sql">false</prop>
</props>
</property>
</bean>
<bean id="mattiz.pageSize" class="java.lang.Integer">
<constructor-arg value="5000"></constructor-arg>
</bean>
<bean id="mattiz.hibernate.sessionfactory"
parent="mattiz.abstract.hibernate.sessionfactory">
<property name="mappingResources">
<list>
<value>resources/author.hbm.xml</value>
<value>resources/book.hbm.xml</value>
</list>
</property>
</bean>
<bean id="mattiz.transactionManager"
class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory">
<ref bean="mattiz.hibernate.sessionfactory" />
</property>
</bean>
<bean id="mattiz.hibernateTemplate"
class="org.springframework.orm.hibernate3.HibernateTemplate">
<property name="sessionFactory">
<ref bean="mattiz.hibernate.sessionfactory" />
</property>
</bean>
<bean id="mattiz.mattizDAO"
class="com.mattiz.persistence.data.MappingDAOImpl"
singleton="true">
<property name="hibernateTemplate">
<ref bean="mattiz.hibernateTemplate" />
</property>
</bean>
</beans>
We write the following java classes to demonstrate the many to one bidirectional relationship:
MappingDAO.java
package com.mattiz.persistence.data;
import java.util.Set;
import com.mattiz.persistence.beans.BookWithMultipleAuthors;
import com.mattiz.persistence.beans.ContributingAuthor;
public interface MappingDAO {
public Set<ContributingAuthor> getContributingAuthors(long isbnCode);
public BookWithMultipleAuthors getBookForAuthor(long authorId);
public void initializeDB();
}
MappingDAOImpl.java
package com.mattiz.persistence.data;
import java.util.Iterator;
import java.util.Random;
import java.util.Set;
import org.springframework.orm.hibernate3.HibernateTemplate;
import com.mattiz.persistence.beans.BookWithMultipleAuthors;
import com.mattiz.persistence.beans.ContributingAuthor;
public class MappingDAOImpl implements MappingDAO {
private HibernateTemplate hibernateTemplate;
public HibernateTemplate getHibernateTemplate() {
return hibernateTemplate;
}
public void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
this.hibernateTemplate = hibernateTemplate;
}
public Set<ContributingAuthor> getContributingAuthors(long isbnCode) {
BookWithMultipleAuthors bookWithMultipleAuthors = (BookWithMultipleAuthors) getHibernateTemplate()
.load(BookWithMultipleAuthors.class, isbnCode);
Set<ContributingAuthor> authors = bookWithMultipleAuthors
.getContributingAuthors();
Iterator<ContributingAuthor> i = authors.iterator();
while (i.hasNext()) {
System.out.println("Author "
+ ((ContributingAuthor) i.next()).getFirstName());
}
return authors;
}
public BookWithMultipleAuthors getBookForAuthor(long authorId) {
ContributingAuthor contributingAuthor = (ContributingAuthor) getHibernateTemplate()
.load(ContributingAuthor.class, authorId);
BookWithMultipleAuthors book = contributingAuthor
.getBookContributedToByAuthor();
System.out.println("BOOK " + book.getTitle());
return book;
}
public void initializeDB() {//fill up db with random named AUTHORs and BOOKs
HibernateTemplate template = getHibernateTemplate();
BookWithMultipleAuthors bookEntity = new BookWithMultipleAuthors();
Random random = new Random();
int ran = generateRandomInteger(65, 74, random);//boiler plate code
bookEntity.setTitle(new String(new char[] { (char) ran,
(char) (ran + 3), (char) (ran + 10), (char) (ran + 15) }));
template.saveOrUpdate(bookEntity);
System.out.println("Inserted Book, ISBN, Title: "
+ bookEntity.getIsbnCode() + " " + bookEntity.getTitle());
for (int i = 0; i < 10; i++) {
ran = generateRandomInteger(65, 85, random);
ContributingAuthor contributingAuthor = newContributingAuthor(ran,
bookEntity);
System.out.println("Inserted Author, First and last name: "
+ contributingAuthor.getFirstName() + " "
+ contributingAuthor.getLastName());
}
}
private ContributingAuthor newContributingAuthor(int i,
BookWithMultipleAuthors bookEntity) {
HibernateTemplate template = getHibernateTemplate();
ContributingAuthor contributingAuthor = new ContributingAuthor();
contributingAuthor.setBookId(i);
contributingAuthor.setFirstName(new String(new char[] { (char) (i + 2),
(char) (i + 1), (char) (i + 3) }));
contributingAuthor.setLastName(new String(new char[] { (char) (i + 3),
(char) (i + 2), (char) (i + 4) }));
contributingAuthor.setBookContributedToByAuthor(bookEntity);
template.save(contributingAuthor);
return contributingAuthor;
}
private static int generateRandomInteger(int aStart, int aEnd,
Random aRandom) {//boiler plate code to generate ascii code for A-Z
if (aStart > aEnd) {
throw new IllegalArgumentException("Start cannot exceed End.");
}
long range = (long) aEnd - (long) aStart + 1;
long fraction = (long) (range * aRandom.nextDouble());
int randomNumber = (int) (fraction + aStart);
return randomNumber;
}
}
AppInvoker.java
package com.mattiz.service.spring;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.mattiz.persistence.data.MappingDAO;
import com.mattiz.persistence.data.MappingDAOImpl;
public class AppInvoker {
private ApplicationContext applicationContext;
private MappingDAO mappingDAOImpl;
public static void main(String[] args) {
AppInvoker appInvoker = new AppInvoker();
appInvoker.setApplicationContext(new ClassPathXmlApplicationContext(
"resources/mattiz.xml"));
appInvoker.getMappingDAO().initializeDB();
appInvoker.getMappingDAO().getContributingAuthors(2L);//demonstrates one to many
appInvoker.getMappingDAO().getBookForAuthor(11);//demonstrates many to one
}
public MappingDAO getMappingDAO() {
mappingDAOImpl = (MappingDAOImpl) applicationContext
.getBean("mattiz.mattizDAO");
return mappingDAOImpl;
}
public void setMapppingDAO(MappingDAO mappingDAOImpl) {
this.mappingDAOImpl = mappingDAOImpl;
}
public ApplicationContext getApplicationContext() {
return applicationContext;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
}
On running the main class in AppInvoker.java you may see an output similar to this on the console:
Inserted Book, ISBN, Title: 9 GJQV
Inserted Author, First and last name: NMO ONP
Inserted Author, First and last name: RQS SRT
Inserted Author, First and last name: JIK KJL
Inserted Author, First and last name: LKM MLN
Inserted Author, First and last name: HGI IHJ
Inserted Author, First and last name: CBD DCE
Inserted Author, First and last name: RQS SRT
Inserted Author, First and last name: UTV VUW
Inserted Author, First and last name: LKM MLN
Inserted Author, First and last name: GFH HGI
Author HGI
Author LKM
Author QPR
Author POQ
Author ONP
Author FEG
Author TSU
Author GFH
Author EDF
Author MLN
BOOK EHOT
The database might look something like this - verify the mapping->
mysql> select * from book;
+-----------+------------+
| isbn_code | book_title |
+-----------+------------+
| 1 | ILSX |
| 2 | EHOT |
| 3 | CFMR |
| 4 | FIPU |
| 5 | GJQV |
| 6 | EHOT |
| 7 | HKRW |
| 8 | BELQ |
| 9 | GJQV |
+-----------+------------+
9 rows in set (0.00 sec)
mysql> select * from author;
+-----------+---------------------+------------+-----------+
| author_id | isbn_code_in_author | first_name | last_name |
+-----------+---------------------+------------+-----------+
| 1 | 1 | CBD | DCE |
| 2 | 1 | MLN | NMO |
| 3 | 1 | MLN | NMO |
| 4 | 1 | GFH | HGI |
| 5 | 1 | FEG | GFH |
| 6 | 1 | ONP | POQ |
| 7 | 1 | VUW | WVX |
| 8 | 1 | RQS | SRT |
| 9 | 1 | GFH | HGI |
| 10 | 1 | FEG | GFH |
| 11 | 2 | FEG | GFH |
| 12 | 2 | QPR | RQS |
| 13 | 2 | HGI | IHJ |
| 14 | 2 | LKM | MLN |
| 15 | 2 | TSU | UTV |
| 16 | 2 | MLN | NMO |
| 17 | 2 | ONP | POQ |
| 18 | 2 | GFH | HGI |
| 19 | 2 | EDF | FEG |
| 20 | 2 | POQ | QPR |
| 21 | 3 | LKM | MLN |
| 22 | 3 | VUW | WVX |
| 23 | 3 | DCE | EDF |
| 24 | 3 | ONP | POQ |
| 25 | 3 | UTV | VUW |
| 26 | 3 | DCE | EDF |
| 27 | 3 | SRT | TSU |
| 28 | 3 | IHJ | JIK |
| 29 | 3 | NMO | ONP |
| 30 | 3 | SRT | TSU |
| 31 | 4 | RQS | SRT |
| 32 | 4 | MLN | NMO |
| 33 | 4 | RQS | SRT |
| 34 | 4 | FEG | GFH |
| 35 | 4 | HGI | IHJ |
| 36 | 4 | EDF | FEG |
| 37 | 4 | GFH | HGI |
| 38 | 4 | VUW | WVX |
| 39 | 4 | LKM | MLN |
| 40 | 4 | SRT | TSU |
Monday, January 30, 2012
Domain java files from hibernate mapping files
This post is related to the discussion on hibernate mappings below.
To take the discussion forward I have created two projects and I shall be dealing with each project in two separate posts.
This post is related to how the hbm2java task creates domain objects mapped to the database on the fly, from hbm.xml files.
The project structure on Eclipse looks like this:
<project>
<generated/src>
author.hbm.xml
book.hbml.xml
<com.mattiz.persistence.beans>
<classes generated by hbm2java.....>
BooksWithMultipleAuthors.java
ContributingAuthors.java
build.xml
hibernate.properties
log4j.properties
<classpath - jars>
commons-collections.jar
commons-logging.jar
dom4j-1.6.1.jar
freemarker.jar
hibernate3.jar
log4j-1.2.15.jar
hibernate-annotation.jar
xercesImpl.jar
xml-apis.jar
hibernate-tools.jar
build.xml
<?xml version="1.0" encoding="UTF-8"?>
<project default="codegen">
<target name="codegen">
<path id="classpath_id">
<pathelement location="I:/packages/spring-framework-2.5.4-with-dependencies/spring-framework-2.5.4/lib/jakarta-commons/commons-collections.jar" />
<pathelement location="I:/packages/spring-framework-2.5.4-with-dependencies/spring-framework-2.5.4/lib/jakarta-commons/commons-logging.jar" />
<pathelement location="I:/packages/spring-framework-2.5.4-with-dependencies/spring-framework-2.5.4/lib/dom4j/dom4j-1.6.1.jar" />
<pathelement location="I:/packages/spring-framework-2.5.4-with-dependencies/spring-framework-2.5.4/lib/freemarker/freemarker.jar" />
<pathelement location="I:/packages/spring-framework-2.5.4-with-dependencies/spring-framework-2.5.4/lib/hibernate/hibernate3.jar" />
<pathelement location="I:/packages/spring-framework-2.5.4-with-dependencies/spring-framework-2.5.4/lib/log4j/log4j-1.2.15.jar" />
<pathelement location="I:/packages/spring-framework-2.5.4-with-dependencies/spring-framework-2.5.4/lib/hibernate/hibernate-annotations.jar" />
<pathelement location="I:/packages/hibernate-tools.jar/hibernate-tools.jar" />
<pathelement location="I:/packages/hsqldb-1_8_0_10.jar" />
</path>
<echo>Zippzip</echo>
<taskdef name="hibernatetool" classname="org.hibernate.tool.ant.HibernateToolTask">
<classpath refid="classpath_id" />
</taskdef>
<hibernatetool destdir="generated/src">
<configuration propertyfile="./hibernate.properties">
<fileset dir=".">
<include name="generated/src/*.hbm.xml" />
</fileset>
</configuration>
<hbm2java />
</hibernatetool>
</target>
</project>
author.hbml.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="com.mattiz.persistence.beans">
<class name="ContributingAuthor" table="Author" lazy="false">
<id name="bookId" column="author_id" type="long">
<generator class="native" />
</id>
<property name="firstName" type="string" length="20" column="first_name" />
<property name="lastName" type="string" length="20" column="last_name" />
<many-to-one name="bookContributedToByAuthor" column="isbn_code_in_author"
class="BookWithMultipleAuthors" not-null="true" lazy="false"/>
</class>
</hibernate-mapping>
book.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="com.mattiz.persistence.beans">
<class name="BookWithMultipleAuthors" table="Book" lazy="false">
<id name="isbnCode" column="isbn_code" type="long">
<generator class="native" />
</id>
<property name="title" type="string" length="20" column="book_title" />
<set name="contributingAuthors" inverse="true" lazy="false">
<key column="isbn_code_in_author" />
<one-to-many class="ContributingAuthor" />
</set>
</class>
</hibernate-mapping>
hibernate.properties
hibernate.connection.driver_class=org.hsqldb.jdbcDriver
hibernate.connection.url=jdbc\:hsqldb\:hsql\://localhost
hibernate.connection.username=user
hibernate.connection.password=user
hibernate.connection.pool_size=2
#
hibernate.dialect=net.sf.hibernate.dialect.HSQLDialect
log4j.properties
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### direct messages to file hibernate.log ###
#log4j.appender.file=org.apache.log4j.FileAppender
#log4j.appender.file.File=hibernate.log
#log4j.appender.file.layout=org.apache.log4j.PatternLayout
#log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### set log levels - for more verbose logging change 'info' to 'debug' ###
log4j.rootLogger=warn, stdout
log4j.logger.net.sf.hibernate=info
### log just the SQL
log4j.logger.net.sf.hibernate.SQL=debug
### log JDBC bind parameters ###
log4j.logger.net.sf.hibernate.type=info
### log schema export/update ###
log4j.logger.net.sf.hibernate.tool.hbm2ddl=debug
### log cache activity ###
log4j.logger.net.sf.hibernate.cache=debug
### enable the following line if you want to track down connection ###
### leakages when using DriverManagerConnectionProvider ###
log4j.logger.net.sf.hibernate.connection.DriverManagerConnectionProvider=trace
When you run build.xml, the following two classes are generated by hbm2java utility
package com.mattiz.persistence.beans;
// Generated 31 Jan by Hibernate Tools 3.2.2.GA
import java.util.HashSet;
import java.util.Set;
/**
* BookWithMultipleAuthors generated by hbm2java
*/
public class BookWithMultipleAuthors implements java.io.Serializable {
private long isbnCode;
private String title;
private Set contributingAuthors = new HashSet(0);
public BookWithMultipleAuthors() {
}
public BookWithMultipleAuthors(String title, Set contributingAuthors) {
this.title = title;
this.contributingAuthors = contributingAuthors;
}
public long getIsbnCode() {
return this.isbnCode;
}
public void setIsbnCode(long isbnCode) {
this.isbnCode = isbnCode;
}
public String getTitle() {
return this.title;
}
public void setTitle(String title) {
this.title = title;
}
public Set getContributingAuthors() {
return this.contributingAuthors;
}
public void setContributingAuthors(Set contributingAuthors) {
this.contributingAuthors = contributingAuthors;
}
}
package com.mattiz.persistence.beans;
// Generated 31 Jan by Hibernate Tools 3.2.2.GA
/**
* ContributingAuthor generated by hbm2java
*/
public class ContributingAuthor implements java.io.Serializable {
private long bookId;
private String firstName;
private String lastName;
private BookWithMultipleAuthors bookContributedToByAuthor;
public ContributingAuthor() {
}
public ContributingAuthor(BookWithMultipleAuthors bookContributedToByAuthor) {
this.bookContributedToByAuthor = bookContributedToByAuthor;
}
public ContributingAuthor(String firstName, String lastName, BookWithMultipleAuthors bookContributedToByAuthor) {
this.firstName = firstName;
this.lastName = lastName;
this.bookContributedToByAuthor = bookContributedToByAuthor;
}
public long getBookId() {
return this.bookId;
}
public void setBookId(long bookId) {
this.bookId = bookId;
}
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return this.lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public BookWithMultipleAuthors getBookContributedToByAuthor() {
return this.bookContributedToByAuthor;
}
public void setBookContributedToByAuthor(BookWithMultipleAuthors bookContributedToByAuthor) {
this.bookContributedToByAuthor = bookContributedToByAuthor;
}
}
To take the discussion forward I have created two projects and I shall be dealing with each project in two separate posts.
This post is related to how the hbm2java task creates domain objects mapped to the database on the fly, from hbm.xml files.
The project structure on Eclipse looks like this:
<project>
<generated/src>
author.hbm.xml
book.hbml.xml
<com.mattiz.persistence.beans>
<classes generated by hbm2java.....>
BooksWithMultipleAuthors.java
ContributingAuthors.java
build.xml
hibernate.properties
log4j.properties
<classpath - jars>
commons-collections.jar
commons-logging.jar
dom4j-1.6.1.jar
freemarker.jar
hibernate3.jar
log4j-1.2.15.jar
hibernate-annotation.jar
xercesImpl.jar
xml-apis.jar
hibernate-tools.jar
build.xml
<?xml version="1.0" encoding="UTF-8"?>
<project default="codegen">
<target name="codegen">
<path id="classpath_id">
<pathelement location="I:/packages/spring-framework-2.5.4-with-dependencies/spring-framework-2.5.4/lib/jakarta-commons/commons-collections.jar" />
<pathelement location="I:/packages/spring-framework-2.5.4-with-dependencies/spring-framework-2.5.4/lib/jakarta-commons/commons-logging.jar" />
<pathelement location="I:/packages/spring-framework-2.5.4-with-dependencies/spring-framework-2.5.4/lib/dom4j/dom4j-1.6.1.jar" />
<pathelement location="I:/packages/spring-framework-2.5.4-with-dependencies/spring-framework-2.5.4/lib/freemarker/freemarker.jar" />
<pathelement location="I:/packages/spring-framework-2.5.4-with-dependencies/spring-framework-2.5.4/lib/hibernate/hibernate3.jar" />
<pathelement location="I:/packages/spring-framework-2.5.4-with-dependencies/spring-framework-2.5.4/lib/log4j/log4j-1.2.15.jar" />
<pathelement location="I:/packages/spring-framework-2.5.4-with-dependencies/spring-framework-2.5.4/lib/hibernate/hibernate-annotations.jar" />
<pathelement location="I:/packages/hibernate-tools.jar/hibernate-tools.jar" />
<pathelement location="I:/packages/hsqldb-1_8_0_10.jar" />
</path>
<echo>Zippzip</echo>
<taskdef name="hibernatetool" classname="org.hibernate.tool.ant.HibernateToolTask">
<classpath refid="classpath_id" />
</taskdef>
<hibernatetool destdir="generated/src">
<configuration propertyfile="./hibernate.properties">
<fileset dir=".">
<include name="generated/src/*.hbm.xml" />
</fileset>
</configuration>
<hbm2java />
</hibernatetool>
</target>
</project>
author.hbml.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="com.mattiz.persistence.beans">
<class name="ContributingAuthor" table="Author" lazy="false">
<id name="bookId" column="author_id" type="long">
<generator class="native" />
</id>
<property name="firstName" type="string" length="20" column="first_name" />
<property name="lastName" type="string" length="20" column="last_name" />
<many-to-one name="bookContributedToByAuthor" column="isbn_code_in_author"
class="BookWithMultipleAuthors" not-null="true" lazy="false"/>
</class>
</hibernate-mapping>
book.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="com.mattiz.persistence.beans">
<class name="BookWithMultipleAuthors" table="Book" lazy="false">
<id name="isbnCode" column="isbn_code" type="long">
<generator class="native" />
</id>
<property name="title" type="string" length="20" column="book_title" />
<set name="contributingAuthors" inverse="true" lazy="false">
<key column="isbn_code_in_author" />
<one-to-many class="ContributingAuthor" />
</set>
</class>
</hibernate-mapping>
hibernate.properties
hibernate.connection.driver_class=org.hsqldb.jdbcDriver
hibernate.connection.url=jdbc\:hsqldb\:hsql\://localhost
hibernate.connection.username=user
hibernate.connection.password=user
hibernate.connection.pool_size=2
#
hibernate.dialect=net.sf.hibernate.dialect.HSQLDialect
log4j.properties
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### direct messages to file hibernate.log ###
#log4j.appender.file=org.apache.log4j.FileAppender
#log4j.appender.file.File=hibernate.log
#log4j.appender.file.layout=org.apache.log4j.PatternLayout
#log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n
### set log levels - for more verbose logging change 'info' to 'debug' ###
log4j.rootLogger=warn, stdout
log4j.logger.net.sf.hibernate=info
### log just the SQL
log4j.logger.net.sf.hibernate.SQL=debug
### log JDBC bind parameters ###
log4j.logger.net.sf.hibernate.type=info
### log schema export/update ###
log4j.logger.net.sf.hibernate.tool.hbm2ddl=debug
### log cache activity ###
log4j.logger.net.sf.hibernate.cache=debug
### enable the following line if you want to track down connection ###
### leakages when using DriverManagerConnectionProvider ###
log4j.logger.net.sf.hibernate.connection.DriverManagerConnectionProvider=trace
When you run build.xml, the following two classes are generated by hbm2java utility
package com.mattiz.persistence.beans;
// Generated 31 Jan by Hibernate Tools 3.2.2.GA
import java.util.HashSet;
import java.util.Set;
/**
* BookWithMultipleAuthors generated by hbm2java
*/
public class BookWithMultipleAuthors implements java.io.Serializable {
private long isbnCode;
private String title;
private Set contributingAuthors = new HashSet(0);
public BookWithMultipleAuthors() {
}
public BookWithMultipleAuthors(String title, Set contributingAuthors) {
this.title = title;
this.contributingAuthors = contributingAuthors;
}
public long getIsbnCode() {
return this.isbnCode;
}
public void setIsbnCode(long isbnCode) {
this.isbnCode = isbnCode;
}
public String getTitle() {
return this.title;
}
public void setTitle(String title) {
this.title = title;
}
public Set getContributingAuthors() {
return this.contributingAuthors;
}
public void setContributingAuthors(Set contributingAuthors) {
this.contributingAuthors = contributingAuthors;
}
}
package com.mattiz.persistence.beans;
// Generated 31 Jan by Hibernate Tools 3.2.2.GA
/**
* ContributingAuthor generated by hbm2java
*/
public class ContributingAuthor implements java.io.Serializable {
private long bookId;
private String firstName;
private String lastName;
private BookWithMultipleAuthors bookContributedToByAuthor;
public ContributingAuthor() {
}
public ContributingAuthor(BookWithMultipleAuthors bookContributedToByAuthor) {
this.bookContributedToByAuthor = bookContributedToByAuthor;
}
public ContributingAuthor(String firstName, String lastName, BookWithMultipleAuthors bookContributedToByAuthor) {
this.firstName = firstName;
this.lastName = lastName;
this.bookContributedToByAuthor = bookContributedToByAuthor;
}
public long getBookId() {
return this.bookId;
}
public void setBookId(long bookId) {
this.bookId = bookId;
}
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return this.lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public BookWithMultipleAuthors getBookContributedToByAuthor() {
return this.bookContributedToByAuthor;
}
public void setBookContributedToByAuthor(BookWithMultipleAuthors bookContributedToByAuthor) {
this.bookContributedToByAuthor = bookContributedToByAuthor;
}
}
Friday, January 20, 2012
Hibernate Mappings With hbm.xml Files
SCENARIO: Many authors contribute to one book
• Unidirectional associations - Many-to-one
create table Author ( author_id bigint not null primary key, book_id bigint not null, first_name varchar, last_name varchar )
create table Book ( book_id bigint not null primary key )
Author.book_id is foreign key from Book.
author.hbml.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="com.mattiz.persistence.beans">
<class name="Author">
<id name="authorid" column="author_Id" type="long">
<generator class="native" />
</id>
<property name="firstName" type="string" length="20" column="first_name" />
<property name="lastName" type="string" length="20" column="last_name" />
<many-to-one name="book" column="book_Id" class="Book" not-null=”true”/>
</class>
</hibernate-mapping>
In the many-to-one tag the name is "book", so that means that if you use a getter it would be author.getBook() or if you use HQL author.book.
The many to one tag implies that book_id is a foreign key for this relationship
book.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="com.mattiz.persistence.beans">
<class name="Book">
<id name="bookid" column="book_id" type="long">
<generator class="native" />
</id>
</class>
</hibernate-mapping>
package com.mattiz.persistence.beans;
public class Author implements java.io.Serializable {
private long authorid;
private String firstName;
private String lastName;
private Book book;
public Author() {
}
public Author(Book book) {
this.book = book;
}
public Author(String firstName, String lastName, Book book) {
this.firstName = firstName;
this.lastName = lastName;
this.book = book;
}
public long getAuthorid() {
return this.authorid;
}
public void setAuthorid(long authorid) {
this.authorid = authorid;
}
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return this.lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Book getBook() {
return this.book;
}
public void setBook(Book book) {
this.book = book;
}
}
package com.mattiz.persistence.beans;
public class Book implements java.io.Serializable {
private long bookid;
public Book() {
}
public long getBookid() {
return this.bookid;
}
public void setBookid(long bookid) {
this.bookid = bookid;
}
}
• Bidirectionality in Many to One
Bidirectional many-to-one
create table Author ( author_id bigint not null primary key, book_id bigint not null, first_name varchar, last_name varchar )
create table Book ( book_id bigint not null primary key )
Author.book_id is foreign key from Book
author.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="com.mattiz.persistence.beans">
<class name="Author">
<id name="authorid" column="author_Id" type="long">
<generator class="native" />
</id>
<property name="firstName" type="string" length="20" column="first_name" />
<property name="lastName" type="string" length="20" column="last_name" />
<many-to-one name="book" column="book_Id" class="Book" not-null="true"/>
<!—book_id is FK in Author table from Book table->
</class>
</hibernate-mapping>
book.hbml.xml
<hibernate-mapping package="com.mattiz.persistence.beans">
<class name="Book">
<id name="bookid" column="book_id" type="long">
<generator class="native" />
</id>
<set name="authors" inverse="true"><!—book_id references column in Author table-->
<key column="book_id" />
<one-to-many class="Author" />
</set>
</class>
</hibernate-mapping>
package com.mattiz.persistence.beans;
public class Author implements java.io.Serializable {
private long authorid;
private String firstName;
private String lastName; private Book book;
public Author() {
}
public Author(Book book) {
this.book = book;
}
public Author(String firstName, String lastName, Book book) {
this.firstName = firstName;
this.lastName = lastName;
this.book = book;
}
public long getAuthorid() {
return this.authorid;
}
public void setAuthorid(long authorid) {
this.authorid = authorid;
}
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return this.lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Book getBook() {
return this.book;
}
public void setBook(Book book) {
this.book = book;
}
}
package com.mattiz.persistence.beans;
import java.util.HashSet;
import java.util.Set;
public class Book implements java.io.Serializable {
private long bookid;
private Set authors = new HashSet(0);
public Book() {
}
public Book(Set authors) {
this.authors = authors;
}
public long getBookid() {
return this.bookid;
}
public void setBookid(long bookid) {
this.bookid = bookid;
}
public Set getAuthors() {
return this.authors;
}
public void setAuthors(Set authors) {
this.authors = authors;
}
}
If author in book is many-to-one, book to author is one- to-many and is a set.
The FK to author is the primary key in book.
From book.hbm.xml:
<set name="authors" inverse="true">
<key column="book_id" />
<one-to-many class="Author" />
</set>
Let’s consider <key column="book_id" /> in book.hbm.xml.
Here the key book_id is in the other table, i.e. Author. So getAuthors() will use the book_id column (FK) in the Author table.
So Author has PK author_id and FK book_id while Book has PK book_id. This relationship gets the authors (looked up based on FK book_id) associated with that Book. So authors are looked up based on their FK which is book_id and you get multiple authors which is a set. Basically in a one to many the key has to be in the other table.
the fk in a many to one, the key is in this table
The underlying table structure has to match hibernate's definition else it will give you a runtime exception.
When you see <key> it means refer to the key in the table where it is an FK.
Here is a simplistic explanation of the references:
author.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="com.mattiz.persistence.beans">
<class name="ContributingAuthor">
<id name="id" column="id" type="long">
<generator class="native" />
</id>
<property name="firstName" type="string" length="20" column="first_name" />
<property name="lastName" type="string" length="20" column="last_name" />
<many-to-one name="bookContributedToByAuthor" column="book_Id_in_Author" class="BookWithMultipleAuthors" not-null="true" />
</class>
</hibernate-mapping>
book.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="com.mattiz.persistence.beans">
<class name="BookWithMultipleAuthors">
<id name="id" column="id" type="long">
<generator class="native" />
</id>
<set name="contributingAuthors" inverse="true">
<key column="book_Id_in_Author" />
<one-to-many class="ContributingAuthor" />
</set>
</class>
</hibernate-mapping>
Generated classes:
package com.mattiz.persistence.beans;
import java.util.HashSet;
import java.util.Set;
public class BookWithMultipleAuthors implements java.io.Serializable {
private long id;
private Set contributingAuthors = new HashSet(0);
public BookWithMultipleAuthors() {
}
public BookWithMultipleAuthors(Set contributingAuthors) {
this.contributingAuthors = contributingAuthors;
}
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
public Set getContributingAuthors() {
return this.contributingAuthors;
}
public void setContributingAuthors(Set contributingAuthors) {
this.contributingAuthors = contributingAuthors;
}
}
package com.mattiz.persistence.beans;
public class ContributingAuthor implements java.io.Serializable {
private long id;
private String firstName;
private String lastName;
private BookWithMultipleAuthors bookContributedToByAuthor;
public ContributingAuthor() {
}
public ContributingAuthor(BookWithMultipleAuthors bookContributedToByAuthor) {
this.bookContributedToByAuthor = bookContributedToByAuthor;
}
public ContributingAuthor(String firstName, String lastName, BookWithMultipleAuthors bookContributedToByAuthor) {
this.firstName = firstName;
this.lastName = lastName;
this.bookContributedToByAuthor = bookContributedToByAuthor;
}
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return this.lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public BookWithMultipleAuthors getBookContributedToByAuthor() {
return this.bookContributedToByAuthor;
}
public void setBookContributedToByAuthor(BookWithMultipleAuthors bookContributedToByAuthor) {
this.bookContributedToByAuthor = bookContributedToByAuthor;
}
}
Executing a one-to-many book.getAuthors() is the equivalent of writing this sql
select * from t_author where book_id=? Where book_id is the FK in t_author
The book_id would have to be already present in book as a rule via referential integrity
Executing a many to one author.getBook() is the equivalent of writing this sql
select * from t_book where book_id = ? where the parameter in the last query would be from the author table; which would return one (obviously) since you are querying by PK.
• Unidirectional one to one
create table Author( author_id bigint not null primary key, book_id bigint not null unique )
create table Book ( book_id bigint not null primary key )
A unidirectional one-to-one association on a foreign key is almost identical. The only difference is the column unique constraint.
Of course the underlying table structure has to match hibernate's definition else it will give you a runtime exception.
create table Book ( isbn bigint not null primary key )
create table Author ( isbn bigint not null primary key )
<class name="Book">
<id name="id" column="isbn">
<generator class="native"/>
</id>
</class>
<class name="Author">
<id name="id" column="isbn">
<generator class="foreign">
<param name="property">book</param>
</generator>
</id>
<one-to-one name="book" constrained="true"/>
</class>
There are two ways of doing a one-to-one, In the second option the two tables share the same id as the primary key which makes sense since it is a one to one. Both tables have isbn as primary key
One method is by sharing the primary key, the other method is by having a Primarykey and foreign key but enforcing uniqueness on the foreign key. Both isbn ids are generated so they must match, so you see only one has a native generator and the other has a 'foreign' generator meaning it relies on the first
If both generate separately they'd get out of synch. We only need to take away that there are two ways of defining a one-to-one
1. Sharing primary key - one table generates the primary key and the other one syncs with the first
2. The other option is to have a PK FK relationship but uniqueness enforced on the FK - so there will only be one.
One attribute that could be useful is the cascade attribute. In a one-to-many or many-to-one relation -
• cascade="none" means that if saving or deleting first hibernate object has no affect on related objects.
• cascade="save" means if first object is saved it automatically saves related objects.
• Similarly cascade = “delete” will delete related objects.
• cascade="all" will save, delete, etc related objects even if they have FK constraints. Everything gets deleted, and hibernate takes care of that.
http://docs.jboss.org/hibernate/core/3.5/reference/en/html/associations.html
http://ndpsoftware.com/HibernateMappingCheatSheet.html
• Unidirectional associations - Many-to-one
create table Author ( author_id bigint not null primary key, book_id bigint not null, first_name varchar, last_name varchar )
create table Book ( book_id bigint not null primary key )
Author.book_id is foreign key from Book.
author.hbml.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="com.mattiz.persistence.beans">
<class name="Author">
<id name="authorid" column="author_Id" type="long">
<generator class="native" />
</id>
<property name="firstName" type="string" length="20" column="first_name" />
<property name="lastName" type="string" length="20" column="last_name" />
<many-to-one name="book" column="book_Id" class="Book" not-null=”true”/>
</class>
</hibernate-mapping>
In the many-to-one tag the name is "book", so that means that if you use a getter it would be author.getBook() or if you use HQL author.book.
The many to one tag implies that book_id is a foreign key for this relationship
book.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="com.mattiz.persistence.beans">
<class name="Book">
<id name="bookid" column="book_id" type="long">
<generator class="native" />
</id>
</class>
</hibernate-mapping>
package com.mattiz.persistence.beans;
public class Author implements java.io.Serializable {
private long authorid;
private String firstName;
private String lastName;
private Book book;
public Author() {
}
public Author(Book book) {
this.book = book;
}
public Author(String firstName, String lastName, Book book) {
this.firstName = firstName;
this.lastName = lastName;
this.book = book;
}
public long getAuthorid() {
return this.authorid;
}
public void setAuthorid(long authorid) {
this.authorid = authorid;
}
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return this.lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Book getBook() {
return this.book;
}
public void setBook(Book book) {
this.book = book;
}
}
package com.mattiz.persistence.beans;
public class Book implements java.io.Serializable {
private long bookid;
public Book() {
}
public long getBookid() {
return this.bookid;
}
public void setBookid(long bookid) {
this.bookid = bookid;
}
}
• Bidirectionality in Many to One
Bidirectional many-to-one
create table Author ( author_id bigint not null primary key, book_id bigint not null, first_name varchar, last_name varchar )
create table Book ( book_id bigint not null primary key )
Author.book_id is foreign key from Book
author.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="com.mattiz.persistence.beans">
<class name="Author">
<id name="authorid" column="author_Id" type="long">
<generator class="native" />
</id>
<property name="firstName" type="string" length="20" column="first_name" />
<property name="lastName" type="string" length="20" column="last_name" />
<many-to-one name="book" column="book_Id" class="Book" not-null="true"/>
<!—book_id is FK in Author table from Book table->
</class>
</hibernate-mapping>
book.hbml.xml
<hibernate-mapping package="com.mattiz.persistence.beans">
<class name="Book">
<id name="bookid" column="book_id" type="long">
<generator class="native" />
</id>
<set name="authors" inverse="true"><!—book_id references column in Author table-->
<key column="book_id" />
<one-to-many class="Author" />
</set>
</class>
</hibernate-mapping>
package com.mattiz.persistence.beans;
public class Author implements java.io.Serializable {
private long authorid;
private String firstName;
private String lastName; private Book book;
public Author() {
}
public Author(Book book) {
this.book = book;
}
public Author(String firstName, String lastName, Book book) {
this.firstName = firstName;
this.lastName = lastName;
this.book = book;
}
public long getAuthorid() {
return this.authorid;
}
public void setAuthorid(long authorid) {
this.authorid = authorid;
}
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return this.lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public Book getBook() {
return this.book;
}
public void setBook(Book book) {
this.book = book;
}
}
package com.mattiz.persistence.beans;
import java.util.HashSet;
import java.util.Set;
public class Book implements java.io.Serializable {
private long bookid;
private Set authors = new HashSet(0);
public Book() {
}
public Book(Set authors) {
this.authors = authors;
}
public long getBookid() {
return this.bookid;
}
public void setBookid(long bookid) {
this.bookid = bookid;
}
public Set getAuthors() {
return this.authors;
}
public void setAuthors(Set authors) {
this.authors = authors;
}
}
If author in book is many-to-one, book to author is one- to-many and is a set.
The FK to author is the primary key in book.
From book.hbm.xml:
<set name="authors" inverse="true">
<key column="book_id" />
<one-to-many class="Author" />
</set>
Let’s consider <key column="book_id" /> in book.hbm.xml.
Here the key book_id is in the other table, i.e. Author. So getAuthors() will use the book_id column (FK) in the Author table.
So Author has PK author_id and FK book_id while Book has PK book_id. This relationship gets the authors (looked up based on FK book_id) associated with that Book. So authors are looked up based on their FK which is book_id and you get multiple authors which is a set. Basically in a one to many the key has to be in the other table.
the fk in a many to one, the key is in this table
The underlying table structure has to match hibernate's definition else it will give you a runtime exception.
When you see <key> it means refer to the key in the table where it is an FK.
Here is a simplistic explanation of the references:
author.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="com.mattiz.persistence.beans">
<class name="ContributingAuthor">
<id name="id" column="id" type="long">
<generator class="native" />
</id>
<property name="firstName" type="string" length="20" column="first_name" />
<property name="lastName" type="string" length="20" column="last_name" />
<many-to-one name="bookContributedToByAuthor" column="book_Id_in_Author" class="BookWithMultipleAuthors" not-null="true" />
</class>
</hibernate-mapping>
book.hbm.xml
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd" >
<hibernate-mapping package="com.mattiz.persistence.beans">
<class name="BookWithMultipleAuthors">
<id name="id" column="id" type="long">
<generator class="native" />
</id>
<set name="contributingAuthors" inverse="true">
<key column="book_Id_in_Author" />
<one-to-many class="ContributingAuthor" />
</set>
</class>
</hibernate-mapping>
Generated classes:
package com.mattiz.persistence.beans;
import java.util.HashSet;
import java.util.Set;
public class BookWithMultipleAuthors implements java.io.Serializable {
private long id;
private Set contributingAuthors = new HashSet(0);
public BookWithMultipleAuthors() {
}
public BookWithMultipleAuthors(Set contributingAuthors) {
this.contributingAuthors = contributingAuthors;
}
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
public Set getContributingAuthors() {
return this.contributingAuthors;
}
public void setContributingAuthors(Set contributingAuthors) {
this.contributingAuthors = contributingAuthors;
}
}
package com.mattiz.persistence.beans;
public class ContributingAuthor implements java.io.Serializable {
private long id;
private String firstName;
private String lastName;
private BookWithMultipleAuthors bookContributedToByAuthor;
public ContributingAuthor() {
}
public ContributingAuthor(BookWithMultipleAuthors bookContributedToByAuthor) {
this.bookContributedToByAuthor = bookContributedToByAuthor;
}
public ContributingAuthor(String firstName, String lastName, BookWithMultipleAuthors bookContributedToByAuthor) {
this.firstName = firstName;
this.lastName = lastName;
this.bookContributedToByAuthor = bookContributedToByAuthor;
}
public long getId() {
return this.id;
}
public void setId(long id) {
this.id = id;
}
public String getFirstName() {
return this.firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return this.lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public BookWithMultipleAuthors getBookContributedToByAuthor() {
return this.bookContributedToByAuthor;
}
public void setBookContributedToByAuthor(BookWithMultipleAuthors bookContributedToByAuthor) {
this.bookContributedToByAuthor = bookContributedToByAuthor;
}
}
Executing a one-to-many book.getAuthors() is the equivalent of writing this sql
select * from t_author where book_id=? Where book_id is the FK in t_author
The book_id would have to be already present in book as a rule via referential integrity
Executing a many to one author.getBook() is the equivalent of writing this sql
select * from t_book where book_id = ? where the parameter in the last query would be from the author table; which would return one (obviously) since you are querying by PK.
• Unidirectional one to one
create table Author( author_id bigint not null primary key, book_id bigint not null unique )
create table Book ( book_id bigint not null primary key )
A unidirectional one-to-one association on a foreign key is almost identical. The only difference is the column unique constraint.
Of course the underlying table structure has to match hibernate's definition else it will give you a runtime exception.
create table Book ( isbn bigint not null primary key )
create table Author ( isbn bigint not null primary key )
<class name="Book">
<id name="id" column="isbn">
<generator class="native"/>
</id>
</class>
<class name="Author">
<id name="id" column="isbn">
<generator class="foreign">
<param name="property">book</param>
</generator>
</id>
<one-to-one name="book" constrained="true"/>
</class>
There are two ways of doing a one-to-one, In the second option the two tables share the same id as the primary key which makes sense since it is a one to one. Both tables have isbn as primary key
One method is by sharing the primary key, the other method is by having a Primarykey and foreign key but enforcing uniqueness on the foreign key. Both isbn ids are generated so they must match, so you see only one has a native generator and the other has a 'foreign' generator meaning it relies on the first
If both generate separately they'd get out of synch. We only need to take away that there are two ways of defining a one-to-one
1. Sharing primary key - one table generates the primary key and the other one syncs with the first
2. The other option is to have a PK FK relationship but uniqueness enforced on the FK - so there will only be one.
One attribute that could be useful is the cascade attribute. In a one-to-many or many-to-one relation -
• cascade="none" means that if saving or deleting first hibernate object has no affect on related objects.
• cascade="save" means if first object is saved it automatically saves related objects.
• Similarly cascade = “delete” will delete related objects.
• cascade="all" will save, delete, etc related objects even if they have FK constraints. Everything gets deleted, and hibernate takes care of that.
http://docs.jboss.org/hibernate/core/3.5/reference/en/html/associations.html
http://ndpsoftware.com/HibernateMappingCheatSheet.html
Sunday, January 08, 2012
The long and short of it all
Q# Describe the structure of a SOAP request/ response
The soap request/ response consists of a SOAP envelope. The soap envelope contains the soap header and soap body. The SOAP header gives you an option of putting some stuff in that would be evaluated before the body is processed. The SOAP header may contain authentication information, namespace and method-name invoked, etc. Soap body may contain request and response content.
From
http://java.sun.com/developer/technicalArticles/xml/webservices/ #Message Format
SOAP performs operations in the context of a standardized message format. The primary part of this message has a MIME type of "text/xml" and contains the SOAP envelope. This envelope is an XML document. The envelope contains a header (optional) and a body (mandatory). The body part of the envelope is always intended for the final recipient of the message, while the header entries may target the nodes that perform intermediate processing, such as endpoint. Attachments, binary or otherwise, may be appended to the body.
..
Headers, for example, may be used to provide digital signatures for a request contained in the body. In this circumstance, an authentication or authorization server could process the header entry -- independent of the body -- stripping out information to validate the signature. Once validated, the rest of the envelope would be passed on to the SOAP server, which would process the body of the message.
Q# How do you authenticate a SOAP request?
I have used weblogic.xml.crypto.wss.provider.CredentialProvider to set the user credentials that will be added to the request header after encryption. This information is stripped from the soap header to authenticate the request. The authorization data is passed to the server through the request header each and every time a request is made. The authorization policy is defined in an xml file that is kept in the META-INF folder of the SOAP war/ ear file. The policy.xml says that user should always send username/ password token. The request should then always contain username/ password token.
We implemented "Standard Web Services Security" using username and password tokens passed in the soap header and a policy xml was deployed on the server which executed an EJB that did authentication.
The method itself was exposed as a webservice using annotations in the javax.jws package which is implemented in weblogic jar. Also jboss-ejb3.jar.
Describe how you have created various web services
In one of our previous project we used ejb3 annotations to expose ordinary pojo methods with annotations such as
@WebService(serviceName = "SomeService", portName = "SomeServicePort", targetNamespace = "http://mattiz.com/calculateInterest")\
which seems to be in the import javax.jws.WebService; package of weblogic.jar.
The wsdl was automatically created and client stubs were generated using the ant clientgen task.
http://java.sun.com/developer/technicalArticles/J2SE/jax_ws_2/ is a rather a simplistic introduction, but similar to one described above.
In another project we created the wsdl manually and generated stubs using something like wsconsume ant task. The generated stubs are used both client side and server side.
Q# What is the difference between restful services and soap?
From my brief reading, it looks like the main difference between SOAP and RESTful webservices is :
* SOAP exposes a function (or a method call) as an HTTP web service. The method can contain complex business logic etc.
* RESTful web service provides pieces of data as an HTTP web service. Think of RESTful web service as exposing a bunch of getter methods as webservice. The client can invoke the getter functions to retrieve pieces of data and then do what it pleases with the retrieved data. The restful web service provides data similar to how a database would provide data. Each invocation is very light since the web service provider doesn't have any logic. It just retrieves and sends data.
I found this article useful
http://137.254.16.27/enterprisetechtips/entry/implementing_restful_web_services_in
"Instead of providing just one endpoint for a particular web service and having that endpoint perform various operations, you provide access to resources." So the soap web service client can be thin - service provider has lot of logic and data access and does lot of work while in RESTful web service, the provider is very thin - just provides resources (e.g. data). The client can do whatever it likes with the data - complex logic etc. So thin server/ thick client model is implemented and separate uri's for each resource (or piece of data) are in place. RESTful services looks different from SOAP. SOAP has a method call which takes params and returns params while RESTful service just has separate uri's for each resource (piece of data).
Here's an example of how a RESTful service would look different from soap:
A soap service could expose a service that looks like this String doComplexStuff(param1, param2, param3, param4) while a RESTful service would look like this http://blah/resource/param1, http://blah/resource/param12 where it is just giving the values asked for and the RESTful service will return param1 and param12 without doing complex processing.
The difference is not whether "light" or "heavy"; the difference is whether you are executing a method call or getting access to resources.Typically RESTful services are supposed to be "light" without complex logic though that's not to say that it can't access the db or that it can't use hibernate.
Restful service provider can use any technique to provide access to resources. The "resource" could be a value retrieved from db using hibernate, DTO, or anything else. For example the client may ask for all a list of DTOs and is returned list of DTOs; its just getting a resource or a list of resources. You can access db resources and return a dto or list of dtos and that would still be RESTful. The difference between SOAP and RESTful services is whether you are exposing a method or exposing your resources. Restful means simply access to resources. You can ignore how the RESTful service is implemented, just focus on the interface exposed to the client. Soap service looks like this - String doComplexStuff(param1, param2, param3, param4) while RESTful service looks like this
http://blah/resource/param1
http://blah/resource/param
It exposes multiple URI's each URI exposing a single resource. For soap, each url would expose a method that performs some complex task and it's not just exposing a resource. The difference is philosophical more of a design difference. We can use one technology on one end of a RESTful service and another at the consumer side, its just like a soap webservice - the mechanism is the same.
Q# How do you implement transaction handling in spring and hibernate?
This looks like a very good explanation of spring and hibernate transaction handling:
http://java.dzone.com/articles/spring-hibernate-persistence
It first discusses hibernate the traditional way, using session etc.then describes hibernate using spring templates.
With Spring, you do not need to implement code for obtaining Session objects, starting and committing transactions, and handling Hibernate exceptions. Instead, you use a HibernateTemplate instance to delegate persistence calls to Hibernate, without direct interaction with Hibernate.
This page would clear any questions about spring/hibernate including transaction handling, etc.
Also
http://static.springsource.org/spring/docs/2.0.8/reference/transaction.html
Q# How does hibernate implement caching programtically?
There are two types of caching - first-level and second-level. First-level cache is hibernate's internal cache that it uses for optimizing. Second-level caching, is what we normally refer to as caching generally. Secondary level caching is usually implemented using an algorithm like Least Recently Used algorithm.
From
http://www.informit.com/articles/article.aspx?p=353736&seqNum=5
By storing the data in a cache instead of relying solely on the database, you may be able to significantly reduce the load on the database, and possibly to increase overall performance as well.
From
http://cuppajava.blogspot.com/2010/07/hibernate-ehcache-and-caching.html
First-level cache is associated with the Session object, while second-level cache is associated with the Session Factory object. By default, Hibernate uses first-level cache on a per-transaction basis. Hibernate uses this cache mainly to reduce the number of SQL queries it needs to generate within a given transaction. For example, if an object is modified several times within the same transaction, Hibernate will generate only one SQL UPDATE statement at the end of the transaction, containing all the modifications.
To reduce database traffic, second-level cache keeps loaded objects at the Session Factory level between transactions. These objects are available to the whole application, not just to the user running the query. This way, each time a query returns an object that is already loaded in the cache, one or more database transactions potentially are avoided.
Implementing caching programatically
We specify which classes to cache and the caching params but we don't really write code to invoke the cache.
http://www.informit.com/articles/article.aspx?p=353736&seqNum=5 #Configuring a Cache.
Q# How do you configure hibernate using spring?
TODO:
Q# Explain lazy loading and eager fetch in hibernate.
If object A has relation with object B, if you load object A, object B is not loaded from db automatically; it will be loaded only when you do getB(). Eager fetch is the opposite technique. In eager fetch B is loaded when A is loaded if A contains B. The default mechanism is lazy loading.
When to use lazy loading and when to use eager fetch:
Lazy load will not automatically load child objects. It will load child objects only when getChild() is called. Eager fetch will automatically load child objects when the parent object is loaded.
Whether to use lazy load or eager fetch depends on your performance requirements.
If you use eager fetch children were already loaded and are already in memory so accessing these objects will be faster. Memory consumption will be more because more objects are in memory even if they are not needed.If you use lazy load, child objects are loaded only when required (when getter method is invoked). Hence accessing child objects will be somewhat slower but will use lesser memory since objects are loaded only if
they are required.
http://powerdream5.wordpress.com/2007/11/09/eager-loading-in-hibernate/
Q# Give some advantages of JPA over hibernate. When do we use hibernate and when do we use JPA?
We always seem to configure the persistence mechanism using hibernate but then we do the querying and QL stuff in JPA. Most of our past projects followed that pattern - only the configuration was done in hibernate all SQL stuff was in JPA.
Hibernate 3.0 implements support JPA. JPA itself doesn't provide the persistence mechanism. The code uses a standard Java API such as JPA so that it is generic. I could switch hibernate to use something else like iBatis etc. Beneath JPA, the underlying persistence mechanism could be hibernate or toplink or jbatis etc. The underlying mechanism could be changed anytime and the JPA code wouldn't change.
http://elope.wordpress.com/2007/09/06/difference-between-jpa-and-hibernate/
And hibernate 3.0 implements the JPA specification so TopLink and other O/R mapping framework.So your code depends upon JPA Api and its implementation is supplied by any JPA impl vendor framework. So in effect if you use JPA , you are using a standard Java Api and not some orthogonal Api and hence you have flexibility in your system. Say initially you have used Hibernate but now want to shift to Other framework.That means JPA supports pluggable persistence providers. JPA specification is heavily influenced by current O/R mapping frameworks like Hibernate and Top-Link.
Q# Give some examples on performance tuning in SQL. What is explain plan? What is indexing? How will you create an index?
Let's assume the db is oracle and you have a query to check if it will perform well, You can do "explain plan" on that query, which is basically by hitting F10 in oracle sql developer. Explain plan will tell you how well the sql will perform.
So let's say the query looks like this
select a.foo from tablea a, tableb b where a.id=b.id and b.bar = ?
It will give you some numbers... like it will say a.id=b.id has "cost = 5" and b.bar has "cost=4000"
So the a.id=b.id comparison is well-performing since it has lower cost. b.bar=? is badly performing since it has higher cost. So if b.bar=? is doing a full table scan it is good to add an index to b.bar column. After adding the index, you can do "explain plan" once again to see if it is better.
So in an un-indexed column the query will read the values of every row in the table to check if the value matches so it is not efficient. However if you create an index it will create a reference which is organized in a certain manner so that it's easy to look lookup. It's like a book's index if you have page numbers; it's easy to lookup instead of scanning every page
Create an index like this:
create index indexname on tablea columnb
Q# What are the class associations in a UML diaagrams?
http://www.smartdraw.com/resources/tutorials/uml-diagrams/
http://www.smartdraw.com/resources/tutorials/uml-class-diagrams/
Q# How do you configure maven? What are the advantages of maven over ant build?
Maven is just a framework of plugins that uses Ant underneath. It stores jar files in a remote repository for easy sharing. Maven is easier to use; there is no need to write big build.xml files since most common tasks are available as plugins.
I don't know maven 2.0. I have only used maven 1.0.
Maven 2.0
You have a settings.xml and several pom.xml.
The settings.xml is optional and is used to specify the repository location, security and deployment settinngs. If it is absent default values are used. Maven will automatically download artifacts from the online maven repository if the dependencies are not locally available in the repository. The jars are placed in a maven repository(relative path=.m2/repo).
The pom.xml(project object model) specifies the url, the artefact version, the artefact name, artefact extension and the type of packaging(jar/ war/ear) and dependencies.
When executing a task or goal, Maven looks for the POM in the current directory. It reads the POM, gets the needed configuration information, then executes the goal. Some of the configuration that can be specified in the POM are the project dependencies, the plugins or goals that can be executed, the build profiles, etc
Maven commands:
mvn-test
mvn compile
mvn package
mvn install
are common maven commands.
Advantages
Maven 1.0: Maven 1.0 has plugins for all "common" tasks so that all you have to do is add some information to configuration files. As an example let's say you create a maven.properties file with two properties
maven.compile.src="/blah/src"
maven.compile.target="blah/classes"
Then you do mvn compile which is a direct call at the cmd prompt. You don't have to write any xml stuff as there is a plugin for java compile which needs only those two properties. No build.xml needed.
First maven project
http://maven.apache.org/guides/getting-started/index.html
pom.xml
http://maven.apache.org/guides/introduction/introduction-to-the-pom.html
Maven configuration: settings.xml
http://maven.apache.org/settings.html
Q# What is cascade-all in Hibernate? What are its advantages?
In a hibernate mapping file, if you specify cascade="none" means that saving or deleting first hibernate object has no affect on related object. cascade="save" means if first object is saved it automatically saves the related object. Similarly cascade delete will delete related object. cascade="all" is everything save, delete etc even if they are related with foreign keys. The FK constraints etc. everything gets deleted and hibernate takes care of that.
eg.
The code to add a Child looks like this
Parent p = (Parent) session.load(Parent.class, pid);
Child c = new Child();
p.addChild(c);
session.save(c);
session.flush()
You can address the frustrations of the explicit call to save() by using cascades.
eg.
<set name="children" inverse="true" cascade="all">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
Q# What is dirty checking in hibernate? What are its advantages?
Dirty checking is hibernate is an internal mechanism that ensures that the value of the hibernate POJO object matches what is in the database(synchronizes). If the value does not match it is said to be "dirty" and hence value is refreshed from the database. Hibernate updates only "dirty" objects - only objects that have changed. Hibernate keeps track of which objects are dirty and occasionally "flushes" to ensure the objects are in sync with the db. The advantages of dirty checking is if hibernate doesn't keep track of which objects are dirty it would have to update all objects that were loaded even if they didn't change at the final commit of the transaction.
Q# Java Messaging Service - here we go again
Two useful links from cuppajava site that proves handy:
http://cuppajava.blogspot.com/2007/10/jms-and-message-driven-beans-i.html
http://cuppajava.blogspot.com/2007/10/jms-and-message-driven-beans-ii.html
Q# Name some design patterns that you have used:
From
http://www.techartifact.com/blogs/2011/11/three-main-categories-of-design-patterns.html
Creational Patterns
• Abstract Factory:- Creates an instance of several families of classes.
• Factory Method:- Creates an instance of several derived classes eg DAOFactory
• Singleton:- A class in which only a single instance can exist eg Spring DI by default
Structural Patterns
• Adapter:-Match interfaces of different classes.
• Bridge:-Separates an object’s abstraction from its implementation.
• Façade:-A single class that represents an entire subsystem.
Q# What is the difference between JAX-WS, JAX-RPC and what is JAX-B?
From
http://www.ibm.com/developerworks/webservices/library/ws-tip-jaxwsrpc/index.html
Web services have been around a while now. First there was SOAP. JAX-rpc
The industry was not merely doing RPC web services, they were also doing message-oriented web services. So "RPC" was removed from the name and replaced with "WS" (for web Services). Thus the successor to JAX-RPC 1.1 is JAX-WS 2.0 - the Java API for XML-based web services.
What is different between JAX-rpc and JAX-ws?
JAX-RPC and JAX-WS support SOAP 1.1. JAX-WS also supports SOAP 1.2.
The WSDL 1.1 specification defined an HTTP binding, which is a means by which you can send XML messages over HTTP without SOAP. JAX-RPC ignored the HTTP binding. JAX-WS adds support for it.
JAX-RPC maps to Java 1.4. JAX-WS maps to Java 5.0. JAX-WS relies on many of the features new in Java 5.0.
Java EE 5, the successor to J2EE 1.4, adds support for JAX-WS, but it also retains support for JAX-RPC, which could be confusing to today's web services novices.
JAX-RPC has its own data mapping model, which covers about 90 percent of all schema types. Those that it does not cover are mapped to javax.xml.soap.SOAPElement.
JAX-WS's data mapping model is JAXB. JAXB promises mappings for all XML schemas.
JAX-WS's dynamic client model is quite different from JAX-RPC's. Many of the changes acknowledge industry needs:
It introduces message-oriented functionality.
It introduces dynamic asynchronous functionality.
JAX-WS also adds a dynamic server model, which JAX-RPC does not have.
JAX-RPC handlers rely on SAAJ 1.2. JAX-WS handlers rely on the new SAAJ 1.3 specification.
Q# For what functionality did you use JMX in any of your applications?
stopping/ starting JVMs, customizing JBoss JMX console to change the state of custom beans at runtime
Q# Have you performed any tomcat or jboss administration?
For the most part developers do very little with jboss and tomcat administration - mainly deployment, datasource configuration. I'm sure jboss and tomcat have a lot of proprietary features that are powerful but normally we don't use them and instead we stick to standard J2EE techniques and third party frameworks etc. In a production environment the jboss/tomcat administrator may do some tuning like change the # of connections in jdbc pool, JVM args, etc. but normally a developer doesn't care about this stuff. jboss has a UI for configuration:
see http://docs.jboss.org/jbossas/6/Admin_Console_Guide/en-US/pdf/Admin_Console_Guide.pdf
For tomcat
http://tomcat.apache.org/tomcat-6.0-doc/introduction.html
Q# How does inter-portlet communication take place in liferay?
The soap request/ response consists of a SOAP envelope. The soap envelope contains the soap header and soap body. The SOAP header gives you an option of putting some stuff in that would be evaluated before the body is processed. The SOAP header may contain authentication information, namespace and method-name invoked, etc. Soap body may contain request and response content.
From
http://java.sun.com/developer/technicalArticles/xml/webservices/ #Message Format
SOAP performs operations in the context of a standardized message format. The primary part of this message has a MIME type of "text/xml" and contains the SOAP envelope. This envelope is an XML document. The envelope contains a header (optional) and a body (mandatory). The body part of the envelope is always intended for the final recipient of the message, while the header entries may target the nodes that perform intermediate processing, such as endpoint. Attachments, binary or otherwise, may be appended to the body.
..
Headers, for example, may be used to provide digital signatures for a request contained in the body. In this circumstance, an authentication or authorization server could process the header entry -- independent of the body -- stripping out information to validate the signature. Once validated, the rest of the envelope would be passed on to the SOAP server, which would process the body of the message.
Q# How do you authenticate a SOAP request?
I have used weblogic.xml.crypto.wss.provider.CredentialProvider to set the user credentials that will be added to the request header after encryption. This information is stripped from the soap header to authenticate the request. The authorization data is passed to the server through the request header each and every time a request is made. The authorization policy is defined in an xml file that is kept in the META-INF folder of the SOAP war/ ear file. The policy.xml says that user should always send username/ password token. The request should then always contain username/ password token.
We implemented "Standard Web Services Security" using username and password tokens passed in the soap header and a policy xml was deployed on the server which executed an EJB that did authentication.
The method itself was exposed as a webservice using annotations in the javax.jws package which is implemented in weblogic jar. Also jboss-ejb3.jar.
Describe how you have created various web services
In one of our previous project we used ejb3 annotations to expose ordinary pojo methods with annotations such as
@WebService(serviceName = "SomeService", portName = "SomeServicePort", targetNamespace = "http://mattiz.com/calculateInterest")\
which seems to be in the import javax.jws.WebService; package of weblogic.jar.
The wsdl was automatically created and client stubs were generated using the ant clientgen task.
http://java.sun.com/developer/technicalArticles/J2SE/jax_ws_2/ is a rather a simplistic introduction, but similar to one described above.
In another project we created the wsdl manually and generated stubs using something like wsconsume ant task. The generated stubs are used both client side and server side.
Q# What is the difference between restful services and soap?
From my brief reading, it looks like the main difference between SOAP and RESTful webservices is :
* SOAP exposes a function (or a method call) as an HTTP web service. The method can contain complex business logic etc.
* RESTful web service provides pieces of data as an HTTP web service. Think of RESTful web service as exposing a bunch of getter methods as webservice. The client can invoke the getter functions to retrieve pieces of data and then do what it pleases with the retrieved data. The restful web service provides data similar to how a database would provide data. Each invocation is very light since the web service provider doesn't have any logic. It just retrieves and sends data.
I found this article useful
http://137.254.16.27/enterprisetechtips/entry/implementing_restful_web_services_in
"Instead of providing just one endpoint for a particular web service and having that endpoint perform various operations, you provide access to resources." So the soap web service client can be thin - service provider has lot of logic and data access and does lot of work while in RESTful web service, the provider is very thin - just provides resources (e.g. data). The client can do whatever it likes with the data - complex logic etc. So thin server/ thick client model is implemented and separate uri's for each resource (or piece of data) are in place. RESTful services looks different from SOAP. SOAP has a method call which takes params and returns params while RESTful service just has separate uri's for each resource (piece of data).
Here's an example of how a RESTful service would look different from soap:
A soap service could expose a service that looks like this String doComplexStuff(param1, param2, param3, param4) while a RESTful service would look like this http://blah/resource/param1, http://blah/resource/param12 where it is just giving the values asked for and the RESTful service will return param1 and param12 without doing complex processing.
The difference is not whether "light" or "heavy"; the difference is whether you are executing a method call or getting access to resources.Typically RESTful services are supposed to be "light" without complex logic though that's not to say that it can't access the db or that it can't use hibernate.
Restful service provider can use any technique to provide access to resources. The "resource" could be a value retrieved from db using hibernate, DTO, or anything else. For example the client may ask for all a list of DTOs and is returned list of DTOs; its just getting a resource or a list of resources. You can access db resources and return a dto or list of dtos and that would still be RESTful. The difference between SOAP and RESTful services is whether you are exposing a method or exposing your resources. Restful means simply access to resources. You can ignore how the RESTful service is implemented, just focus on the interface exposed to the client. Soap service looks like this - String doComplexStuff(param1, param2, param3, param4) while RESTful service looks like this
http://blah/resource/param1
http://blah/resource/param
It exposes multiple URI's each URI exposing a single resource. For soap, each url would expose a method that performs some complex task and it's not just exposing a resource. The difference is philosophical more of a design difference. We can use one technology on one end of a RESTful service and another at the consumer side, its just like a soap webservice - the mechanism is the same.
Q# How do you implement transaction handling in spring and hibernate?
This looks like a very good explanation of spring and hibernate transaction handling:
http://java.dzone.com/articles/spring-hibernate-persistence
It first discusses hibernate the traditional way, using session etc.then describes hibernate using spring templates.
With Spring, you do not need to implement code for obtaining Session objects, starting and committing transactions, and handling Hibernate exceptions. Instead, you use a HibernateTemplate instance to delegate persistence calls to Hibernate, without direct interaction with Hibernate.
This page would clear any questions about spring/hibernate including transaction handling, etc.
Also
http://static.springsource.org/spring/docs/2.0.8/reference/transaction.html
Q# How does hibernate implement caching programtically?
There are two types of caching - first-level and second-level. First-level cache is hibernate's internal cache that it uses for optimizing. Second-level caching, is what we normally refer to as caching generally. Secondary level caching is usually implemented using an algorithm like Least Recently Used algorithm.
From
http://www.informit.com/articles/article.aspx?p=353736&seqNum=5
By storing the data in a cache instead of relying solely on the database, you may be able to significantly reduce the load on the database, and possibly to increase overall performance as well.
From
http://cuppajava.blogspot.com/2010/07/hibernate-ehcache-and-caching.html
First-level cache is associated with the Session object, while second-level cache is associated with the Session Factory object. By default, Hibernate uses first-level cache on a per-transaction basis. Hibernate uses this cache mainly to reduce the number of SQL queries it needs to generate within a given transaction. For example, if an object is modified several times within the same transaction, Hibernate will generate only one SQL UPDATE statement at the end of the transaction, containing all the modifications.
To reduce database traffic, second-level cache keeps loaded objects at the Session Factory level between transactions. These objects are available to the whole application, not just to the user running the query. This way, each time a query returns an object that is already loaded in the cache, one or more database transactions potentially are avoided.
Implementing caching programatically
We specify which classes to cache and the caching params but we don't really write code to invoke the cache.
http://www.informit.com/articles/article.aspx?p=353736&seqNum=5 #Configuring a Cache.
Q# How do you configure hibernate using spring?
TODO:
Q# Explain lazy loading and eager fetch in hibernate.
If object A has relation with object B, if you load object A, object B is not loaded from db automatically; it will be loaded only when you do getB(). Eager fetch is the opposite technique. In eager fetch B is loaded when A is loaded if A contains B. The default mechanism is lazy loading.
When to use lazy loading and when to use eager fetch:
Lazy load will not automatically load child objects. It will load child objects only when getChild() is called. Eager fetch will automatically load child objects when the parent object is loaded.
Whether to use lazy load or eager fetch depends on your performance requirements.
If you use eager fetch children were already loaded and are already in memory so accessing these objects will be faster. Memory consumption will be more because more objects are in memory even if they are not needed.If you use lazy load, child objects are loaded only when required (when getter method is invoked). Hence accessing child objects will be somewhat slower but will use lesser memory since objects are loaded only if
they are required.
http://powerdream5.wordpress.com/2007/11/09/eager-loading-in-hibernate/
Q# Give some advantages of JPA over hibernate. When do we use hibernate and when do we use JPA?
We always seem to configure the persistence mechanism using hibernate but then we do the querying and QL stuff in JPA. Most of our past projects followed that pattern - only the configuration was done in hibernate all SQL stuff was in JPA.
Hibernate 3.0 implements support JPA. JPA itself doesn't provide the persistence mechanism. The code uses a standard Java API such as JPA so that it is generic. I could switch hibernate to use something else like iBatis etc. Beneath JPA, the underlying persistence mechanism could be hibernate or toplink or jbatis etc. The underlying mechanism could be changed anytime and the JPA code wouldn't change.
http://elope.wordpress.com/2007/09/06/difference-between-jpa-and-hibernate/
And hibernate 3.0 implements the JPA specification so TopLink and other O/R mapping framework.So your code depends upon JPA Api and its implementation is supplied by any JPA impl vendor framework. So in effect if you use JPA , you are using a standard Java Api and not some orthogonal Api and hence you have flexibility in your system. Say initially you have used Hibernate but now want to shift to Other framework.That means JPA supports pluggable persistence providers. JPA specification is heavily influenced by current O/R mapping frameworks like Hibernate and Top-Link.
Q# Give some examples on performance tuning in SQL. What is explain plan? What is indexing? How will you create an index?
Let's assume the db is oracle and you have a query to check if it will perform well, You can do "explain plan" on that query, which is basically by hitting F10 in oracle sql developer. Explain plan will tell you how well the sql will perform.
So let's say the query looks like this
select a.foo from tablea a, tableb b where a.id=b.id and b.bar = ?
It will give you some numbers... like it will say a.id=b.id has "cost = 5" and b.bar has "cost=4000"
So the a.id=b.id comparison is well-performing since it has lower cost. b.bar=? is badly performing since it has higher cost. So if b.bar=? is doing a full table scan it is good to add an index to b.bar column. After adding the index, you can do "explain plan" once again to see if it is better.
So in an un-indexed column the query will read the values of every row in the table to check if the value matches so it is not efficient. However if you create an index it will create a reference which is organized in a certain manner so that it's easy to look lookup. It's like a book's index if you have page numbers; it's easy to lookup instead of scanning every page
Create an index like this:
create index indexname on tablea columnb
Q# What are the class associations in a UML diaagrams?
http://www.smartdraw.com/resources/tutorials/uml-diagrams/
http://www.smartdraw.com/resources/tutorials/uml-class-diagrams/
Q# How do you configure maven? What are the advantages of maven over ant build?
Maven is just a framework of plugins that uses Ant underneath. It stores jar files in a remote repository for easy sharing. Maven is easier to use; there is no need to write big build.xml files since most common tasks are available as plugins.
I don't know maven 2.0. I have only used maven 1.0.
Maven 2.0
You have a settings.xml and several pom.xml.
The settings.xml is optional and is used to specify the repository location, security and deployment settinngs. If it is absent default values are used. Maven will automatically download artifacts from the online maven repository if the dependencies are not locally available in the repository. The jars are placed in a maven repository(relative path=.m2/repo).
The pom.xml(project object model) specifies the url, the artefact version, the artefact name, artefact extension and the type of packaging(jar/ war/ear) and dependencies.
When executing a task or goal, Maven looks for the POM in the current directory. It reads the POM, gets the needed configuration information, then executes the goal. Some of the configuration that can be specified in the POM are the project dependencies, the plugins or goals that can be executed, the build profiles, etc
Maven commands:
mvn-test
mvn compile
mvn package
mvn install
are common maven commands.
Advantages
Maven 1.0: Maven 1.0 has plugins for all "common" tasks so that all you have to do is add some information to configuration files. As an example let's say you create a maven.properties file with two properties
maven.compile.src="/blah/src"
maven.compile.target="blah/classes"
Then you do mvn compile which is a direct call at the cmd prompt. You don't have to write any xml stuff as there is a plugin for java compile which needs only those two properties. No build.xml needed.
First maven project
http://maven.apache.org/guides/getting-started/index.html
pom.xml
http://maven.apache.org/guides/introduction/introduction-to-the-pom.html
Maven configuration: settings.xml
http://maven.apache.org/settings.html
Q# What is cascade-all in Hibernate? What are its advantages?
In a hibernate mapping file, if you specify cascade="none" means that saving or deleting first hibernate object has no affect on related object. cascade="save" means if first object is saved it automatically saves the related object. Similarly cascade delete will delete related object. cascade="all" is everything save, delete etc even if they are related with foreign keys. The FK constraints etc. everything gets deleted and hibernate takes care of that.
eg.
The code to add a Child looks like this
Parent p = (Parent) session.load(Parent.class, pid);
Child c = new Child();
p.addChild(c);
session.save(c);
session.flush()
You can address the frustrations of the explicit call to save() by using cascades.
eg.
<set name="children" inverse="true" cascade="all">
<key column="parent_id"/>
<one-to-many class="Child"/>
</set>
Q# What is dirty checking in hibernate? What are its advantages?
Dirty checking is hibernate is an internal mechanism that ensures that the value of the hibernate POJO object matches what is in the database(synchronizes). If the value does not match it is said to be "dirty" and hence value is refreshed from the database. Hibernate updates only "dirty" objects - only objects that have changed. Hibernate keeps track of which objects are dirty and occasionally "flushes" to ensure the objects are in sync with the db. The advantages of dirty checking is if hibernate doesn't keep track of which objects are dirty it would have to update all objects that were loaded even if they didn't change at the final commit of the transaction.
Q# Java Messaging Service - here we go again
Two useful links from cuppajava site that proves handy:
http://cuppajava.blogspot.com/2007/10/jms-and-message-driven-beans-i.html
http://cuppajava.blogspot.com/2007/10/jms-and-message-driven-beans-ii.html
Q# Name some design patterns that you have used:
From
http://www.techartifact.com/blogs/2011/11/three-main-categories-of-design-patterns.html
Creational Patterns
• Abstract Factory:- Creates an instance of several families of classes.
• Factory Method:- Creates an instance of several derived classes eg DAOFactory
• Singleton:- A class in which only a single instance can exist eg Spring DI by default
Structural Patterns
• Adapter:-Match interfaces of different classes.
• Bridge:-Separates an object’s abstraction from its implementation.
• Façade:-A single class that represents an entire subsystem.
Q# What is the difference between JAX-WS, JAX-RPC and what is JAX-B?
From
http://www.ibm.com/developerworks/webservices/library/ws-tip-jaxwsrpc/index.html
Web services have been around a while now. First there was SOAP. JAX-rpc
The industry was not merely doing RPC web services, they were also doing message-oriented web services. So "RPC" was removed from the name and replaced with "WS" (for web Services). Thus the successor to JAX-RPC 1.1 is JAX-WS 2.0 - the Java API for XML-based web services.
What is different between JAX-rpc and JAX-ws?
JAX-RPC and JAX-WS support SOAP 1.1. JAX-WS also supports SOAP 1.2.
The WSDL 1.1 specification defined an HTTP binding, which is a means by which you can send XML messages over HTTP without SOAP. JAX-RPC ignored the HTTP binding. JAX-WS adds support for it.
JAX-RPC maps to Java 1.4. JAX-WS maps to Java 5.0. JAX-WS relies on many of the features new in Java 5.0.
Java EE 5, the successor to J2EE 1.4, adds support for JAX-WS, but it also retains support for JAX-RPC, which could be confusing to today's web services novices.
JAX-RPC has its own data mapping model, which covers about 90 percent of all schema types. Those that it does not cover are mapped to javax.xml.soap.SOAPElement.
JAX-WS's data mapping model is JAXB. JAXB promises mappings for all XML schemas.
JAX-WS's dynamic client model is quite different from JAX-RPC's. Many of the changes acknowledge industry needs:
It introduces message-oriented functionality.
It introduces dynamic asynchronous functionality.
JAX-WS also adds a dynamic server model, which JAX-RPC does not have.
JAX-RPC handlers rely on SAAJ 1.2. JAX-WS handlers rely on the new SAAJ 1.3 specification.
Q# For what functionality did you use JMX in any of your applications?
stopping/ starting JVMs, customizing JBoss JMX console to change the state of custom beans at runtime
Q# Have you performed any tomcat or jboss administration?
For the most part developers do very little with jboss and tomcat administration - mainly deployment, datasource configuration. I'm sure jboss and tomcat have a lot of proprietary features that are powerful but normally we don't use them and instead we stick to standard J2EE techniques and third party frameworks etc. In a production environment the jboss/tomcat administrator may do some tuning like change the # of connections in jdbc pool, JVM args, etc. but normally a developer doesn't care about this stuff. jboss has a UI for configuration:
see http://docs.jboss.org/jbossas/6/Admin_Console_Guide/en-US/pdf/Admin_Console_Guide.pdf
For tomcat
http://tomcat.apache.org/tomcat-6.0-doc/introduction.html
Q# How does inter-portlet communication take place in liferay?
Friday, September 16, 2011
Service Oriented Architecture
SOA is short for Service Oriented Architecture. SOA is basically an architecture where there are a set of services which are typically exposed as a set of web services and can be invoked by webservice clients. It's a readymade framework. For e.g.you could have a front end interface for internet banking which would invoke a remote webservice to authenticate the user on login, another webservice to get a list of accounts and yet another webservice to get account details etc. So there is this bank of services which is the SOA layer that provides information to external clients services eg. as web services.
The primary motive is to decouple the producer and consumer of information so that one is agnostic about the internals of the other.
It is usually a set of webservices, but not always. For example you could have have a layer of services that are invoked by Flex (Flash UI) from a browser where flex is a thick client like applet/flex. (Thin client is html/javascript).
So in our case, the thick browser client is invoking services on the app server.
SOA is not really anything different, it's just a design pattern or architecture. One example would be EJBs where you have a bunch of EJBs providing services and the client code will lookup an EJB and invoke a method on it.You could deploy updates to the EJBs without impacting the client code as long as the method signature doesn't change, but mostly SOA is a set of webservices; that's the most common usage. For e.g. in the internet bank example one could do releases of our own, without any change on the service provider and the service provider could publish new APIs but would still support old APIs that the client was using, until it could get a chance to migrate to new APIs.
That's all there is to SOA - the implementation is quite varying. If the client invokes a service getCustomer(customerID) and the service provider creates a new method getCustomer(customerName) the SOA consumer could continue to use the older signature even if newer versions with the new signature were deployed, until there was a chance to update their calls. Deprecated signatures signatures would be maintained ensuring backward compatibility.In that sense most webservices are SOA, but SOA might be a host of webservices.
If you search for SOA on the internet you'll find a lot of literature, but a lot of them won't make sense because all vendors tout their own frameworks so instead of stating the theory they dive into the detailed implementations.
Here are some links...
IBM's http://www-01.ibm.com/software/solutions/soa/
microsoft http://msdn.microsoft.com/en-us/library/aa480021.aspx
oracle http://www.oracle.com/us/technologies/soa/index.html
Most of the literature maybe marketing stuff.
Here is a generic explanation of SOA
http://javaboutique.internet.com/tutorials/serv_orient/
The primary motive is to decouple the producer and consumer of information so that one is agnostic about the internals of the other.
It is usually a set of webservices, but not always. For example you could have have a layer of services that are invoked by Flex (Flash UI) from a browser where flex is a thick client like applet/flex. (Thin client is html/javascript).
So in our case, the thick browser client is invoking services on the app server.
SOA is not really anything different, it's just a design pattern or architecture. One example would be EJBs where you have a bunch of EJBs providing services and the client code will lookup an EJB and invoke a method on it.You could deploy updates to the EJBs without impacting the client code as long as the method signature doesn't change, but mostly SOA is a set of webservices; that's the most common usage. For e.g. in the internet bank example one could do releases of our own, without any change on the service provider and the service provider could publish new APIs but would still support old APIs that the client was using, until it could get a chance to migrate to new APIs.
That's all there is to SOA - the implementation is quite varying. If the client invokes a service getCustomer(customerID) and the service provider creates a new method getCustomer(customerName) the SOA consumer could continue to use the older signature even if newer versions with the new signature were deployed, until there was a chance to update their calls. Deprecated signatures signatures would be maintained ensuring backward compatibility.In that sense most webservices are SOA, but SOA might be a host of webservices.
If you search for SOA on the internet you'll find a lot of literature, but a lot of them won't make sense because all vendors tout their own frameworks so instead of stating the theory they dive into the detailed implementations.
Here are some links...
IBM's http://www-01.ibm.com/software/solutions/soa/
microsoft http://msdn.microsoft.com/en-us/library/aa480021.aspx
oracle http://www.oracle.com/us/technologies/soa/index.html
Most of the literature maybe marketing stuff.
Here is a generic explanation of SOA
http://javaboutique.internet.com/tutorials/serv_orient/
Sunday, September 11, 2011
Using JBPM for workflow
This example demostrates the use of JBPM as a workflow engine. I have used the jars in the package jbpm-jpdl-suite-3.2.3.zip available on the sourceforge site. In addition to this you will need the mysql jar containing the mysql jdbc driver.
I have used a standalone example to demonstrate the use of jbpm while in real practise it would require a full fledged GUI where state changes in ItemRequest would take place due to user actions. The various states that a RequestItem object can go into is shown in the diagram below and described in processdefinition.xml as a workflow.
processdefinition.xml
<?xml version="1.0" encoding="UTF-8"?>
<process-definition xmlns="" name="Mattiz_Process_Flow">
<start-state name="Start">
<transition to="Created" name="Created"></transition>
</start-state>
<state name="Created">
<event type="node-enter">
<action class="com.mattiz.jbpm.handler.CounterHandler"
name="Counter Update">
<status>Created</status>
</action>
<action class="com.mattiz.jbpm.handler.StatusHandler"
name="Status Update">
<status>Created</status>
</action>
</event>
<transition to="Submitted" name="Submitted"></transition>
</state>
<state name="Submitted">
<event type="node-enter">
<action class="com.mattiz.jbpm.handler.CounterHandler"
name="Counter Update">
<status>Submitted</status>
</action>
<action class="com.mattiz.jbpm.handler.StatusHandler"
name="Status Update">
<status>Submitted</status>
</action>
</event>
<transition to="Approved" name="Approved"></transition>
<transition to="Rejected" name="Rejected"></transition>
</state>
<state name="Approved">
<event type="node-enter">
<action class="com.mattiz.jbpm.handler.CounterHandler"
name="Counter Update">
<status>Approved</status>
</action>
<action class="com.mattiz.jbpm.handler.StatusHandler"
name="Status Update">
<status>Approved</status>
</action>
</event>
<transition to="Cancelled" name="Cancelled"></transition>
<transition to="Rejected" name="Rejected"></transition>
<transition to="Archived" name="Archived"></transition>
</state>
<state name="Rejected">
<event type="node-enter">
<action class="com.mattiz.jbpm.handler.CounterHandler"
name="Counter Update">
<status>Rejected</status>
</action>
<action class="com.mattiz.jbpm.handler.StatusHandler"
name="Status Update">
<status>Rejected</status>
</action>
</event>
<transition to="Archived" name="Archived"></transition>
</state>
<state name="Cancelled">
<event type="node-enter">
<action class="com.mattiz.jbpm.handler.CounterHandler"
name="Counter Update">
<status>Cancelled</status>
</action>
<action class="com.mattiz.jbpm.handler.StatusHandler"
name="Status Update">
<status>Cancelled</status>
</action>
</event>
<transition to="Archived" name="Archived"></transition>
</state>
<end-state name="Archived">
<event type="node-enter">
<action class="com.mattiz.jbpm.handler.CounterHandler"
name="Counter Update">
<status>Archived</status>
</action>
<action class="com.mattiz.jbpm.handler.StatusHandler"
name="Status Update">
<status>Archived</status>
</action>
</event>
</end-state>
</process-definition>
JBPM requires an initial setup in the database. JBPM tables are created in the mysql table when the following line(commented) in the main class is executed.
new JBPMTest().setupSchema();//run this only once for setup
This is a one time execution and should be run only once or whenever there is a change to processdefinition.xml.
Database jbpmappdb need to be created in mysql before running the above line.
The main class below:
package com.mattiz.jbpm;
import org.jbpm.JbpmConfiguration;
import org.jbpm.JbpmContext;
import org.jbpm.graph.def.ProcessDefinition;
import com.mattiz.jbpm.dto.ItemRequest;
import com.mattiz.jbpm.handler.CounterHandler;
public class JBPMTest {
public static void main(String args[]) {
//new JBPMTest().setupSchema();//run this only once for setup
new JBPMTest().testHelloWorldProcess();
}
public void testHelloWorldProcess() {
ItemRequestController ic = new ItemRequestController();
ItemRequest itemRequest = new ItemRequest();
itemRequest.setId("1");
itemRequest.setName("My Item Request");
itemRequest.setStatus("Submitted");
ic.createItemRequest(itemRequest);
System.out.println("PENDING "
+ CounterHandler.itemRequestsPendingCounter);
System.out.println("CONFIRMED "
+ CounterHandler.itemRequestsConfirmedCounter);
System.out.println("===================================");
String oldStatus = itemRequest.getStatus();
itemRequest.setStatus("Approved");
ic.updateItemRequest(itemRequest, oldStatus);
System.out.println("PENDING "
+ CounterHandler.itemRequestsPendingCounter);
System.out.println("CONFIRMED "
+ CounterHandler.itemRequestsConfirmedCounter);
System.out.println("===================================");
oldStatus = itemRequest.getStatus();
itemRequest.setStatus("Cancelled");
ic.updateItemRequest(itemRequest, oldStatus);
System.out.println("PENDING "
+ CounterHandler.itemRequestsPendingCounter);
System.out.println("CONFIRMED "
+ CounterHandler.itemRequestsConfirmedCounter);
System.out.println("===================================");
System.out.println("===================================");
itemRequest = new ItemRequest();
itemRequest.setId("2");
itemRequest.setName("My Second Item Request");
itemRequest.setStatus("Submitted");
ic = new ItemRequestController();
ic.createItemRequest(itemRequest);
System.out.println("PENDING "
+ CounterHandler.itemRequestsPendingCounter);
System.out.println("CONFIRMED "
+ CounterHandler.itemRequestsConfirmedCounter);
System.out.println("===================================");
oldStatus = itemRequest.getStatus();
itemRequest.setStatus("Approved");
ic.updateItemRequest(itemRequest, oldStatus);
System.out.println("PENDING "
+ CounterHandler.itemRequestsPendingCounter);
System.out.println("CONFIRMED "
+ CounterHandler.itemRequestsConfirmedCounter);
System.out.println("===================================");
oldStatus = itemRequest.getStatus();
itemRequest.setStatus("Rejected");
ic.updateItemRequest(itemRequest, oldStatus);
System.out.println("PENDING "
+ CounterHandler.itemRequestsPendingCounter);
System.out.println("CONFIRMED "
+ CounterHandler.itemRequestsConfirmedCounter);
System.out.println("===================================");
System.out.println("===================================");
itemRequest = new ItemRequest();
itemRequest.setId("3");
itemRequest.setName("My Third Item Request");
itemRequest.setStatus("Submitted");
ic = new ItemRequestController();
ic.createItemRequest(itemRequest);
System.out.println("PENDING "
+ CounterHandler.itemRequestsPendingCounter);
System.out.println("CONFIRMED "
+ CounterHandler.itemRequestsConfirmedCounter);
System.out.println("===================================");
oldStatus = itemRequest.getStatus();
itemRequest.setStatus("Approved");
ic.updateItemRequest(itemRequest, oldStatus);
System.out.println("PENDING "
+ CounterHandler.itemRequestsPendingCounter);
System.out.println("CONFIRMED "
+ CounterHandler.itemRequestsConfirmedCounter);
System.out.println("===================================");
System.out.println("===================================");
itemRequest = new ItemRequest();
itemRequest.setId("4");
itemRequest.setName("My Fourth Item Request");
itemRequest.setStatus("Submitted");
ic = new ItemRequestController();
ic.createItemRequest(itemRequest);
System.out.println("PENDING "
+ CounterHandler.itemRequestsPendingCounter);
System.out.println("CONFIRMED "
+ CounterHandler.itemRequestsConfirmedCounter);
System.out.println("===================================");
oldStatus = itemRequest.getStatus();
itemRequest.setStatus("Approved");
ic.updateItemRequest(itemRequest, oldStatus);
System.out.println("PENDING "
+ CounterHandler.itemRequestsPendingCounter);
System.out.println("CONFIRMED "
+ CounterHandler.itemRequestsConfirmedCounter);
System.out.println("===================================");
oldStatus = itemRequest.getStatus();
itemRequest.setStatus("Rejected");
ic.updateItemRequest(itemRequest, oldStatus);
System.out.println("PENDING "
+ CounterHandler.itemRequestsPendingCounter);
System.out.println("CONFIRMED "
+ CounterHandler.itemRequestsConfirmedCounter);
System.out.println("===================================");
System.out.println("===================================");
}
public void setupSchema() {
JbpmConfiguration config = JbpmConfiguration
.parseResource("jbpm.cfg.xml");
ProcessDefinition processDefinition = ProcessDefinition
.parseXmlResource("processdefinition.xml");
// create schema.
config.createSchema();
JbpmContext context = config.createJbpmContext();
// deploy process definition.
try {
context.deployProcessDefinition(processDefinition);
} catch (Exception e) {
e.printStackTrace();
} finally {
context.close();
}
}
}
The hibernate configuration file hibernate.cfg.xml below:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- JDBC connection properties -->
<property name="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name="hibernate.connection.driver_class">
com.mysql.jdbc.Driver
</property>
<property name="hibernate.connection.url">
jdbc:mysql://localhost:3306/jbpmappdb
</property>
<property name="hibernate.connection.username">admin</property>
<property name="hibernate.connection.password">admin</property>
<!-- JDBC connection properties (end) -->
<!-- Simple memory-only cache -->
<property name="hibernate.cache.provider_class">
org.hibernate.cache.HashtableCacheProvider
</property>
<!-- logging properties -->
<property name="hibernate.format_sql">true</property>
<property name="hibernate.use_sql_comments">true</property>
<mapping resource="org/jbpm/db/hibernate.queries.hbm.xml" />
<mapping resource="org/jbpm/graph/action/MailAction.hbm.xml" />
<mapping
resource="org/jbpm/graph/def/ProcessDefinition.hbm.xml" />
<mapping resource="org/jbpm/graph/def/Node.hbm.xml" />
<mapping resource="org/jbpm/graph/def/Transition.hbm.xml" />
<mapping resource="org/jbpm/graph/def/Event.hbm.xml" />
<mapping resource="org/jbpm/graph/def/Action.hbm.xml" />
<mapping resource="org/jbpm/graph/def/SuperState.hbm.xml" />
<mapping resource="org/jbpm/graph/def/ExceptionHandler.hbm.xml" />
<mapping resource="org/jbpm/instantiation/Delegation.hbm.xml" />
<mapping resource="org/jbpm/graph/action/Script.hbm.xml" />
<mapping resource="org/jbpm/graph/node/StartState.hbm.xml" />
<mapping resource="org/jbpm/graph/node/EndState.hbm.xml" />
<mapping resource="org/jbpm/graph/node/ProcessState.hbm.xml" />
<mapping resource="org/jbpm/graph/node/Decision.hbm.xml" />
<mapping resource="org/jbpm/graph/node/Fork.hbm.xml" />
<mapping resource="org/jbpm/graph/node/Join.hbm.xml" />
<mapping resource="org/jbpm/graph/node/MailNode.hbm.xml" />
<mapping resource="org/jbpm/graph/node/State.hbm.xml" />
<mapping resource="org/jbpm/graph/node/TaskNode.hbm.xml" />
<mapping
resource="org/jbpm/context/def/ContextDefinition.hbm.xml" />
<mapping resource="org/jbpm/context/def/VariableAccess.hbm.xml" />
<mapping resource="org/jbpm/bytes/ByteArray.hbm.xml" />
<mapping
resource="org/jbpm/module/def/ModuleDefinition.hbm.xml" />
<mapping resource="org/jbpm/file/def/FileDefinition.hbm.xml" />
<mapping
resource="org/jbpm/taskmgmt/def/TaskMgmtDefinition.hbm.xml" />
<mapping resource="org/jbpm/taskmgmt/def/Swimlane.hbm.xml" />
<mapping resource="org/jbpm/taskmgmt/def/Task.hbm.xml" />
<mapping
resource="org/jbpm/taskmgmt/def/TaskController.hbm.xml" />
<mapping
resource="org/jbpm/scheduler/def/CreateTimerAction.hbm.xml" />
<mapping
resource="org/jbpm/scheduler/def/CancelTimerAction.hbm.xml" />
<mapping resource="org/jbpm/graph/exe/Comment.hbm.xml" />
<mapping resource="org/jbpm/graph/exe/ProcessInstance.hbm.xml" />
<mapping resource="org/jbpm/graph/exe/Token.hbm.xml" />
<mapping resource="org/jbpm/graph/exe/RuntimeAction.hbm.xml" />
<mapping resource="org/jbpm/module/exe/ModuleInstance.hbm.xml" />
<mapping
resource="org/jbpm/context/exe/ContextInstance.hbm.xml" />
<mapping
resource="org/jbpm/context/exe/TokenVariableMap.hbm.xml" />
<mapping
resource="org/jbpm/context/exe/VariableInstance.hbm.xml" />
<mapping
resource="org/jbpm/context/exe/variableinstance/ByteArrayInstance.hbm.xml" />
<mapping
resource="org/jbpm/context/exe/variableinstance/DateInstance.hbm.xml" />
<mapping
resource="org/jbpm/context/exe/variableinstance/DoubleInstance.hbm.xml" />
<mapping
resource="org/jbpm/context/exe/variableinstance/HibernateLongInstance.hbm.xml" />
<mapping
resource="org/jbpm/context/exe/variableinstance/HibernateStringInstance.hbm.xml" />
<mapping
resource="org/jbpm/context/exe/variableinstance/LongInstance.hbm.xml" />
<mapping
resource="org/jbpm/context/exe/variableinstance/NullInstance.hbm.xml" />
<mapping
resource="org/jbpm/context/exe/variableinstance/StringInstance.hbm.xml" />
<mapping
resource="org/jbpm/taskmgmt/exe/TaskMgmtInstance.hbm.xml" />
<mapping resource="org/jbpm/taskmgmt/exe/TaskInstance.hbm.xml" />
<mapping resource="org/jbpm/taskmgmt/exe/PooledActor.hbm.xml" />
<mapping
resource="org/jbpm/taskmgmt/exe/SwimlaneInstance.hbm.xml" />
<mapping resource="org/jbpm/logging/log/ProcessLog.hbm.xml" />
<mapping resource="org/jbpm/logging/log/MessageLog.hbm.xml" />
<mapping resource="org/jbpm/logging/log/CompositeLog.hbm.xml" />
<mapping resource="org/jbpm/graph/log/ActionLog.hbm.xml" />
<mapping resource="org/jbpm/graph/log/NodeLog.hbm.xml" />
<mapping
resource="org/jbpm/graph/log/ProcessInstanceCreateLog.hbm.xml" />
<mapping
resource="org/jbpm/graph/log/ProcessInstanceEndLog.hbm.xml" />
<mapping resource="org/jbpm/graph/log/ProcessStateLog.hbm.xml" />
<mapping resource="org/jbpm/graph/log/SignalLog.hbm.xml" />
<mapping resource="org/jbpm/graph/log/TokenCreateLog.hbm.xml" />
<mapping resource="org/jbpm/graph/log/TokenEndLog.hbm.xml" />
<mapping resource="org/jbpm/graph/log/TransitionLog.hbm.xml" />
<mapping resource="org/jbpm/context/log/VariableLog.hbm.xml" />
<mapping
resource="org/jbpm/context/log/VariableCreateLog.hbm.xml" />
<mapping
resource="org/jbpm/context/log/VariableDeleteLog.hbm.xml" />
<mapping
resource="org/jbpm/context/log/VariableUpdateLog.hbm.xml" />
<mapping
resource="org/jbpm/context/log/variableinstance/ByteArrayUpdateLog.hbm.xml" />
<mapping
resource="org/jbpm/context/log/variableinstance/DateUpdateLog.hbm.xml" />
<mapping
resource="org/jbpm/context/log/variableinstance/DoubleUpdateLog.hbm.xml" />
<mapping
resource="org/jbpm/context/log/variableinstance/HibernateLongUpdateLog.hbm.xml" />
<mapping
resource="org/jbpm/context/log/variableinstance/HibernateStringUpdateLog.hbm.xml" />
<mapping
resource="org/jbpm/context/log/variableinstance/LongUpdateLog.hbm.xml" />
<mapping
resource="org/jbpm/context/log/variableinstance/StringUpdateLog.hbm.xml" />
<mapping resource="org/jbpm/taskmgmt/log/TaskLog.hbm.xml" />
<mapping resource="org/jbpm/taskmgmt/log/TaskCreateLog.hbm.xml" />
<mapping resource="org/jbpm/taskmgmt/log/TaskAssignLog.hbm.xml" />
<mapping resource="org/jbpm/taskmgmt/log/TaskEndLog.hbm.xml" />
<mapping resource="org/jbpm/taskmgmt/log/SwimlaneLog.hbm.xml" />
<mapping
resource="org/jbpm/taskmgmt/log/SwimlaneCreateLog.hbm.xml" />
<mapping
resource="org/jbpm/taskmgmt/log/SwimlaneAssignLog.hbm.xml" />
<class-cache class="org.jbpm.context.def.VariableAccess"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.file.def.FileDefinition.processFiles"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.action.Script.variableAccesses"
usage="nonstrict-read-write" />
<class-cache class="org.jbpm.graph.def.Action"
usage="nonstrict-read-write" />
<class-cache class="org.jbpm.graph.def.Event"
usage="nonstrict-read-write" />
<collection-cache collection="org.jbpm.graph.def.Event.actions"
usage="nonstrict-read-write" />
<class-cache class="org.jbpm.graph.def.ExceptionHandler"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.def.ExceptionHandler.actions"
usage="nonstrict-read-write" />
<class-cache class="org.jbpm.graph.def.Node"
usage="nonstrict-read-write" />
<collection-cache collection="org.jbpm.graph.def.Node.events"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.def.Node.exceptionHandlers"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.def.Node.leavingTransitions"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.def.Node.arrivingTransitions"
usage="nonstrict-read-write" />
<class-cache class="org.jbpm.graph.def.ProcessDefinition"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.def.ProcessDefinition.events"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.def.ProcessDefinition.exceptionHandlers"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.def.ProcessDefinition.nodes"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.def.ProcessDefinition.actions"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.def.ProcessDefinition.definitions"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.def.SuperState.nodes"
usage="nonstrict-read-write" />
<class-cache class="org.jbpm.graph.def.Transition"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.def.Transition.events"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.def.Transition.exceptionHandlers"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.node.Decision.decisionConditions"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.node.ProcessState.variableAccesses"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.node.TaskNode.tasks"
usage="nonstrict-read-write" />
<class-cache class="org.jbpm.instantiation.Delegation"
usage="nonstrict-read-write" />
<class-cache class="org.jbpm.module.def.ModuleDefinition"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.taskmgmt.def.Swimlane.tasks"
usage="nonstrict-read-write" />
<class-cache class="org.jbpm.taskmgmt.def.TaskController"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.taskmgmt.def.TaskController.variableAccesses"
usage="nonstrict-read-write" />
<class-cache class="org.jbpm.taskmgmt.def.Task"
usage="nonstrict-read-write" />
<collection-cache collection="org.jbpm.taskmgmt.def.Task.events"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.taskmgmt.def.Task.exceptionHandlers"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.taskmgmt.def.TaskMgmtDefinition.swimlanes"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.taskmgmt.def.TaskMgmtDefinition.tasks"
usage="nonstrict-read-write" />
</session-factory>
</hibernate-configuration>
The jbpm configuration file jbpm.cfg.xml below:
<jbpm-configuration>
<jbpm-context>
<service name="authentication"
factory="org.jbpm.security.authentication.DefaultAuthenticationServiceFactory" />
<!-- Logging Service (begin) -->
<service name="logging"
factory="org.jbpm.logging.db.DbLoggingServiceFactory" />
<!-- Logging Service (end) -->
<service name="message"
factory="org.jbpm.msg.db.DbMessageServiceFactory" />
<service name="persistence"
factory="org.jbpm.persistence.db.DbPersistenceServiceFactory" />
<service name="scheduler"
factory="org.jbpm.scheduler.db.DbSchedulerServiceFactory" />
<service name="tx" factory="org.jbpm.tx.TxServiceFactory" />
</jbpm-context>
<string name="resource.hibernate.cfg.xml" value="hibernate.cfg.xml" />
<!-- configuration resource files pointing to default configuration files in jbpm-{version}.jar -->
<string name="resource.business.calendar"
value="org/jbpm/calendar/jbpm.business.calendar.properties" />
<string name="resource.default.modules"
value="org/jbpm/graph/def/jbpm.default.modules.properties" />
<string name="resource.action.types"
value="org/jbpm/graph/action/action.types.xml" />
<string name="resource.node.types"
value="org/jbpm/graph/node/node.types.xml" />
<string name="resource.parsers"
value="org/jbpm/jpdl/par/jbpm.parsers.xml" />
<string name="resource.varmapping"
value="org/jbpm/context/exe/jbpm.varmapping.xml" />
<string name="resource.mail.templates"
value="jbpm.mail.templates.xml" />
<!-- make sure the block size matches the length in ByteArray.hbm.xml -->
<int name="jbpm.byte.block.size" value="1024" singleton="true" />
<bean name="jbpm.task.instance.factory"
class="org.jbpm.taskmgmt.impl.DefaultTaskInstanceFactoryImpl"
singleton="true" />
<bean name="jbpm.variable.resolver"
class="org.jbpm.jpdl.el.impl.JbpmVariableResolver" singleton="true" />
<string name="jbpm.mail.smtp.host" value="localhost" />
<bean name="jbpm.mail.address.resolver"
class="org.jbpm.identity.mail.IdentityAddressResolver"
singleton="true" />
<string name="jbpm.mail.from.address" value="jbpm@noreply" />
<bean name="jbpm.job.executor"
class="org.jbpm.job.executor.JobExecutor">
<field name="jbpmConfiguration">
<ref bean="jbpmConfiguration" />
</field>
<field name="name">
<string value="JbpmJobExecutor" />
</field>
<field name="nbrOfThreads">
<int value="1" />
</field>
<field name="idleInterval">
<int value="5000" />
</field>
<field name="maxIdleInterval">
<int value="3600000" />
</field>
<field name="historyMaxSize">
<int value="20" />
</field>
<field name="maxLockTime">
<int value="600000" />
</field>
<field name="lockMonitorInterval">
<int value="60000" />
</field>
<field name="lockBufferTime">
<int value="5000" />
</field>
</bean>
</jbpm-configuration>
The domain class is ItemRequest.java
package com.mattiz.jbpm.dto;
public class ItemRequest {
private String id;
private String name;
private String status;
/** JBPM mapping. */
private Long processInstanceId;
public Long getProcessInstanceId() {
return processInstanceId;
}
public void setProcessInstanceId(Long processInstanceId) {
this.processInstanceId = processInstanceId;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
Note that it defines a variable processInstanceId required for JBPM.
The class that makes JBPM work below:
package com.mattiz.jbpm.workflow;
import java.util.ArrayList;
import java.util.List;
import org.jbpm.JbpmConfiguration;
import org.jbpm.JbpmContext;
import org.jbpm.JbpmException;
import org.jbpm.graph.def.Transition;
import org.jbpm.graph.exe.ProcessInstance;
import com.mattiz.jbpm.dto.ItemRequest;
public class JBPMWorkFlow {
JbpmConfiguration config = JbpmConfiguration.parseResource("jbpm.cfg.xml");
public Long createItemRequestFlow(ItemRequest ItemRequest) {
JbpmContext context = config.createJbpmContext();
ProcessInstance pi = null;
try {
pi = context.newProcessInstance("Mattiz_Process_Flow");
pi.getContextInstance().setVariable("ItemRequestId",
ItemRequest.getId());
pi.signal();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
context.close();
}
return pi.getId();
}
public List<String> makeTransition(Long processInstanceId,
String transitionName) throws RuntimeException {
JbpmContext context = config.createJbpmContext();
List<String> transitionNames = new ArrayList<String>();
try {
ProcessInstance pi = context.getProcessInstance(processInstanceId);
List<Transition> leavingTransitions = pi.getRootToken().getNode()
.getLeavingTransitions();
for (Transition t : leavingTransitions) {
transitionNames.add(t.getName());
}
System.out.println("Choices are " + leavingTransitions);
System.out.println("ACTION IS--->" + transitionName);
pi.signal(transitionName);
} catch (JbpmException e) {
throw new RuntimeException(e);
} finally {
context.close();
}
return transitionNames;
}
public void makeCounterChanges(Long processInstanceId, String oldStatus)
throws RuntimeException {
JbpmContext context = config.createJbpmContext();
try {
ProcessInstance pi = context.getProcessInstance(processInstanceId);
pi.getContextInstance().setVariable("oldStatus", oldStatus);
} catch (JbpmException e) {
throw new RuntimeException();
} finally {
context.close();
}
}
}
We define two JBPM handler classes.
CounterHandler keeps track of the number of ItemRequest objects in submitted state and in the approved state using two static counters whose values are printed out in the main class whenever a state change occurs.
package com.mattiz.jbpm.handler;
import java.util.HashMap;
import java.util.Map;
import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.exe.ExecutionContext;
public class CounterHandler implements ActionHandler {
private static Map<String, String> actionForStatusChange = new HashMap<String, String>();
public static int itemRequestsPendingCounter;
public static int itemRequestsConfirmedCounter;
//status is passed on implicitly from the processdefinition xml
private String status;
static {
actionForStatusChange.put("NEW" + "_" + "Submitted",
"addToItemRequestsPending");
actionForStatusChange.put("Submitted" + "_" + "Approved",
"addToItemRequestsConfirmedAndSubtractFromItemRequestsPending");
actionForStatusChange.put("Approved" + "_" + "Rejected",
"subtractFromItemRequestsConfirmed");
actionForStatusChange.put("Submitted" + "_" + "Rejected",
"subtractFromItemRequestsPending");
actionForStatusChange.put("Approved" + "_" + "Cancelled",
"subtractFromItemRequestsConfirmed");
}
public void execute(ExecutionContext executionContext) throws Exception {
String oldStatus = (String) executionContext.getContextInstance()
.getVariable("oldStatus");
if (oldStatus == null) {
oldStatus = "NEW";
}
String action = actionForStatusChange.get(oldStatus + "_" + status);
if (null != action) {
if (action.equals("addToItemRequestsPending")) {
itemRequestsPendingCounter++;
} else if (action.equals("addToItemRequestsConfirmed")) {
itemRequestsConfirmedCounter++;
} else if (action.equals("subtractFromItemRequestsConfirmed")) {
itemRequestsConfirmedCounter--;
} else if (action.equals("subtractFromItemRequestsPending")) {
itemRequestsPendingCounter--;
}else if (action.equals("addToItemRequestsConfirmedAndSubtractFromItemRequestsPending")){
itemRequestsPendingCounter--;
itemRequestsConfirmedCounter++;
}else if (action.equals("subtractFromItemRequestsPending")){
itemRequestsPendingCounter--;
}
}
}
}
StatusHandler is also a listener which is triggered whenever status change occurs. In this app it does not do anything, but in a real app can be used to update the status of ItemRequest in the database.
package com.mattiz.jbpm.handler;
import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.exe.ExecutionContext;
public class StatusHandler implements ActionHandler {
private String status;
public void execute(ExecutionContext executionContext) throws Exception {
String itemRequestId = (String) executionContext.getContextInstance()
.getVariable("ItemRequestId");
System.out.println("Item "+itemRequestId);
executionContext.getContextInstance().setVariable("status", status);
//do something important like update the status in the database using the itemRequestId
}
}
ItemRequestController is the interface to the frontend and calls methods in the workflow service class:
package com.mattiz.jbpm;
import java.util.List;
import com.mattiz.jbpm.dto.ItemRequest;
import com.mattiz.jbpm.workflow.JBPMWorkFlow;
public class ItemRequestController {
JBPMWorkFlow workflowService = new JBPMWorkFlow();
public List<String> createItemRequest(ItemRequest itemRequest) {
Long processInstanceId = workflowService
.createItemRequestFlow(itemRequest);
itemRequest.setProcessInstanceId(processInstanceId);
workflowService.makeCounterChanges(processInstanceId, null);
List<String> transitions = workflowService.makeTransition(
itemRequest.getProcessInstanceId(), itemRequest.getStatus());
return transitions;
}
public List<String> updateItemRequest(ItemRequest itemRequest, String oldStatus) {
Long processInstanceId = itemRequest.getProcessInstanceId();
workflowService.makeCounterChanges(processInstanceId, oldStatus);
List<String> transitions = workflowService.makeTransition(processInstanceId, itemRequest
.getStatus());
return transitions;
}
}
The output of the main class looks something like this:
Choices are [Transition(Submitted)]
ACTION IS--->Submitted
Item 1
PENDING 1
CONFIRMED 0
===================================
Choices are [Transition(Approved), Transition(Rejected)]
ACTION IS--->Approved
Item 1
PENDING 0
CONFIRMED 1
===================================
Choices are [Transition(Cancelled), Transition(Rejected), Transition(Archived)]
ACTION IS--->Cancelled
Item 1
PENDING 0
CONFIRMED 0
===================================
===================================
Choices are [Transition(Submitted)]
ACTION IS--->Submitted
Item 2
PENDING 1
CONFIRMED 0
===================================
Choices are [Transition(Approved), Transition(Rejected)]
ACTION IS--->Approved
Item 2
PENDING 0
CONFIRMED 1
===================================
Choices are [Transition(Cancelled), Transition(Rejected), Transition(Archived)]
ACTION IS--->Rejected
Item 2
PENDING 0
CONFIRMED 0
===================================
===================================
Choices are [Transition(Submitted)]
ACTION IS--->Submitted
Item 3
PENDING 1
CONFIRMED 0
===================================
Choices are [Transition(Approved), Transition(Rejected)]
ACTION IS--->Approved
Item 3
PENDING 0
CONFIRMED 1
===================================
===================================
Choices are [Transition(Submitted)]
ACTION IS--->Submitted
Item 4
PENDING 1
CONFIRMED 1
===================================
Choices are [Transition(Approved), Transition(Rejected)]
ACTION IS--->Approved
Item 4
PENDING 0
CONFIRMED 2
===================================
Choices are [Transition(Cancelled), Transition(Rejected), Transition(Archived)]
ACTION IS--->Rejected
Item 4
PENDING 0
CONFIRMED 1
===================================
===================================
I have used a standalone example to demonstrate the use of jbpm while in real practise it would require a full fledged GUI where state changes in ItemRequest would take place due to user actions. The various states that a RequestItem object can go into is shown in the diagram below and described in processdefinition.xml as a workflow.
processdefinition.xml
<?xml version="1.0" encoding="UTF-8"?>
<process-definition xmlns="" name="Mattiz_Process_Flow">
<start-state name="Start">
<transition to="Created" name="Created"></transition>
</start-state>
<state name="Created">
<event type="node-enter">
<action class="com.mattiz.jbpm.handler.CounterHandler"
name="Counter Update">
<status>Created</status>
</action>
<action class="com.mattiz.jbpm.handler.StatusHandler"
name="Status Update">
<status>Created</status>
</action>
</event>
<transition to="Submitted" name="Submitted"></transition>
</state>
<state name="Submitted">
<event type="node-enter">
<action class="com.mattiz.jbpm.handler.CounterHandler"
name="Counter Update">
<status>Submitted</status>
</action>
<action class="com.mattiz.jbpm.handler.StatusHandler"
name="Status Update">
<status>Submitted</status>
</action>
</event>
<transition to="Approved" name="Approved"></transition>
<transition to="Rejected" name="Rejected"></transition>
</state>
<state name="Approved">
<event type="node-enter">
<action class="com.mattiz.jbpm.handler.CounterHandler"
name="Counter Update">
<status>Approved</status>
</action>
<action class="com.mattiz.jbpm.handler.StatusHandler"
name="Status Update">
<status>Approved</status>
</action>
</event>
<transition to="Cancelled" name="Cancelled"></transition>
<transition to="Rejected" name="Rejected"></transition>
<transition to="Archived" name="Archived"></transition>
</state>
<state name="Rejected">
<event type="node-enter">
<action class="com.mattiz.jbpm.handler.CounterHandler"
name="Counter Update">
<status>Rejected</status>
</action>
<action class="com.mattiz.jbpm.handler.StatusHandler"
name="Status Update">
<status>Rejected</status>
</action>
</event>
<transition to="Archived" name="Archived"></transition>
</state>
<state name="Cancelled">
<event type="node-enter">
<action class="com.mattiz.jbpm.handler.CounterHandler"
name="Counter Update">
<status>Cancelled</status>
</action>
<action class="com.mattiz.jbpm.handler.StatusHandler"
name="Status Update">
<status>Cancelled</status>
</action>
</event>
<transition to="Archived" name="Archived"></transition>
</state>
<end-state name="Archived">
<event type="node-enter">
<action class="com.mattiz.jbpm.handler.CounterHandler"
name="Counter Update">
<status>Archived</status>
</action>
<action class="com.mattiz.jbpm.handler.StatusHandler"
name="Status Update">
<status>Archived</status>
</action>
</event>
</end-state>
</process-definition>
JBPM requires an initial setup in the database. JBPM tables are created in the mysql table when the following line(commented) in the main class is executed.
new JBPMTest().setupSchema();//run this only once for setup
This is a one time execution and should be run only once or whenever there is a change to processdefinition.xml.
Database jbpmappdb need to be created in mysql before running the above line.
The main class below:
package com.mattiz.jbpm;
import org.jbpm.JbpmConfiguration;
import org.jbpm.JbpmContext;
import org.jbpm.graph.def.ProcessDefinition;
import com.mattiz.jbpm.dto.ItemRequest;
import com.mattiz.jbpm.handler.CounterHandler;
public class JBPMTest {
public static void main(String args[]) {
//new JBPMTest().setupSchema();//run this only once for setup
new JBPMTest().testHelloWorldProcess();
}
public void testHelloWorldProcess() {
ItemRequestController ic = new ItemRequestController();
ItemRequest itemRequest = new ItemRequest();
itemRequest.setId("1");
itemRequest.setName("My Item Request");
itemRequest.setStatus("Submitted");
ic.createItemRequest(itemRequest);
System.out.println("PENDING "
+ CounterHandler.itemRequestsPendingCounter);
System.out.println("CONFIRMED "
+ CounterHandler.itemRequestsConfirmedCounter);
System.out.println("===================================");
String oldStatus = itemRequest.getStatus();
itemRequest.setStatus("Approved");
ic.updateItemRequest(itemRequest, oldStatus);
System.out.println("PENDING "
+ CounterHandler.itemRequestsPendingCounter);
System.out.println("CONFIRMED "
+ CounterHandler.itemRequestsConfirmedCounter);
System.out.println("===================================");
oldStatus = itemRequest.getStatus();
itemRequest.setStatus("Cancelled");
ic.updateItemRequest(itemRequest, oldStatus);
System.out.println("PENDING "
+ CounterHandler.itemRequestsPendingCounter);
System.out.println("CONFIRMED "
+ CounterHandler.itemRequestsConfirmedCounter);
System.out.println("===================================");
System.out.println("===================================");
itemRequest = new ItemRequest();
itemRequest.setId("2");
itemRequest.setName("My Second Item Request");
itemRequest.setStatus("Submitted");
ic = new ItemRequestController();
ic.createItemRequest(itemRequest);
System.out.println("PENDING "
+ CounterHandler.itemRequestsPendingCounter);
System.out.println("CONFIRMED "
+ CounterHandler.itemRequestsConfirmedCounter);
System.out.println("===================================");
oldStatus = itemRequest.getStatus();
itemRequest.setStatus("Approved");
ic.updateItemRequest(itemRequest, oldStatus);
System.out.println("PENDING "
+ CounterHandler.itemRequestsPendingCounter);
System.out.println("CONFIRMED "
+ CounterHandler.itemRequestsConfirmedCounter);
System.out.println("===================================");
oldStatus = itemRequest.getStatus();
itemRequest.setStatus("Rejected");
ic.updateItemRequest(itemRequest, oldStatus);
System.out.println("PENDING "
+ CounterHandler.itemRequestsPendingCounter);
System.out.println("CONFIRMED "
+ CounterHandler.itemRequestsConfirmedCounter);
System.out.println("===================================");
System.out.println("===================================");
itemRequest = new ItemRequest();
itemRequest.setId("3");
itemRequest.setName("My Third Item Request");
itemRequest.setStatus("Submitted");
ic = new ItemRequestController();
ic.createItemRequest(itemRequest);
System.out.println("PENDING "
+ CounterHandler.itemRequestsPendingCounter);
System.out.println("CONFIRMED "
+ CounterHandler.itemRequestsConfirmedCounter);
System.out.println("===================================");
oldStatus = itemRequest.getStatus();
itemRequest.setStatus("Approved");
ic.updateItemRequest(itemRequest, oldStatus);
System.out.println("PENDING "
+ CounterHandler.itemRequestsPendingCounter);
System.out.println("CONFIRMED "
+ CounterHandler.itemRequestsConfirmedCounter);
System.out.println("===================================");
System.out.println("===================================");
itemRequest = new ItemRequest();
itemRequest.setId("4");
itemRequest.setName("My Fourth Item Request");
itemRequest.setStatus("Submitted");
ic = new ItemRequestController();
ic.createItemRequest(itemRequest);
System.out.println("PENDING "
+ CounterHandler.itemRequestsPendingCounter);
System.out.println("CONFIRMED "
+ CounterHandler.itemRequestsConfirmedCounter);
System.out.println("===================================");
oldStatus = itemRequest.getStatus();
itemRequest.setStatus("Approved");
ic.updateItemRequest(itemRequest, oldStatus);
System.out.println("PENDING "
+ CounterHandler.itemRequestsPendingCounter);
System.out.println("CONFIRMED "
+ CounterHandler.itemRequestsConfirmedCounter);
System.out.println("===================================");
oldStatus = itemRequest.getStatus();
itemRequest.setStatus("Rejected");
ic.updateItemRequest(itemRequest, oldStatus);
System.out.println("PENDING "
+ CounterHandler.itemRequestsPendingCounter);
System.out.println("CONFIRMED "
+ CounterHandler.itemRequestsConfirmedCounter);
System.out.println("===================================");
System.out.println("===================================");
}
public void setupSchema() {
JbpmConfiguration config = JbpmConfiguration
.parseResource("jbpm.cfg.xml");
ProcessDefinition processDefinition = ProcessDefinition
.parseXmlResource("processdefinition.xml");
// create schema.
config.createSchema();
JbpmContext context = config.createJbpmContext();
// deploy process definition.
try {
context.deployProcessDefinition(processDefinition);
} catch (Exception e) {
e.printStackTrace();
} finally {
context.close();
}
}
}
The hibernate configuration file hibernate.cfg.xml below:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<!-- JDBC connection properties -->
<property name="hibernate.dialect">
org.hibernate.dialect.MySQLDialect
</property>
<property name="hibernate.connection.driver_class">
com.mysql.jdbc.Driver
</property>
<property name="hibernate.connection.url">
jdbc:mysql://localhost:3306/jbpmappdb
</property>
<property name="hibernate.connection.username">admin</property>
<property name="hibernate.connection.password">admin</property>
<!-- JDBC connection properties (end) -->
<!-- Simple memory-only cache -->
<property name="hibernate.cache.provider_class">
org.hibernate.cache.HashtableCacheProvider
</property>
<!-- logging properties -->
<property name="hibernate.format_sql">true</property>
<property name="hibernate.use_sql_comments">true</property>
<mapping resource="org/jbpm/db/hibernate.queries.hbm.xml" />
<mapping resource="org/jbpm/graph/action/MailAction.hbm.xml" />
<mapping
resource="org/jbpm/graph/def/ProcessDefinition.hbm.xml" />
<mapping resource="org/jbpm/graph/def/Node.hbm.xml" />
<mapping resource="org/jbpm/graph/def/Transition.hbm.xml" />
<mapping resource="org/jbpm/graph/def/Event.hbm.xml" />
<mapping resource="org/jbpm/graph/def/Action.hbm.xml" />
<mapping resource="org/jbpm/graph/def/SuperState.hbm.xml" />
<mapping resource="org/jbpm/graph/def/ExceptionHandler.hbm.xml" />
<mapping resource="org/jbpm/instantiation/Delegation.hbm.xml" />
<mapping resource="org/jbpm/graph/action/Script.hbm.xml" />
<mapping resource="org/jbpm/graph/node/StartState.hbm.xml" />
<mapping resource="org/jbpm/graph/node/EndState.hbm.xml" />
<mapping resource="org/jbpm/graph/node/ProcessState.hbm.xml" />
<mapping resource="org/jbpm/graph/node/Decision.hbm.xml" />
<mapping resource="org/jbpm/graph/node/Fork.hbm.xml" />
<mapping resource="org/jbpm/graph/node/Join.hbm.xml" />
<mapping resource="org/jbpm/graph/node/MailNode.hbm.xml" />
<mapping resource="org/jbpm/graph/node/State.hbm.xml" />
<mapping resource="org/jbpm/graph/node/TaskNode.hbm.xml" />
<mapping
resource="org/jbpm/context/def/ContextDefinition.hbm.xml" />
<mapping resource="org/jbpm/context/def/VariableAccess.hbm.xml" />
<mapping resource="org/jbpm/bytes/ByteArray.hbm.xml" />
<mapping
resource="org/jbpm/module/def/ModuleDefinition.hbm.xml" />
<mapping resource="org/jbpm/file/def/FileDefinition.hbm.xml" />
<mapping
resource="org/jbpm/taskmgmt/def/TaskMgmtDefinition.hbm.xml" />
<mapping resource="org/jbpm/taskmgmt/def/Swimlane.hbm.xml" />
<mapping resource="org/jbpm/taskmgmt/def/Task.hbm.xml" />
<mapping
resource="org/jbpm/taskmgmt/def/TaskController.hbm.xml" />
<mapping
resource="org/jbpm/scheduler/def/CreateTimerAction.hbm.xml" />
<mapping
resource="org/jbpm/scheduler/def/CancelTimerAction.hbm.xml" />
<mapping resource="org/jbpm/graph/exe/Comment.hbm.xml" />
<mapping resource="org/jbpm/graph/exe/ProcessInstance.hbm.xml" />
<mapping resource="org/jbpm/graph/exe/Token.hbm.xml" />
<mapping resource="org/jbpm/graph/exe/RuntimeAction.hbm.xml" />
<mapping resource="org/jbpm/module/exe/ModuleInstance.hbm.xml" />
<mapping
resource="org/jbpm/context/exe/ContextInstance.hbm.xml" />
<mapping
resource="org/jbpm/context/exe/TokenVariableMap.hbm.xml" />
<mapping
resource="org/jbpm/context/exe/VariableInstance.hbm.xml" />
<mapping
resource="org/jbpm/context/exe/variableinstance/ByteArrayInstance.hbm.xml" />
<mapping
resource="org/jbpm/context/exe/variableinstance/DateInstance.hbm.xml" />
<mapping
resource="org/jbpm/context/exe/variableinstance/DoubleInstance.hbm.xml" />
<mapping
resource="org/jbpm/context/exe/variableinstance/HibernateLongInstance.hbm.xml" />
<mapping
resource="org/jbpm/context/exe/variableinstance/HibernateStringInstance.hbm.xml" />
<mapping
resource="org/jbpm/context/exe/variableinstance/LongInstance.hbm.xml" />
<mapping
resource="org/jbpm/context/exe/variableinstance/NullInstance.hbm.xml" />
<mapping
resource="org/jbpm/context/exe/variableinstance/StringInstance.hbm.xml" />
<mapping
resource="org/jbpm/taskmgmt/exe/TaskMgmtInstance.hbm.xml" />
<mapping resource="org/jbpm/taskmgmt/exe/TaskInstance.hbm.xml" />
<mapping resource="org/jbpm/taskmgmt/exe/PooledActor.hbm.xml" />
<mapping
resource="org/jbpm/taskmgmt/exe/SwimlaneInstance.hbm.xml" />
<mapping resource="org/jbpm/logging/log/ProcessLog.hbm.xml" />
<mapping resource="org/jbpm/logging/log/MessageLog.hbm.xml" />
<mapping resource="org/jbpm/logging/log/CompositeLog.hbm.xml" />
<mapping resource="org/jbpm/graph/log/ActionLog.hbm.xml" />
<mapping resource="org/jbpm/graph/log/NodeLog.hbm.xml" />
<mapping
resource="org/jbpm/graph/log/ProcessInstanceCreateLog.hbm.xml" />
<mapping
resource="org/jbpm/graph/log/ProcessInstanceEndLog.hbm.xml" />
<mapping resource="org/jbpm/graph/log/ProcessStateLog.hbm.xml" />
<mapping resource="org/jbpm/graph/log/SignalLog.hbm.xml" />
<mapping resource="org/jbpm/graph/log/TokenCreateLog.hbm.xml" />
<mapping resource="org/jbpm/graph/log/TokenEndLog.hbm.xml" />
<mapping resource="org/jbpm/graph/log/TransitionLog.hbm.xml" />
<mapping resource="org/jbpm/context/log/VariableLog.hbm.xml" />
<mapping
resource="org/jbpm/context/log/VariableCreateLog.hbm.xml" />
<mapping
resource="org/jbpm/context/log/VariableDeleteLog.hbm.xml" />
<mapping
resource="org/jbpm/context/log/VariableUpdateLog.hbm.xml" />
<mapping
resource="org/jbpm/context/log/variableinstance/ByteArrayUpdateLog.hbm.xml" />
<mapping
resource="org/jbpm/context/log/variableinstance/DateUpdateLog.hbm.xml" />
<mapping
resource="org/jbpm/context/log/variableinstance/DoubleUpdateLog.hbm.xml" />
<mapping
resource="org/jbpm/context/log/variableinstance/HibernateLongUpdateLog.hbm.xml" />
<mapping
resource="org/jbpm/context/log/variableinstance/HibernateStringUpdateLog.hbm.xml" />
<mapping
resource="org/jbpm/context/log/variableinstance/LongUpdateLog.hbm.xml" />
<mapping
resource="org/jbpm/context/log/variableinstance/StringUpdateLog.hbm.xml" />
<mapping resource="org/jbpm/taskmgmt/log/TaskLog.hbm.xml" />
<mapping resource="org/jbpm/taskmgmt/log/TaskCreateLog.hbm.xml" />
<mapping resource="org/jbpm/taskmgmt/log/TaskAssignLog.hbm.xml" />
<mapping resource="org/jbpm/taskmgmt/log/TaskEndLog.hbm.xml" />
<mapping resource="org/jbpm/taskmgmt/log/SwimlaneLog.hbm.xml" />
<mapping
resource="org/jbpm/taskmgmt/log/SwimlaneCreateLog.hbm.xml" />
<mapping
resource="org/jbpm/taskmgmt/log/SwimlaneAssignLog.hbm.xml" />
<class-cache class="org.jbpm.context.def.VariableAccess"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.file.def.FileDefinition.processFiles"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.action.Script.variableAccesses"
usage="nonstrict-read-write" />
<class-cache class="org.jbpm.graph.def.Action"
usage="nonstrict-read-write" />
<class-cache class="org.jbpm.graph.def.Event"
usage="nonstrict-read-write" />
<collection-cache collection="org.jbpm.graph.def.Event.actions"
usage="nonstrict-read-write" />
<class-cache class="org.jbpm.graph.def.ExceptionHandler"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.def.ExceptionHandler.actions"
usage="nonstrict-read-write" />
<class-cache class="org.jbpm.graph.def.Node"
usage="nonstrict-read-write" />
<collection-cache collection="org.jbpm.graph.def.Node.events"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.def.Node.exceptionHandlers"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.def.Node.leavingTransitions"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.def.Node.arrivingTransitions"
usage="nonstrict-read-write" />
<class-cache class="org.jbpm.graph.def.ProcessDefinition"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.def.ProcessDefinition.events"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.def.ProcessDefinition.exceptionHandlers"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.def.ProcessDefinition.nodes"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.def.ProcessDefinition.actions"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.def.ProcessDefinition.definitions"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.def.SuperState.nodes"
usage="nonstrict-read-write" />
<class-cache class="org.jbpm.graph.def.Transition"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.def.Transition.events"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.def.Transition.exceptionHandlers"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.node.Decision.decisionConditions"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.node.ProcessState.variableAccesses"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.graph.node.TaskNode.tasks"
usage="nonstrict-read-write" />
<class-cache class="org.jbpm.instantiation.Delegation"
usage="nonstrict-read-write" />
<class-cache class="org.jbpm.module.def.ModuleDefinition"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.taskmgmt.def.Swimlane.tasks"
usage="nonstrict-read-write" />
<class-cache class="org.jbpm.taskmgmt.def.TaskController"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.taskmgmt.def.TaskController.variableAccesses"
usage="nonstrict-read-write" />
<class-cache class="org.jbpm.taskmgmt.def.Task"
usage="nonstrict-read-write" />
<collection-cache collection="org.jbpm.taskmgmt.def.Task.events"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.taskmgmt.def.Task.exceptionHandlers"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.taskmgmt.def.TaskMgmtDefinition.swimlanes"
usage="nonstrict-read-write" />
<collection-cache
collection="org.jbpm.taskmgmt.def.TaskMgmtDefinition.tasks"
usage="nonstrict-read-write" />
</session-factory>
</hibernate-configuration>
The jbpm configuration file jbpm.cfg.xml below:
<jbpm-configuration>
<jbpm-context>
<service name="authentication"
factory="org.jbpm.security.authentication.DefaultAuthenticationServiceFactory" />
<!-- Logging Service (begin) -->
<service name="logging"
factory="org.jbpm.logging.db.DbLoggingServiceFactory" />
<!-- Logging Service (end) -->
<service name="message"
factory="org.jbpm.msg.db.DbMessageServiceFactory" />
<service name="persistence"
factory="org.jbpm.persistence.db.DbPersistenceServiceFactory" />
<service name="scheduler"
factory="org.jbpm.scheduler.db.DbSchedulerServiceFactory" />
<service name="tx" factory="org.jbpm.tx.TxServiceFactory" />
</jbpm-context>
<string name="resource.hibernate.cfg.xml" value="hibernate.cfg.xml" />
<!-- configuration resource files pointing to default configuration files in jbpm-{version}.jar -->
<string name="resource.business.calendar"
value="org/jbpm/calendar/jbpm.business.calendar.properties" />
<string name="resource.default.modules"
value="org/jbpm/graph/def/jbpm.default.modules.properties" />
<string name="resource.action.types"
value="org/jbpm/graph/action/action.types.xml" />
<string name="resource.node.types"
value="org/jbpm/graph/node/node.types.xml" />
<string name="resource.parsers"
value="org/jbpm/jpdl/par/jbpm.parsers.xml" />
<string name="resource.varmapping"
value="org/jbpm/context/exe/jbpm.varmapping.xml" />
<string name="resource.mail.templates"
value="jbpm.mail.templates.xml" />
<!-- make sure the block size matches the length in ByteArray.hbm.xml -->
<int name="jbpm.byte.block.size" value="1024" singleton="true" />
<bean name="jbpm.task.instance.factory"
class="org.jbpm.taskmgmt.impl.DefaultTaskInstanceFactoryImpl"
singleton="true" />
<bean name="jbpm.variable.resolver"
class="org.jbpm.jpdl.el.impl.JbpmVariableResolver" singleton="true" />
<string name="jbpm.mail.smtp.host" value="localhost" />
<bean name="jbpm.mail.address.resolver"
class="org.jbpm.identity.mail.IdentityAddressResolver"
singleton="true" />
<string name="jbpm.mail.from.address" value="jbpm@noreply" />
<bean name="jbpm.job.executor"
class="org.jbpm.job.executor.JobExecutor">
<field name="jbpmConfiguration">
<ref bean="jbpmConfiguration" />
</field>
<field name="name">
<string value="JbpmJobExecutor" />
</field>
<field name="nbrOfThreads">
<int value="1" />
</field>
<field name="idleInterval">
<int value="5000" />
</field>
<field name="maxIdleInterval">
<int value="3600000" />
</field>
<field name="historyMaxSize">
<int value="20" />
</field>
<field name="maxLockTime">
<int value="600000" />
</field>
<field name="lockMonitorInterval">
<int value="60000" />
</field>
<field name="lockBufferTime">
<int value="5000" />
</field>
</bean>
</jbpm-configuration>
The domain class is ItemRequest.java
package com.mattiz.jbpm.dto;
public class ItemRequest {
private String id;
private String name;
private String status;
/** JBPM mapping. */
private Long processInstanceId;
public Long getProcessInstanceId() {
return processInstanceId;
}
public void setProcessInstanceId(Long processInstanceId) {
this.processInstanceId = processInstanceId;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
}
Note that it defines a variable processInstanceId required for JBPM.
The class that makes JBPM work below:
package com.mattiz.jbpm.workflow;
import java.util.ArrayList;
import java.util.List;
import org.jbpm.JbpmConfiguration;
import org.jbpm.JbpmContext;
import org.jbpm.JbpmException;
import org.jbpm.graph.def.Transition;
import org.jbpm.graph.exe.ProcessInstance;
import com.mattiz.jbpm.dto.ItemRequest;
public class JBPMWorkFlow {
JbpmConfiguration config = JbpmConfiguration.parseResource("jbpm.cfg.xml");
public Long createItemRequestFlow(ItemRequest ItemRequest) {
JbpmContext context = config.createJbpmContext();
ProcessInstance pi = null;
try {
pi = context.newProcessInstance("Mattiz_Process_Flow");
pi.getContextInstance().setVariable("ItemRequestId",
ItemRequest.getId());
pi.signal();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
context.close();
}
return pi.getId();
}
public List<String> makeTransition(Long processInstanceId,
String transitionName) throws RuntimeException {
JbpmContext context = config.createJbpmContext();
List<String> transitionNames = new ArrayList<String>();
try {
ProcessInstance pi = context.getProcessInstance(processInstanceId);
List<Transition> leavingTransitions = pi.getRootToken().getNode()
.getLeavingTransitions();
for (Transition t : leavingTransitions) {
transitionNames.add(t.getName());
}
System.out.println("Choices are " + leavingTransitions);
System.out.println("ACTION IS--->" + transitionName);
pi.signal(transitionName);
} catch (JbpmException e) {
throw new RuntimeException(e);
} finally {
context.close();
}
return transitionNames;
}
public void makeCounterChanges(Long processInstanceId, String oldStatus)
throws RuntimeException {
JbpmContext context = config.createJbpmContext();
try {
ProcessInstance pi = context.getProcessInstance(processInstanceId);
pi.getContextInstance().setVariable("oldStatus", oldStatus);
} catch (JbpmException e) {
throw new RuntimeException();
} finally {
context.close();
}
}
}
We define two JBPM handler classes.
CounterHandler keeps track of the number of ItemRequest objects in submitted state and in the approved state using two static counters whose values are printed out in the main class whenever a state change occurs.
package com.mattiz.jbpm.handler;
import java.util.HashMap;
import java.util.Map;
import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.exe.ExecutionContext;
public class CounterHandler implements ActionHandler {
private static Map<String, String> actionForStatusChange = new HashMap<String, String>();
public static int itemRequestsPendingCounter;
public static int itemRequestsConfirmedCounter;
//status is passed on implicitly from the processdefinition xml
private String status;
static {
actionForStatusChange.put("NEW" + "_" + "Submitted",
"addToItemRequestsPending");
actionForStatusChange.put("Submitted" + "_" + "Approved",
"addToItemRequestsConfirmedAndSubtractFromItemRequestsPending");
actionForStatusChange.put("Approved" + "_" + "Rejected",
"subtractFromItemRequestsConfirmed");
actionForStatusChange.put("Submitted" + "_" + "Rejected",
"subtractFromItemRequestsPending");
actionForStatusChange.put("Approved" + "_" + "Cancelled",
"subtractFromItemRequestsConfirmed");
}
public void execute(ExecutionContext executionContext) throws Exception {
String oldStatus = (String) executionContext.getContextInstance()
.getVariable("oldStatus");
if (oldStatus == null) {
oldStatus = "NEW";
}
String action = actionForStatusChange.get(oldStatus + "_" + status);
if (null != action) {
if (action.equals("addToItemRequestsPending")) {
itemRequestsPendingCounter++;
} else if (action.equals("addToItemRequestsConfirmed")) {
itemRequestsConfirmedCounter++;
} else if (action.equals("subtractFromItemRequestsConfirmed")) {
itemRequestsConfirmedCounter--;
} else if (action.equals("subtractFromItemRequestsPending")) {
itemRequestsPendingCounter--;
}else if (action.equals("addToItemRequestsConfirmedAndSubtractFromItemRequestsPending")){
itemRequestsPendingCounter--;
itemRequestsConfirmedCounter++;
}else if (action.equals("subtractFromItemRequestsPending")){
itemRequestsPendingCounter--;
}
}
}
}
StatusHandler is also a listener which is triggered whenever status change occurs. In this app it does not do anything, but in a real app can be used to update the status of ItemRequest in the database.
package com.mattiz.jbpm.handler;
import org.jbpm.graph.def.ActionHandler;
import org.jbpm.graph.exe.ExecutionContext;
public class StatusHandler implements ActionHandler {
private String status;
public void execute(ExecutionContext executionContext) throws Exception {
String itemRequestId = (String) executionContext.getContextInstance()
.getVariable("ItemRequestId");
System.out.println("Item "+itemRequestId);
executionContext.getContextInstance().setVariable("status", status);
//do something important like update the status in the database using the itemRequestId
}
}
ItemRequestController is the interface to the frontend and calls methods in the workflow service class:
package com.mattiz.jbpm;
import java.util.List;
import com.mattiz.jbpm.dto.ItemRequest;
import com.mattiz.jbpm.workflow.JBPMWorkFlow;
public class ItemRequestController {
JBPMWorkFlow workflowService = new JBPMWorkFlow();
public List<String> createItemRequest(ItemRequest itemRequest) {
Long processInstanceId = workflowService
.createItemRequestFlow(itemRequest);
itemRequest.setProcessInstanceId(processInstanceId);
workflowService.makeCounterChanges(processInstanceId, null);
List<String> transitions = workflowService.makeTransition(
itemRequest.getProcessInstanceId(), itemRequest.getStatus());
return transitions;
}
public List<String> updateItemRequest(ItemRequest itemRequest, String oldStatus) {
Long processInstanceId = itemRequest.getProcessInstanceId();
workflowService.makeCounterChanges(processInstanceId, oldStatus);
List<String> transitions = workflowService.makeTransition(processInstanceId, itemRequest
.getStatus());
return transitions;
}
}
The output of the main class looks something like this:
Choices are [Transition(Submitted)]
ACTION IS--->Submitted
Item 1
PENDING 1
CONFIRMED 0
===================================
Choices are [Transition(Approved), Transition(Rejected)]
ACTION IS--->Approved
Item 1
PENDING 0
CONFIRMED 1
===================================
Choices are [Transition(Cancelled), Transition(Rejected), Transition(Archived)]
ACTION IS--->Cancelled
Item 1
PENDING 0
CONFIRMED 0
===================================
===================================
Choices are [Transition(Submitted)]
ACTION IS--->Submitted
Item 2
PENDING 1
CONFIRMED 0
===================================
Choices are [Transition(Approved), Transition(Rejected)]
ACTION IS--->Approved
Item 2
PENDING 0
CONFIRMED 1
===================================
Choices are [Transition(Cancelled), Transition(Rejected), Transition(Archived)]
ACTION IS--->Rejected
Item 2
PENDING 0
CONFIRMED 0
===================================
===================================
Choices are [Transition(Submitted)]
ACTION IS--->Submitted
Item 3
PENDING 1
CONFIRMED 0
===================================
Choices are [Transition(Approved), Transition(Rejected)]
ACTION IS--->Approved
Item 3
PENDING 0
CONFIRMED 1
===================================
===================================
Choices are [Transition(Submitted)]
ACTION IS--->Submitted
Item 4
PENDING 1
CONFIRMED 1
===================================
Choices are [Transition(Approved), Transition(Rejected)]
ACTION IS--->Approved
Item 4
PENDING 0
CONFIRMED 2
===================================
Choices are [Transition(Cancelled), Transition(Rejected), Transition(Archived)]
ACTION IS--->Rejected
Item 4
PENDING 0
CONFIRMED 1
===================================
===================================
Subscribe to:
Posts (Atom)
