Spring基础入门6 - Spring JDBC

JDBC(Java DataBase Connectivity)是一套Java访问关系型数据库的标准, JDBC只定义接口,具体实现由各个厂商(框架)负责。 Spring使用的是javax.sql.DataSource接口获取数据库连接, DataSource代表一个数据源(获取数据库工厂),相较于直接通过java.sql.Driver获取数据库连接(通常是通过DriverManager根据SPI规范查找Drive实现, 并调用匹配的Driver创建连接),DataSource最大的优势就是可以实现连接池。

开始之前我们先创建一个mysql的测试库:

create database learning;
use learning;
create table account (
    acct_id int           not null auto_increment,
    name    varchar(16)   not null,
	balance decimal(14,2) not null default 0.0,
    primary key (acct_id)
);
create user 'learning'@'%' identified by 'mypassword';
grant all on learning.* to 'learning'@'%';
show grants for 'learning'@'%';

insert into account(name, balance) values('Tuzki', 20000.00);
insert into account(name, balance) values('Judy', 40000.00);
insert into account(name, balance) values('Bugs', 13400.00);

1. 创建数据源(DataSource) Bean

DataSource只是一个接口,我们配置一个数据源Bean,需要指定具体实现,目前常用的连接池有
HikariCP, Druid, Apache Commons DBCP2, C3P0, … 我们先引入相关依赖:

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<maven.compiler.source>11</maven.compiler.source>
		<maven.compiler.target>11</maven.compiler.target>
	</properties>
	<dependencies>
		<!-- HikariCP dependency -->	
		<dependency>
			<groupId>com.zaxxer</groupId>
			<artifactId>HikariCP</artifactId>
			<version>5.0.1</version> <!-- 需要JDK 11+版本 -->
		</dependency>
		
		<!-- Alibaba Druid dependency -->
		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>1.2.8</version>
		</dependency>
		
		<!-- Apache DBCP2 dependency -->
		<dependency>
			<groupId>org.apache.commons</groupId>
			<artifactId>commons-dbcp2</artifactId>
			<version>2.9.0</version>
		</dependency>

		<!-- C3P0 dependency -->
		<dependency>
			<groupId>com.mchange</groupId>
			<artifactId>c3p0</artifactId>
			<version>0.9.5.5</version>
		</dependency>
		
		<!-- MySQL JDBC driver -->
		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>8.0.28</version>
		</dependency>
		...
	</dependencies>

1.1. 通过xml文件配置DataSource

每种连接池的定义的属性名不相同,但含义是相近的,可以自行在网上找。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
	...
	<!--  <context:property-placeholder location="datasource.properties"/> -->
	<bean id="hikariDataSource" class="com.zaxxer.hikari.HikariDataSource" destroy-method="close">
		<property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
		<!--'&'字符转义: &amp; -->
		<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/learning?autoReconnect=true&amp;useUnicode=true" />
		<property name="username" value="learning" />
		<property name="password" value="mypassword" />
		<property name="connectionTestQuery" value="SELECT 1" />
		<property name="maximumPoolSize" value="10" />
	</bean>

	<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
		<property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
		<property name="url" value="jdbc:mysql://127.0.0.1:3306/learning?autoReconnect=true&amp;useUnicode=true" />
		<property name="username" value="learning" />
		<property name="password" value="mypassword" />
		<property name="validationQuery" value="SELECT 1" />
		<property name="maxActive" value="10" />
	</bean>

	<bean id="dbcp2DataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
		<property name="driverClassName" value="com.mysql.cj.jdbc.Driver" />
		<property name="url" value="jdbc:mysql://127.0.0.1:3306/learning?autoReconnect=true&amp;useUnicode=true" />
		<property name="username" value="learning" />
		<property name="password" value="mypassword" />
		<property name="validationQuery" value="SELECT 1" />
		<property name="maxTotal" value="10" />
	</bean>

	<bean id="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
		<property name="driverClass" value="com.mysql.cj.jdbc.Driver" />
		<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/learning?autoReconnect=true&amp;useUnicode=true" />
		<property name="user" value="learning" />
		<property name="password" value="mypassword" />
		<property name="preferredTestQuery" value="SELECT 1" />
		<property name="maxPoolSize" value="10" />
	</bean>
