Voorbeeld van Spring JDBC

Spring JDBC is het onderwerp van deze tutorial. Databases zijn een integraal onderdeel van de meeste zakelijke applicaties. Dus als het gaat om een Java EE-framework, is goede integratie met JDBC erg belangrijk.

Spring JDBC

Het Spring Framework biedt uitstekende integratie met de JDBC API en levert de hulpprogrammaklasse JdbcTemplate, die we kunnen gebruiken om boilerplate-code te vermijden bij onze databasebewerkingen, zoals het openen/sluiten van de verbinding, ResultSet, PreparedStatement, enzovoort. Laten we eerst naar een eenvoudig voorbeeld van een Spring JDBC-toepassing kijken, en daarna zullen we zien hoe de klasse JdbcTemplate ons kan helpen bij het schrijven van modulaire code met gemak, zonder ons zorgen te hoeven maken over het correct sluiten van resources. De Spring Tool Suite is zeer nuttig bij het ontwikkelen van op Spring gebaseerde toepassingen, dus we zullen STS gebruiken om onze Spring JDBC-toepassing te maken. Onze uiteindelijke projectstructuur zal eruitzien zoals afgebeeld in . Maak een eenvoudig Spring Maven-project vanuit het STS-menu; je kunt een willekeurige naam kiezen of de projectnaam als SpringJDBCExample behouden.

Spring JDBC-afhankelijkheden

Ten eerste moeten we Spring JDBC en database-drivers opnemen in het pom.xml-bestand van het Maven-project. Mijn uiteindelijke pom.xml-bestand ziet er als volgt uit:

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

Het grootste deel wordt automatisch gegenereerd door STS, maar ik heb de Spring Framework-versie bijgewerkt naar de nieuwste versie, namelijk 4.0.2.RELEASE. Ook hebben we de vereiste artefacten toegevoegd: spring-jdbc bevat de ondersteuningsklassen voor Spring JDBC, en mysql-connector-java is de database driver. De eerste bevat ondersteuningsklassen voor Spring JDBC, en de tweede is de database driver. Ik gebruik MySQL-database voor onze testdoeleinden, dus ik heb MySQL JConnector jar-afhankelijkheden toegevoegd. Als je een ander RDBMS gebruikt, moet je overeenkomstige wijzigingen aanbrengen in de afhankelijkheden.

Voorbeeld van Spring JDBC – Database-instellingen

Laten we een eenvoudige tabel maken die we zullen gebruiken in ons toepassingsvoorbeeld voor CRUD-operaties.

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;

Voorbeeld van Spring JDBC – Modelklasse

We zullen het DAO-patroon gebruiken voor JDBC-operaties, dus laten we een Java Bean maken die onze werknemerstabel zal modelleren.

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

Voorbeeld van Spring JDBC – DAO-interface en implementatie

Voor het DAO-patroon zullen we eerst een interface hebben die alle bewerkingen declareert die we willen implementeren.

package com.journaldev.spring.jdbc.dao;

import java.util.List;

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

//CRUD-bewerkingen
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);
	//Alles ophalen
	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;
	}

}

De implementatie van CRUD-bewerkingen is eenvoudig te begrijpen. Als je meer wilt weten over DataSource, lees dan JDBC DataSource Voorbeeld.

Spring JDBC Voorbeeld – Bean-configuratie

Als je naar alle klassen hierboven kijkt, gebruiken ze allemaal de standaard JDBC API en er is geen verwijzing naar het Spring JDBC-framework. Spring JDBC-frameworkklassen komen in beeld wanneer we een Spring Bean-configuratiebestand maken en de beans definiëren. We zullen de DataSource maken in het Spring Bean-contextbestand en deze instellen op onze DAO-implementatieklasse. Mijn Spring Bean-configuratiebestand ziet er als volgt uit.

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

