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" />
<!--'&'字符转义: & -->
<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/learning?autoReconnect=true&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&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&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&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 {
//...
}
执行测试:
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);
}
}