Showing posts with label Hibernate. Show all posts
Showing posts with label Hibernate. Show all posts

Points To Remember


  • You need to inject the SessionFactory object in the service or the controller, where you want to use it.
  • Get the current session from the session factory and execute the query in this session using sessionFactory.getCurrentSession().
  • You can also execute the query in a new session by using sessionFactory.openSession().

Executing SQL query in grails

Person.groovy
package com.ekiras.grails;

class Person{

String username
String email
String password

static mapping = {

}

static constraints = {
username nullable: true
password nullable: false, blank: false
email nullable: false, blank: false
}



}
PersonService.groovy
package com.ekiras.grails;

import org.hibernate.SessionFactory;
import grails.transaction.Transactional

import com.ekiras.grails.Person;

@Transactional
class PersonService{

SessionFactory sessionFactory;

def listPersons(){
String query = "select distinct username from person";
def personList = sessionFactory.getCurrentSession().createSQLQuery(query).list();
return personList;
}

boolean deletePerson(Person person){
String query = "delete from person where username=${person.username}";
Integer rowsEffected = sessionFactory.getCurrentSession().createSQLQuery(query).executeUpdate();
if(rowsEffected == 1);
return true;
else
return false;
}

Person updatePerson(Person person){
String query = "update person set username=${person.username} where id={person.id}";
Integer rowsEffected = sessionFactory.getCurrentSession().createSQLQuery(query).executeUpdate();
if(rowsEffected == 1);
return person;
else
// throw new Exception();
}


}

This is how you can execute the simple SQL queries in Grails using hibernate sessionFactory.

Syntax to use OrderBy in Hibernate Criteria Query

The order can be specified using addOrder on a Criteria Object
criteria.addOrder(Order.asc("propertyName"))
criteria.addOrder(Order.desc("propertyName"))

Order results according to an order in Hibernate 

Suppose we have a class
Category.java
package com.ekiras.domain;

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;

@Entity
@Table(name="category")
public class Category {

public Category(){}
public Category(Long id){
this.id = id;
}
public Category(Long id, String name){
this.id = id;
this.name = name;
}

@Id
@Column(nullable=false, name="id")
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;

@Column(nullable=false, name="name")
private String name;

// Getters and Setters

}
So if we need to order the results of this class according to a given order we can do it like following

  • Order by propertyName id in ascending order
    sessionFactory.getCurrentSession()
    .createCriteria(Catgeory.class)
    .addOrder(Order.asc("id"))
    .list();
  • Order by propertyName name in descending order
    sessionFactory.getCurrentSession()
    .createCriteria(Catgeory.class)
    .addOrder(Order.desc("name"))
    .list();
  • Order by propertyName name in random order
    sessionFactory.getCurrentSession()
    .createCriteria(Catgeory.class)
    criteria.add(Restrictions.sqlRestriction("1=1 order by rand()"));
    .list();
    The above code will return the list of Category domain objects in a random order.


Syntax to get List of domain Object

list can be used on the Criteria object as shown below.
criteria.list()

How to get a List of a Domain class using Hibernate's Criteria Query.

If we have a domain class Category as shown below.

Catgeory.class
package com.ekiras.domain;

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;

@Entity
@Table(name="category")
public class Category {

public Category(){}
public Category(Long id){
this.id = id;
}
public Category(Long id, String name){
this.id = id;
this.name = name;
}

@Id
@Column(nullable=false, name="id")
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;

@Column(nullable=false, name="name")
private String name;

// Getters and Setters

}

We can get the list of all the categories using Criteria Query in the following ways

  • Simplest way to get a list of a Domain class (Category) in our example is
    sessionFactory.getCurrentSession().createCriteria(Category.class).list();
  • You can use pagination in the following way with list()
    @SuppressWarnings("unchecked")
    public List<Category>; list(Integer offset){
    return sessionFactory.getCurrentSession()
    .createCriteria(Category.class)
    .setFirstResult(offset!=null?offset:0)
    .setMaxResults(10)
    .list();
    }
  • Use this method as a generic method to get a list of all the records of a domain in the database.
    @SuppressWarnings("rawtypes")
    public List list(Class clazz){
    return (List)getSession().createCriteria(clazz).list();
    }
  • You can Order your list according to any property like
    public List<Category> list(Integer offset){
    return getSession()
    .createCriteria(Category.class)
    .addOrder(Order.desc("id"))
    .list();
    }

Points To Remember