Allereerst maken we een DataSource-object van de klasse `DriverManagerDataSource` aan. Deze klasse biedt de basisimplementatie van DataSource die we kunnen gebruiken. We geven de MySQL-database-URL, gebruikersnaam en wachtwoord door als eigenschappen aan het DataSource-bean. Opnieuw wordt het dataSource-bean ingesteld op het `EmployeeDAOImpl`-bean en zijn we klaar met onze Spring JDBC-implementatie. De implementatie is losjes gekoppeld en als we willen overschakelen naar een andere implementatie of naar een andere databaseserver willen verplaatsen, hoeven we alleen maar overeenkomstige wijzigingen aan te brengen in de beanconfiguraties. Dit is een van de belangrijke voordelen die het Spring JDBC-framework biedt.

Spring JDBC-testklasse

Laten we een eenvoudige testklasse schrijven om ervoor te zorgen dat alles goed werkt.

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) {
		// Haal de Spring-context op
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
		
		// Haal het EmployeeDAO-bean op
		EmployeeDAO employeeDAO = ctx.getBean("employeeDAO", EmployeeDAO.class);
		
		// Voer enkele tests uit voor JDBC-CRUD-operaties
		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);
		
		// Haal alles op
		List empList = employeeDAO.getAll();
		System.out.println(empList);
		
		//Delete
		employeeDAO.deleteById(rand);
		
		// Sluit de Spring-context
		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

Voorbeeld van Spring JdbcTemplate

Als je naar de DAO-implementatieklasse kijkt, zie je veel standaardcode waar we verbindingen, PreparedStatements en ResultSet openen en sluiten. Dit kan leiden tot resourcelekken als iemand vergeet de resources op de juiste manier te sluiten. We kunnen de klasse org.springframework.jdbc.core.JdbcTemplate gebruiken om deze fouten te voorkomen. Spring JdbcTemplate is de centrale klasse in het Spring JDBC-corepakket en biedt veel methoden om query’s uit te voeren en ResultSet automatisch te parsen om het Object of de lijst met Objecten te krijgen. Het enige wat we hoeven te doen is de argumenten als Object-array te verstrekken en Callback-interfaces zoals PreparedStatementSetter en RowMapper te implementeren voor het mappen van argumenten of het converteren van ResultSet-gegevens naar bean-objecten. Laten we eens kijken naar een andere implementatie van EmployeeDAO waar we de Spring JdbcTemplate-klasse zullen gebruiken om verschillende soorten query’s uit te voeren.

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);
		
		// door een anonieme RowMapper-klasse te gebruiken, kunnen we een aparte RowMapper maken voor hergebruik
		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;
	}

}

Belangrijke punten om naar te kijken in de bovenstaande code voor Spring JdbcTemplate zijn:

  • Gebruik van Object-array om PreparedStatement-argumenten door te geven, we zouden ook een implementatie van PreparedStatementSetter kunnen gebruiken, maar het doorgeven van Object-array lijkt gemakkelijker in gebruik.
  • Geen code met betrekking tot het openen en sluiten van verbindingen, statements of resultset. Dat wordt allemaal intern afgehandeld door de Spring JdbcTemplate-klasse.
  • Implementatie van een anonieme RowMapper-klasse om de ResultSet-gegevens naar een Employee bean-object te mappen in de methode queryForObject().
  • queryForList()-methode retourneert een lijst van Map waar Map de rijgegevens bevat die zijn gekoppeld met de sleutel als de kolomnaam en de waarde uit de database rij die overeenkomt met de criteria.

Om de Spring JdbcTemplate-implementatie te gebruiken, hoeven we alleen de employeeDAO-klasse in het Spring Bean-configuratiebestand te wijzigen zoals hieronder wordt getoond.

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

Wanneer je de hoofdklasse uitvoert, zal de uitvoer van de Spring JdbcTemplate-implementatie vergelijkbaar zijn met die van de normale JDBC-implementatie. Dat is alles voor de Spring JDBC voorbeeldzelfstudie, download het voorbeeldproject via onderstaande link en experimenteer ermee om meer te leren.

Download Spring JDBC Project

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