Spring JDBC教程:Java数据库连接与操作示例详解

本教程的主题是Spring JDBC。数据库是大多数企业应用程序的重要组成部分。因此,在涉及Java EE框架时,与JDBC有良好的集成非常重要。

Spring是Java开发人员常用的JDBC框架之一

Spring Framework提供了与JDBC API的出色集成,并提供了JdbcTemplate实用类,我们可以使用它来避免数据库操作逻辑中的样板代码,如打开/关闭连接、结果集、预编译语句等。让我们首先看一个简单的Spring JDBC示例应用程序,然后我们将看到JdbcTemplate类如何帮助我们轻松编写模块化代码,无需担心资源是否正确关闭。使用Spring Tool Suite开发基于Spring的应用程序非常有帮助,因此我们将使用STS来创建我们的Spring JDBC应用程序。我们最终的项目结构将如下图所示。从STS菜单中创建一个简单的Spring Maven项目,您可以选择任何您喜欢的名称,或者坚持使用我的项目名称SpringJDBCExample。

Spring JDBC依赖

首先,我们需要在Maven项目的pom.xml文件中包含Spring JDBC和数据库驱动程序。最后,我的pom.xml文件如下所示。

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

大部分部分是由STS自动生成的,然而我已经将Spring Framework版本更新为最新的4.0.2.RELEASE。另外,我们还添加了所需的artifacts spring-jdbc和mysql-connector-java。第一个包含了Spring JDBC支持类,而第二个是数据库驱动程序。我正在使用MySQL数据库进行测试,因此我已经添加了MySQL JConnector jar依赖。如果您使用其他RDBMS,则应在依赖项中进行相应的更改。

Spring JDBC示例 – 数据库设置

Spring JDBC示例 – 数据库配置

让我们创建一个简单的表格,在我们的应用程序中用于展示增删改查操作的示例。

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;

Spring JDBC示例 – 模型类

我们将使用DAO模式进行JDBC操作,所以让我们创建一个Java Bean来建模我们的Employee表格。

package com.Olivia.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+"}";
	}
}

Spring JDBC示例 – DAO接口和实现

对于DAO模式,我们首先需要一个声明我们想要实现的所有操作的接口。

这是文章《Spring JDBC示例》的第2部分(共4部分)。

package com.Olivia.spring.jdbc.dao;

import java.util.List;

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

//CRUD operations
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);
	//Get All
	public List<Employee> getAll();
}
package com.Olivia.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.Olivia.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;
	}

}

对于实现CRUD操作非常简单易懂。如果你想了解更多关于数据源的内容,请阅读JDBC数据源示例。

Spring JDBC示例-Bean配置

如果你查看上述所有代码,它们都使用了标准的JDBC API,没有涉及到Spring JDBC框架。当我们创建Spring Bean配置文件并定义bean时,就会涉及到Spring JDBC框架的类。我们会在Spring Bean上下文文件中创建DataSource,并将其设置为我们的DAO实现类。我的Spring Bean配置文件如下所示。

这是文章《Spring JDBC示例》的第3部分(共4部分)。

<?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.Olivia.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="scdev" />
		<property name="password" value="scdev123" />
	</bean>

</beans>

首先,我们正在创建一个DriverManagerDataSource类的DataSource对象。该类提供了我们可以使用的DataSource的基本实现。我们将MySQL数据库的URL、用户名和密码作为属性传递给DataSource bean。再次,将dataSource bean设置为EmployeeDAOImpl bean,并准备好了我们的Spring JDBC实现。该实现是松耦合的,如果我们想要切换到其他实现或迁移到其他数据库服务器,只需要在bean配置中进行相应的更改即可。这是Spring JDBC框架提供的主要优势之一。

Spring JDBC测试类

让我们编写一个简单的测试类来确保一切都正常工作。

package com.Olivia.spring.jdbc.main;

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

import org.springframework.context.support.ClassPathXmlApplicationContext;

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

public class SpringMain {

	public static void main(String[] args) {
		//获取Spring上下文
		ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
		
		//获取EmployeeDAO Bean
		EmployeeDAO employeeDAO = ctx.getBean("employeeDAO", EmployeeDAO.class);
		
		//运行一些JDBC CRUD操作测试
		Employee emp = new Employee();
		int rand = new Random().nextInt(1000);
		emp.setId(rand);
		emp.setName("Pankaj");
		emp.setRole("Java Developer");
		
		//创建
		employeeDAO.save(emp);
		
		//读取
		Employee emp1 = employeeDAO.getById(rand);
		System.out.println("检索到的员工::"+emp1);
		
		//更新
		emp.setRole("CEO");
		employeeDAO.update(emp);
		
		//获取所有
		List<Employee> empList = employeeDAO.getAll();
		System.out.println(empList);
		
		//删除
		employeeDAO.deleteById(rand);
		
		//关闭Spring上下文
		ctx.close();
		
		System.out.println("完成");
	}

}

我正在使用Random类来生成员工ID的随机数。当我们运行上述程序时,我们得到以下输出结果。

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
员工保存,ID=726
找到员工::{ID=726,Name=Pankaj,Role=Java Developer}
检索到的员工::{ID=726,Name=Pankaj,Role=Java Developer}
员工更新,ID=726
[{ID=726,Name=Pankaj,Role=CEO}]
员工删除,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
完成

Spring JdbcTemplate 示例

如果你看DAO实现类,有很多模板代码,在这些代码中我们打开和关闭Connection、PreparedStatements和ResultSet。如果有人忘记正确关闭资源,这可能导致资源泄漏。我们可以使用org.springframework.jdbc.core.JdbcTemplate类来避免这些错误。Spring JdbcTemplate是Spring JDBC核心包中的中心类,提供了许多执行查询和自动解析ResultSet以获取对象或对象列表的方法。我们所需要的只是提供对象数组作为参数,并实现回调接口,如PreparedStatementSetter和RowMapper,用于映射参数或将ResultSet数据转换为bean对象。让我们看另一个EmployeeDAO的实现,我们将使用Spring JdbcTemplate类来执行不同类型的查询。

这是文章《春季JDBC示例》的第4部分(共4部分)。

package com.Olivia.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.Olivia.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);
		
		//using RowMapper anonymous class, we can create a separate RowMapper for reuse
		Employee emp = jdbcTemplate.queryForObject(query, new Object[]{id}, new RowMapper<Employee>(){

			@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<Employee> getAll() {
		String query = "select id, name, role from Employee";
		JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
		List<Employee> empList = new ArrayList<Employee>();

		List<Map<String,Object>> empRows = jdbcTemplate.queryForList(query);
		
		for(Map<String,Object> 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;
	}

}

对于Spring JdbcTemplate的代码,需要注意的重点有:

  • 使用对象数组传递PreparedStatement参数,我们也可以使用PreparedStatementSetter实现,但传递对象数组似乎更易于使用。
  • 没有关于打开和关闭连接、语句或结果集的代码。所有这些都由Spring JdbcTemplate类内部处理。
  • 在queryForObject()方法中使用RowMapper匿名类实现将ResultSet数据映射到Employee bean对象。
  • queryForList()方法返回Map列表,其中Map包含以列名为键、以符合条件的数据库行值为值的行数据映射。

要使用Spring JdbcTemplate实现,我们只需要如下所示,在Spring Bean配置文件中更改employeeDAO类。

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

当您运行主类时,Spring JdbcTemplate 实现的输出将与上面看到的普通 JDBC 实现类似。这就是关于 Spring JDBC 示例教程的全部内容,从下面的链接下载示例项目并进行尝试,以便学到更多知识。

下载Spring JDBC项目

bannerAds