Ejemplo de Spring JDBC

Spring JDBC es el tema de este tutorial. Las bases de datos son una parte integral de la mayoría de las Aplicaciones Empresariales. Por lo tanto, cuando se trata de un framework Java EE, tener una buena integración con JDBC es muy importante.

Spring JDBC

El Framework Spring proporciona una excelente integración con la API JDBC y ofrece la clase de utilidad JdbcTemplate que podemos utilizar para evitar código repetitivo en nuestra lógica de operaciones de base de datos, como la apertura/cierre de la conexión, ResultSet, PreparedStatement, etc. Primero veamos un ejemplo simple de una aplicación Spring JDBC y luego veremos cómo la clase JdbcTemplate puede ayudarnos a escribir código modular con facilidad, sin preocuparnos si los recursos se cierran correctamente o no. La Suite de Herramientas Spring para desarrollar aplicaciones basadas en Spring es muy útil, así que usaremos STS para crear nuestra aplicación Spring JDBC. La estructura final de nuestro proyecto se verá como la imagen a continuación. Crea un Proyecto Maven Spring simple desde el Menú STS, puedes elegir el nombre que desees o quedarte con el nombre de mi proyecto como SpringJDBCExample.

Dependencias de Spring JDBC

En primer lugar, necesitamos incluir Spring JDBC y los controladores de la base de datos en el archivo pom.xml del proyecto Maven. Mi archivo pom.xml final se ve así:

<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>org.springframework.samples</groupId>
	<artifactId>SpringJDBCExample</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<properties>

		<!-- Generic properties -->
		<java.version>1.6</java.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>

		<!-- Spring -->
		<spring-framework.version>4.0.2.RELEASE</spring-framework.version>

		<!-- Logging -->
		<logback.version>1.0.13</logback.version>
		<slf4j.version>1.7.5</slf4j.version>

	</properties>

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

		<!-- Spring JDBC Support -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>${spring-framework.version}</version>
		</dependency>
		
		<!-- MySQL Driver -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.0.5</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>

	</dependencies>
</project>

La mayor parte se genera automáticamente por STS, sin embargo, he actualizado la versión del Framework Spring para usar la última versión, que es la 4.0.2.RELEASE. También hemos añadido los artefactos necesarios spring-jdbc y mysql-connector-java. El primero contiene las clases de soporte de Spring JDBC y el segundo es el controlador de la base de datos. Estoy utilizando la base de datos MySQL para nuestros propósitos de prueba, así que he añadido las dependencias del archivo JConnector de MySQL. Si estás utilizando otro sistema de gestión de bases de datos relacionales, entonces debes hacer los cambios correspondientes en las dependencias.

Ejemplo de Spring JDBC – Configuración de la Base de Datos

Creemos una tabla simple que utilizaremos en nuestra aplicación para el ejemplo de operaciones CRUD.

