19 - Spring JDBC

19.1 Overview

Database access is a common requirement for all the applications because data is stored in a database. Applications use frameworks like JDBC, Hibernate etc to access the database.

JDBC (Java Database Connectivity) is a set of interfaces and API and every database vendor implements the APIs. With the help of JDBC, applications need not to write a vendor specific code to access the data.

 DAO stands for Data Access Objects and is a pattern to access the data of database.

Spring comes with a JDBC framework with which developers need to just focus on core logic and Spring handles all low level details and repetitive tasks like opening and closing of connections, exceptions, iteration of results, transactions  etc for us.

Traditional JDBC does not provide a proper hierarchy of exceptions and forces developers to catch SQLException as this is a checked exception. Ideally, we catch the exceptions which we can deal with but JDBC  always throws SQLException and developer need to look much deeper to make sure if the exception can be handled or not. 

Spring JDBC defines a hierarchy of exceptions and developer needs to handle the ones he should deal with. All the Spring JDBC exceptions are the child of DataAccessException and DataAccessException is a Runtime Exception which means developers are not forced to handle the exceptions.

19.2 Spring Data Access Approaches

There are four approaches with which database can be accessed.

  1. JdbcTemplate – JdbcTemplate approach works for JDK 1.4 onwards and is widely used. JdbcTemplate is used internally in other three approaches. JdbcTemplate handles the  repetitive operations like opening and closing of connections and can execute select, insert, update statements, calls to stored procedures. This class is thread safe.  
  2. NamedParameterJdbcTemplate This approach supports the use of named parameters in place of traditional place holders ( ? ) and works with JDK 1.4 onwards.
  3. SimpleJdbcTemplate- This approach uses both JdbcTemplate and NamedParameterJdbcTemplate approaches and provides additional support of Java 5 features like autoboxing, generics etc. This approach requires JDF 5 onwards. 
  4. SimpleJdbcInsert and SimpleJdbcCallThis approach utilizes the database metadata and hence reduces the configuration overhead.

19.3 Database

In this tutorial we would need a database. There are several databases available in the market but and our code will not be tied to any database. We would need a vendor specific jar file and need to configure the database.

I will be using MySQL  database in this tutorial because MySQL is freely available, lightweight and easy to install.

You can also download and install MySQL from http://dev.mysql.com/downloads/windows/installer/5.5.html  

MySQL server does not ship with any GUI tool so if you want, you can install MySQL workbench as well from http://dev.mysql.com/downloads/workbench

Let's create a schema named “SpringJDBC”. To do so you can run below query

create schema SpringJDBC;

Now create a table named “Employee” with four columns

  1. ID
  2.  Name
  3. Age
  4. Salary

     You can use below SQL query to create a “Employee” table

CREATE  TABLE  'springjdbc'. 'Employee' (

   'D' INT NOT NULL ,

  'Name' VARCHAR(45) NULL ,

  'Age' INT NULL ,

  'Salary' INT NULL ,

   PRIMARY KEY ('ID') );

 

Insert one record in the Employee table using below Query

insert into 'SpringJDBC'.'Employee' value ( 100,  'Employee A' ,23 , 1000 );

19.4 Configure Build Path

Add the spring-jdbc-4.1.1.RELEASE.jar , spring-tx-4.1.1.RELEASE.jar  and mysql-connector-java-5.1.18-bin.jar file in build path as described in Chapter 3 and 4.

19.5 Data Source Configuration

DataSource is the database configuration and would be required by template and Dao classes.

To configure Data Source add below configuration in beans.xml file

<bean id="dataSource"
      class="org.springframework.jdbc.datasource.DriverManagerDataSource">
           <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
           <property name="url" value="jdbc:mysql://localhost:3306/SpringJDBC"/>
           <property name="username" value="root"/>
           <property name="password" value="password"/>
      </bean>

a) Make sure the id of bean is “dataSource” only because template and DAO classes has a corresponding field.

b) com.mysql.jdbc.Driver is the vendor Driver class name. In our examples we will use MySQL database so we will use mysql driver