Pagination in Spring Hibernate Application using Bootstrap


  • Create a Domain Person.
  • Create a Service PersonService
  • Create a Dao PersonDao.
  • Create a Controller PersonController.
  • Create a Taglib PaginationTaglib.

Person.java
package com.ekiras.domain;

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

@Entity
@Table(name="person")
public class Person {

public Person(){};
public Person(String name,Integer age){
this.name = name;
this.age = age;
}


@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@Column(name="id")
private Long id;

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

@Column(name="age")
private Integer age;

// GETTERS and SETTERS

PersonService.java
package com.ekiras.service;

import java.util.List;

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

import com.ekiras.dao.PersonDAO;
import com.ekiras.domain.Person;

@Service
public class PersonService {

@Autowired
private PersonDAO personDAO;


public List<Person> list(Integer offset, Integer maxResults){
return personDAO.list(offset, maxResults);
}

public Long count(){
return personDAO.count();
}

public void save(){
personDAO.save();
}

}

PersonDAO.java
package com.ekiras.dao;

import java.util.List;

import org.hibernate.SessionFactory;
import org.hibernate.criterion.Projections;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import com.ekiras.domain.Person;

@Repository
@Transactional
public class PersonDAO {

@Autowired
private SessionFactory sessionFactory;

@SuppressWarnings("unchecked")
@Transactional
public List<Person> list(Integer offset, Integer maxResults){
return sessionFactory.openSession()
.createCriteria(Person.class)
.setFirstResult(offset!=null?offset:0)
.setMaxResults(maxResults!=null?maxResults:10)
.list();
}


public Long count(){
return (Long)sessionFactory.openSession()
.createCriteria(Person.class)
.setProjection(Projections.rowCount())
.uniqueResult();
}


public void save(){
for(int itr=1;itr <= 100 ; itr++){
Person person = new Person("Person_"+itr,Math.max(25, (itr%2)*35));
sessionFactory.openSession()
.save(person);
}



}

}

PersonController.java
package com.ekiras.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;

import com.ekiras.service.PersonService;

@Controller
@RequestMapping(value="/person")
public class PersonController {

@Autowired
private PersonService personService;

@RequestMapping(value="/list")
public String list(Model model, Integer offset, Integer maxResults){
model.addAttribute("persons", personService.list(offset, maxResults));
model.addAttribute("count", personService.count());
model.addAttribute("offset", offset);
return "/person/list";
}

@RequestMapping(value="/save")
public String save(){
personService.save();
return "/person/list";
}

}

PaginationTaglib.java
package com.ekiras.taglib;

import java.io.Writer;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.SimpleTagSupport;

public class PaginationTaglib extends SimpleTagSupport {
private String uri;
private int offset;
private int count;
private int max = 10;
private int steps = 10;
private String previous = "Previous";
private String next = "Next";

private Writer getWriter() {
JspWriter out = getJspContext().getOut();
return out;
}

@Override
public void doTag() throws JspException {
Writer out = getWriter();

try {
out.write("<nav>");
out.write("<ul class=\"pagination\">");

if(offset<steps)
out.write(constructLink(1, previous, "disabled", true));
else
out.write(constructLink(offset-steps, previous, null, false));

for(int itr=0;itr<count;itr+=steps) {
if(offset==itr)
out.write(constructLink((itr/10+1)-1 *steps, String.valueOf(itr/10+1), "active", true));
else
out.write(constructLink(itr/10*steps, String.valueOf(itr/10+1), null , false));
}

if(offset+steps>=count)
out.write(constructLink(offset+steps, next, "disabled", true));
else
out.write(constructLink(offset+steps, next, null , false));


out.write("</ul>");
out.write("</nav>");
} catch (java.io.IOException ex) {
throw new JspException("Error in Paginator tag", ex);
}
}


private String constructLink(int page, String text, String className, boolean disabled) {
StringBuilder link = new StringBuilder("<li");
if (className != null) {
link.append(" class=\"");
link.append(className);
link.append("\"");
}
if(disabled)
link.append(">").append("<a href=\"#\">"+text+"</a></li>");
else
link.append(">").append("<a href=\""+uri+"?offset="+page + "\">"+text+"</a></li>");
return link.toString();
}

public String getUri() {
return uri;
}

public void setUri(String uri) {
this.uri = uri;
}

public int getOffset() {
return offset;
}

public void setOffset(int offset) {
this.offset = offset;
}

public int getCount() {
return count;
}

public void setCount(int count) {
this.count = count;
}

public int getMax() {
return max;
}

public void setMax(int max) {
this.max = max;
}

public String getPrevious() {
return previous;
}

public void setPrevious(String previous) {
this.previous = previous;
}

public String getNext() {
return next;
}

public void setNext(String next) {
this.next = next;
}

public int getSteps() {
return steps;
}

public void setSteps(int steps) {
this.steps = steps;
}

}

list.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%@ taglib prefix="tag" uri="/WEB-INF/taglibs/customTaglib.tld"%>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="stylesheet" type="text/css" href="/css/bootstrap.css" />
<link rel="stylesheet" type="text/css" href="/css/bootstrap.min.css" />
<link rel="stylesheet" type="text/css" href="/css/font-awesome.min.css" />
<title>| Ekiras</title>
</head>
<body>
<nav class="navbar navbar-default">
<div class="container-fluid">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed"
data-toggle="collapse" data-target="#bs-example-navbar-collapse-1">
<span class="sr-only">Toggle navigation</span> <span
class="icon-bar"></span> <span class="icon-bar"></span> <span
class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">Brand</a>
</div>

<!-- Collect the nav links, forms, and other content for toggling -->
<div class="collapse navbar-collapse"
id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li class="active"><a href="#">Link <span class="sr-only">(current)</span></a></li>
<li><a href="#">Link</a></li>
<li class="dropdown"><a href="#" class="dropdown-toggle"
data-toggle="dropdown" role="button" aria-expanded="false">Dropdown
<span class="caret"></span>
</a>
<ul class="dropdown-menu" role="menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li class="divider"></li>
<li><a href="#">Separated link</a></li>
<li class="divider"></li>
<li><a href="#">One more separated link</a></li>
</ul></li>
</ul>
<form class="navbar-form navbar-left" role="search">
<div class="form-group">
<input type="text" class="form-control" placeholder="Search">
</div>
<button type="submit" class="btn btn-default">Submit</button>
</form>
<ul class="nav navbar-nav navbar-right">
<li><a href="#">Link</a></li>
<li class="dropdown"><a href="#" class="dropdown-toggle"
data-toggle="dropdown" role="button" aria-expanded="false">Dropdown
<span class="caret"></span>
</a>
<ul class="dropdown-menu" role="menu">
<li><a href="#">Action</a></li>
<li><a href="#">Another action</a></li>
<li><a href="#">Something else here</a></li>
<li class="divider"></li>
<li><a href="#">Separated link</a></li>
</ul></li>
</ul>
</div>
<!-- /.navbar-collapse -->
</div>
<!-- /.container-fluid --> </nav>

<div class="container">
<div class="well">
<strong>List of Persons</strong>
</div>
<table class="table table-stripped">
<tr>
<th>S.No</th>
<th>Name</th>
<th>Age</th>
</tr>
<c:forEach items="${persons}" var="person" varStatus="itr">
<tr>
<td>${offset + itr.index +1 }</td>
<td>${person.name }</td>
<td>${person.age }</td>
</tr>
</c:forEach>
</table>
<tag:paginate max="15" offset="${offset}" count="${count}"
uri="/person/list" next="&raquo;" previous="&laquo;" />
</div>


<script type="text/javascript" src="/js/jquery.js"></script>
<script type="text/javascript" src="/js/bootstrap.min.js"></script>
</body>
</html>

This is how you can do pagination using bootstrap style in spring hibernate mvc application.


Points To Remember


