点击上面“天码营”,加入我们,快速成长~
「内容简介」数据库操作是我们搭建应用的基本操作,今天我们来学习一下如何利用Spring JDBC访问关系型数据库吧。
目标
使用Spring JDBC访问关系型数据库,在Java代码中执行数据库增删改查操作
环境准备
一个称手的文本编辑器(例如Vim、Emacs、Sublime Text)或者IDE(Eclipse、Idea Intellij)
Java环境(JDK 1.7或以上版本)
Maven 3.0+(Eclipse和Idea IntelliJ内置,如果使用IDE并且不使用命令行工具可以不安装)
MySQL关系型数据库
JDBC
package main;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
public class Mysql {
/**
* 入口函数
* @param arg
*/
public static void main(String arg[]) {
try {
Connection con = null; //定义一个MYSQL链接对象
Class.forName("com.mysql.jdbc.Driver").newInstance(); //MYSQL驱动
con = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test", "root", "root"); //链接本地MYSQL
Statement stmt; //创建声明
stmt = con.createStatement();
//新增一条数据
stmt.executeUpdate("INSERT INTO user (username, password) VALUES ('init', '123456')");
ResultSet res = stmt.executeQuery("select LAST_INSERT_ID()");
int ret_id;
if (res.next()) {
ret_id = res.getInt(1);
System.out.print(ret_id);
}
} catch (Exception e) {
System.out.print("MYSQL ERROR:" + e.getMessage());
}
}
}这是一段非常经典的使用JDBC访问MySQL数据库的代码,它的作用是向数据库user
表中增加一条记录('init', '123456')
并获取自增id。其中的步骤可以抽象为:
定义连接参数(包括动态加载驱动类
com.mysql.jdbc.Driver
)建立数据库连接
指定Sql语句并参数化
执行Sql语句
获取查询结果并处理
处理异常
事务管理
释放各类资源——
Statement
,ResultSet
,Connection
对于上述步骤,实际上很多部分都是通用的——即对于每一次数据库访问都没有变化,例如定义参数、打开连接、处理异常、事务处理、资源释放。完全没有必要再每一次访问里都编写这些代码,一种办法是将这些代码封装起来。Spring JDBC正是提供了这样一种封装,它将与JDBC API交互的诸多细节隐藏起来,通过Spring JDBC开发者能够更加专注于业务代码(建立并执行Sql语句,处理查询结果等)的开发。
JdbcTemplate
上一节中提到的JDBC访问数据库的步骤,在Spring JDBC中被抽象为JdbcTemplate
,这是Spring JDBC中最核心的类。以下是一些JdbcTemplate
的常用方法:
获取Table中记录数量
int rowCount = this.jdbcTemplate.queryForObject("select count(*) from t_actor", Integer.class);SQL语句参数化
int countOfActorsNamedJoe = this.jdbcTemplate.queryForObject(
"select count(*) from t_actor where first_name = ?", Integer.class, "Joe");其中Sql语句中需要被参数化的对象用?
作为占位符替代,可以指定多个占位符,这个方法最后的参数也是变长的。
获取String对象
String lastName = this.jdbcTemplate.queryForObject(
"select last_name from t_actor where id = ?",
new Object[]{1212L}, String.class);获取业务对象
通常查询结果不是一个简单的基本类型(Integer
, String
),而是我们自定义的业务对象,例如:
public class Actor {
private String firstName;
private String lastName;
public Actor(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
//Getter/Setter略
}这时不能直接在queryForObject()
方法中直接指定参数Actor.class
,因为JdbcTempate
无法知道应该如何将ResultSet
转化为User
对象,所以这时需要实现转换的方法:
List<Actor> actors = this.jdbcTemplate.query(
"select first_name, last_name from t_actor",
new RowMapper<Actor>() {
public Actor mapRow(ResultSet rs, int rowNum) throws SQLException {
return new Actor(rs.getString("first_name"), rs.getString("last_name"));
}
});接口RowMapper<T>
就是实现转换的一个回调接口。在Java 8中可以使用Lambda表达式简化:
List<Actor> actors = this.jdbcTemplate.query(
"select first_name, last_name from t_actor",
(rs, num) -> new Actor(rs.getString("first_name"), rs.getString("last_name"));可以想象一下,如果上述功能用JDBC API来实现,需要编写多少代码。
JdbcTemplate的增加/删除/更新方法
上面提到的都是查询方法,而对于数据库的写操作相对于查询来说,要简单很多,在写操作中,使用?
参数化也是常用的手段:
this.jdbcTemplate.update(
"insert into t_actor (first_name, last_name) values (?, ?)",
"Leonor", "Watling");this.jdbcTemplate.update(
"update t_actor set last_name = ? where id = ?",
"Banjo", 5276L);this.jdbcTemplate.update(
"delete from actor where id = ?",
Long.valueOf(actorId));JdbcTemplate其他操作
JdbcTemplate.execute(..)
方法可以执行任何Sql语句,例如创建Table:
this.jdbcTemplate.execute("create table mytable (id integer, name varchar(100))");调用存储过程:
this.jdbcTemplate.update(
"call SUPPORT.REFRESH_ACTORS_SUMMARY(?)",
Long.valueOf(unionId));在Spring中使用JdbcTemplate
JdbcTemplate
的使用需要注入一个DataSource
对象来管理数据库连接,DataSource
对象可以这样定义:
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>在代码中获取DataSource
对象后,就可以直接创建JdbcTemplate
,以@Autowired
为例:
private JdbcTemplate jdbcTemplate;
@Autowired
public void setDataSource(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}提示
JdbcTemplate
是线程安全的,所以在Spring应用上下文中只要创建一个对象即可。
结合Spring Boot快速开发应用
在传统的Spring应用开发中,如果想要使用Spring JDBC
,还是需要在XML或者Java Config中定义相应的Bean。Spring Boot框架的出现,大大简化了繁琐的配置,只需要简单的引入依赖,就可以获得开箱即用(out-of-the-box)的功能。
引入Maven依赖
在pom.xml
中定义Spring Boot依赖:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.5.RELEASE</version>
<relativePath/>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>Application类
@SpringBootApplication
public class SpringJdbcDataAccessingApplication implements CommandLineRunner {
private static final Logger logger = LoggerFactory.getLogger(SpringJdbcDataAccessingApplication.class);
@Autowired
private JdbcTemplate jdbcTemplate;
public static void main(String[] args) {
SpringApplication.run(SpringJdbcDataAccessingApplication.class, args);
}
}Spring Boot应用启动后,它一旦发现spring-jdbc
以及内存数据库h2database
在类路径上,就会在Spring上下文中创建响应的DataSource
、JdbcTemplate
对象,所以在上述代码中使用@Autowired
注解注入JdbcTemplate
是正确的,即时我们没有手动创建它。
初始化工作
使用内存数据库仅仅用作开发和测试,在应用启动时数据库是空的,如果希望此时能够完成一些初始化工作,可以:
@SpringBootApplication
public class SpringJdbcDataAccessingApplication implements CommandLineRunner {
@Autowired
private JdbcTemplate jdbcTemplate;
@Override
public void run(String... strings) throws Exception {
jdbcTemplate.execute("DROP TABLE users IF EXISTS");
jdbcTemplate.execute("CREATE TABLE users(" +
"id SERIAL, first_name VARCHAR(255), last_name VARCHAR(255))");
}
}实现CommandLineRunner
接口的类在Spring Boot启动时都会调用其回调接口run(String... args)
方法。
操作数据库
接下来操作数据就非常简单了,为了方便我们还是在CommandLineRunner
的run()
方法中进行:
@Override
public void run(String... strings) throws Exception {
logger.info("Creating tables");
jdbcTemplate.execute("DROP TABLE users IF EXISTS");
jdbcTemplate.execute("CREATE TABLE users(" +
"id SERIAL, first_name VARCHAR(255), last_name VARCHAR(255))");
// Split up the array of whole names into an array of first/last names
List<Object[]> splitUpNames = Arrays.asList("Zhang San", "Li Si", "Wang Wu").stream()
.map(name -> name.split(" "))
.collect(Collectors.toList());
logger.info("Insert data into tables");
jdbcTemplate.batchUpdate("INSERT INTO users(last_name, first_name) VALUES (?,?)", splitUpNames);
logger.info("query data");
jdbcTemplate.query("SELECT id, first_name, last_name FROM users WHERE first_name = ?",
new Object[]{"San"},
(rs, rowNum) -> new User(rs.getLong("id"), rs.getString("first_name"), rs.getString("last_name"))
).forEach(u -> logger.info("{}", u));
}上述方法中创建了数据库表,添加了三条数据,并根据条件对数据进行查询。以上只是一个简单的例子,如果需要实现更多业务功能,可以在Spring Bean中注入JdbcTemplate
。
使用MySQL
数据库的一个最重要的功能是持久化,所以在真正的应用环境里,我们必须要使用MySQL这一类数据库进行数据存储。但是,在内存数据库的例子中,没有任何代码定义了数据库地址,Spring Boot就直接使用了H2Database
。如果需要使用MySQL呢?
Spring Boot在初始化DataSource
对象时,会根据外部配置来进行。如果我们不进行任何配置,那么它会使用H2Database
的JDBC驱动以及连接地址。如果我们需要使用其它类型的数据库,编辑src/main/resources/application.properties
:
spring.datasource.url=jdbc:mysql://localhost/test spring.datasource.username=dbuser spring.datasource.password=dbpass spring.datasource.driver-class-name=com.mysql.jdbc.Driver
这是一个样例文件,需要根据自己的实际情况将几个参数重写。当然也不要忘了在Maven依赖中加入mysql-jdbc
驱动:
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>
这样Spring Boot应用启动后,会根据spring.datasource.*
属性创建一个我们需要的DataSource
对象,这样就能够对MySQL数据库进行操作了。本质上它和我们在XML和Java Config文件中自己顶一个DataSource
的Bean是完全一样的,但是这样更加简洁、高效。


点击下方“阅读原文”,可以获得更多天码营教程。





