Exemple Spring JDBC

Spring JDBC est le sujet de ce tutoriel. Les bases de données font partie intégrante de la plupart des applications d’entreprise. Donc, en ce qui concerne un framework Java EE, il est très important d’avoir une bonne intégration avec JDBC.

Spring JDBC

Le framework Spring offre une excellente intégration avec l’API JDBC et fournit la classe utilitaire JdbcTemplate que nous pouvons utiliser pour éviter le code redondant de notre logique d’opérations sur la base de données telles que l’ouverture/fermeture de la connexion, ResultSet, PreparedStatement, etc. Regardons d’abord un exemple simple d’application Spring JDBC, puis nous verrons comment la classe JdbcTemplate peut nous aider à écrire un code modulaire facilement, sans se soucier si les ressources sont correctement fermées ou non. L’outil Spring Tool Suite pour développer des applications basées sur Spring est très utile, donc nous utiliserons STS pour créer notre application Spring JDBC. La structure finale de notre projet ressemblera à l’image ci-dessous. Créez un projet Maven Spring simple à partir du menu STS, vous pouvez choisir n’importe quel nom que vous aimez ou rester avec le nom de mon projet comme SpringJDBCExample.

Dépendances Spring JDBC

Tout d’abord, nous devons inclure Spring JDBC et les pilotes de base de données dans le fichier pom.xml du projet Maven. Mon fichier pom.xml final ressemble à ceci.

<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 plupart de la partie est générée automatiquement par STS, cependant j’ai mis à jour la version du Framework Spring pour utiliser la dernière version 4.0.2.RELEASE. De plus, nous avons ajouté les artefacts requis spring-jdbc et mysql-connector-java. Le premier contient les classes de support JDBC Spring et le second est le pilote de base de données. J’utilise la base de données MySQL à des fins de test, donc j’ai ajouté les dépendances du jar MySQL JConnector. Si vous utilisez un autre SGBDR, vous devez apporter les modifications correspondantes dans les dépendances.

Exemple JDBC Spring – Configuration de la base de données

Créons une table simple que nous utiliserons dans notre application pour l’exemple des opérations 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;

Exemple JDBC Spring – Classe modèle

Nous utiliserons le motif DAO pour les opérations JDBC, donc créons une classe bean Java qui modélisera notre table 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+"}";
	}
}

Exemple JDBC Spring – Interface et implémentation DAO

Pour le modèle DAO, nous aurons d’abord une interface déclarant toutes les opérations que nous voulons implémenter.

package com.journaldev.spring.jdbc.dao;

import java.util.List;

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

//Opérations 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);
	//Obtenir tout
	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’implémentation des opérations CRUD est simple à comprendre. Si vous souhaitez en savoir plus sur DataSource, veuillez lire Exemple de JDBC DataSource.

Exemple de Spring JDBC – Configuration du Bean

Si vous regardez toutes les classes ci-dessus, elles utilisent toutes l’API JDBC standard et il n’y a aucune référence au framework Spring JDBC. Les classes du framework Spring JDBC entrent en jeu lorsque nous créons le fichier de configuration du Bean Spring et définissons les beans. Nous créerons le DataSource dans le fichier de contexte du Bean Spring et le définirons pour notre classe d’implémentation DAO. Mon fichier de configuration du Bean Spring ressemble à ce qui suit.

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

Tout d’abord, nous créons un objet DataSource de la classe DriverManagerDataSource. Cette classe fournit l’implémentation de base de DataSource que nous pouvons utiliser. Nous passons l’URL de la base de données MySQL, le nom d’utilisateur et le mot de passe en tant que propriétés au bean DataSource. Encore une fois, le bean dataSource est défini sur le bean EmployeeDAOImpl et nous sommes prêts avec notre implémentation Spring JDBC. L’implémentation est faiblement couplée et si nous voulons passer à une autre implémentation ou passer à un autre serveur de base de données, tout ce dont nous avons besoin est de faire les modifications correspondantes dans les configurations des beans. C’est l’un des principaux avantages offerts par le framework Spring JDBC.

Classe de test Spring JDBC

Écrivons maintenant une classe de test simple pour nous assurer que tout fonctionne correctement.

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) {
		//Obtenir le contexte Spring
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
		
		//Obtenir le bean EmployeeDAO
		EmployeeDAO employeeDAO = ctx.getBean("employeeDAO", EmployeeDAO.class);
		
		//Exécuter quelques tests pour les opérations 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);
		
		//Obtenir tous les
		List empList = employeeDAO.getAll();
		System.out.println(empList);
		
		//Delete
		employeeDAO.deleteById(rand);
		
		//Fermer le contexte 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

Exemple de Spring JdbcTemplate

Si vous regardez la classe d’implémentation DAO, il y a beaucoup de code redondant où nous ouvrons et fermons les connexions, les PreparedStatements et les ResultSet. Cela peut entraîner des fuites de ressources si quelqu’un oublie de fermer correctement les ressources. Nous pouvons utiliser la classe org.springframework.jdbc.core.JdbcTemplate pour éviter ces erreurs. Spring JdbcTemplate est la classe centrale du package de base JDBC de Spring et fournit de nombreuses méthodes pour exécuter des requêtes et analyser automatiquement le ResultSet pour obtenir l’objet ou la liste d’objets. Tout ce dont nous avons besoin est de fournir les arguments sous forme de tableau d’objets et de mettre en œuvre des interfaces de rappel telles que PreparedStatementSetter et RowMapper pour mapper les arguments ou convertir les données ResultSet en objets bean. Regardons une autre implémentation de EmployeeDAO où nous utiliserons la classe Spring JdbcTemplate pour exécuter différents types de requêtes.

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);
		
		// en utilisant une classe RowMapper anonyme, nous pouvons créer une classe RowMapper séparée pour la réutilisation
		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;
	}

}

Points importants à examiner dans le code ci-dessus pour Spring JdbcTemplate:

  • Utilisation d’un tableau d’objets pour transmettre les arguments PreparedStatement, nous pourrions également utiliser une implémentation PreparedStatementSetter, mais passer un tableau d’objets semble plus facile à utiliser.
  • Aucun code lié à l’ouverture et à la fermeture de connexions, de déclarations ou de jeux de résultats. Tout cela est géré en interne par la classe Spring JdbcTemplate.
  • Implémentation d’une classe RowMapper anonyme pour mapper les données ResultSet à l’objet Employee dans la méthode queryForObject().
  • La méthode queryForList() renvoie une liste de Map, où chaque Map contient les données de la ligne mappée avec la clé en tant que nom de colonne et la valeur provenant de la ligne de la base de données correspondant aux critères.

Pour utiliser l’implémentation Spring JdbcTemplate, tout ce dont nous avons besoin est de modifier la classe employeeDAO dans le fichier de configuration des Spring Beans, comme indiqué ci-dessous.

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

Lorsque vous exécutez la classe principale, la sortie de l’implémentation Spring JdbcTemplate sera similaire à celle vue ci-dessus avec une implémentation JDBC normale. C’est tout pour le tutoriel sur l’exemple Spring JDBC. Téléchargez le projet d’exemple depuis le lien ci-dessous et explorez-le pour en savoir plus.

Télécharger le projet Spring JDBC

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