  • Offset may be required to do pagination or get the results after a particular result from the database.
  • Using offset you can define the first result to be included in the result set from the database. 
  • You can use setFirstResult on the CriteriaQuery to set the first result that you want.

Using offset to get the results

SampleDAO.java
@SuppressWarnings("unchecked")
public List<Person> getCategories(Integer offset){
return getSession()
.createCriteria(Person.class)
.setFirstResult(offset!=null?offset:0)
.setMaxResults(10)
.list();
}
This is how you can set the first result you want to fetch from the database. In this example we have set the first result and the maximum number of records that we need to fetch.

Points To Remember


  • Enums are special java classes that are used to declare constants and methods.
  • Enums have all the variables declared as public static final.
  • Enums variables can be compared using == operator.
  • End of a enum can be declared by a semi colon, but this is optional.
  • There are two ways to use Enums 
    • ORDINAL (saves the enum values in integer format starting from 0.)
    • STRING (saves the enum value in String, takes the value of the field itself. )

Use Enums in Hibernate

In this example we will be using Enums to save a value in database using hibernate.
Suppose we have a Enum named Level and we want to save it in database using hibernate persistence.Then we can map this in the following way.

Level.java
This is an Enum that defines the level of difficulty of a question.
package com.ekiras.enums;

public enum Level {

LEVEL_ONE,
LEVEL_TWO,
LEVEL_THREE,
LEVEL_FOUR,
LEVEL_FIVE;
}

Qusetion.java
This class contains the enum as a field that will be saved to the database.
package com.ekiras.domain;

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

import com.ekiras.enums.Level;

@Entity
@Table(name="question")
public class Question {

@Id
@Column(name="id")
@GeneratedValue(strategy=GenerationType.AUTO)
private Long id;

@Column(name="question")
private String question;

@Column(name="level")
@Enumerated(EnumType.ORDINAL)
private Level status = Level.LEVEL_ONE;

// GETTERS and SETTERS

}
This will save the Enum in integer format, i.e it will save the int value of the enum. If you want to save the value in String format you need to add the following Enumerated property to the field.

        
@Column(name="level")
@Enumerated(EnumType.STRING)
private Level status = Level.LEVEL_ONE;

This will save the value of the field in String format, i.e it will save LEVEL_ONE in database while @Enumerated(EnumType.ORDINAL) will save the values in integer format i.e it will save integer values for LEVEL_ONE  = 0 and so on(it has nothing to do with the value of the enum, it always starts from zero and increments and is defined by the order in which they are defined. ).


Points To Remember

You need to add the following dependencies to pom.xml