CREATE TABLE `Employee` (
  `id` int(11) unsigned NOT NULL,
  `name` varchar(20) DEFAULT NULL,
  `role` varchar(20) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Ejemplo de Spring JDBC – Clase de Modelo

Utilizaremos el Patrón DAO para las operaciones JDBC, así que creemos un bean Java que modele nuestra tabla de Empleados.

package com.journaldev.spring.jdbc.model;

public class Employee {

	private int id;
	private String name;
	private String role;
	
	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 String getRole() {
		return role;
	}
	public void setRole(String role) {
		this.role = role;
	}
	
	@Override
	public String toString(){
		return "{ID="+id+",Name="+name+",Role="+role+"}";
	}
}

Ejemplo de Spring JDBC – Interfaz y Implementación DAO

Para el patrón DAO, primero tendremos una interfaz que declare todas las operaciones que queremos implementar.

package com.journaldev.spring.jdbc.dao;

import java.util.List;

import com.journaldev.spring.jdbc.model.Employee;

// Operaciones CRUD
public interface EmployeeDAO {
	
	//Create
	public void save(Employee employee);
	//Read
	public Employee getById(int id);
	//Update
	public void update(Employee employee);
	//Delete
	public void deleteById(int id);
	// Obtener todo
	public List getAll();
}
package com.journaldev.spring.jdbc.dao;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import javax.sql.DataSource;

import com.journaldev.spring.jdbc.model.Employee;

public class EmployeeDAOImpl implements EmployeeDAO {

	private DataSource dataSource;

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

	@Override
	public void save(Employee employee) {
		String query = "insert into Employee (id, name, role) values (?,?,?)";
		Connection con = null;
		PreparedStatement ps = null;
		try{
			con = dataSource.getConnection();
			ps = con.prepareStatement(query);
			ps.setInt(1, employee.getId());
			ps.setString(2, employee.getName());
			ps.setString(3, employee.getRole());
			int out = ps.executeUpdate();
			if(out !=0){
				System.out.println("Employee saved with id="+employee.getId());
			}else System.out.println("Employee save failed with id="+employee.getId());
		}catch(SQLException e){
			e.printStackTrace();
		}finally{
			try {
				ps.close();
				con.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	@Override
	public Employee getById(int id) {
		String query = "select name, role from Employee where id = ?";
		Employee emp = null;
		Connection con = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try{
			con = dataSource.getConnection();
			ps = con.prepareStatement(query);
			ps.setInt(1, id);
			rs = ps.executeQuery();
			if(rs.next()){
				emp = new Employee();
				emp.setId(id);
				emp.setName(rs.getString("name"));
				emp.setRole(rs.getString("role"));
				System.out.println("Employee Found::"+emp);
			}else{
				System.out.println("No Employee found with id="+id);
			}
		}catch(SQLException e){
			e.printStackTrace();
		}finally{
			try {
				rs.close();
				ps.close();
				con.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return emp;
	}

	@Override
	public void update(Employee employee) {
		String query = "update Employee set name=?, role=? where id=?";
		Connection con = null;
		PreparedStatement ps = null;
		try{
			con = dataSource.getConnection();
			ps = con.prepareStatement(query);
			ps.setString(1, employee.getName());
			ps.setString(2, employee.getRole());
			ps.setInt(3, employee.getId());
			int out = ps.executeUpdate();
			if(out !=0){
				System.out.println("Employee updated with id="+employee.getId());
			}else System.out.println("No Employee found with id="+employee.getId());
		}catch(SQLException e){
			e.printStackTrace();
		}finally{
			try {
				ps.close();
				con.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	@Override
	public void deleteById(int id) {
		String query = "delete from Employee where id=?";
		Connection con = null;
		PreparedStatement ps = null;
		try{
			con = dataSource.getConnection();
			ps = con.prepareStatement(query);
			ps.setInt(1, id);
			int out = ps.executeUpdate();
			if(out !=0){
				System.out.println("Employee deleted with id="+id);
			}else System.out.println("No Employee found with id="+id);
		}catch(SQLException e){
			e.printStackTrace();
		}finally{
			try {
				ps.close();
				con.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}

	@Override
	public List<Employee> getAll() {
		String query = "select id, name, role from Employee";
		List<Employee> empList = new ArrayList<Employee>();
		Connection con = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		try{
			con = dataSource.getConnection();
			ps = con.prepareStatement(query);
			rs = ps.executeQuery();
			while(rs.next()){
				Employee emp = new Employee();
				emp.setId(rs.getInt("id"));
				emp.setName(rs.getString("name"));
				emp.setRole(rs.getString("role"));
				empList.add(emp);
			}
		}catch(SQLException e){
			e.printStackTrace();
		}finally{
			try {
				rs.close();
				ps.close();
				con.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		return empList;
	}

}

La implementación de las operaciones CRUD es simple de entender. Si deseas aprender más sobre DataSource, por favor lee Ejemplo de DataSource JDBC.

Ejemplo de Spring JDBC – Configuración de Bean

Si observas todas las clases anteriores, están utilizando la API JDBC estándar y no hay referencia al framework Spring JDBC. Las clases del framework Spring JDBC entran en juego cuando creamos el archivo de configuración del Bean de Spring y definimos los beans. Crearemos el DataSource en el archivo de contexto del Bean de Spring y lo asignaremos a nuestra clase de implementación DAO. Mi archivo de configuración del Bean de Spring se ve así.

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

	<bean id="employeeDAO" class="com.journaldev.spring.jdbc.dao.EmployeeDAOImpl">
		<property name="dataSource" ref="dataSource" />
	</bean>
	
	<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/TestDB" />
		<property name="username" value="pankaj" />
		<property name="password" value="pankaj123" />
	</bean>

</beans>

Primero que todo, estamos creando un objeto DataSource de la clase DriverManagerDataSource. Esta clase proporciona la implementación básica de DataSource que podemos utilizar. Estamos pasando la URL de la base de datos MySQL, el nombre de usuario y la contraseña como propiedades al bean DataSource. Nuevamente, el bean dataSource se establece en el bean EmployeeDAOImpl y estamos listos con nuestra implementación de Spring JDBC. La implementación está débilmente acoplada y si deseamos cambiar a alguna otra implementación o cambiar a otro servidor de base de datos, todo lo que necesitamos hacer es realizar cambios correspondientes en las configuraciones del bean. Esta es una de las principales ventajas proporcionadas por el marco Spring JDBC.

Clase de Prueba de Spring JDBC

Escrebamos una clase de prueba simple para asegurarnos de que todo funcione correctamente.

package com.journaldev.spring.jdbc.main;

import java.util.List;
import java.util.Random;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.journaldev.spring.jdbc.dao.EmployeeDAO;
import com.journaldev.spring.jdbc.model.Employee;

public class SpringMain {

	public static void main(String[] args) {
		// Obtener el contexto de Spring
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
		
		// Obtener el bean EmployeeDAO
		EmployeeDAO employeeDAO = ctx.getBean("employeeDAO", EmployeeDAO.class);
		
		// Ejecutar algunas pruebas para operaciones JDBC CRUD
		Employee emp = new Employee();
		int rand = new Random().nextInt(1000);
		emp.setId(rand);
		emp.setName("Pankaj");
		emp.setRole("Java Developer");
		
		//Create
		employeeDAO.save(emp);
		
		//Read
		Employee emp1 = employeeDAO.getById(rand);
		System.out.println("Employee Retrieved::"+emp1);
		
		//Update
		emp.setRole("CEO");
		employeeDAO.update(emp);
		
		// Obtener todos
		List empList = employeeDAO.getAll();
		System.out.println(empList);
		
		//Delete
		employeeDAO.deleteById(rand);
		
		// Cerrar el contexto de Spring
		ctx.close();
		
		System.out.println("DONE");
	}

}

I am using Random Class to generate random number for employee id. When we run above program, we get following output.

Mar 25, 2014 12:54:18 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4b9af9a9: startup date [Tue Mar 25 12:54:18 PDT 2014]; root of context hierarchy
Mar 25, 2014 12:54:18 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
INFO: Loading XML bean definitions from class path resource [spring.xml]
Mar 25, 2014 12:54:19 PM org.springframework.jdbc.datasource.DriverManagerDataSource setDriverClassName
INFO: Loaded JDBC driver: com.mysql.jdbc.Driver
Employee saved with id=726
Employee Found::{ID=726,Name=Pankaj,Role=Java Developer}
Employee Retrieved::{ID=726,Name=Pankaj,Role=Java Developer}
Employee updated with id=726
[{ID=726,Name=Pankaj,Role=CEO}]
Employee deleted with id=726
Mar 25, 2014 12:54:19 PM org.springframework.context.support.ClassPathXmlApplicationContext doClose
INFO: Closing org.springframework.context.support.ClassPathXmlApplicationContext@4b9af9a9: startup date [Tue Mar 25 12:54:18 PDT 2014]; root of context hierarchy
DONE

Ejemplo de Spring JdbcTemplate

Si miras la clase de implementación DAO, hay mucho código repetitivo donde estamos abriendo y cerrando Connection, PreparedStatements y ResultSet. Esto puede llevar a fugas de recursos si alguien olvida cerrar los recursos correctamente. Podemos usar la clase org.springframework.jdbc.core.JdbcTemplate para evitar estos errores. Spring JdbcTemplate es la clase central en el paquete principal de Spring JDBC y proporciona muchos métodos para ejecutar consultas y analizar automáticamente ResultSet para obtener el Objeto o la lista de Objetos. Todo lo que necesitamos es proporcionar los argumentos como un array de objetos e implementar interfaces de Callback como PreparedStatementSetter y RowMapper para mapear argumentos o convertir datos de ResultSet en objetos bean. Veamos otra implementación de EmployeeDAO donde usaremos la clase Spring JdbcTemplate para ejecutar diferentes tipos de consultas.

package com.journaldev.spring.jdbc.dao;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.sql.DataSource;

import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;

import com.journaldev.spring.jdbc.model.Employee;

public class EmployeeDAOJDBCTemplateImpl implements EmployeeDAO {

	private DataSource dataSource;

	public void setDataSource(DataSource dataSource) {
		this.dataSource = dataSource;
	}
	
	@Override
	public void save(Employee employee) {
		String query = "insert into Employee (id, name, role) values (?,?,?)";
		
		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
		
		Object[] args = new Object[] {employee.getId(), employee.getName(), employee.getRole()};
		
		int out = jdbcTemplate.update(query, args);
		
		if(out !=0){
			System.out.println("Employee saved with id="+employee.getId());
		}else System.out.println("Employee save failed with id="+employee.getId());
	}

	@Override
	public Employee getById(int id) {
		String query = "select id, name, role from Employee where id = ?";
		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
		
		//usando una clase RowMapper anónima, podemos crear un RowMapper separado para reutilizar
		Employee emp = jdbcTemplate.queryForObject(query, new Object[]{id}, new RowMapper(){

			@Override
			public Employee mapRow(ResultSet rs, int rowNum)
					throws SQLException {
				Employee emp = new Employee();
				emp.setId(rs.getInt("id"));
				emp.setName(rs.getString("name"));
				emp.setRole(rs.getString("role"));
				return emp;
			}});
		
		return emp;
	}

	@Override
	public void update(Employee employee) {
		String query = "update Employee set name=?, role=? where id=?";
		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
		Object[] args = new Object[] {employee.getName(), employee.getRole(), employee.getId()};
		
		int out = jdbcTemplate.update(query, args);
		if(out !=0){
			System.out.println("Employee updated with id="+employee.getId());
		}else System.out.println("No Employee found with id="+employee.getId());
	}

	@Override
	public void deleteById(int id) {

		String query = "delete from Employee where id=?";
		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
		
		int out = jdbcTemplate.update(query, id);
		if(out !=0){
			System.out.println("Employee deleted with id="+id);
		}else System.out.println("No Employee found with id="+id);
	}

	@Override
	public List getAll() {
		String query = "select id, name, role from Employee";
		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
		List empList = new ArrayList();

		List> empRows = jdbcTemplate.queryForList(query);
		
		for(Map empRow : empRows){
			Employee emp = new Employee();
			emp.setId(Integer.parseInt(String.valueOf(empRow.get("id"))));
			emp.setName(String.valueOf(empRow.get("name")));
			emp.setRole(String.valueOf(empRow.get("role")));
			empList.add(emp);
		}
		return empList;
	}

}

Puntos importantes para analizar en el código anterior para Spring JdbcTemplate:

  • Uso de array de objetos para pasar argumentos PreparedStatement, también podríamos usar la implementación PreparedStatementSetter pero pasar array de objetos parece más fácil de usar.
  • No hay código relacionado con la apertura y cierre de conexiones, declaraciones o conjunto de resultados. Todo eso es manejado internamente por la clase Spring JdbcTemplate.
  • Implementación de clase RowMapper anónima para mapear los datos ResultSet a un objeto bean de Employee en el método queryForObject().
  • El método queryForList() devuelve una lista de Mapas, mientras que el Mapa contiene los datos de la fila mapeados con la clave como el nombre de la columna y el valor de la fila de la base de datos que coincide con el criterio.

Para utilizar la implementación de Spring JdbcTemplate, todo lo que necesitamos hacer es cambiar la clase employeeDAO en el archivo de configuración de Spring Bean como se muestra a continuación.

<bean id="employeeDAO" class="com.journaldev.spring.jdbc.dao.EmployeeDAOJDBCTemplateImpl">
	<property name="dataSource" ref="dataSource" />
</bean>

Cuando ejecutes la clase principal, la salida de la implementación de Spring JdbcTemplate será similar a la que se ve arriba con la implementación normal de JDBC. Eso es todo para el tutorial de ejemplo de Spring JDBC, descarga el proyecto de muestra desde el siguiente enlace y juega con él para aprender más.

Descargar Proyecto Spring JDBC

Source:
https://www.digitalocean.com/community/tutorials/spring-jdbc-example