c) jdbc:mysql://localhost:3306/SpringJDBC is the URL of the database. localhost:3306 specifies that database server is running on localhost and on 3306 port number. SpringJDBC is the name of Schema.

d) Username and password, specify the username and password to of the database.

19.6 RowMapper Interface

Spring provides a RowMapper interface under org.springframework.jdbc.core.RowMapper package.  This interface is used to map a Result set returned by SQL to a custom object per row. The developer needs to implement this interface and implement the actual logic of mapping rows of result set to custom object.

This interface defines one method with the below signature

public Object mapRow(ResultSet resultSet , int rownumber)throws SQLException                     

19.7 JDBC template API

JDBC template requires DataSource object which can be injected either programmatically or declaratively.  We will use declarative way because that is configurable and any change in database configuration will not require code changes.

Following are the key methods of JdbcTemplate class

  1. queryForInt(Sting SQL) – This method returns the integer value so input SQL must return a integer value

Refer below code snippet to understand how we can use this API

int count = jdbcTemplate.queryForInt("select count (*) from employee");

  1. queryForInt(Sting SQL, Object[]) – This method returns the integer value and allow us to bind the query parameters . SQL must return a integer value.

Refer below code snippet to understand how we can use this API

int count = jdbcTemplate.queryForInt("select count (*) from employee where name = ?", new Object[]{"Employee A"});

  1. queryForObject(String sql, Object[], RowMapper)-  This method returns one Object and allow us to bind the query parameters . Refer below code snippet to understand how we can use this API
Employee emp = (Employee)jdbcTemplate.queryForObject
("select id, name , age , salary  from Employee  where name = ?", 
new Object[]{"Employee A"},
      new RowMapper(){
      @Override
      public Object mapRow(ResultSet rs, int rowNum)
             throws SQLException {
                 Employee emp = new Employee();
    
                emp.setId(rs.getInt("id"));
                emp.setName(rs.getString("name"));
                emp.setAge(rs.getInt("age"));
                emp.setSalary(rs.getInt("salary"));
                return emp;
                } 
    }        
);

Note: Above 3 API must return exactly one Object else exception will be raised.

a. query(String sql, Object[], RowMapper)-  This method returns a list of Objects and allow us to bind the query parameters . Refer below code snippet to understand how we can use this API

List<Employee> emp = jdbcTemplate.queryForObject
("select id, name , age , salary  from Employee where name = ?  ", 
new Object[]{"Employee A"},
     new RowMapper(){
     @Override
     public Object mapRow(ResultSet rs, int rowNum)
            throws SQLException {             
               Employee emp = new Employee();    
               emp.setId(rs.getInt("id"));
               emp.setName(rs.getString("name"));
               emp.setAge(rs.getInt("age"));
               emp.setSalary(rs.getInt("salary"));
               return emp;
              }            
    }        
);

e.   update(String, Object[])this API is used to execute  insert delete and update SQL statements.

Refer below code snippet.

jdbcTemplate.update("delete from employee where name = ?", new Object[]{"Employee A"});           

jdbcTemplate.update("update employee set name = ? where id = ?",

new Object[]{"Employee A", new Integer(100)});

19.8 JdbcTemplate Example

Below example will use the employee table created under springJdbc schema.

a. Employee.java – This is simple POJO class and is the implementation of Employee Object

package jdbc;
public class Employee {

     private  int id;
     private String name;
     private int age;
     private int salary;
     public int getId() {
        return id;
     }
     public void setId(int id) {
         this.id = id;
     }
     public String getName() {
        return name;
     }
     public void setName(String name) {
         this.name = name;
     }
     public int getAge() {
         return age;
     }
     public void setAge(int age) {
         this.age = age;
     }
     public int getSalary() {
        return salary;
     }
     public void setSalary(int salary) {
        this.salary = salary;
     }
     @Override
     public String toString() {
     return "Employee [id=" + id + ", name=" + name + ", age=" + age
                + ", salary=" + salary + "]";
     }
}


b.  EmployeeRowMapper.java – RowMapper implementation for Employee Object

package jdbc;

import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;
public class EmployeeRowMapper implements RowMapper<Employee> {