  1. spring-core
  2. spring-context
  3. spring-web
  4. spring-webmvc
  5. spring-tx
  6. spring-orm
  7. mysql-connector-java
  8. jstl
  9. hibernate 

Create  Maven Spring Hibernate Sitemesh Annotation based Hello World project

pom.xml
pom.xml should contain the following 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>org.springframework.samples.service.service</groupId>
<artifactId>ekiras</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>


<properties>
<java.version>1.7</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<spring.version>3.2.8.RELEASE</spring.version>
<junit.version>4.11</junit.version>
<sitemesh.version>3.0.0</sitemesh.version>
<hibernate.version>4.2.1.Final</hibernate.version>
<logback.version>1.0.13</logback.version>
<slf4j.version>1.7.5</slf4j.version>
<jsp.version>2.2</jsp.version>
<jstl.version>1.2</jstl.version>
<servlet.version>2.5</servlet.version>
</properties>



<dependencies>


<!-- Spring 3 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-web</artifactId>
<version>${spring.version}</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</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</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>

<!-- MySQL -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.31</version>
</dependency>

<!-- jstl for jsp page -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>${jstl.version}</version>
</dependency>

<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>${servlet.version}</version>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>${jsp.version}</version>
<scope>provided</scope>
</dependency>

<!-- Sitemesh -->
<dependency>
<groupId>org.sitemesh</groupId>
<artifactId>sitemesh</artifactId>
<version>${sitemesh.version}</version>
</dependency>


<!-- Spring and Transactions -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>

<!-- Logging with SLF4J & LogBack -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
<scope>compile</scope>
</dependency>

<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>${logback.version}</version>
<scope>runtime</scope>
</dependency>

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

<!-- Test Artifacts -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>${junit.version}</version>
<scope>test</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
<version>1.0.0.RELEASE</version>
</dependency>
</dependencies>
</project>

web.xml
web.xml should contain the dispatcher servlet and other config files.
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="WebApp_ID" version="2.5">

<display-name>ekiras</display-name>

<!-- - Location of the XML file that defines the root application context.
- Applied by ContextLoaderListener. -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath:spring/application-config.xml,
/WEB-INF/conf/mvc-config.xml
</param-value>
</context-param>


<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>



<!-- ****************** Servlet Dispatcher ********************** -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/conf/mvc-config.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/resources/css/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/resources/js/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/resources/images/*</url-pattern>
</servlet-mapping>





<!-- ****************** Sitemesh ********************** -->

<filter>
<filter-name>sitemesh</filter-name>
<filter-class>org.sitemesh.config.ConfigurableSiteMeshFilter</filter-class>
<!-- Changing default sitemsh xml file name and path -->
<init-param>
<param-name>configFile</param-name>
<param-value>/WEB-INF/conf/sitemesh.xml</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>sitemesh</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>


<!-- ****************** ERROR PAGES ********************** -->

<error-page>
<error-code>400</error-code>
<location>/400</location>
</error-page>

<error-page>
<error-code>404</error-code>
<location>/404</location>
</error-page>

<error-page>
<error-code>500</error-code>
<location>/500</location>
</error-page>

<error-page>
<error-code>503</error-code>
<location>/503</location>
</error-page>


</web-app>
mvc-config.xml
mcv config should contain the configurations related to web properties and database properties.
<?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:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">


<context:property-placeholder location="/WEB-INF/conf/jdbc.properties" />
<context:component-scan base-package="com.ekiras" />


<mvc:annotation-driven />
<tx:annotation-driven />
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>



<mvc:resources location="/resources/css/" mapping="/css/**" />
<mvc:resources location="/resources/js/" mapping="/js/**" />

<bean id="jspViewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views/" />
<property name="suffix" value=".jsp" />
</bean>
<!-- **************** HIBERNATE PROPERTIES ******************** -->

<bean id="dataSource"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>

<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="com.ekiras.domain" />
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>

<!-- **************** END HIBERNATE PROPERTIES **************** -->
</beans>
database.properties
database.properties should contain all the database relates configurations
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test_db
jdbc.username=root
jdbc.password=password

All the other project structure is available in the downloadable example below. It contains the project structure based on best practices.


Points To Remember

Suppose we have two domains/ entities User and Role and we want to create a many to many relationship between them them, then we can do it like following in hibernate.

Many to Many relationship between two Entities.

User.java
Create a User class as following
package com.ekiras.domain;

import java.util.ArrayList;
import java.util.Collection;

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.ManyToMany;
import javax.persistence.Table;

@Entity
@Table(name = "user")
public class User {

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@Column(name="id")
private Long id;

@Column(name="email")
private String email;

@Column(name="password")
private String password;

@Column(name="enabled")
private boolean enabled;

@ManyToMany(cascade=CascadeType.ALL)
private Collection<Role> roles = new ArrayList<Role>();

// GETTERS and SETTERS

Role.java
Create the Role class as follows
package com.ekiras.domain;

import java.util.ArrayList;
import java.util.Collection;

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

@Entity
@Table(name="role")
public class Role {

public Role(){}
public Role(String authority){
this.authority=authority;
}

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@Column(name="id")
private Long id;

@Column(name="authority")
private String authority;

@ManyToMany(mappedBy="roles")
private Collection<User> users = new ArrayList<User>();

// GETTERS and SETTERS

}

What we have done here is

  • We have created the User class and taken a Collection of Roles in User class, as a User can have multiple roles. This class will do the mapping for the User and Role relationship.
  • We have then created a class Role and taken a Collection of User, since a Role can be assigned to multiple users.
  • We have used property mappedBy="roles" in Role class, since our mapping has already been done by the users field of the User class.
  • If we do not specify mappedBy property in either of the class, then there will be two tables, one by User class and one by Role class.
  • In our case the extra table that will be created will be user_role if want to create the table other way round, then we have to set property mappedBy="users" in User class and remover mappedBy property from Role class.
  • The property cascade=CascadeType.ALL is used to save nested objects, this way you do not need to save the nested objects separately. If you did not set this property then you will have to first save Role objects and then save User object or else it would give the following expection.
    org.hibernate.TransientPropertyValueException: object references an unsaved transient 

TestController.java
Create the TestController class as follows
package com.ekiras.controller;

import java.util.ArrayList;
import java.util.Collection;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.ekiras.domain.Role;
import com.ekiras.domain.User;

@Controller
@RequestMapping(value={"","/**"})
public class TestController {

@Autowired
protected SessionFactory sessionFactory;

@RequestMapping(value="/test")
public String test(){

Role role1 = new Role("ROLE_ADMIN");
Role role2 = new Role("ROLE_USER");

Collection<Role> roles = new ArrayList<Role>();
roles.add(role1);
roles.add(role2);

User user = new User();
user.setEmail("ekansh@ekiras.com");
user.setPassword("password");
user.setEnabled(true);
user.setRoles(roles);


Session session = sessionFactory.openSession();
session.beginTransaction();
boolean result = (Long)session.save(user)!=0;

session.getTransaction().commit();
session.close();

System.out.println("User object saved = " + result);


return "/home";
}

}

This will save the user with two roles, the following image shows the data created by the test controller.



Points To Remember


There are two ways to create hibernate many to many relational mappings in Hibernate. We will discuss both of them in detail here.
  1. First one is to use default hibernate mapping to create and manage these relations.
  2. By creating a new domain that will manage these relations for us.
Suppose we want to have a simple User and Role mapping where a user can have multiple roles and a role can be assigned to multiple users. Then we can create and manage these relations in the following ways.

Strategy 1 : Default Hibernate Mappings

You can use this strategy as applied like shown in this article. This will make the hibernate to deal with all the mappings and operations like save , update, fetch etc.

Advantage : This will make hibernate responsible for the mapping and operations like save, fetch, update etc.

Disadvantage : You will not have control over the mapped relation. You will not be able to query the table it will create for mapping.

Strategy 2 : Using Domain to Handle Mappings

This strategy can be applied like shown in this article . So this will actually help us take control of the mappings and will give more control on how we manage and use this.

Advantage : This approach can be used when you want to take control of the mapping from hibernate to your own hands or when you want to use the mappings with some additional data related to the mappings.

Disadvantage : In this approach you will have to make sure that you save the mappings on your own. Hibernate will not fetch or save these mappings on its own.

Points To Remember

  • You need to create an Embedded Object to create a composite key in Hibernate.
  • You can create use an Embeddable Object as an EmbeddedId in class to make it a composite key.

Create a Composite Key in Hibernate

Suppose we have three classes User, Role and UserRole. In class UserRole we want to have a composite id that will include userId and roleId. Then we will first create an embedded object and then using this we will create a composite key.

Let us first create an Embeddable object and
User.java
package com.ekiras.domain;

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

@Entity
@Table(name = "user")
public class User {

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@Column(name="id")
private Long id;

@Column(name="email")
private String email;

@Column(name="password")
private String password;

// GETTERS and SETTERS
}

Role.java
package com.ekiras.domain;

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

@Entity
@Table(name="role")
public class Role {

@Id
@GeneratedValue(strategy=GenerationType.AUTO)
@Column(name="id")
private Long id;

@Column(name="authority")
private String authority;

// GETTERS and SETTERS

}


UserRoleEmbed.java
package com.ekiras.embeddeble;

import java.io.Serializable;

import javax.persistence.Column;
import javax.persistence.Embeddable;

@Embeddable
public class UserRoleEmbed implements Serializable {

private static final long serialVersionUID = 1L;

@Column(name="user_id")
private Long userId;

@Column(name="role_id")
private Long roleId;

// GETTERS and SETTERS

}


UserRole.java
package com.ekiras.domain;

import javax.persistence.EmbeddedId;
import javax.persistence.Entity;
import javax.persistence.Table;

import com.ekiras.ekjam.embeddeble.UserRoleEmbed;

@Entity
@Table(name="user_role")
public class UserRole {

@EmbeddedId
private UserRoleEmbed userRole;

// GETTERS and SETTERS
}

So this will create the Composite key for class UserRole that we wanted and it will map key={userId, roleId}



The above image shows the database schema that the above code snippets created.

Hibernate provides the annotation @NotFound.  This annotation can be used in cases where we have relationships between the Entities e.g. ManyToOne or OneToMany or ManyToMany etc.

Syntax - @NotFound(action=NotFoundException.IGNORE)

When we want to access an entity using the relation from another entity, if the value of this entity is not available then the hibernate throws an exception. We  can use @NotFound annotation to ignore these exceptions. NotFoundException.IGNORE tells the hibernate to avoid throwing the exception if the record for the entity is not found.

@Entity(name="user")
@Table(name="user")
public class User{
}
There are 4 types of Generation Strategies that can be used to generate Id in hibernate.
  1. AUTO  e.g. strategy = GenerationType.AUTO
  2. IDENTITY e.g. strategy = GenerationType.IDENTITY
  3. SEQUENCE e.g. strategy = GenerationType.SEQUENCE
  4. TABLE e.g. strategy = GenerationType.TABLE
strategy = GenerationType.AUTO
This is the default Generation Strategy that is used by the hibernate. This strategy allows hibernate to decide what strategy to be used to generate the unique key for the table. Strategies used by hibernate can be different for different types of databases used.
strategy = GenerationType.IDENTITY
In this Generation Strategy the hibernate is going to use an identity column. Identity column is a feature that is provided in some of the databases, this is not a generic feature and is provided by a few databases only.
strategy = GenerationType.SEQUENCE
In this Generation Strategy, the hibernate generate unique keys by a sequence hilo. This uses a sequence object to generate a sequence of ids, and sequence.nextval will always give the next value of the sequence.  Databases manages this on their own.
strategy = GenerationType.TABLE
In this Generation Strategy, the hibernate uses a separate table to provide unique key. The table stores the latest value to store the last used primary key so that we can get this value and increment the value to create new primary key.

I would recommend to use  strategy = GenerationType.AUTO because it lets hibernate to use the best generation strategy based on the database we use.