</beans>

附上测试使用代码:

package org.littlestar.learning.package7;

import java.sql.Connection;
import javax.sql.DataSource;

import org.junit.Assume;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class) //SpringJUnit4ClassRunner requires JUnit 4.12 or higher.
@ContextConfiguration(locations={"classpath:org/littlestar/learning/package6/package6-bean-config.xml"})
public class SpringJdbcTest {
	
	@Autowired
	@Qualifier("hikariDataSource")
	DataSource hikariDataSource;
	@Test
	public void testHikariDataSource() throws Exception {
		try(Connection connection =  hikariDataSource.getConnection()){
			Assume.assumeTrue(connection.isValid(60));
		}
	}
	
	@Autowired
	@Qualifier("druidDataSource")
	DataSource druidDataSource;
	@Test
	public void testDruidDataSource() throws Exception {
		try(Connection connection =  druidDataSource.getConnection()){
			Assume.assumeTrue(connection.isValid(60));
		}
	}
	
	@Autowired
	@Qualifier("dbcp2DataSource")
	DataSource dbcp2DataSource;
	@Test
	public void testDbcp2DataSource() throws Exception {
		try(Connection connection =  dbcp2DataSource.getConnection()){
			Assume.assumeTrue(connection.isValid(60));
		}
	}
	
	@Autowired
	@Qualifier("c3p0DataSource")
	DataSource c3p0DataSource;
	@Test
	public void testc3p0DataSource() throws Exception {
		try(Connection connection =  c3p0DataSource.getConnection()){
			Assume.assumeTrue(connection.isValid(60));
		}
	}
}

1.2. 通过配置类配置DataSource

也可通过Spring配置类进行配置:

package org.littlestar.learning.package6;

import javax.sql.DataSource;

import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import com.alibaba.druid.pool.DruidDataSource;
import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.zaxxer.hikari.HikariDataSource;

@Configuration
@PropertySource({"classpath:org/littlestar/learning/package6/datasource.properties"})
public class DataSourceConfig {
	@Value("${spring.jdbc.driverClassName}")
	private String driverClassName;
	
	@Value("${spring.jdbc.url}")
	private String url;

	@Value("${spring.jdbc.username}")
	private String username;

	@Value("${spring.jdbc.password}")
	private String password;
	
	@Value("${spring.jdbc.connectionTestQuery}")
	private String connectionTestQuery;
	
	@Value("${spring.jdbc.maxPoolSize}")
	private int maxPoolSize;
	
	@Bean(name = "hikariDataSource", destroyMethod="close")
	public DataSource hikariDataSource() {
		HikariDataSource dataSource = new HikariDataSource();
		dataSource.setDriverClassName(driverClassName);
		dataSource.setJdbcUrl(url);
		dataSource.setUsername(username);
		dataSource.setPassword(password);
		dataSource.setConnectionTestQuery(connectionTestQuery);
		dataSource.setMaximumPoolSize(maxPoolSize);
		return dataSource;
	}
	
	@Bean(name = "druidDataSource", destroyMethod="close")
	public DataSource druidDataSource() {
		DruidDataSource dataSource = new DruidDataSource();
		dataSource.setDriverClassName(driverClassName);
		dataSource.setUrl(url);
		dataSource.setUsername(username);
		dataSource.setPassword(password);
		dataSource.setValidationQuery(connectionTestQuery);
		dataSource.setMaxActive(maxPoolSize);
		return dataSource;
	}
	
	@Bean(name = "dbcp2DataSource", destroyMethod="close")
	public DataSource dbcp2DataSource() {
		BasicDataSource dataSource = new BasicDataSource();
		dataSource.setDriverClassName(driverClassName);
		dataSource.setUrl(url);
		dataSource.setUsername(username);
		dataSource.setPassword(password);
		dataSource.setValidationQuery(connectionTestQuery);
		dataSource.setMaxTotal(maxPoolSize);
		return dataSource;
	}
	