       public Employee mapRow(ResultSet rs, int rowNum)
            throws SQLException {
 
             Employee emp = new Employee();
             emp.setId(rs.getInt("id"));
             emp.setName(rs.getString("name"));
             emp.setAge(rs.getInt("age"));
             emp.setSalary(rs.getInt("salary"));             
             return emp;
       }
} 

c.  EmployeeJdbcTemplate-  Template class to implement CRUD operations

package jdbc;

import java.util.List;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;

public class EmployeeJdbcTemplate {
    private JdbcTemplate jdbcTemplate;
    private DataSource dataSource;

    public void setDataSource(DataSource dataSource) {
         this.dataSource = dataSource;
         this.jdbcTemplate = new JdbcTemplate(dataSource);
    }

    public int  getTotalNumberOfEmployees(){
        String sql = "select count(*) from employee";
        int count = jdbcTemplate.queryForInt(sql);
        return count;
    }

    public Employee getEmployeeById(int id){
         String sql = "select * from employee where id= ?";
         Employee employee = jdbcTemplate.queryForObject(sql, new Object[]{new Integer(id)} , new EmployeeRowMapper());
         return employee;
    }

    public List<Employee> getAllEmployees(){
         String sql = "select * from employee ";
         List<Employee> employee = jdbcTemplate.query(sql,  new EmployeeRowMapper());
         return employee;
    }

    public Employee updateEmployee(String name, int id){
         String sql ="update employee set name = ? where id = ?";
         jdbcTemplate.update(sql, new Object[]{name,new Integer(id)}); 
         Employee emp = getEmployeeById(id);
         return emp;
     }

    public void insertEmployee(int id, String name , int age, int salary){
         String sql = "insert into Employee value (?,?,?,? )";
        jdbcTemplate.update(sql, new Object[]{new Integer(id), name,new Integer(age),new Integer(salary) }); 
    }

    public void deleteEmployee(int id){
         String sql ="delete from employee where id = ?";
        jdbcTemplate.update(sql, new Object[]{new Integer(id)}); 
    }
}

d Beans.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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans 
       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-3.0.xsd
       http://www.springframework.org/schema/aop 
       http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

      <bean id="dataSource"
         class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/SpringJDBC"/>
            <property name="username" value="root"/>
            <property name="password" value="password"/>
      </bean>

      <bean id="employeeJdbcTemplate" 
         class="jdbc.EmployeeJdbcTemplate">
            <property name="dataSource"  ref="dataSource" />    
      </bean>
</beans>

e.  TestEmployeeJdbcTemplate.java

package jdbc;
import java.util.List;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestEmployeeJdbcTemplate {
 
    public static void main(String args[]){
         ApplicationContext context = 
             new ClassPathXmlApplicationContext("beans.xml");
             EmployeeJdbcTemplate employeeJdbcTemplate = 
                (EmployeeJdbcTemplate)context.getBean("employeeJdbcTemplate");

           // Display current state of Employee table 
           List<Employee> emp = employeeJdbcTemplate.getAllEmployees();
           System.out.println("Current State of employee table -");
           System.out.println(emp); 

           // insert new employee
           employeeJdbcTemplate.insertEmployee(200, "Employee B", 31, 2000);

           // Display inserted employee  
           Employee insertedEmployee = employeeJdbcTemplate.getEmployeeById(200);
           System.out.println("Inserted Employee Information from Employee Table - ");
           System.out.println(insertedEmployee);

           // update employee
           Employee updatedEmployee =         employeeJdbcTemplate.updateEmployee("Employee B updated ", 200);
           System.out.println("Updated Employee Information from Employee Table - ");
           System.out.println(updatedEmployee);
        
           //delete employee    
           employeeJdbcTemplate.deleteEmployee(100);

           // display total number of employees        
           int count = employeeJdbcTemplate.getTotalNumberOfEmployees();        
           System.out.println("Total number of Employees in employee table ");
           System.out.println(count);

           emp = employeeJdbcTemplate.getAllEmployees();
           System.out.println("Current State of employee table -");
           System.out.println(emp);
    }
}

f. Run TestEmployeeJdbcTemplate.java

    

     

 19.9 JdbcDaoSupport

