JPA

Spring Data JPA

Posted on Updated on

Spring Data JPA provides a more cleaner approach of using JPA for DAO layer. It removes boilerplate code and developers can concentrate on actual logic.

Our DataModel

07 September 2013 PM 06-14-30
First create a maven project and add below dependencies:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.mumz.test.springdata.jpa</groupId>
	<artifactId>SpringDataJPA</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<properties>
		<spring.version>3.2.4.RELEASE</spring.version>
		<spring.data.version>1.3.4.RELEASE</spring.data.version>
		<hibernate.version>4.2.5.FINAL</hibernate.version>
		<jpa.version>1.0.1.FINAL</jpa.version>
		<querydsl.version>3.2.3</querydsl.version>
		<junit.version>4.8.2</junit.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>${junit.version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${spring.version}</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-jpa</artifactId>
			<version>${spring.data.version}</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate.javax.persistence</groupId>
			<artifactId>hibernate-jpa-2.0-api</artifactId>
			<version>${jpa.version}</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>${hibernate.version}</version>
		</dependency>
		<dependency>
			<groupId>com.mysema.querydsl</groupId>
			<artifactId>querydsl-jpa</artifactId>
			<version>${querydsl.version}</version>
		</dependency>
		<dependency>
			<groupId>javax.inject</groupId>
			<artifactId>javax.inject</artifactId>
			<version>1</version>
		</dependency>
		<dependency>
			<groupId>commons-logging</groupId>
			<artifactId>commons-logging</artifactId>
			<version>1.1.3</version>
		</dependency>
		<dependency>
			<groupId>commons-pool</groupId>
			<artifactId>commons-pool</artifactId>
			<version>1.6</version>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>2.3.2</version>
				<executions>
					<execution>
						<id>default-testCompile</id>
						<phase>test-compile</phase>
						<goals>
							<goal>testCompile</goal>
						</goals>
						<configuration>
							<source>1.6</source>
							<target>1.6</target>
						</configuration>
					</execution>
					<execution>
						<id>default-compile</id>
						<phase>compile</phase>
						<goals>
							<goal>compile</goal>
						</goals>
						<configuration>
							<source>1.6</source>
							<target>1.6</target>
						</configuration>
					</execution>
				</executions>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

Given our datamodel and maven dependcies ready, below is the Book Entity class. Book is a simple POJO with JPA annotations.

package com.mumz.test.springdata.jpa.entity;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;

/**
 * The Class Book.
 * 
 * @author prabhat.jha
 */
@Entity(name = "BOOK")
public class Book implements Serializable{

	/**
	 * 
	 */
	private static final long serialVersionUID = -5163349208616552005L;

	/** The id. */
	private Long id = null;

	/** The name. */
	private String name = null;

	/** The author. */
	private String author = null;

	/**
	 * Gets the id.
	 * 
	 * @return the id
	 */
	@Id
	@Column(name = "ID")
	@GeneratedValue(strategy = GenerationType.AUTO)
	public Long getId() {
		return id;
	}

	/**
	 * Sets the id.
	 * 
	 * @param id
	 *            the new id
	 */
	public void setId(Long id) {
		this.id = id;
	}

	/**
	 * Gets the name.
	 * 
	 * @return the name
	 */
	@Column(name = "NAME")
	public String getName() {
		return name;
	}

	/**
	 * Sets the name.
	 * 
	 * @param name
	 *            the new name
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * Gets the author.
	 * 
	 * @return the author
	 */
	@Column(name = "AUTHOR")
	public String getAuthor() {
		return author;
	}

	/**
	 * Sets the author.
	 * 
	 * @param author
	 *            the new author
	 */
	public void setAuthor(String author) {
		this.author = author;
	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((author == null) ? 0 : author.hashCode());
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (!(obj instanceof Book)) {
			return false;
		}
		Book other = (Book) obj;
		if (author == null) {
			if (other.author != null) {
				return false;
			}
		} else if (!author.equals(other.author)) {
			return false;
		}
		if (id == null) {
			if (other.id != null) {
				return false;
			}
		} else if (!id.equals(other.id)) {
			return false;
		}
		if (name == null) {
			if (other.name != null) {
				return false;
			}
		} else if (!name.equals(other.name)) {
			return false;
		}
		return true;
	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "Book [id=" + id + ", name=" + name + ", author=" + author + "]";
	}
}

The major difference from plain old JPA and "Spring Data JPA" is usage of repositories. A Repository provides you with the generic framework for dealing with persistence layer boilerplate code like below:

public void save(MyEntity myEntity){
    if(myEntity.getId() != null) {
        entityManager.merge(myEntity);
    } else {
        entityManager.persist(myEntity);
    }
}

Spring Data JPARepository provides you with enough generic services to take care of most of the scenarios. Given this let's write our Repository called as IBookRepository which is an interface and extends JpaRepository. JpaRepository is parameterized interface so we need to provide the Entity class and class of the identifier(Primary Key). In this case our Entity class is Book and identifier is Long.

package com.mumz.test.springdata.jpa.repository;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;

import com.mumz.test.springdata.jpa.entity.Book;

/**
 * The Interface IBookRepository.
 * 
 * @author prabhat.jha
 */
public interface IBookRepository extends JpaRepository<Book, Long> {
	/**
	 * Find book by id.
	 * 
	 * @param bookId
	 *            the book id
	 * @return the book
	 */
	public Book findBookById(Long bookId);

	/**
	 * Find all books.
	 * 
	 * @return the list
	 */
	public List<Book> findAll();
}

Next we will write our Service interface IBookService and it's implementation class BookServiceImpl. IBookService is a plain interface and self-explanatory.

package com.mumz.test.springdata.jpa.service;

import java.util.List;

import com.mumz.test.springdata.jpa.entity.Book;

/**
 * The Interface IBookService.
 * @author prabhat.jha
 */
public interface IBookService {
	
	/**
	 * Save.
	 *
	 * @param book the book
	 * @return the book
	 */
	public Book save(Book book);
	
	/**
	 * Find book by id.
	 *
	 * @param bookId the book id
	 * @return the book
	 */
	public Book findBookById(Long bookId);
	
	/**
	 * Find all books.
	 *
	 * @return the list
	 */
	public List<Book> findAllBooks();
}

Next is our service implementation class BookServiceImpl. If you take a closer look you will see we don't have any EntityManager instead we have our IBookRepository. Same IBookRepository would be used to save and retrieve records.

package com.mumz.test.springdata.jpa.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.mumz.test.springdata.jpa.entity.Book;
import com.mumz.test.springdata.jpa.repository.IBookRepository;

/**
 * The Class BookServiceImpl.
 * 
 * @author prabhat.jha
 */
@Service(value = "bookService")
@Transactional
public class BookServiceImpl implements IBookService {

	/** The book repository. */
	@Autowired
	private IBookRepository bookRepository = null;

	/**
	 * (non-Javadoc)
	 * 
	 * @see com.mumz.test.springdata.jpa.service.IBookService#save(com.mumz.test.springdata.jpa.entity.Book)
	 */
	@Override
	public Book save(Book book) {
		return bookRepository.save(book);
	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see com.mumz.test.springdata.jpa.service.IBookService#findBookById(java.lang.Long)
	 */
	@Override
	public Book findBookById(Long bookId) {
		return bookRepository.findBookById(bookId);
	}

	/**
	 * (non-Javadoc)
	 * 
	 * @see com.mumz.test.springdata.jpa.service.IBookService#findAllBooks()
	 */
	@Override
	public List<Book> findAllBooks() {
		return bookRepository.findAll();
	}

	/**
	 * Gets the book repository.
	 * 
	 * @return the book repository
	 */
	public IBookRepository getBookRepository() {
		return bookRepository;
	}

	/**
	 * Sets the book repository.
	 * 
	 * @param bookRepository
	 *            the new book repository
	 */
	public void setBookRepository(IBookRepository bookRepository) {
		this.bookRepository = bookRepository;
	}
}

And finally our applicationContext.xml. Since we are using annotation driven configuration it is important to tell Spring where to search for our repositories and so that it can search and keep it ready for DI.

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

	<tx:annotation-driven />
	<jpa:repositories base-package="com.mumz.test.springdata.jpa" />
	<context:component-scan base-package="com.mumz.test.springdata.jpa" />
	<bean id="dataSource"
		class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName">
			<value>oracle.jdbc.driver.OracleDriver</value>
		</property>
		<property name="url">
			<value>jdbc:oracle:thin:@localhost:1521:MYDB</value>
		</property>
		<property name="username">
			<value>MYUSER</value>
		</property>
		<property name="password">
			<value>MYPASSWORD</value>
		</property>
	</bean>

	<bean id="entityManagerFactory"
		class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="jpaVendorAdapter">
			<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
				<property name="database">
					<value>ORACLE</value>
				</property>
				<property name="showSql" value="true" />
				 <property name="databasePlatform" value="org.hibernate.dialect.Oracle10gDialect" />
			</bean>
		</property>
	</bean>

	<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	</bean>
</beans>

We also need persistence.xml since we are dealing with JPA.

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
	xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
	<persistence-unit name="springdata" >
	</persistence-unit>
</persistence>

Finally our test class BookServiceTest

/**
 * 
 */
package com.mumz.test.springdata.jpa;

import static org.junit.Assert.assertNotNull;

import java.util.List;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.TestExecutionListeners;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.support.DependencyInjectionTestExecutionListener;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.test.context.transaction.TransactionalTestExecutionListener;
import org.springframework.transaction.annotation.Transactional;

import com.mumz.test.springdata.jpa.entity.Book;
import com.mumz.test.springdata.jpa.repository.IBookRepository;
import com.mumz.test.springdata.jpa.service.IBookService;

/**
 * The Class BookServiceTest.
 *
 * @author prabhat.jha
 */
@RunWith(SpringJUnit4ClassRunner.class)
@TestExecutionListeners({ TransactionalTestExecutionListener.class,
		DependencyInjectionTestExecutionListener.class })
@ContextConfiguration(locations = { "/META-INF/applicationContext.xml" })
@Transactional
@TransactionConfiguration(defaultRollback = false)
public class BookServiceTest {

	/** The last created book id. */
	private Long lastCreatedBookId = null;
	
	/** The book service. */
	@Autowired
	@Qualifier(value = "bookService")
	private IBookService bookService;

	/** The book repository. */
	@Autowired
	private IBookRepository bookRepository;

	/**
	 * Sets the up.
	 *
	 * @throws Exception the exception
	 */
	@Before
	public void setUp() throws Exception {
	}

	/**
	 * Tear down.
	 *
	 * @throws Exception the exception
	 */
	@After
	public void tearDown() throws Exception {
	}

	/**
	 * Test create book.
	 */
	@Test
	public void testCreateBook() {
		Book book = new Book();
		book.setName("Spring Data JPA - Reference Documentation");
		book.setAuthor("Oliver Gierke");
		book = this.getBookRepository().save(book);
		assertNotNull("Test failed create book returned null id",book.getId());
		lastCreatedBookId = book.getId();
	}

	/**
	 * Test find book by id.
	 */
	@Test
	public void testFindBookById() {
		Book book = this.getBookRepository().findBookById(lastCreatedBookId);
		assertNotNull(book);
	}

	/**
	 * Test find all books.
	 */
	@Test
	public void testFindAllBooks() {
		List<Book> books = this.getBookRepository().findAll();
		assertNotNull("Test failed findAllBooks returned null", books);
		if (books != null) {
			for (Book book : books) {
				System.out.println(book);
			}
		}
	}

	/**
	 * Gets the book service.
	 *
	 * @return the book service
	 */
	public IBookService getBookService() {
		return bookService;
	}

	/**
	 * Sets the book service.
	 *
	 * @param bookService the new book service
	 */
	public void setBookService(IBookService bookService) {
		this.bookService = bookService;
	}

	/**
	 * Gets the book repository.
	 *
	 * @return the book repository
	 */
	public IBookRepository getBookRepository() {
		return bookRepository;
	}

	/**
	 * Sets the book repository.
	 *
	 * @param bookRepository the new book repository
	 */
	public void setBookRepository(IBookRepository bookRepository) {
		this.bookRepository = bookRepository;
	}
}

Hope this helps!
N.B: You can download the code from Spring Data JPA

Full Text Search with Hibernate Search 4.1, Lucene and JPA

Posted on Updated on

Earlier we worked directly with Lucene API to create and search index

  1. Index and Search a Directory using Apache Lucene
  2. Create Lucene Index in database using JdbcDirectory”>

Instead we can use HibernateSearch which internally uses Lucene functionality to index and search content. With that let’s get some code behind us. We will extend our code from JPA OneToMany Unidirectional without Join Table.

First let’s add hibernate-search lib in our pom.
pom.xml

<project
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://maven.apache.org/POM/4.0.0"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
>
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.mumz.test.hibernatesearch</groupId>
	<artifactId>MumzHibernateSearch</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>
	<name>MumzHibernateSearch</name>
	<url>http://maven.apache.org</url>
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-search</artifactId>
			<version>4.1.1.Final</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate.javax.persistence</groupId>
			<artifactId>hibernate-jpa-2.0-api</artifactId>
			<version>1.0.1.Final</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>4.1.7.Final</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>4.1.7.Final</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.21</version>
		</dependency>
	</dependencies>
</project>

Next we will add hibernate search specific annotation to our Entity classes, first MHSBookEntityBean.

MHSBookEntityBean.java

package com.mumz.test.hibernatesearch.entitybeans;

import java.io.Serializable;

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

import org.hibernate.search.annotations.Analyze;
import org.hibernate.search.annotations.DocumentId;
import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.Index;
import org.hibernate.search.annotations.Indexed;
import org.hibernate.search.annotations.Store;

/**
 * The Class MHSBookEntityBean.
 * @author prabhat.jha
 */
@Entity
@Table(name = "BOOK")
//This annotation tells hibernate search that this class has to be indexed
@Indexed
public class MHSBookEntityBean implements Serializable {
	
	/** The Constant serialVersionUID. */
	private static final long	serialVersionUID	= -5129783468137830152L;
	
	/** The id. */
	private Long				id					= null;
	
	/** The name. */
	private String				name				= null;
	
	/** The author. */
	private String				author				= null;
	
	/**
	 * Instantiates a new mHS book entity bean.
	 */
	public MHSBookEntityBean() {
		super();
	}
	
	/**
	 * Gets the id.
	 * 
	 * @return the id
	 */
	@Id
	// This is optional if @Id is present, hibernate search needs untokenized id to ensure index is unique
	@DocumentId
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name = "BOOK_ID")
	public Long getId() {
		return this.id;
	}
	
	/**
	 * Sets the id.
	 * 
	 * @param id
	 *            the new id
	 */
	public void setId(Long id) {
		this.id = id;
	}
	
	/**
	 * Gets the name.
	 * 
	 * @return the name
	 */
	@Column(name = "BOOK_NAME")
	// This annotation tells that this field has to be indexed and also analyzed (break the long sentence and ignore common words), store tells if this field
	// will be part of Index, by Store.Yes it means it will be part of Index, so that query will be faster, downside is that size of Index increases
	@Field(index = Index.YES, analyze = Analyze.YES, store = Store.NO)
	public String getName() {
		return this.name;
	}
	
	/**
	 * Sets the name.
	 * 
	 * @param name
	 *            the new name
	 */
	public void setName(String name) {
		this.name = name;
	}
	
	/**
	 * Gets the author.
	 * 
	 * @return the author
	 */
	@Column(name = "BOOK_AUTHOR")
	// This annotation tells that this field has to be indexed and also analyzed (break the long sentence and ignore common words), store tells if this field
	// will be part of Index, by Store.Yes it means it will be part of Index, so that query will be faster, downside is that size of Index increases
	@Field(index = Index.YES, analyze = Analyze.YES, store = Store.NO)
	public String getAuthor() {
		return author;
	}
	
	/**
	 * Sets the author.
	 * 
	 * @param author
	 *            the new author
	 */
	public void setAuthor(String author) {
		this.author = author;
	}
	
	/**
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((author == null) ? 0 : author.hashCode());
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}
	
	/**
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (!(obj instanceof MHSBookEntityBean)) {
			return false;
		}
		MHSBookEntityBean other = (MHSBookEntityBean) obj;
		if (author == null) {
			if (other.author != null) {
				return false;
			}
		} else if (!author.equals(other.author)) {
			return false;
		}
		if (id == null) {
			if (other.id != null) {
				return false;
			}
		} else if (!id.equals(other.id)) {
			return false;
		}
		if (name == null) {
			if (other.name != null) {
				return false;
			}
		} else if (!name.equals(other.name)) {
			return false;
		}
		return true;
	}
	
	/**
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "MHSBookEntityBean [id=" + id + ", name=" + name + ", author=" + author + "]";
	}
}

Highlighted section in code above explains usage of each annotation.

Next we will update our MHSBookShelfEntityBean so that it can be indexed as well.

MHSBookShelfEntityBean.java

package com.mumz.test.hibernatesearch.entitybeans;

import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;

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

import org.hibernate.search.annotations.Analyze;
import org.hibernate.search.annotations.DocumentId;
import org.hibernate.search.annotations.Field;
import org.hibernate.search.annotations.Index;
import org.hibernate.search.annotations.Indexed;
import org.hibernate.search.annotations.Store;

/**
 * The Class MHSBookShelfEntityBean.
 * 
 * @author prabhat.jha
 */
@Entity
@Table(name = "BOOK_SHELF")
// This annotation tells hibernate search that this class has to be indexed
@Indexed
public class MHSBookShelfEntityBean implements Serializable {
	
	/** The Constant serialVersionUID. */
	private static final long		serialVersionUID	= -7127365575633206221L;
	
	/** The id. */
	private Long					id;
	
	/** The name. */
	private String					name;
	
	/** The books. */
	private Set<MHSBookEntityBean>	books				= new HashSet<MHSBookEntityBean>();
	
	/**
	 * Instantiates a new mHS book shelf entity bean.
	 */
	public MHSBookShelfEntityBean() {
		super();
	}
	
	/**
	 * Gets the id.
	 * 
	 * @return the id
	 */
	@Id
	// This is optional if @Id is present, hibernate search needs untokenized id to ensure index is unique
	@DocumentId
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name = "BOOK_SHELF_ID")
	public Long getId() {
		return this.id;
	}
	
	/**
	 * Sets the id.
	 * 
	 * @param id
	 *            the new id
	 */
	public void setId(Long id) {
		this.id = id;
	}
	
	/**
	 * Gets the name.
	 * 
	 * @return the name
	 */
	@Column(name = "BOOK_SHELF_NAME")
	// This annotation tells that this field has to be indexed and also analyzed (break the long sentence and ignore common words), store tells if this field
	// will be part of Index, by Store.Yes it means it will be part of Index, so that query will be faster, downside is that size of Index increases
	@Field(index = Index.YES, analyze = Analyze.YES, store = Store.NO)
	public String getName() {
		return this.name;
	}
	
	/**
	 * Sets the name.
	 * 
	 * @param name
	 *            the new name
	 */
	public void setName(String name) {
		this.name = name;
	}
	
	/**
	 * Gets the books.
	 * 
	 * @return the books
	 */
	@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
	@JoinColumn(name = "BOOK_SHELF_ID", referencedColumnName = "BOOK_SHELF_ID")
	public Set<MHSBookEntityBean> getBooks() {
		return books;
	}
	
	/**
	 * Sets the books.
	 * 
	 * @param books
	 *            the new books
	 */
	public void setBooks(Set<MHSBookEntityBean> books) {
		this.books = books;
	}
	
	/**
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((books == null) ? 0 : books.hashCode());
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}
	
	/**
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (!(obj instanceof MHSBookShelfEntityBean)) {
			return false;
		}
		MHSBookShelfEntityBean other = (MHSBookShelfEntityBean) obj;
		if (books == null) {
			if (other.books != null) {
				return false;
			}
		} else if (!books.equals(other.books)) {
			return false;
		}
		if (id == null) {
			if (other.id != null) {
				return false;
			}
		} else if (!id.equals(other.id)) {
			return false;
		}
		if (name == null) {
			if (other.name != null) {
				return false;
			}
		} else if (!name.equals(other.name)) {
			return false;
		}
		return true;
	}
	
	/**
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "MHSBookShelfEntityBean [id=" + id + ", name=" + name + ", books=" + books + "]";
	}
}

Finally we will write our code which will start indexing and then will search against index created.

TestHibernateSearch.java

package com.mumz.test.hibernatesearch.entitybeans;

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

import javax.persistence.EntityManager;
import javax.persistence.Persistence;
import javax.persistence.Query;

import org.hibernate.search.jpa.FullTextEntityManager;
import org.hibernate.search.jpa.Search;
import org.hibernate.search.query.dsl.QueryBuilder;

/**
 * The Class TestHibernateSearch.
 * 
 * @author prabhat.jha
 */
public class TestHibernateSearch {
	
	/**
	 * The main method.
	 * 
	 * @param args
	 *            the arguments
	 */
	@SuppressWarnings("unchecked")
	public static void main(String[] args) {
		EntityManager entityManager = Persistence.createEntityManagerFactory("MumzHibernateSearch").createEntityManager();
		FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);
		try {
			// This will ensure that index for already inserted data is created.
			fullTextEntityManager.createIndexer().startAndWait();
			// Add some more record, lucene will index every new object inserted, removed or updated.
			addMoreRecords(entityManager);
			// Search for Book
			QueryBuilder qb = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(MHSBookEntityBean.class).get();
			org.apache.lucene.search.Query query = qb.keyword().onFields("name", "author").matching("Pro Android 4").createQuery();
			Query jpaQuery = fullTextEntityManager.createFullTextQuery(query, MHSBookEntityBean.class);
			
			// execute search
			List<MHSBookEntityBean> bookResult = jpaQuery.getResultList();
			
			if (bookResult != null) {
				for (MHSBookEntityBean mhsBookEntityBean : bookResult) {
					System.out.println("Book found = " + mhsBookEntityBean);
				}
			}
			// Seach for book shelf
			qb = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(MHSBookShelfEntityBean.class).get();
			query = qb.keyword().onFields("name").matching("Technical").createQuery();
			jpaQuery = fullTextEntityManager.createFullTextQuery(query, MHSBookShelfEntityBean.class);
			
			// execute search
			List<MHSBookShelfEntityBean> bookShelfResult = jpaQuery.getResultList();
			
			if (bookShelfResult != null) {
				for (MHSBookShelfEntityBean mhsBookShelfEntityBean : bookShelfResult) {
					System.out.println("Book Shelf Found = " + mhsBookShelfEntityBean);
				}
			}
			
		} catch (InterruptedException e) {
			e.printStackTrace();
		} finally {
			if (fullTextEntityManager != null) {
				fullTextEntityManager.close();
			}
			fullTextEntityManager = null;
		}
	}
	
	/**
	 * Adds the more records.
	 * 
	 * @param entityManager
	 *            the entity manager
	 */
	private static void addMoreRecords(EntityManager entityManager) {
		Set<MHSBookEntityBean> books = new HashSet<MHSBookEntityBean>();
		MHSBookEntityBean mhsBookEntityBean = new MHSBookEntityBean();
		mhsBookEntityBean.setName("Pro Spring 3");
		mhsBookEntityBean.setAuthor("Clarence Ho and Rob Harrop");
		books.add(mhsBookEntityBean);
		mhsBookEntityBean = new MHSBookEntityBean();
		mhsBookEntityBean.setName("Pro JPA 2 Mastering the Java Persistence API");
		mhsBookEntityBean.setAuthor("Mike Keith and Merrick Schincariol");
		books.add(mhsBookEntityBean);
		
		// Fetch the book shelf id, in my db id was 3 so I added it as 3, you can use a query or something
		Query query = entityManager.createQuery("SELECT BOOKSHELF FROM " + MHSBookShelfEntityBean.class.getName() + " BOOKSHELF");
		MHSBookShelfEntityBean bookShelfEntityBean = (MHSBookShelfEntityBean) query.getSingleResult();
		bookShelfEntityBean.setName("Technical Books");
		bookShelfEntityBean.setBooks(books);
		
		entityManager.getTransaction().begin();
		entityManager.persist(bookShelfEntityBean);
		entityManager.getTransaction().commit();
	}
}

Lucene works on a concept of Directory which in this case we will use as FileSystem, since we are using JPA, we will provide these in persistence.xml.

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    version="2.0"
    xmlns="http://java.sun.com/xml/ns/persistence"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" >

    <persistence-unit name="MumzHibernateSearch" >
    	<class>com.mumz.test.hibernatesearch.entitybeans.MHSBookEntityBean</class>
    	<class>com.mumz.test.hibernatesearch.entitybeans.MHSBookShelfEntityBean</class>
        <properties>
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
            <property name="hibernate.connection.password" value="root"/>
            <property name="hibernate.connection.url" value="jdbc:mysql://localhost/jpa_schema"/>
            <property name="hibernate.connection.username" value="root"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
            <property name="hibernate.search.default.directory_provider" value="filesystem" />
            <property name="hibernate.search.default.indexBase" value="c:/lucene/indexes/first" />
        </properties>
    </persistence-unit>
</persistence>

That’s all we have to do get Hibernate search up and running.

JPA OneToMany Unidirectional without Join Table

Posted on

In earlier post we had OneToMany uni-directional mapping with the help of JoinTable. In this post we will see how can we make it unidirectional without using JoinTable.
Let’s create our eclipse project and then add update our pom.xml.

pom.xml

<project
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://maven.apache.org/POM/4.0.0"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
>
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.mumz.test.hibernatesearch</groupId>
	<artifactId>MumzHibernateSearch</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>
	<name>MumzHibernateSearch</name>
	<url>http://maven.apache.org</url>
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.hibernate.javax.persistence</groupId>
			<artifactId>hibernate-jpa-2.0-api</artifactId>
			<version>1.0.1.Final</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>4.1.7.Final</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>4.1.7.Final</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.21</version>
		</dependency>
	</dependencies>
</project>

Next we will write our Entity to model Book and BookShelf tables. BookShelf will contain a set of books. First we will write our MHSBookEntityBean.

MHSBookEntityBean.java

package com.mumz.test.hibernatesearch.entitybeans;

import java.io.Serializable;
import java.lang.Long;
import java.lang.String;
import javax.persistence.*;

/**
 * The Class MHSBookEntityBean.
 * @author prabhat.jha
 */
@Entity
@Table(name = "BOOK")
public class MHSBookEntityBean implements Serializable {

	/** The Constant serialVersionUID. */
	private static final long	serialVersionUID	= -5129783468137830152L;

	/** The id. */
	private Long				id					= null;

	/** The name. */
	private String				name				= null;

	/** The author. */
	private String				author				= null;

	/**
	 * Instantiates a new mHS book entity bean.
	 */
	public MHSBookEntityBean() {
		super();
	}

	/**
	 * Gets the id.
	 *
	 * @return the id
	 */
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name = "BOOK_ID")
	public Long getId() {
		return this.id;
	}

	/**
	 * Sets the id.
	 *
	 * @param id
	 *            the new id
	 */
	public void setId(Long id) {
		this.id = id;
	}

	/**
	 * Gets the name.
	 *
	 * @return the name
	 */
	@Column(name = "BOOK_NAME")
	public String getName() {
		return this.name;
	}

	/**
	 * Sets the name.
	 *
	 * @param name
	 *            the new name
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * Gets the author.
	 *
	 * @return the author
	 */
	@Column(name = "BOOK_AUTHOR")
	public String getAuthor() {
		return author;
	}

	/**
	 * Sets the author.
	 *
	 * @param author
	 *            the new author
	 */
	public void setAuthor(String author) {
		this.author = author;
	}

	/** (non-Javadoc)
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((author == null) ? 0 : author.hashCode());
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	/** (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (!(obj instanceof MHSBookEntityBean)) {
			return false;
		}
		MHSBookEntityBean other = (MHSBookEntityBean) obj;
		if (author == null) {
			if (other.author != null) {
				return false;
			}
		} else if (!author.equals(other.author)) {
			return false;
		}
		if (id == null) {
			if (other.id != null) {
				return false;
			}
		} else if (!id.equals(other.id)) {
			return false;
		}
		if (name == null) {
			if (other.name != null) {
				return false;
			}
		} else if (!name.equals(other.name)) {
			return false;
		}
		return true;
	}

	/** (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "MHSBookEntityBean [id=" + id + ", name=" + name + ", author=" + author + "]";
	}
}

Next we will write our MHSBookShelfEntityBean

MHSBookShelfEntityBean.java

package com.mumz.test.hibernatesearch.entitybeans;

import java.io.Serializable;
import java.lang.Long;
import java.lang.String;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.*;

/**
 * The Class MHSBookShelfEntityBean.
 * 
 * @author prabhat.jha
 */
@Entity
@Table(name = "BOOK_SHELF")
public class MHSBookShelfEntityBean implements Serializable {
	
	/** The Constant serialVersionUID. */
	private static final long		serialVersionUID	= -7127365575633206221L;
	
	/** The id. */
	private Long					id;
	
	/** The name. */
	private String					name;
	
	/** The books. */
	private Set<MHSBookEntityBean>	books				= new HashSet<MHSBookEntityBean>();
	
	/**
	 * Instantiates a new mHS book shelf entity bean.
	 */
	public MHSBookShelfEntityBean() {
		super();
	}
	
	/**
	 * Gets the id.
	 * 
	 * @return the id
	 */
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name = "BOOK_SHELF_ID")
	public Long getId() {
		return this.id;
	}
	
	/**
	 * Sets the id.
	 * 
	 * @param id
	 *            the new id
	 */
	public void setId(Long id) {
		this.id = id;
	}
	
	/**
	 * Gets the name.
	 * 
	 * @return the name
	 */
	@Column(name = "BOOK_SHELF_NAME")
	public String getName() {
		return this.name;
	}
	
	/**
	 * Sets the name.
	 * 
	 * @param name
	 *            the new name
	 */
	public void setName(String name) {
		this.name = name;
	}
	
	/**
	 * Gets the books.
	 * 
	 * @return the books
	 */
	@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
	@JoinColumn(name = "BOOK_SHELF_ID", referencedColumnName = "BOOK_SHELF_ID")
	public Set<MHSBookEntityBean> getBooks() {
		return books;
	}
	
	/**
	 * Sets the books.
	 * 
	 * @param books
	 *            the new books
	 */
	public void setBooks(Set<MHSBookEntityBean> books) {
		this.books = books;
	}
	
	/**
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((books == null) ? 0 : books.hashCode());
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}
	
	/**
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (!(obj instanceof MHSBookShelfEntityBean)) {
			return false;
		}
		MHSBookShelfEntityBean other = (MHSBookShelfEntityBean) obj;
		if (books == null) {
			if (other.books != null) {
				return false;
			}
		} else if (!books.equals(other.books)) {
			return false;
		}
		if (id == null) {
			if (other.id != null) {
				return false;
			}
		} else if (!id.equals(other.id)) {
			return false;
		}
		if (name == null) {
			if (other.name != null) {
				return false;
			}
		} else if (!name.equals(other.name)) {
			return false;
		}
		return true;
	}
	
	/**
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "MHSBookShelfEntityBean [id=" + id + ", name=" + name + ", books=" + books + "]";
	}
}

Highlighted code above shows removal of JoinTable (name is the name of the column in BOOK table and referencedColumnName is the name of column in BOOK_SHELF table).

Finally our persistence.xml and some test code to check our model.
persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    version="2.0"
    xmlns="http://java.sun.com/xml/ns/persistence"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" >
    <persistence-unit name="MumzJPA" transaction-type="RESOURCE_LOCAL">
    	<class>com.mumz.test.hibernatesearch.entitybeans.MHSBookEntityBean</class>
    	<class>com.mumz.test.hibernatesearch.entitybeans.MHSBookShelfEntityBean</class>
        <properties>
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
            <property name="hibernate.connection.password" value="root"/>
            <property name="hibernate.connection.url" value="jdbc:mysql://localhost/jpa_schema"/>
            <property name="hibernate.connection.username" value="root"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
        </properties>
    </persistence-unit>
</persistence>

TestOneToManyJoinTable.java

package com.mumz.test.hibernatesearch.entitybeans;

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

import javax.persistence.EntityManager;
import javax.persistence.Persistence;

/**
 * The Class TestOneToManyJoinTable.
 * @author prabhat.jha
 */
public class TestOneToManyJoinTable {

	/**
	 * The main method.
	 *
	 * @param args
	 *            the arguments
	 */
	public static void main(String[] args) {
		EntityManager entityManager = Persistence.createEntityManagerFactory("MumzJPA").createEntityManager();
		try {
			Set<MHSBookEntityBean> books = new HashSet<MHSBookEntityBean>();
			MHSBookEntityBean mhsBookEntityBean = new MHSBookEntityBean();
			mhsBookEntityBean.setName("Java EE Pocket Guide");
			mhsBookEntityBean.setAuthor("Arun Gupta");
			books.add(mhsBookEntityBean);
			mhsBookEntityBean = new MHSBookEntityBean();
			mhsBookEntityBean.setName("Pro Android 4");
			mhsBookEntityBean.setAuthor("Satya Komatineni and Dave MacLean");
			books.add(mhsBookEntityBean);

			MHSBookShelfEntityBean bookShelfEntityBean = new MHSBookShelfEntityBean();
			bookShelfEntityBean.setName("Technical Books");
			bookShelfEntityBean.setBooks(books);

			entityManager.getTransaction().begin();
			entityManager.persist(bookShelfEntityBean);
			entityManager.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			entityManager.getTransaction().rollback();
		} finally {
			if (entityManager != null) {
				entityManager.close();
			}
			entityManager = null;
		}
	}
}

Database schema used for this code.

SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL';

DROP SCHEMA IF EXISTS `jpa_schema` ;
CREATE SCHEMA IF NOT EXISTS `jpa_schema` DEFAULT CHARACTER SET utf8 ;
USE `jpa_schema` ;

-- -----------------------------------------------------
-- Table `jpa_schema`.`book_shelf`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `jpa_schema`.`book_shelf` ;

CREATE  TABLE IF NOT EXISTS `jpa_schema`.`book_shelf` (
  `BOOK_SHELF_ID` INT(11) NOT NULL AUTO_INCREMENT ,
  `BOOK_SHELF_NAME` VARCHAR(45) NULL DEFAULT NULL ,
  PRIMARY KEY (`BOOK_SHELF_ID`) )
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;

-- -----------------------------------------------------
-- Table `jpa_schema`.`book`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `jpa_schema`.`book` ;

CREATE  TABLE IF NOT EXISTS `jpa_schema`.`book` (
  `BOOK_ID` INT(11) NOT NULL AUTO_INCREMENT ,
  `BOOK_AUTHOR` VARCHAR(45) NOT NULL ,
  `BOOK_NAME` VARCHAR(45) NOT NULL ,
  `BOOK_SHELF_ID` INT(11) NULL DEFAULT NULL ,
  PRIMARY KEY (`BOOK_ID`) ,
  INDEX `FK_BOOK_SHELF_ID` (`BOOK_SHELF_ID` ASC) ,
  CONSTRAINT `FK_BOOK_SHELF_ID`
    FOREIGN KEY (`BOOK_SHELF_ID` )
    REFERENCES `jpa_schema`.`book_shelf` (`BOOK_SHELF_ID` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB
DEFAULT CHARACTER SET = utf8;

SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;

JPA OneToMany Unidirectional with Join Table

Posted on

In earlier post we had OneToMany bi-directional mapping. In this post we will see how can we make it unidirectional with the usage of a JoinTable.
Let’s create our eclipse project and then add update our pom.xml.

pom.xml

<project
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://maven.apache.org/POM/4.0.0"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
>
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.mumz.test.hibernatesearch</groupId>
	<artifactId>MumzHibernateSearch</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>
	<name>MumzHibernateSearch</name>
	<url>http://maven.apache.org</url>
	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
	</properties>
	<dependencies>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>3.8.1</version>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.hibernate.javax.persistence</groupId>
			<artifactId>hibernate-jpa-2.0-api</artifactId>
			<version>1.0.1.Final</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>4.1.7.Final</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>4.1.7.Final</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.21</version>
		</dependency>
	</dependencies>
</project>

Next we will write our Entity to model Book and BookShelf tables. BookShelf will contain a set of books. First we will write our MHSBookEntityBean.

MHSBookEntityBean.java

package com.mumz.test.hibernatesearch.entitybeans;

import java.io.Serializable;
import java.lang.Long;
import java.lang.String;
import javax.persistence.*;

/**
 * The Class MHSBookEntityBean.
 * @author prabhat.jha
 */
@Entity
@Table(name = "BOOK")
public class MHSBookEntityBean implements Serializable {
	
	/** The Constant serialVersionUID. */
	private static final long	serialVersionUID	= -5129783468137830152L;
	
	/** The id. */
	private Long				id					= null;
	
	/** The name. */
	private String				name				= null;
	
	/** The author. */
	private String				author				= null;
	
	/**
	 * Instantiates a new mHS book entity bean.
	 */
	public MHSBookEntityBean() {
		super();
	}
	
	/**
	 * Gets the id.
	 * 
	 * @return the id
	 */
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name = "BOOK_ID")
	public Long getId() {
		return this.id;
	}
	
	/**
	 * Sets the id.
	 * 
	 * @param id
	 *            the new id
	 */
	public void setId(Long id) {
		this.id = id;
	}
	
	/**
	 * Gets the name.
	 * 
	 * @return the name
	 */
	@Column(name = "BOOK_NAME")
	public String getName() {
		return this.name;
	}
	
	/**
	 * Sets the name.
	 * 
	 * @param name
	 *            the new name
	 */
	public void setName(String name) {
		this.name = name;
	}
	
	/**
	 * Gets the author.
	 * 
	 * @return the author
	 */
	@Column(name = "BOOK_AUTHOR")
	public String getAuthor() {
		return author;
	}
	
	/**
	 * Sets the author.
	 * 
	 * @param author
	 *            the new author
	 */
	public void setAuthor(String author) {
		this.author = author;
	}

	/** (non-Javadoc)
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((author == null) ? 0 : author.hashCode());
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	/** (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (!(obj instanceof MHSBookEntityBean)) {
			return false;
		}
		MHSBookEntityBean other = (MHSBookEntityBean) obj;
		if (author == null) {
			if (other.author != null) {
				return false;
			}
		} else if (!author.equals(other.author)) {
			return false;
		}
		if (id == null) {
			if (other.id != null) {
				return false;
			}
		} else if (!id.equals(other.id)) {
			return false;
		}
		if (name == null) {
			if (other.name != null) {
				return false;
			}
		} else if (!name.equals(other.name)) {
			return false;
		}
		return true;
	}

	/** (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "MHSBookEntityBean [id=" + id + ", name=" + name + ", author=" + author + "]";
	}
}

Next we will write our MHSBookShelfEntityBean

MHSBookShelfEntityBean.java

package com.mumz.test.hibernatesearch.entitybeans;

import java.io.Serializable;
import java.lang.Long;
import java.lang.String;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.*;

/**
 * The Class MHSBookShelfEntityBean.
 * 
 * @author prabhat.jha
 */
@Entity
@Table(name = "BOOK_SHELF")
public class MHSBookShelfEntityBean implements Serializable {
	
	/** The Constant serialVersionUID. */
	private static final long		serialVersionUID	= -7127365575633206221L;
	
	/** The id. */
	private Long					id;
	
	/** The name. */
	private String					name;
	
	/** The books. */
	private Set<MHSBookEntityBean>	books				= new HashSet<MHSBookEntityBean>();
	
	/**
	 * Instantiates a new mHS book shelf entity bean.
	 */
	public MHSBookShelfEntityBean() {
		super();
	}
	
	/**
	 * Gets the id.
	 * 
	 * @return the id
	 */
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name = "BOOK_SHELF_ID")
	public Long getId() {
		return this.id;
	}
	
	/**
	 * Sets the id.
	 * 
	 * @param id
	 *            the new id
	 */
	public void setId(Long id) {
		this.id = id;
	}
	
	/**
	 * Gets the name.
	 * 
	 * @return the name
	 */
	@Column(name = "BOOK_SHELF_NAME")
	public String getName() {
		return this.name;
	}
	
	/**
	 * Sets the name.
	 * 
	 * @param name
	 *            the new name
	 */
	public void setName(String name) {
		this.name = name;
	}
	
	/**
	 * Gets the books.
	 * 
	 * @return the books
	 */
	@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
	@JoinTable(name = "BOOK_SHELF_BOOK_MAPPING", joinColumns = {
		@JoinColumn(name = "BOOK_SHELF_ID", referencedColumnName = "BOOK_SHELF_ID")
	}, inverseJoinColumns = {
		@JoinColumn(name = "BOOK_ID", referencedColumnName = "BOOK_ID", unique = true)
	})
	public Set<MHSBookEntityBean> getBooks() {
		return books;
	}
	
	/**
	 * Sets the books.
	 * 
	 * @param books
	 *            the new books
	 */
	public void setBooks(Set<MHSBookEntityBean> books) {
		this.books = books;
	}
	
	/**
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((books == null) ? 0 : books.hashCode());
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}
	
	/**
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (!(obj instanceof MHSBookShelfEntityBean)) {
			return false;
		}
		MHSBookShelfEntityBean other = (MHSBookShelfEntityBean) obj;
		if (books == null) {
			if (other.books != null) {
				return false;
			}
		} else if (!books.equals(other.books)) {
			return false;
		}
		if (id == null) {
			if (other.id != null) {
				return false;
			}
		} else if (!id.equals(other.id)) {
			return false;
		}
		if (name == null) {
			if (other.name != null) {
				return false;
			}
		} else if (!name.equals(other.name)) {
			return false;
		}
		return true;
	}
	
	/**
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "MHSBookShelfEntityBean [id=" + id + ", name=" + name + ", books=" + books + "]";
	}
}

Highlighted code above signifies the Join table usage.

Finally our persistence.xml and some test code to check our model.

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    version="2.0"
    xmlns="http://java.sun.com/xml/ns/persistence"
    xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd" >
    <persistence-unit name="MumzJPA" transaction-type="RESOURCE_LOCAL">
    	<class>com.mumz.test.hibernatesearch.entitybeans.MHSBookEntityBean</class>
    	<class>com.mumz.test.hibernatesearch.entitybeans.MHSBookShelfEntityBean</class>
        <properties>
            <property name="hibernate.show_sql" value="true"/>
            <property name="hibernate.connection.driver_class" value="com.mysql.jdbc.Driver"/>
            <property name="hibernate.connection.password" value="root"/>
            <property name="hibernate.connection.url" value="jdbc:mysql://localhost/jpa_schema"/>
            <property name="hibernate.connection.username" value="root"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.MySQLDialect"/>
        </properties>
    </persistence-unit>
</persistence>

TestOneToManyJoinTable.java

package com.mumz.test.hibernatesearch.entitybeans;

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

import javax.persistence.EntityManager;
import javax.persistence.Persistence;

/**
 * The Class TestOneToManyJoinTable.
 * @author prabhat.jha
 */
public class TestOneToManyJoinTable {
	
	/**
	 * The main method.
	 * 
	 * @param args
	 *            the arguments
	 */
	public static void main(String[] args) {
		EntityManager entityManager = Persistence.createEntityManagerFactory("MumzJPA").createEntityManager();
		try {
			Set<MHSBookEntityBean> books = new HashSet<MHSBookEntityBean>();
			MHSBookEntityBean mhsBookEntityBean = new MHSBookEntityBean();
			mhsBookEntityBean.setName("Java EE Pocket Guide");
			mhsBookEntityBean.setAuthor("Arun Gupta");
			books.add(mhsBookEntityBean);
			mhsBookEntityBean = new MHSBookEntityBean();
			mhsBookEntityBean.setName("Pro Android 4");
			mhsBookEntityBean.setAuthor("Satya Komatineni and Dave MacLean");
			books.add(mhsBookEntityBean);
			
			MHSBookShelfEntityBean bookShelfEntityBean = new MHSBookShelfEntityBean();
			bookShelfEntityBean.setName("Technical Books");
			bookShelfEntityBean.setBooks(books);
			
			entityManager.getTransaction().begin();
			entityManager.persist(bookShelfEntityBean);
			entityManager.getTransaction().commit();
		} catch (Exception e) {
			e.printStackTrace();
			entityManager.getTransaction().rollback();
		} finally {
			if (entityManager != null) {
				entityManager.close();
			}
			entityManager = null;
		}
	}
	
}

Database schema used for this code.

SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL';

DROP SCHEMA IF EXISTS `jpa_schema` ;
CREATE SCHEMA IF NOT EXISTS `jpa_schema` DEFAULT CHARACTER SET utf8 ;
USE `jpa_schema` ;

-- -----------------------------------------------------
-- Table `jpa_schema`.`book`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `jpa_schema`.`book` ;

CREATE  TABLE IF NOT EXISTS `jpa_schema`.`book` (
  `BOOK_ID` INT(11) NOT NULL AUTO_INCREMENT ,
  `BOOK_AUTHOR` VARCHAR(45) NOT NULL ,
  `BOOK_NAME` VARCHAR(45) NOT NULL ,
  PRIMARY KEY (`BOOK_ID`) )
ENGINE = InnoDB
AUTO_INCREMENT = 5
DEFAULT CHARACTER SET = utf8;

-- -----------------------------------------------------
-- Table `jpa_schema`.`book_shelf`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `jpa_schema`.`book_shelf` ;

CREATE  TABLE IF NOT EXISTS `jpa_schema`.`book_shelf` (
  `BOOK_SHELF_ID` INT(11) NOT NULL AUTO_INCREMENT ,
  `BOOK_SHELF_NAME` VARCHAR(45) NULL DEFAULT NULL ,
  PRIMARY KEY (`BOOK_SHELF_ID`) )
ENGINE = InnoDB
AUTO_INCREMENT = 3
DEFAULT CHARACTER SET = utf8;

-- -----------------------------------------------------
-- Table `jpa_schema`.`book_shelf_book_mapping`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `jpa_schema`.`book_shelf_book_mapping` ;

CREATE  TABLE IF NOT EXISTS `jpa_schema`.`book_shelf_book_mapping` (
  `BOOK_SHELF_BOOK_MAP_ID` INT(11) NOT NULL AUTO_INCREMENT ,
  `BOOK_ID` INT(11) NULL DEFAULT NULL ,
  `BOOK_SHELF_ID` INT(11) NULL DEFAULT NULL ,
  PRIMARY KEY (`BOOK_SHELF_BOOK_MAP_ID`) ,
  INDEX `FK_BOOK_ID` (`BOOK_ID` ASC) ,
  INDEX `FK_BOOK_SHELF_ID` (`BOOK_SHELF_ID` ASC) ,
  CONSTRAINT `FK_BOOK_ID`
    FOREIGN KEY (`BOOK_ID` )
    REFERENCES `jpa_schema`.`book` (`BOOK_ID` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION,
  CONSTRAINT `FK_BOOK_SHELF_ID`
    FOREIGN KEY (`BOOK_SHELF_ID` )
    REFERENCES `jpa_schema`.`book_shelf` (`BOOK_SHELF_ID` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB
AUTO_INCREMENT = 5
DEFAULT CHARACTER SET = utf8;

SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;

Audit Framework with Spring AOP and JPA

Posted on Updated on

Every now and then we come across a common requirement of building an Audit framework. In simple words it means whenever a record is added, edited or removed there should be an entry made in a separate table so that different actions of users can be monitored. Today in this tutorial we will build an audit framework in Java using Spring AOP and JPA. Records will be saved in xml form. We will reuse code from JPA OneToMany Mapping Using Hibernate and MySQL tutorial.

Our database schema consists of BookShelf, Book and Audit.

Database Schema

First we will setup our maven dependency (Need help creating Maven Java Project in Eclipse ?)

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>AuditFrameWork</groupId>
	<artifactId>AuditFrameWork</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<build>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.7</source>
					<target>1.7</target>
				</configuration>
			</plugin>
			<plugin>
				<groupId>org.apache.maven.plugins</groupId>
				<artifactId>maven-clean-plugin</artifactId>
				<version>2.5</version>
			</plugin>
		</plugins>
	</build>
	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-tx</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-orm</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>cglib</groupId>
			<artifactId>cglib</artifactId>
			<version>2.2.2</version>
		</dependency>
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>1.6.11</version>
		</dependency>
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.6.11</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.21</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate.javax.persistence</groupId>
			<artifactId>hibernate-jpa-2.0-api</artifactId>
			<version>1.0.1.Final</version>
		</dependency>
		<dependency>
			<groupId>com.thoughtworks.xstream</groupId>
			<artifactId>xstream</artifactId>
			<version>1.4.2</version>
		</dependency>
		<dependency>
			<groupId>commons-lang</groupId>
			<artifactId>commons-lang</artifactId>
			<version>2.6</version>
		</dependency>
		<dependency>
			<groupId>commons-collections</groupId>
			<artifactId>commons-collections</artifactId>
			<version>3.2</version>
		</dependency>
		<dependency>
			<groupId>commons-beanutils</groupId>
			<artifactId>commons-beanutils</artifactId>
			<version>1.8.3</version>
		</dependency>
		<dependency>
			<groupId>javax.enterprise</groupId>
			<artifactId>cdi-api</artifactId>
			<version>1.0</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-core</artifactId>
			<version>${hibernate.version}</version>
		</dependency>
		<dependency>
			<groupId>org.hibernate</groupId>
			<artifactId>hibernate-entitymanager</artifactId>
			<version>${hibernate.version}</version>
			<exclusions>
				<exclusion>
					<groupId>cglib</groupId>
					<artifactId>cglib</artifactId>
				</exclusion>
				<exclusion>
					<groupId>dom4j</groupId>
					<artifactId>dom4j</artifactId>
				</exclusion>
			</exclusions>
		</dependency>
	</dependencies>
	<properties>
		<hibernate.version>3.6.7.Final</hibernate.version>
		<spring.version>3.1.2.RELEASE</spring.version>
	</properties>
</project>

Second we will start with our JPA mapping code. There are few important classes and interfaces involved.

  1. IEntity – A marker interface to depict an database entity
  2. IXMLConvertable – As name suggests this interface let’s conversion from Java to XML generic
  3. IAuditable – A marker interface to depict an entity which can be auditable

Let’s start building our framework.

IAuditable


package com.mumz.test.audit.interfaces;

import java.io.Serializable;

/**
 * The Interface IAuditable.
 * 
 * @author prabhat.jha
 */
public interface IAuditable extends Serializable{

	/** The Constant OPERATION_INSERT. */
	public static final String OPERATION_INSERT = "Insert";
	
	/** The Constant OPERATION_UPDATE. */
	public static final String OPERATION_UPDATE = "Update";
	
	/** The Constant OPERATION_DELETE. */
	public static final String OPERATION_DELETE = "Delete";
}

IEntity


package com.mumz.test.audit.interfaces;

import java.io.Serializable;

/**
 * The Interface IEntity.
 * @author prabhat.jha
 */
public interface IEntity extends Serializable{
}

IXMLConvertable


package com.mumz.test.audit.interfaces;

import java.io.Serializable;
import java.util.List;
import java.util.Map;

/**
 * The Interface IXMLConvertable.
 * @author prabhat.jha
 */
public interface IXMLConvertable extends Serializable {
	
	/**
	 * Gets the class alias.
	 * 
	 * @return the class alias
	 */
	public String getClassAlias();

	/**
	 * Gets the field to be omitted.
	 * 
	 * @return the field to be omitted
	 */
	public List<String> getFieldsToBeOmitted();

	/**
	 * Gets the field aliases.
	 * 
	 * @return the field aliases
	 */
	public Map<String, String> getFieldAliases();
}

With the basic structure of our code base setup we will reuse the code from this post. Explanation on OneToMany can be obtained from the link.

Book.java

package com.mumz.test.audit.beans;

import java.util.List;
import java.util.Map;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.NamedQueries;
import javax.persistence.NamedQuery;
import javax.persistence.Table;
import javax.persistence.Transient;

import com.mumz.test.audit.interfaces.IAuditable;
import com.mumz.test.audit.interfaces.IEntity;
import com.mumz.test.audit.interfaces.IXMLConvertable;

/**
 * The Class Book.
 * 
 * @author prabhat.jha
 */
@Entity
@Table(name = "BOOK")
@NamedQueries({ @NamedQuery(name = "fetchAllBooks", query = "SELECT ALLBOOKS FROM Book ALLBOOKS") })
public class Book implements IXMLConvertable, IEntity, IAuditable {

	/** The Constant serialVersionUID. */
	private static final long serialVersionUID = -4788522141255171404L;

	/** The id. */
	private Long id = null;

	/** The name. */
	private String name = null;

	/** The author. */
	private String author = null;

	/** The book shelf. */
	private BookShelf bookShelf = null;

	/**
	 * Gets the id.
	 * 
	 * @return the id
	 */
	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name = "BOOK_ID")
	public Long getId() {
		return id;
	}

	/**
	 * Sets the id.
	 * 
	 * @param id
	 *            the id to set
	 */
	public void setId(Long id) {
		this.id = id;
	}

	/**
	 * Gets the name.
	 * 
	 * @return the name
	 */
	@Column(name = "BOOK_NAME")
	public String getName() {
		return name;
	}

	/**
	 * Sets the name.
	 * 
	 * @param name
	 *            the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * Gets the author.
	 * 
	 * @return the author
	 */
	@Column(name = "BOOK_AUTHOR")
	public String getAuthor() {
		return author;
	}

	/**
	 * Sets the author.
	 * 
	 * @param author
	 *            the author to set
	 */
	public void setAuthor(String author) {
		this.author = author;
	}

	/**
	 * Gets the book shelf.
	 * 
	 * @return the bookShelf
	 */
	@ManyToOne
	@JoinColumn(name = "BOOK_SHELF_ID")
	public BookShelf getBookShelf() {
		return bookShelf;
	}

	/**
	 * Sets the book shelf.
	 * 
	 * @param bookShelf
	 *            the bookShelf to set
	 */
	public void setBookShelf(BookShelf bookShelf) {
		this.bookShelf = bookShelf;
	}

	/** (non-Javadoc)
	 * @see com.mumz.test.audit.interfaces.IXMLConvertable#getClassAlias()
	 */
	@Override
	@Transient
	public String getClassAlias() {
		return "Book";
	}

	/** (non-Javadoc)
	 * @see com.mumz.test.audit.interfaces.IXMLConvertable#getFieldsToBeOmitted()
	 */
	@Override
	@Transient
	public List<String> getFieldsToBeOmitted() {
		return null;
	}

	/** (non-Javadoc)
	 * @see com.mumz.test.audit.interfaces.IXMLConvertable#getFieldAliases()
	 */
	@Override
	@Transient
	public Map<String, String> getFieldAliases() {
		return null;
	}

	/**
	 * (non-Javadoc).
	 * 
	 * @return the int
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((author == null) ? 0 : author.hashCode());
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	/**
	 * (non-Javadoc).
	 * 
	 * @param obj
	 *            the obj
	 * @return true, if successful
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (!(obj instanceof Book)) {
			return false;
		}
		Book other = (Book) obj;
		if (author == null) {
			if (other.author != null) {
				return false;
			}
		} else if (!author.equals(other.author)) {
			return false;
		}
		if (id == null) {
			if (other.id != null) {
				return false;
			}
		} else if (!id.equals(other.id)) {
			return false;
		}
		if (name == null) {
			if (other.name != null) {
				return false;
			}
		} else if (!name.equals(other.name)) {
			return false;
		}
		return true;
	}

	/**
	 * (non-Javadoc).
	 * 
	 * @return the string
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return String.format("Book [id=%s, name=%s, author=%s]", id, name,
				author);
	}
}

BookShelf.java


package com.mumz.test.audit.beans;

import java.util.List;
import java.util.Map;

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

import com.mumz.test.audit.interfaces.IEntity;
import com.mumz.test.audit.interfaces.IXMLConvertable;

/**
 * The Class BookShelf.
 * @author prabhat.jha
 */
@Entity
@Table(name = "BOOK_SHELF")
@NamedQueries({
	@NamedQuery(name="fetchAllBookShelves", query="SELECT ALLBOOKSHELVES FROM BookShelf ALLBOOKSHELVES")
})
public class BookShelf implements IEntity, IXMLConvertable{
	
	/** The Constant serialVersionUID. */
	private static final long serialVersionUID = -7867320637075813912L;

	/** The id. */
	private Long	id		= null;

	/** The name. */
	private String	name	= null;
	
	/** The books. */
	private List<Book> books = null;

	/**
	 * Gets the id.
	 * 
	 * @return the id
	 */
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	@Column(name="BOOK_SHELF_ID")
	public Long getId() {
		return id;
	}

	/**
	 * Sets the id.
	 * 
	 * @param id
	 *            the id to set
	 */
	public void setId(Long id) {
		this.id = id;
	}

	/**
	 * Gets the name.
	 * 
	 * @return the name
	 */
	@Column(name="BOOK_SHELF_NAME")
	public String getName() {
		return name;
	}

	/**
	 * Sets the name.
	 * 
	 * @param name
	 *            the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * Gets the books.
	 * 
	 * @return the books
	 */
	@OneToMany(cascade=CascadeType.ALL, mappedBy="bookShelf")
	public List<Book> getBooks() {
		return books;
	}

	/**
	 * Sets the books.
	 * 
	 * @param books
	 *            the books to set
	 */
	public void setBooks(List<Book> books) {
		this.books = books;
	}

	/** (non-Javadoc)
	 * @see com.mumz.test.audit.interfaces.IXMLConvertable#getClassAlias()
	 */
	@Override
	@Transient
	public String getClassAlias() {
		return "BookShelf";
	}

	/** (non-Javadoc)
	 * @see com.mumz.test.audit.interfaces.IXMLConvertable#getFieldsToBeOmitted()
	 */
	@Override
	@Transient
	public List<String> getFieldsToBeOmitted() {
		return null;
	}

	/** (non-Javadoc)
	 * @see com.mumz.test.audit.interfaces.IXMLConvertable#getFieldAliases()
	 */
	@Override
	@Transient
	public Map<String, String> getFieldAliases() {
		return null;
	}
	/**
	 * (non-Javadoc).
	 * 
	 * @return the int
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((books == null) ? 0 : books.hashCode());
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	/**
	 * (non-Javadoc).
	 * 
	 * @param obj
	 *            the obj
	 * @return true, if successful
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (!(obj instanceof BookShelf)) {
			return false;
		}
		BookShelf other = (BookShelf) obj;
		if (books == null) {
			if (other.books != null) {
				return false;
			}
		} else if (!books.equals(other.books)) {
			return false;
		}
		if (id == null) {
			if (other.id != null) {
				return false;
			}
		} else if (!id.equals(other.id)) {
			return false;
		}
		if (name == null) {
			if (other.name != null) {
				return false;
			}
		} else if (!name.equals(other.name)) {
			return false;
		}
		return true;
	}

	/**
	 * (non-Javadoc).
	 * 
	 * @return the string
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return String.format("BookShelf [id=%s, name=%s, books=%s]", id, name, books);
	}
}

And our Audit bean which will hold pre and post image of record being inserted, updated.

Audit.java


package com.mumz.test.audit.beans;

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

import com.mumz.test.audit.interfaces.IEntity;

/**
 * The Class Auditable.
 * 
 * @author prabhat.jha
 */
@Entity
@Table(name="AUDIT")
public class Audit implements IEntity {

	/** The Constant serialVersionUID. */
	private static final long serialVersionUID = 6161413362358931496L;

	/** The id. */
	private Long id = null;
	
	/** The pre image. */
	private String preImage = null;

	/** The post image. */
	private String postImage = null;
	
	/** The operation. */
	private String operation = null;

	@Id
	@GeneratedValue(strategy = GenerationType.AUTO)
	@Column(name="AUDIT_ID")
	public Long getId() {
		return id;
	}

	/**
	 * Sets the id.
	 * 
	 * @param id
	 *            the new id
	 */
	public void setId(Long id) {
		this.id = id;
	}

	/**
	 * Sets the pre image.
	 * 
	 * @param preImage
	 *            the new pre image
	 */
	public void setPreImage(String preImage) {
		this.preImage = preImage;
	}
	
	/**
	 * Gets the pre image.
	 * 
	 * @return the pre image
	 */
	@Column(name="AUDIT_PRE_IMAGE")
	public String getPreImage() {
		return this.preImage;
	}

	/**
	 * Gets the post image.
	 * 
	 * @return the post image
	 */
	@Column(name="AUDIT_POST_IMAGE")
	public String getPostImage() {
		return this.postImage;
	}

	/**
	 * Sets the post image.
	 * 
	 * @param postImage
	 *            the new post image
	 */
	public void setPostImage(String postImage) {
		this.postImage = postImage;
	}
	
	/**
	 * Sets the operation.
	 * 
	 * @param operation
	 *            the new operation
	 */
	public void setOperation(String operation) {
		this.operation = operation;
	}

	/**
	 * Gets the operation.
	 * 
	 * @return the operation
	 */
	@Column(name="AUDIT_OPERATION")
	public String getOperation() {
		return this.operation;
	}

	/** (non-Javadoc)
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((operation == null) ? 0 : operation.hashCode());
		result = prime * result
				+ ((postImage == null) ? 0 : postImage.hashCode());
		result = prime * result
				+ ((preImage == null) ? 0 : preImage.hashCode());
		return result;
	}

	/** (non-Javadoc)
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (!(obj instanceof Audit)) {
			return false;
		}
		Audit other = (Audit) obj;
		if (operation == null) {
			if (other.operation != null) {
				return false;
			}
		} else if (!operation.equals(other.operation)) {
			return false;
		}
		if (postImage == null) {
			if (other.postImage != null) {
				return false;
			}
		} else if (!postImage.equals(other.postImage)) {
			return false;
		}
		if (preImage == null) {
			if (other.preImage != null) {
				return false;
			}
		} else if (!preImage.equals(other.preImage)) {
			return false;
		}
		return true;
	}

	/** (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "Auditable [preImage=" + preImage + ", postImage=" + postImage
				+ ", operation=" + operation + "]";
	}
}

Since we are trying to build a framework, all actions should be routed through a controller, hence we will build an interface IAuditController and implementation class AuditControllerImpl.

IAuditController


package com.mumz.test.audit.controller;

import java.util.List;

import com.mumz.test.audit.beans.Book;
import com.mumz.test.audit.beans.BookShelf;

/**
 * The Interface IAuditController.
 * @author prabhat.jha
 */
public interface IAuditController {
	
	/**
	 * Adds the book.
	 * 
	 * @param book
	 *            the book
	 * @return the book
	 */
	public Book addBook(Book book);
	
	/**
	 * Update book.
	 * 
	 * @param book
	 *            the book
	 * @return the book
	 */
	public Book updateBook(Book book);
	
	/**
	 * Removes the book.
	 * 
	 * @param book
	 *            the book
	 * @return the book
	 */
	public Book removeBook(Book book);
	
	/**
	 * Fetch all books.
	 * 
	 * @return the list
	 */
	public List<Book> fetchAllBooks();
	
	/**
	 * Adds the book shelf.
	 * 
	 * @param bookShelf
	 *            the book shelf
	 * @return the book shelf
	 */
	public BookShelf addBookShelf(BookShelf bookShelf);
	
	/**
	 * Update book shelf.
	 * 
	 * @param bookShelf
	 *            the book shelf
	 * @return the book shelf
	 */
	public BookShelf updateBookShelf(BookShelf bookShelf);
	
	/**
	 * Removes the book shelf.
	 * 
	 * @param bookShelf
	 *            the book shelf
	 * @return the book shelf
	 */
	public BookShelf removeBookShelf(BookShelf bookShelf);
	
	/**
	 * Fetch all book shelves.
	 * 
	 * @return the list
	 */
	public List<BookShelf> fetchAllBookShelves();
}

AuditControllerImpl


package com.mumz.test.audit.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Controller;

import com.mumz.test.audit.beans.Book;
import com.mumz.test.audit.beans.BookShelf;
import com.mumz.test.audit.dao.IAuditPersistableService;
import com.mumz.test.audit.dao.IAuditQueryService;

/**
 * The Class AuditController.
 * 
 * @author prabhat.jha
 */
@Controller(value="auditControllerImpl")
public class AuditControllerImpl implements IAuditController {

	/** The book dao service. */
	@Autowired
	private IAuditPersistableService auditPersistableService = null;

	/** The audit query service. */
	@Autowired
	private IAuditQueryService auditQueryService = null;

	/**
	 * Gets the audit persistable service.
	 * 
	 * @return the audit persistable service
	 */
	public IAuditPersistableService getAuditPersistableService() {
		return auditPersistableService;
	}

	/**
	 * Sets the audit persistable service.
	 * 
	 * @param auditPersistableService
	 *            the new audit persistable service
	 */
	public void setAuditPersistableService(
			IAuditPersistableService auditPersistableService) {
		this.auditPersistableService = auditPersistableService;
	}

	/**
	 * Gets the audit query service.
	 * 
	 * @return the audit query service
	 */
	public IAuditQueryService getAuditQueryService() {
		return auditQueryService;
	}

	/**
	 * Sets the audit query service.
	 * 
	 * @param auditQueryService
	 *            the new audit query service
	 */
	public void setAuditQueryService(IAuditQueryService auditQueryService) {
		this.auditQueryService = auditQueryService;
	}

       /**
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.mumz.test.audit.controller.IAuditController#addBook(com.mumz.test.
	 * audit.beans.Book)
	 */
	@Override
	public Book addBook(Book book) {
		return this.getAuditPersistableService().addEntity(book);
	}

       /** (non-Javadoc)
	 * @see com.mumz.test.audit.controller.IAuditController#updateBook(com.mumz.test.audit.beans.Book)
	 */
	@Override
	public Book updateBook(Book book) {
		return this.getAuditPersistableService().updateEntity(book);
	}

       /**
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.mumz.test.audit.controller.IAuditController#removeBook(com.mumz.test
	 * .audit.beans.Book)
	 */
	@Override
	public Book removeBook(Book book) {
		return this.getAuditPersistableService().removeEntity(book);
	}

       /**
	 * (non-Javadoc)
	 * 
	 * @see com.mumz.test.audit.controller.IAuditController#fetchAllBooks()
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<Book> fetchAllBooks() {
		return (List<Book>) this.getAuditQueryService().fetchAllBooks();
	}

       /**
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.mumz.test.audit.controller.IAuditController#addBookShelf(com.mumz.
	 * test.audit.beans.BookShelf)
	 */
	@Override
	public BookShelf addBookShelf(BookShelf bookShelf) {
		return this.getAuditPersistableService().addEntity(bookShelf);
	}

       /** (non-Javadoc)
	 * @see com.mumz.test.audit.controller.IAuditController#updateBookShelf(com.mumz.test.audit.beans.BookShelf)
	 */
	public BookShelf updateBookShelf(BookShelf bookShelf) {
		return this.getAuditPersistableService().updateEntity(bookShelf);
	}

       /**
	 * (non-Javadoc)
	 * 
	 * @see
	 * com.mumz.test.audit.controller.IAuditController#removeBookShelf(com.mumz
	 * .test.audit.beans.BookShelf)
	 */
	@Override
	public BookShelf removeBookShelf(BookShelf bookShelf) {
		return this.getAuditPersistableService().removeEntity(bookShelf);
	}

       /**
	 * (non-Javadoc)
	 * 
	 * @see com.mumz.test.audit.controller.IAuditController#fetchAllBookShelves()
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<BookShelf> fetchAllBookShelves() {
		return (List<BookShelf>) this.getAuditQueryService()
				.fetchAllBookShelves();
	}
}

Next we will implement our dao layer. IAuditPersistableService will be used when we are doing any add or update where as IAuditQueryService will be used fetching records.

IAuditPersistableService.java


package com.mumz.test.audit.dao;

import com.mumz.test.audit.interfaces.IEntity;

/**
 * The Interface IAuditPersistableService.
 * 
 * @author prabhat.jha
 */
public interface IAuditPersistableService {

	/**
	 * Adds the book.
	 * 
	 * @param entityToBeAdded
	 *            the book
	 * @return the book
	 */
	public <T extends IEntity> T addEntity(T entityToBeAdded);

	/**
	 * Update book.
	 * 
	 * @param entityToBeUpdated
	 *            the book
	 * @return the book
	 */
	public <T extends IEntity> T updateEntity(T entityToBeUpdated);

	/**
	 * Removes the book.
	 * 
	 * @param entityToBeRemoved
	 *            the book
	 * @return the book
	 */
	public <T extends IEntity> T removeEntity(T entityToBeRemoved);

}

AuditPersistableServiceImpl.java


package com.mumz.test.audit.dao;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.mumz.test.audit.interfaces.IEntity;

/**
 * The Class AuditPersistableServiceImpl.
 * @author prabhat.jha
 */
@Transactional
@Service
public class AuditPersistableServiceImpl implements IAuditPersistableService {

	/** The entity manager. */
	@PersistenceContext
	private EntityManager entityManager = null;

	/**
	 * Gets the entity manager.
	 *
	 * @return the entity manager
	 */
	public EntityManager getEntityManager() {
		return entityManager;
	}

	/**
	 * Sets the entity manager.
	 *
	 * @param entityManager the new entity manager
	 */
	public void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}
	
	/** (non-Javadoc)
	 * @see com.mumz.test.audit.dao.IAuditPersistableService#addBook(com.mumz.test.audit.beans.Book)
	 */
	@Override
	public <T extends IEntity> T addEntity(T entityToBeAdded) {
		return this.getEntityManager().merge(entityToBeAdded);
	}

	/** (non-Javadoc)
	 * @see com.mumz.test.audit.dao.IAuditPersistableService#removeBook(com.mumz.test.audit.beans.Book)
	 */
	@Override
	public <T extends IEntity> T removeEntity(T entityToBeRemoved) {
		//Un-tested code, I think JPA doesn't allow direct remove, first a fetch and then subsequent remove is required.
                this.getEntityManager().remove(entityToBeRemoved);
		return entityToBeRemoved;
	}


	/** (non-Javadoc)
	 * @see com.mumz.test.audit.dao.IAuditPersistableService#updateBook(com.mumz.test.audit.beans.Book)
	 */
	@Override
	public <T extends IEntity> T updateEntity(T entityToBeUpdated) {
		return this.getEntityManager().merge(entityToBeUpdated);
	}
}

IAuditQueryService


package com.mumz.test.audit.dao;

import java.util.List;

import com.mumz.test.audit.interfaces.IEntity;

/**
 * The Interface IAuditQueryService.
 * @author prabhat.jha
 */
public interface IAuditQueryService {
	
	/**
	 * Fetch all books.
	 * 
	 * @return the list
	 */
	public List<? extends IEntity> fetchAllBooks();
	
	/**
	 * Fetch all book shelves.
	 * 
	 * @return the list
	 */
	public List<? extends IEntity> fetchAllBookShelves();
}

AuditQueryServiceImpl.java


package com.mumz.test.audit.dao;

import java.util.Collections;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.mumz.test.audit.beans.Book;
import com.mumz.test.audit.beans.BookShelf;

/**
 * The Class AuditQueryServiceImpl.
 * @author prabhat.jha
 */
@Transactional(readOnly = true)
@Service
public class AuditQueryServiceImpl implements IAuditQueryService {

	/** The entity manager. */
	@PersistenceContext
	private EntityManager entityManager = null;
	
	/**
	 * Gets the entity manager.
	 * 
	 * @return the entity manager
	 */
	public EntityManager getEntityManager() {
		return entityManager;
	}

	/**
	 * Sets the entity manager.
	 * 
	 * @param entityManager
	 *            the new entity manager
	 */
	public void setEntityManager(EntityManager entityManager) {
		this.entityManager = entityManager;
	}

	/** (non-Javadoc)
	 * @see com.mumz.test.audit.dao.IAuditPersistableService#fetchAllBooks()
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<Book> fetchAllBooks() {
		try {
			Query namedQuery = this.getEntityManager().createNamedQuery("fetchAllBooks");
			return namedQuery.getResultList();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return Collections.emptyList();
	}

	/** (non-Javadoc)
	 * @see com.mumz.test.audit.dao.IAuditPersistableService#fetchAllBookShelves()
	 */
	@SuppressWarnings("unchecked")
	@Override
	public List<BookShelf> fetchAllBookShelves() {
		try {
			Query namedQuery = this.getEntityManager().createNamedQuery("fetchAllBookShelves");
			return namedQuery.getResultList();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
}

Next is our Audit Advice which actually does the audit entry.

AuditAdvice.java


package com.mumz.test.audit.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.mumz.test.audit.beans.Audit;
import com.mumz.test.audit.dao.IAuditPersistableService;
import com.mumz.test.audit.interfaces.IAuditable;
import com.mumz.test.audit.interfaces.IXMLConvertable;
import com.mumz.test.audit.utils.XStreamUtils;

/**
 * The Class BookAdvice.
 * 
 * @author prabhat.jha
 */
@Aspect
@Component
public class AuditAdvice {

	/** The audit persistable service. */
	@Autowired
	private IAuditPersistableService auditPersistableService = null;

	/**
	 * Gets the audit persistable service.
	 * 
	 * @return the audit persistable service
	 */
	public IAuditPersistableService getAuditPersistableService() {
		return auditPersistableService;
	}

	/**
	 * Sets the audit persistable service.
	 * 
	 * @param auditPersistableService
	 *            the new audit persistable service
	 */
	public void setAuditPersistableService(
			IAuditPersistableService auditPersistableService) {
		this.auditPersistableService = auditPersistableService;
	}

	/**
	 * Around remove advice.
	 * 
	 * @param pjp
	 *            the pjp
	 */
	@Around("execution(* com.mumz.test.audit.controller.AuditControllerImpl.*(..))")
	public void aroundAddBookShelfAdvice(ProceedingJoinPoint pjp) {
		String methodName = pjp.getSignature().getName();
		String operation = null;
		if (methodName.toUpperCase().contains("ADD")) {
			operation = IAuditable.OPERATION_INSERT;
		} else if (methodName.toUpperCase().contains("UPDATE")) {
			operation = IAuditable.OPERATION_UPDATE;
		} else {
			operation = IAuditable.OPERATION_DELETE;
		}
		Object[] arguments = pjp.getArgs();
		IXMLConvertable preImage = null;
		IXMLConvertable postImage = null;
		for (Object object : arguments) {
			if (!operation.equalsIgnoreCase(IAuditable.OPERATION_INSERT)
					&& object instanceof IAuditable
					&& object instanceof IXMLConvertable) {
				preImage = (IXMLConvertable) object;
			}
		}
		try {
			Object returnValue = pjp.proceed();
			if (operation.equalsIgnoreCase(IAuditable.OPERATION_UPDATE)
					&& returnValue instanceof IAuditable
					&& returnValue instanceof IXMLConvertable) {
				postImage = (IXMLConvertable) returnValue;
			}
		} catch (Throwable e) {
			e.printStackTrace();
		}
		if (preImage != null || postImage != null) {
			Audit auditableEntity = new Audit();
			auditableEntity.setOperation(IAuditable.OPERATION_INSERT);
			auditableEntity.setPreImage(XStreamUtils.getXMLFromObject(preImage,
					preImage.getClass().getName(), preImage.getFieldAliases(),
					preImage.getFieldsToBeOmitted()));
			auditableEntity.setPostImage(XStreamUtils.getXMLFromObject(
					postImage, postImage.getClass().getName(),
					postImage.getFieldAliases(),
					postImage.getFieldsToBeOmitted()));
			this.getAuditPersistableService().addEntity(auditableEntity);
		}
	}
}

Next is our XStreamUtils which is a generic code and converts given IXMLConvertable object and returns a XML representation of it.

XStreamUtils.java


package com.mumz.test.audit.utils;

import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;

import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.io.xml.DomDriver;

/**
 * The Class XStreamUtils.
 * 
 * @author prabhat.jha
 */
public class XStreamUtils {

	/**
	 * Gets the xML from object.
	 * 
	 * @param toBeConverted
	 *            the to be converted
	 * @param classNameAlias
	 *            the class name alias
	 * @param fieldAlias
	 *            the field alias
	 * @param fieldsToBeOmitted
	 *            the fields to be omitted
	 * @return the xML from object
	 */
	public static String getXMLFromObject(Object toBeConverted,
			String classNameAlias, Map<String, String> fieldAlias,
			List<String> fieldsToBeOmitted) {
		StringBuilder objectAsXML = new StringBuilder();
		if (toBeConverted != null) {
			XStream xStream = new XStream(new DomDriver());
			if (StringUtils.isNotEmpty(classNameAlias)) {
				xStream.alias(classNameAlias, toBeConverted.getClass());
			}
			if (fieldAlias != null && !fieldAlias.isEmpty()) {
				for (Entry<String, String> entry : fieldAlias.entrySet()) {
					xStream.aliasField(entry.getKey(),
							toBeConverted.getClass(), entry.getValue());
				}
			}
			if (CollectionUtils.isNotEmpty(fieldsToBeOmitted)) {
				for (String fieldToBeOmitted : fieldsToBeOmitted) {
					xStream.omitField(toBeConverted.getClass(),
							fieldToBeOmitted);
				}
			}
			objectAsXML.append(xStream.toXML(toBeConverted));
		}
		return objectAsXML.toString();
	}
}

Next is our spring configuration file.

audit-bean-definition.xml


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

	<bean id="propertyConfigurer"
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="location">
			<value>jdbc.properties</value>
		</property>
	</bean>

	<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor" />
	
	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName">
			<value>${jdbc.driverClassName}</value>
		</property>
		<property name="url">
			<value>${jdbc.url}</value>
		</property>
		<property name="username">
			<value>${jdbc.username}</value>
		</property>
		<property name="password">
			<value>${jdbc.password}</value>
		</property>
	</bean>
	
	<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="jpaVendorAdapter">
			<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
				<property name="database">
					<value>${jdbc.databaseVendor}</value>
				</property>
				<property name="showSql" value="false" />
			</bean>
		</property>
	</bean>
	
	<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
		<property name="entityManagerFactory" ref="entityManagerFactory" />
	</bean>

	<tx:annotation-driven transaction-manager="transactionManager" />
	
	<context:component-scan base-package="com.mumz.test.audit"></context:component-scan>
	
	<context:annotation-config></context:annotation-config>
	
	<aop:aspectj-autoproxy/>
</beans>

I have placed all the database properties inside jdbc.properties file

jdbc.properties


jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc\:mysql\://localhost\:3306/AuditSchema
jdbc.username=root
jdbc.password=root
hibernate.dialect=org.hibernate.dialect.MySQLDialect
jdbc.databaseVendor=MYSQL

JPA needs persistence.xml inside META-INF folder in the classpath, so we will create our persistence.xml.

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence" version="1.0">
	<persistence-unit name="auditPersitenceUnit">
		<class>com.mumz.test.audit.beans.Book</class>
		<class>com.mumz.test.audit.beans.BookShelf</class>
		<class>com.mumz.test.audit.beans.Audit</class>
	</persistence-unit>
</persistence>

Finally some test code to check our implementation.

AuditMainApp.java


package com.mumz.test.audit.app;

import java.util.ArrayList;
import java.util.List;

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

import com.mumz.test.audit.beans.Book;
import com.mumz.test.audit.beans.BookShelf;
import com.mumz.test.audit.interfaces.IAuditController;

public class AuditMainApp {
	public static void main(String[] args) {
		ApplicationContext applicationContext = new ClassPathXmlApplicationContext("audit-bean-definition.xml");
		IAuditController auditController = applicationContext.getBean(IAuditController.class);
		BookShelf bookShelf = new BookShelf();
		Book book  = new Book();
		book.setAuthor("Test Author 1");
		book.setName("Test Book 1");
		book.setBookShelf(bookShelf);
		List<Book> books = new ArrayList<Book>();
		books.add(book);
		bookShelf.setBooks(books);
		bookShelf.setName("Test 1");
		auditController.addBookShelf(bookShelf);
	}
}

Database schema used for this post is below.

Database Schema


SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL';

DROP SCHEMA IF EXISTS `auditschema` ;
CREATE SCHEMA IF NOT EXISTS `auditschema` DEFAULT CHARACTER SET utf8 ;
USE `auditschema` ;

-- -----------------------------------------------------
-- Table `auditschema`.`audit`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `auditschema`.`audit` ;

CREATE  TABLE IF NOT EXISTS `auditschema`.`audit` (
  `AUDIT_ID` INT(11) NOT NULL AUTO_INCREMENT ,
  `AUDIT_PRE_IMAGE` VARCHAR(3000) NULL DEFAULT NULL ,
  `AUDIT_POST_IMAGE` VARCHAR(3000) NULL DEFAULT NULL ,
  `AUDIT_OPERATION` VARCHAR(45) NULL DEFAULT NULL ,
  PRIMARY KEY (`AUDIT_ID`) )
ENGINE = InnoDB
AUTO_INCREMENT = 2
DEFAULT CHARACTER SET = utf8;

-- -----------------------------------------------------
-- Table `auditschema`.`book_shelf`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `auditschema`.`book_shelf` ;

CREATE  TABLE IF NOT EXISTS `auditschema`.`book_shelf` (
  `BOOK_SHELF_ID` INT(11) NOT NULL AUTO_INCREMENT ,
  `BOOK_SHELF_NAME` VARCHAR(45) NOT NULL ,
  PRIMARY KEY (`BOOK_SHELF_ID`) )
ENGINE = InnoDB
AUTO_INCREMENT = 10
DEFAULT CHARACTER SET = utf8;

-- -----------------------------------------------------
-- Table `auditschema`.`book`
-- -----------------------------------------------------
DROP TABLE IF EXISTS `auditschema`.`book` ;

CREATE  TABLE IF NOT EXISTS `auditschema`.`book` (
  `BOOK_ID` INT(11) NOT NULL AUTO_INCREMENT ,
  `BOOK_NAME` VARCHAR(45) NOT NULL ,
  `BOOK_AUTHOR` VARCHAR(45) NOT NULL ,
  `BOOK_SHELF_ID` INT(11) NOT NULL ,
  PRIMARY KEY (`BOOK_ID`) ,
  INDEX `FK_BOOK_SHELF_ID` (`BOOK_SHELF_ID` ASC) ,
  CONSTRAINT `FK_BOOK_SHELF_ID`
    FOREIGN KEY (`BOOK_SHELF_ID` )
    REFERENCES `auditschema`.`book_shelf` (`BOOK_SHELF_ID` )
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB
AUTO_INCREMENT = 9
DEFAULT CHARACTER SET = utf8;

SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;

If you have followed every step your code structure should look similar to:
Project Structure

That's all, It has been a very long post, I hope you find it useful.

Java Thread Info

Posted on Updated on

I was asked once in an interview How will you check the different status of thread ?

The question was to write a java code which will check which thread is waiting for which lock (if any) or if it dead etc.

Here is one way of doing it….

/**
 * THIS CODE IS DISTRIBUTED "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 */
package com.mumz.test.thread.threadinfo;

/**
 * @author prabhat.jha
 */
public class MyOwnThread implements Runnable {
	
	public StringBuilder	builder	= new StringBuilder();
	/**
	 * @param data
	 */
	public void addData(String data) {
		builder.append(isValidString(data) ? data : "");
	}
	
	/**
	 * (non-Javadoc)
	 * 
	 * @see java.lang.Runnable#run()
	 */
	public void run() {
		synchronized (builder) {
			if (builder.toString().length() > 10) {
				try {
					builder.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			if (builder.toString().length() > 10) {
				String content = builder.toString().substring(0, 9);
				builder.delete(0, builder.length() - 1);
				builder.append(content);
			}
			if (builder.length() > 10) {
				builder.notifyAll();
			}
		}
	}
	/**
	 * @param string
	 * @return
	 */
	private boolean isValidString(String string) {
		return (string != null && !string.equals("") && string.trim().length() > 0);
	}
}

Lets create thread and then check if they are waiting for lock to be acquired or else just print there status, here is the code snippet:

package com.mumz.test.thread.threadinfo;

/**
 * THIS CODE IS DISTRIBUTED "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 */

import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;

/**
 * @author prabhat.jha
 *
 */
public class ThreadFactory {
	
	public static void main(String[] args) {
		MyOwnThread myOwnThread = null;
		Thread thread = null;
		long[] threadIds = new long[20];
		for (int iDx = 0; iDx < 20; iDx++) {
			myOwnThread = new MyOwnThread();
			myOwnThread.addData("Thread : " + iDx);
			thread = new Thread(myOwnThread);
			thread.setName(String.valueOf(iDx));
			threadIds[iDx] = thread.getId();
			thread.start();
		}
		ThreadFactory factory = new ThreadFactory();
		factory.checkThreadStatus(threadIds);
	}
	
	/**
	 * @param threadIds
	 */
	private void checkThreadStatus(long[] threadIds) {
		ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean();
		ThreadInfo[] threadInfos = threadMXBean.getThreadInfo(threadIds);
		StringBuilder info = new StringBuilder();
		for (ThreadInfo threadInfo : threadInfos) {
			if (threadInfo == null) {
				continue;
			}
			info.append("threadInfo.getThreadId() : " + threadInfo.getThreadId());
			info.append("\n");
			info.append("threadInfo.getThreadName() : " + threadInfo.getThreadName());
			info.append("\n");
			info.append("threadInfo.getThreadState() : " + threadInfo.getThreadState());
			info.append("\n");
			info.append("threadInfo.getLockName() : " + threadInfo.getLockName());
			info.append("\n");
			info.append("threadInfo.getLockOwnerId() : " + threadInfo.getLockOwnerId());
			info.append("\n");
			info.append("threadInfo.getLockOwnerName() : " + threadInfo.getLockOwnerName());
		}
		writeDataToFile(info.toString());
	}
	
	/**
	 * @param info
	 */
	private void writeDataToFile(String info) {
		File file = new File("c://" + System.currentTimeMillis());
		PrintWriter pw = null;
		BufferedOutputStream bos = null;
		try {
			if (!file.exists()) {
				file.createNewFile();
			}
			bos = new BufferedOutputStream(new FileOutputStream(file));
			pw = new PrintWriter(bos);
			pw.write(info);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			pw.close();
			try {
				bos.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

JPA OneToMany Mapping Using Hibernate and MySQL

Posted on

OneToMany as name suggest is the scenario where one entity can be linked to multiple entities.

For e.g. one BookShelf can contain multiple books. In this tutorial we will use the same problem statement and model it using JPA with Hibernate as the JPA implementation provider.

First we will write our simple BookShelf entity, BookShelf will contain a List of Books.

BookShelf.java

package com.mumz.jpa.mapping.onetomany;

import java.util.List;

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

/**
 * The Class BookShelf.
 * @author prabhat.jha
 */
@Entity
@Table(name = "BOOK_SHELF")
public class BookShelf {
	
	/** The id. */
	private Long	id		= null;

	/** The name. */
	private String	name	= null;
	
	/** The books. */
	private List<Book> books = null;

	/**
	 * Gets the id.
	 * 
	 * @return the id
	 */
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	@Column(name="BOOK_SHELF_ID")
	public Long getId() {
		return id;
	}

	/**
	 * Sets the id.
	 * 
	 * @param id
	 *            the id to set
	 */
	public void setId(Long id) {
		this.id = id;
	}

	/**
	 * Gets the name.
	 * 
	 * @return the name
	 */
	@Column(name="BOOK_SHELF_NAME")
	public String getName() {
		return name;
	}

	/**
	 * Sets the name.
	 * 
	 * @param name
	 *            the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * Gets the books.
	 * 
	 * @return the books
	 */
	@OneToMany(cascade=CascadeType.ALL, mappedBy="bookShelf")
	public List<Book> getBooks() {
		return books;
	}

	/**
	 * Sets the books.
	 * 
	 * @param books
	 *            the books to set
	 */
	public void setBooks(List<Book> books) {
		this.books = books;
	}

	/**
	 * (non-Javadoc).
	 * 
	 * @return the int
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((books == null) ? 0 : books.hashCode());
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	/**
	 * (non-Javadoc).
	 * 
	 * @param obj
	 *            the obj
	 * @return true, if successful
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (!(obj instanceof BookShelf)) {
			return false;
		}
		BookShelf other = (BookShelf) obj;
		if (books == null) {
			if (other.books != null) {
				return false;
			}
		} else if (!books.equals(other.books)) {
			return false;
		}
		if (id == null) {
			if (other.id != null) {
				return false;
			}
		} else if (!id.equals(other.id)) {
			return false;
		}
		if (name == null) {
			if (other.name != null) {
				return false;
			}
		} else if (!name.equals(other.name)) {
			return false;
		}
		return true;
	}

	/**
	 * (non-Javadoc).
	 * 
	 * @return the string
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return String.format("BookShelf [id=%s, name=%s, books=%s]", id, name, books);
	}
}

On line number 78 we have added @OneToMany annotation with :

  1. cacade=CascadeType.ALL – Suggests what to do with Books when BookShelf is deleted
  2. mappedBy – Name of the owner object

Second we will write our Book entity which is a simple entity with @ManyToOne mapping.

Book.java

package com.mumz.jpa.mapping.onetomany;

import java.io.Serializable;

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

/**
 * The Class Book.
 * @author prabhat.jha
 */
@Entity
@Table(name = "BOOK")
public class Book implements Serializable{
	
	/** The Constant serialVersionUID. */
	private static final long	serialVersionUID	= -4788522141255171404L;

	/** The id. */
	private Long		id			= null;

	/** The name. */
	private String		name		= null;

	/** The author. */
	private String		author		= null;

	/** The book shelf. */
	private BookShelf	bookShelf	= null;

	/**
	 * Gets the id.
	 * 
	 * @return the id
	 */
	@Id
	@GeneratedValue(strategy=GenerationType.AUTO)
	@Column(name="BOOK_ID")
	public Long getId() {
		return id;
	}

	/**
	 * Sets the id.
	 * 
	 * @param id
	 *            the id to set
	 */
	public void setId(Long id) {
		this.id = id;
	}

	/**
	 * Gets the name.
	 * 
	 * @return the name
	 */
	@Column(name="BOOK_NAME")
	public String getName() {
		return name;
	}

	/**
	 * Sets the name.
	 * 
	 * @param name
	 *            the name to set
	 */
	public void setName(String name) {
		this.name = name;
	}

	/**
	 * Gets the author.
	 * 
	 * @return the author
	 */
	@Column(name="BOOK_AUTHOR")
	public String getAuthor() {
		return author;
	}

	/**
	 * Sets the author.
	 * 
	 * @param author
	 *            the author to set
	 */
	public void setAuthor(String author) {
		this.author = author;
	}

	/**
	 * Gets the book shelf.
	 * 
	 * @return the bookShelf
	 */
	@ManyToOne
	@JoinColumn(name="BOOK_SHELF_ID")
	public BookShelf getBookShelf() {
		return bookShelf;
	}

	/**
	 * Sets the book shelf.
	 * 
	 * @param bookShelf
	 *            the bookShelf to set
	 */
	public void setBookShelf(BookShelf bookShelf) {
		this.bookShelf = bookShelf;
	}

	/**
	 * (non-Javadoc).
	 * 
	 * @return the int
	 * @see java.lang.Object#hashCode()
	 */
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((author == null) ? 0 : author.hashCode());
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		result = prime * result + ((name == null) ? 0 : name.hashCode());
		return result;
	}

	/**
	 * (non-Javadoc).
	 * 
	 * @param obj
	 *            the obj
	 * @return true, if successful
	 * @see java.lang.Object#equals(java.lang.Object)
	 */
	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}
		if (obj == null) {
			return false;
		}
		if (!(obj instanceof Book)) {
			return false;
		}
		Book other = (Book) obj;
		if (author == null) {
			if (other.author != null) {
				return false;
			}
		} else if (!author.equals(other.author)) {
			return false;
		}
		if (id == null) {
			if (other.id != null) {
				return false;
			}
		} else if (!id.equals(other.id)) {
			return false;
		}
		if (name == null) {
			if (other.name != null) {
				return false;
			}
		} else if (!name.equals(other.name)) {
			return false;
		}
		return true;
	}

	/**
	 * (non-Javadoc).
	 * 
	 * @return the string
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return String.format("Book [id=%s, name=%s, author=%s]", id, name, author);
	}
}

Third we will write a simple class to test out our application.

TestOneToMany


package com.mumz.jpa.mapping.onetomany;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

/**
 * The Class TestOneToMany.
 * @author prabhat.jha
 */
public class TestOneToMany {

	/**
	 * The main method.
	 * 
	 * @param args
	 *            the arguments
	 */
	public static void main(String[] args) {
		EntityManager entityManager = Persistence.createEntityManagerFactory("jpaPersistenceUnit").createEntityManager();

		BookShelf bookShelf = new BookShelf();
		bookShelf.setName("JPA Books");
		List<Book> books = new ArrayList<Book>();

		Book book = new Book();
		book.setName("Java Persistence with Hibernate");
		book.setAuthor("Christian Bauer and Gavin King");
		book.setBookShelf(bookShelf);

		books.add(book);

		book = new Book();
		book.setName("Hibernate Made Easy");
		book.setAuthor("Hibernate Made Easy");
		book.setBookShelf(bookShelf);

		books.add(book);

		bookShelf.setBooks(books);
		EntityTransaction entityTransaction = entityManager.getTransaction();
		try {
			entityTransaction.begin();
			entityManager.persist(bookShelf);
			entityTransaction.commit();
		} catch (Exception e) {
			e.printStackTrace();
			entityTransaction.rollback();
		} finally {
			entityManager.close();
		}
	}
}

Fourth our persistence.xml which holds our mapped classes and database details.

persistence.xml

<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
	xmlns="http://java.sun.com/xml/ns/persistence" 
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation=
		"http://java.sun.com/xml/ns/persistence 
		http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd">
		
	<persistence-unit name="jpaPersistenceUnit"
		transaction-type="RESOURCE_LOCAL">
		<class>com.mumz.jpa.mapping.onetomany.BookShelf</class>
		<class>com.mumz.jpa.mapping.onetomany.Book</class>
		<properties>
			<property name="javax.persistence.jdbc.driver" value="com.mysql.jdbc.Driver" />
			<property name="javax.persistence.jdbc.url" value="jdbc:mysql://localhost:3306/jpa_schema" />
			<property name="javax.persistence.jdbc.user" value="root" />
			<property name="javax.persistence.jdbc.password" value="root" />
		</properties>
	</persistence-unit>
</persistence>

Finally our pom.xml to maintain our dependency.

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
		http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.mumz.test.jpa</groupId>
	<artifactId>TestJPAProject</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>TestJPAProject</name>
	<description>TestJPAProject</description>
	<dependencies>
		<dependency>
			<groupId>org.hibernate.javax.persistence</groupId>
			<artifactId>hibernate-jpa-2.0-api</artifactId>
			<version>1.0.0.Final</version>
		</dependency>
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.6</version>
		</dependency>
	</dependencies>
	<build>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<configuration>
					<source>1.6</source>
					<target>1.6</target>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>