	@Bean(name = "c3p0DataSource", destroyMethod="close")
	public DataSource c3p0DataSource() throws Exception {
		ComboPooledDataSource dataSource = new ComboPooledDataSource();
		dataSource.setDriverClass(driverClassName);
		dataSource.setJdbcUrl(url);
		dataSource.setUser(username);
		dataSource.setPassword(password);
		dataSource.setPreferredTestQuery(connectionTestQuery);
		dataSource.setMaxPoolSize(maxPoolSize);
		return dataSource;
	}
}

对应的配置文件:org/littlestar/learning/package6/datasource.properties

spring.jdbc.driverClassName=com.mysql.cj.jdbc.Driver
spring.jdbc.url=jdbc:mysql://127.0.0.1:3306/learning?autoReconnect=true&useUnicode=true
spring.jdbc.username=learning
spring.jdbc.password=mypassword
spring.jdbc.connectionTestQuery=select 1
spring.jdbc.maxPoolSize=10

测试类只需指定配置类即可:

package org.littlestar.learning.package7;
...
@RunWith(SpringRunner.class) //SpringJUnit4ClassRunner requires JUnit 4.12 or higher.
//@ContextConfiguration(locations={"classpath:org/littlestar/learning/package6/package6-bean-config.xml"})
@ContextConfiguration(classes = DataSourceConfig.class) // --> 指向我们的配置类即可
public class SpringJdbcTest {
	//...
}

执行测试:
Spring基础入门6 - Spring JDBC

2. 使用JdbcTemplate

JdbcTemplate是Spring对JDBC的封装,类似于DBUtils。JdbcTemplate包含在spring-jdbc中,需要先引入依赖:

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-jdbc</artifactId>
			<version>5.3.15</version>
		</dependency>

通过xml配置JdbcTemplate Bean:

	...
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<constructor-arg name="dataSource" ref="hikariDataSource" />
	</bean>
	...

或者通过配置类配置JdbcTemplate Bean:

@RunWith(SpringRunner.class) //SpringJUnit4ClassRunner requires JUnit 4.12 or higher.
//@ContextConfiguration(locations={"classpath:org/littlestar/learning/package6/package6-bean-config.xml"})
@ContextConfiguration(classes = DataSourceConfig.class)
public class SpringJdbcTest {
	...
	@Autowired
	@Qualifier("hikariDataSource")
	DataSource jdbcTemplateDataSource;
	@Bean
	public JdbcTemplate jdbcTemplate() {
		return new JdbcTemplate(jdbcTemplateDataSource);
	}
	...

使用JdbcTemplate操作数据库:

	@Autowired
	JdbcTemplate jdbcTemplate;
	// Query
	@Test 
	public void jdbcTemplate_query() {
		List<Account> accounts = jdbcTemplate.query("select * from learning.account", 
				new RowMapper<Account>() {
			@Override
			public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
				Account account = new Account();
				account.setId(rs.getLong("acct_id"));
				account.setName(rs.getString("name"));
				account.setBalance(rs.getDouble("balance"));
				return account;
			}
		});
		
		for(Account account : accounts) {
			System.out.println(account.getName());
		}
	}
	
	// Update 
	@Test 
	public void jdbcTemplate_update() {
		int row = jdbcTemplate.update("update account set name=? where acct_id=?", new Object[] { "rubbit", 1 });
		Assume.assumeTrue(row == 1);
	}
	
	// Delete
	@Test
	public void jdbcTemplate_delete() {
		int row = jdbcTemplate.update("delete from account where acct_id=?", 1);
		Assume.assumeTrue(row == 1);
	}
	
	// Insert
	@Test
	public void jdbcTemplate_insert() {
		int row = jdbcTemplate.update("insert into account(acct_id, name, balance) values (?, ?, ?)", 1, "Tuzki", 20000.00);
		Assume.assumeTrue(row == 1);
	}
}
上一篇:短视频平台源码,Spring配置数据源的三种方式


下一篇:Spring框架之事务