Spring provides JdbcDaoSupport classes  which internally handles the template creation part and requires only datasource.  Custom Dao classes need to extend JdbcDaoSupport class and can get the template object from getjdbcTemplate() method provided by JdbcDaoSupport class.

Lets create following classes which will use Employee and EmployeeRowMapper classes created in section 19.8.

a. EmployeeJdbcDaoSupport.java

package jdbc;

import java.util.List;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

public class EmployeeJdbcDaoSupport extends JdbcDaoSupport {
 
    public int  getTotalNumberOfEmployees(){
        String sql = "select count(*) from employee";
        int count = getJdbcTemplate().queryForInt(sql);
        return count;
    }

    public Employee getEmployeeById(int id){
        String sql = "select * from employee where id= ?";
        Employee employee = 
           getJdbcTemplate().queryForObject(sql, new Object[]{new Integer(id)} , new EmployeeRowMapper());
        return employee;
    }

    public List<Employee> getAllEmployees(){
        String sql = "select * from employee ";
        List<Employee> employee = 
              getJdbcTemplate().query(sql,  new EmployeeRowMapper());
        return employee;
    }

    public Employee updateEmployee(String name, int id){
        String sql ="update employee set name = ? where id = ?";
        getJdbcTemplate().update(sql, new Object[]{name,new Integer(id)}); 
        Employee emp = getEmployeeById(id);
        return emp;
    }
 
    public void insertEmployee(int id, String name , int age, int salary){
        String sql = "insert into Employee value (?,?,?,? )";
        getJdbcTemplate().update(sql, new Object[]{new Integer(id), name,new Integer(age),new Integer(salary) }); 
    }
    
    public void deleteEmployee(int id){
         String sql ="delete from employee where id = ?";
         getJdbcTemplate().update(sql, new Object[]{new Integer(id)});
    }

}

 b.  beans.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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

   <bean id="dataSource"
       class="org.springframework.jdbc.datasource.DriverManagerDataSource">
       <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
       <property name="url" value="jdbc:mysql://localhost:3306/SpringJDBC"/>
       <property name="username" value="root"/>
       <property name="password" value="password"/>
   </bean>

   <bean id="employeeJdbcDaoSupport" 
      class="jdbc.EmployeeJdbcDaoSupport">
     <property name="dataSource"  ref="dataSource" />    
   </bean>
</beans>

c. TestEmployeeJdbcDaoSupport.java

package jdbc;

