Esempio Spring JDBC

Spring JDBC è l’argomento di questo tutorial. I database sono una parte integrante della maggior parte delle applicazioni enterprise. Quindi, quando si tratta di un framework Java EE, è molto importante avere una buona integrazione con JDBC.

Spring JDBC

Il framework Spring fornisce un’eccellente integrazione con l’API JDBC e offre la classe di utilità JdbcTemplate che possiamo utilizzare per evitare codice ridondante nelle nostre operazioni di database, come l’apertura/chiusura della connessione, ResultSet, PreparedStatement, ecc. Vediamo prima un semplice esempio di applicazione Spring JDBC e poi vedremo come la classe JdbcTemplate può aiutarci a scrivere codice modulare con facilità, senza preoccuparci se le risorse vengono chiuse correttamente o meno. Lo strumento Spring Tool Suite per lo sviluppo di applicazioni basate su Spring è molto utile, quindi utilizzeremo STS per creare la nostra applicazione Spring JDBC. La nostra struttura finale del progetto sarà simile all’immagine sottostante. Crea un semplice progetto Maven Spring dal menu di STS, puoi scegliere qualunque nome preferisci o mantenere il nome del mio progetto come SpringJDBCExample.

Dipendenze Spring JDBC

Innanzitutto, dobbiamo includere le dipendenze Spring JDBC e i driver del database nel file pom.xml del progetto Maven. Il mio file pom.xml finale appare come segue.

<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 maggior parte della parte viene generata automaticamente da STS, tuttavia ho aggiornato la versione di Spring Framework per utilizzare l’ultima versione, ovvero la 4.0.2.RELEASE. Inoltre, abbiamo aggiunto gli artefatti richiesti spring-jdbc e mysql-connector-java. Il primo contiene le classi di supporto di Spring JDBC e il secondo è il driver del database. Sto utilizzando il database MySQL per scopi di test, quindi ho aggiunto le dipendenze del jar di MySQL JConnector. Se si sta utilizzando un altro RDBMS, è necessario apportare le modifiche corrispondenti alle dipendenze.

Esempio di Spring JDBC – Configurazione del database

Creiamo una semplice tabella che utilizzeremo nella nostra applicazione per un esempio di operazioni 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;

Esempio di Spring JDBC – Classe Modello

Utilizzeremo il pattern DAO per le operazioni JDBC, quindi creiamo un Java bean che modellerà la nostra tabella Employee.

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+"}";
	}
}

Esempio di Spring JDBC – Interfaccia e implementazione del DAO

Per il pattern DAO, avremo prima un’interfaccia che dichiara tutte le operazioni che vogliamo implementare.

package com.journaldev.spring.jdbc.dao;

import java.util.List;

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

//Operazioni 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);
	//Ottieni tutti
	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;
	}

}

L’implementazione delle operazioni CRUD è semplice da capire. Se vuoi saperne di più su DataSource, leggi Esempio di DataSource JDBC.

Esempio di Spring JDBC – Configurazione del bean

Se guardi tutte le classi sopra, stanno utilizzando tutte l’API JDBC standard e non c’è alcun riferimento al framework Spring JDBC. Le classi del framework Spring JDBC entrano in gioco quando creiamo il file di configurazione dei bean di Spring e definiamo i bean. Creeremo il DataSource nel file di contesto del bean di Spring e lo impostiamo sulla nostra classe di implementazione del DAO. Il mio file di configurazione dei bean di Spring ha questo aspetto.

<?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>

Prima di tutto stiamo creando un oggetto DataSource della classe DriverManagerDataSource. Questa classe fornisce l’implementazione di base di DataSource che possiamo utilizzare. Stiamo passando l’URL del database MySQL, il nome utente e la password come proprietà al bean DataSource. Di nuovo il bean dataSource è impostato sul bean EmployeeDAOImpl e siamo pronti con la nostra implementazione di Spring JDBC. L’implementazione è debolmente accoppiata e se vogliamo passare a un’altra implementazione o spostarci su un altro server di database, tutto ciò di cui abbiamo bisogno è di apportare le modifiche corrispondenti nelle configurazioni dei bean. Questo è uno dei principali vantaggi forniti dal framework Spring JDBC.

Classe di Test Spring JDBC

Scriviamo una semplice classe di test per assicurarci che tutto funzioni correttamente.

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) {
		//Ottieni il Contesto Spring
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
		
		//Ottieni il Bean EmployeeDAO
		EmployeeDAO employeeDAO = ctx.getBean("employeeDAO", EmployeeDAO.class);
		
		//Esegui alcuni test per le operazioni CRUD JDBC
		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);
		
		//Ottieni Tutto
		List empList = employeeDAO.getAll();
		System.out.println(empList);
		
		//Delete
		employeeDAO.deleteById(rand);
		
		//Chiudi il Contesto 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

Esempio di Spring JdbcTemplate

Se si guarda alla classe di implementazione DAO, c’è molto codice boiler-plate in cui apriamo e chiudiamo la connessione, i PreparedStatements e i ResultSet. Ciò può portare a una perdita di risorse se qualcuno dimentica di chiudere correttamente le risorse. Possiamo utilizzare la classe org.springframework.jdbc.core.JdbcTemplate per evitare questi errori. Spring JdbcTemplate è la classe centrale nel pacchetto core di Spring JDBC e fornisce molti metodi per eseguire query e convertire automaticamente il ResultSet per ottenere l’oggetto o la lista di oggetti. Tutto ciò di cui abbiamo bisogno è fornire gli argomenti come un array di oggetti e implementare le interfacce di callback come PreparedStatementSetter e RowMapper per mappare gli argomenti o convertire i dati del ResultSet in oggetti bean. Vediamo un’altra implementazione di EmployeeDAO in cui utilizzeremo la classe Spring JdbcTemplate per eseguire diversi tipi di query.

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);
		
		//utilizzando una classe anonima RowMapper, possiamo creare un RowMapper separato per riutilizzo
		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;
	}

}

Punti importanti da considerare nel codice sopra per Spring JdbcTemplate sono:

  • Uso di un array di oggetti per passare gli argomenti PreparedStatement, potremmo anche utilizzare un’implementazione di PreparedStatementSetter ma passare un array di oggetti sembra più semplice da usare.
  • Nessun codice relativo all’apertura e alla chiusura delle connessioni, delle istruzioni o dei risultati. Tutto ciò è gestito internamente dalla classe Spring JdbcTemplate.
  • Implementazione di una classe anonima RowMapper per mappare i dati del ResultSet all’oggetto bean Employee nel metodo queryForObject().
  • queryForList() restituisce una lista di Map dove ogni mappa contiene i dati di una riga mappati con la chiave come nome della colonna e il valore dalla riga del database che corrisponde ai criteri.

Per utilizzare l’implementazione Spring JdbcTemplate, tutto ciò di cui abbiamo bisogno è modificare la classe employeeDAO nel file di configurazione Spring Bean come mostrato di seguito.

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

Quando eseguirai la classe principale, l’output dell’implementazione Spring JdbcTemplate sarà simile a quello visto sopra con l’implementazione JDBC normale. Questo è tutto per il tutorial di esempio di Spring JDBC, scarica il progetto di esempio dal link sottostante e gioca con esso per imparare di più.

Scarica il progetto Spring JDBC

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