import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestEmployeeJdbcDaoSupport {

import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationCont

public class TestEmployeeJdbcDaoSupport {
     
       public static void main(String args[]){
            ApplicationContext context = 
                new ClassPathXmlApplicationContext("beans.xml");        
            EmployeeJdbcDaoSupport employeeJdbcTemplate = 
                (EmployeeJdbcDaoSupport)context.getBean("employeeJdbcDaoSupport");
 
            // Display current state of Employee table         
            List<Employee> emp = employeeJdbcTemplate.getAllEmployees();
            System.out.println("Current State of employee table -");
            System.out.println(emp);

            // insert new employee    
            employeeJdbcTemplate.insertEmployee(200, "Employee B", 31, 2000);

            // Display inserted employee        
            Employee insertedEmployee = employeeJdbcTemplate.getEmployeeById(200);
            System.out.println("Inserted Employee Information from Employee Table - ");
            System.out.println(insertedEmployee);

            // update employee        
            Employee updatedEmployee = employeeJdbcTemplate.updateEmployee("Employee B updated ", 200);
            System.out.println("Updated Employee Information from Employee Table - ");        
            System.out.println(updatedEmployee);
  
            //delete employee        
            employeeJdbcTemplate.deleteEmployee(100);

            // display total number of employees
            int count = employeeJdbcTemplate.getTotalNumberOfEmployees();
            System.out.println("total number of employees : " + count);        
            emp = employeeJdbcTemplate.getAllEmployees();
            System.out.println("Current State of employee table -");
            System.out.println(emp); 
     }
}

   

   

19.10 Calling Stored Procedures

Spring JDBC framework provides SimpleJdbcCall class to call the stored procedures and functions and supports IN and OUT parameters as well.

a.  Lets create a product table with below create table statement.

 

CREATE TABLE 'products' (

   'id' int(11) NOT NULL,

  'name'  varchar(45) DEFAULT NULL,

  'brand'  varchar(45) DEFAULT NULL,

  'price' int(11) DEFAULT NULL,

  PRIMARY KEY ('id')

 ) ;

b.  Lets create a Stored procedure to insert a record in products table .

 

DELIMITER $$

CREATE  PROCEDURE  'springJdbc'. 'insert_product_procedure'(

IN product_id INTEGER,

IN product_name VARCHAR(45),

IN brand VARCHAR(45),

IN price INTEGER)

BEGIN

    insert into products values (product_id,product_name,brand,price);

END$$

DELIMITER ;

  

c. Create Product.java 

package jdbc;

public class Product {
 
       private int productId;
       private String productName;
       private int price;
       private String brand;
       public int getProductId() {
           return productId;
       }
       public void setProductId(int productId) {
          this.productId = productId;
       }
       public String getProductName() {
          return productName;
       }
       public void setProductName(String productName) {
          this.productName = productName;
       }
       public int getPrice() {
          return price;
       }
       public void setPrice(int price) {
           this.price = price;
       }
       public String getBrand() {
           return brand;
       }
       public void setBrand(String brand) {
          this.brand = brand;
       }
       @Override
       public String toString() {
          return "Product [productId=" + productId + ", productName="
                + productName + ", price=" + price + ", brand=" + brand + "]";
       }

}

 d. Create ProductMapper.java

package jdbc;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.core.RowMapper;

public class ProductRowMapper implements RowMapper<Product> {
    public Product mapRow(ResultSet rs, int rowNum)
            throws SQLException {

             Product product = new Product();

             product.setProductId(rs.getInt("id"));
             product.setProductName(rs.getString("name"));
             product.setBrand(rs.getString("brand"));
             product.setPrice(rs.getInt("price"));

            return product;
    }
}

e.  Create ProductJdbcCall.java

package jdbc;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.simple.SimpleJdbcCall;

public class ProductJdbcCall  {
    private JdbcTemplate jdbcTemplate;    
    private SimpleJdbcCall jdbcCall;

    public void setDataSource(DataSource dataSource) {
        this.jdbcTemplate = new JdbcTemplate(dataSource);
        this.jdbcCall= new SimpleJdbcCall(dataSource).withProcedureName("insert_product_procedure");
    }

    public List<Product> getAllProducts(){
        String sql = "select * from products";
        List<Product> products = jdbcTemplate.query(sql, new ProductRowMapper()); 
        return products;
    }

    public void  callProcedure(int id,String name , String brand , int price ){
        Map<String,Object> inputMap = new HashMap<String,Object>();
        inputMap.put("product_id",id);
        inputMap.put("product_name", name);
        inputMap.put("price", price);
        inputMap.put("brand", brand);        
        jdbcCall.execute(inputMap);
    }

} 

e. Beans.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:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.0.xsd
           http://www.springframework.org/schema/aop 
           http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">

    <bean id="dataSource"
        class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/SpringJDBC"/>
        <property name="username" value="root"/>
        <property name="password" value="password"/>
    </bean>

    <bean id="productJdbcCall" 
        class="jdbc.ProductJdbcCall">
        <property name="dataSource"  ref="dataSource" />    
   </bean>

</beans>

 g. Create TestProductJdbcCall.java 

package jdbc;

import java.util.List;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestProductJdbcCall {    
      public static void main(String args[]){
            ApplicationContext context = 
                new ClassPathXmlApplicationContext("beans.xml");        
            ProductJdbcCall productJdbcCall = (ProductJdbcCall)context.getBean("productJdbcCall");        
            productJdbcCall.callProcedure(104,"Product A","Brand A",230);        
            productJdbcCall.callProcedure(105,"Product B","Brand B",130);        
            List<Product> products = productJdbcCall.getAllProducts();
            System.out.println(products);
     }
} 

h. Run TestProductJdbcCall.java

 

Like us on Facebook