Spring入门(十五):使用Spring JDBC操作数据库
在本系列的之前博客中,我们从没有讲解过操作数据库的方法,但是在实际的工作中,几乎所有的系统都离不开数据的持久化,所以掌握操作数据库的使用方法就非常重要。
在Spring中,操作数据库有很多种方法,我们可以使用JDBC、Hibernate、MyBatis或者其他的数据持久化框架,本篇博客的重点是讲解下在Spring中如何通过JDBC操作数据库。
1. 项目构建失败解决
在讲解JDBC前,我们先解决一个问题,因为本来构建正常的程序在重新构建打包时,竟然报了如下错误:
网上查找资料后,说是依赖的版本有冲突,于是检查了pom.xml中之前添加的Spring的依赖:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<!--spring aop支持-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.18.RELEASE</version>
<scope>test</scope>
</dependency>
其中spring-aop的版本是5.1.8.RELEASE,而其余3个包的版本是4.3.18.RELEASE,将spring-aop版本也修改为4.3.18.RELEASE:
<!--spring aop支持-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
此时重新构建打包,不再报错,打包成功:
不过上面的依赖还可以简化成下面这样的:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.18.RELEASE</version>
<scope>test</scope>
</dependency>
因为spring-webmvc包已经包含了spring-context和spring-aop,因此没有必要重复添加这2个依赖:
2. 配置数据源
首先执行如下语句创建MySql数据库spring_action_db:
CREATE DATABASE spring_action_db DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
然后执行如下语句创建表book:
use spring_action_db;
create table Book
(
book_id bigint auto_increment comment '书籍id',
book_name varchar(50) not null comment '书名',
author varchar(50) not null comment '作者',
create_by varchar(20) not null comment '创建人',
create_time datetime not null comment '创建时间',
modify_by varchar(20) not null comment '修改人',
modify_time datetime not null comment '修改时间',
constraint Book_pk
primary key (book_id)
)
comment '书籍';
准备就绪后,新建配置类配置下数据源:
package chapter10.config;
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("chapter10")
public class DataSourceConfig {
@Bean
public BasicDataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/spring_action_db");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
}
因为我们使用的是MySql数据库,所以驱动名称设置的是:com.mysql.jdbc.Driver。
如果你使用的是其他类型的数据库,需要修改成对应的名称。
因为使用到了MySql驱动,所以我们需要在pom.xml中添加如下依赖,否则在访问数据库时会获取不到连接:
<!-- MySql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
3. 使用原始的JDBC代码
首先,新建数据库实体类Book:
package chapter10.domain;
import java.util.Date;
public class Book {
private Long bookId;
private String bookName;
private String author;
private String createBy;
private Date createTime;
private String modifyBy;
private Date modifyTime;
public Book(String bookName, String author, String createBy) {
this.bookName = bookName;
this.author = author;
this.createBy = createBy;
this.createTime = new Date();
this.modifyBy=createBy;
this.modifyTime=new Date();
}
public Book(Long bookId, String bookName, String author, String modifyBy) {
this.bookId = bookId;
this.bookName = bookName;
this.author = author;
this.modifyBy = modifyBy;
}
public Book() {
}
// 省略get和set方法
}
然后定义数据访问接口BookRepository,暂时只添加addBook方法:
package chapter10.db;
import chapter10.domain.Book;
public interface BookRepository {
void addBook(Book book);
}
3.1 新增数据
新建数据访问实现类JdbcBookRepository如下所示:
package chapter10.db.jdbc;
import chapter10.db.BookRepository;
import chapter10.domain.Book;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Date;
@Repository
public class JdbcBookRepository implements BookRepository {
private static final String SQL_INSERT_BOOK =
"INSERT INTO book(book_name, author, create_by, create_time, modify_by, modify_time) VALUES (?,?,?,?,?,?);";
@Autowired
private DataSource dataSource;
@Override
public void addBook(Book book) {
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
connection = dataSource.getConnection();
preparedStatement = connection.prepareStatement(SQL_INSERT_BOOK);
preparedStatement.setString(1, book.getBookName());
preparedStatement.setString(2, book.getAuthor());
preparedStatement.setString(3, book.getCreateBy());
preparedStatement.setTimestamp(4, new Timestamp(calendar.getTimeInMillis()));
preparedStatement.setString(5, book.getModifyBy());
preparedStatement.setTimestamp(6, new Timestamp(calendar.getTimeInMillis()));
preparedStatement.execute();
} catch (SQLException e) {
// 异常处理相关代码
} finally {
try {
if (preparedStatement != null) {
preparedStatement.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
// 异常处理相关代码
}
}
}
}
注意事项:该类添加了@Repository注解,以便Spring能够扫描到将其注册为bean。
值得注意的是,在这段代码中,我们竟然捕获SQLException捕获了2次,这是因为connection = dataSource.getConnection();
,preparedStatement.execute();
,preparedStatement.close();
,connection.close();
都会抛出检查型异常SQLException,所以方法中必须捕获,否则会导致编译不通过:
Connection getConnection() throws SQLException;
boolean execute() throws SQLException;
void close() throws SQLException;
void close() throws SQLException;
最后,新建单元测试类BookRepositoryTest如下所示:
package chapter10;
import chapter10.config.DataSourceConfig;
import chapter10.db.BookRepository;
import chapter10.domain.Book;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = DataSourceConfig.class)
public class BookRepositoryTest {
@Autowired
private BookRepository bookRepository;
@Test
public void testAddBook() {
Book book = new Book("Spring实战(第4版)", "Craig Walls", "申城异乡人");
bookRepository.addBook(book);
book = new Book("Java EE开发的颠覆者:Spring Boot实战", "汪云飞", "申城异乡人");
bookRepository.addBook(book);
book = new Book("RabbitMQ实战指南", "朱忠华", "申城异乡人");
bookRepository.addBook(book);
}
}
运行测试方法testAddBook(),数据成功新增到数据库:
3.2 更新数据
首先,在数据访问接口BookRepository中添加更新方法:
void updateBook(Book book);
然后在数据访问实现类JdbcBookRepository中实现该方法:
private static final String SQL_UPDATE_BOOK =
"UPDATE Book SET book_name = ?,author = ?,modify_by = ?,modify_time=? WHERE book_id = ?;";
@Override
public void updateBook(Book book) {
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
connection = dataSource.getConnection();
preparedStatement = connection.prepareStatement(SQL_UPDATE_BOOK);
preparedStatement.setString(1, book.getBookName());
preparedStatement.setString(2, book.getAuthor());
preparedStatement.setString(3, book.getModifyBy());
preparedStatement.setTimestamp(4, new Timestamp(calendar.getTimeInMillis()));
preparedStatement.setLong(5, book.getBookId());
preparedStatement.execute();
} catch (SQLException e) {
// 异常处理相关代码
} finally {
try {
if (preparedStatement != null) {
preparedStatement.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
// 异常处理相关代码
}
}
}
是不是发现它的代码和之前的新增代码几乎是一样的,而且也不得不对检查型异常SQLException捕获了2次,有代码洁癖的人是不是忍不住想重构,哈哈。
最后,在测试类BookRepositoryTest中添加测试方法testUpdateBook,如下所示:
@Test
public void testUpdateBook() {
Book book = new Book(1L, "Spring实战(第4版)", "Craig Walls", "zwwhnly");
bookRepository.updateBook(book);
book = new Book(2L, "Java EE开发的颠覆者:Spring Boot实战", "汪云飞", "zwwhnly");
bookRepository.updateBook(book);
book = new Book(3L, "RabbitMQ实战指南", "朱忠华", "zwwhnly");
bookRepository.updateBook(book);
}
执行该测试方法,数据更新成功:
3.3 查找数据
首先,在数据访问接口BookRepository中添加更新方法:
Book findBook(long bookId);
然后在数据访问实现类JdbcBookRepository中实现该方法:
private static final String SQL_SELECT_BOOK =
"SELECT book_id,book_name,author,create_by,create_time,modify_by,modify_time FROM book WHERE book_id = ?;";
@Override
public Book findBook(long bookId) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
Book book = null;
try {
connection = dataSource.getConnection();
preparedStatement = connection.prepareStatement(SQL_SELECT_BOOK);
preparedStatement.setLong(1, bookId);
resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
book = new Book();
book.setBookId(resultSet.getLong("book_id"));
book.setBookName(resultSet.getString("book_name"));
book.setAuthor(resultSet.getString("author"));
book.setCreateBy(resultSet.getString("create_by"));
book.setCreateTime(resultSet.getTimestamp("create_time"));
book.setModifyBy(resultSet.getString("modify_by"));
book.setModifyTime(resultSet.getTimestamp("modify_time"));
}
} catch (SQLException e) {
// 异常处理相关代码
} finally {
try {
if (resultSet != null) {
resultSet.close();
}
if (preparedStatement != null) {
preparedStatement.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
// 异常处理相关代码
}
}
return book;
}
是不是发现它的代码和之前的新增、更新代码大部分是一样的,而且也不得不对检查型异常SQLException捕获了2次,有代码洁癖的人是不是已经开始动手重构了,哈哈。
最后,在测试类BookRepositoryTest中添加测试方法testFindBook,如下所示:
@Test
public void testFindBook() {
Book book = bookRepository.findBook(1L);
Assert.assertNotNull(book);
Assert.assertEquals(book.getBookName(), "Spring实战(第4版)");
}
执行该测试方法,数据查询成功:
4. 使用JDBC模板
使用了原始的JDBC操作数据库后,好多有代码洁癖的同学都忍不住开始重构了,因为大部分代码都是样板代码,只有少部分才和业务逻辑相关,好消息是Spring已经帮我们重构过了,Spring将数据访问的样板代码抽象到模板类之中,我们可以直接使用模板类,从而简化了JDBC代码。
4.1 新增数据
首先,在配置类DataSourceConfig中添加如下配置:
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
然后将之前新建的类JdbcBookRepository上的@Repository注解移除掉。
接着,新建数据访问实现类JdbcTemplateBookRepository如下所示:
package chapter10.db.jdbc;
import chapter10.db.BookRepository;
import chapter10.domain.Book;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.stereotype.Repository;
import java.sql.Date;
@Repository
public class JdbcTemplateBookRepository implements BookRepository {
private static final String SQL_INSERT_BOOK =
"INSERT INTO book(book_name, author, create_by, create_time, modify_by, modify_time) VALUES (?,?,?,?,?,?);";
@Autowired
private JdbcOperations jdbcOperations;
@Override
public void addBook(Book book) {
jdbcOperations.update(SQL_INSERT_BOOK, book.getBookName(),
book.getAuthor(),
book.getCreateBy(),
new Date(System.currentTimeMillis()),
book.getModifyBy(),
new Date(System.currentTimeMillis()));
}
}
注意事项:该类添加了@Repository注解,以便Spring能够扫描到将其注册为bean。
很简洁有没有,从之前的代码优化到现在的代码,有代码洁癖的同学估计开心死了。
因为之前测试类BookRepositoryTest中,我们注入的是接口,所以我们不需要修改测试类的代码,即可直接访问到新建的JdbcTemplateBookRepository类的实现方法:
@Autowired
private BookRepository bookRepository;
运行之前的测试方法testAddBook(),数据成功新增到数据库:
4.2 更新数据
在数据访问实现类JdbcTemplateBookRepository中添加如下代码:
private static final String SQL_UPDATE_BOOK =
"UPDATE Book SET book_name = ?,author = ?,modify_by = ?,modify_time=? WHERE book_id = ?;";
@Override
public void updateBook(Book book) {
jdbcOperations.update(SQL_UPDATE_BOOK, book.getBookName(),
book.getAuthor(),
book.getModifyBy(),
new Timestamp(System.currentTimeMillis()),
book.getBookId());
}
然后简单修改下之前的测试方法testUpdateBook():
@Test
public void testUpdateBook() {
Book book = new Book(4L, "Spring实战(第4版)", "Craig Walls", "zwwhnly");
bookRepository.updateBook(book);
book = new Book(5L, "Java EE开发的颠覆者:Spring Boot实战", "汪云飞", "zwwhnly");
bookRepository.updateBook(book);
book = new Book(6L, "RabbitMQ实战指南", "朱忠华", "zwwhnly");
bookRepository.updateBook(book);
}
运行之前的测试方法testUpdateBook(),数据更新成功:
4.3 查找数据
在数据访问实现类JdbcTemplateBookRepository中添加如下代码:
private static final String SQL_SELECT_BOOK =
"SELECT book_id,book_name,author,create_by,create_time,modify_by,modify_time FROM book WHERE book_id = ?;";
@Override
public Book findBook(long bookId) {
return jdbcOperations.queryForObject(SQL_SELECT_BOOK, new BookRowMapper(), bookId);
}
private static final class BookRowMapper implements RowMapper<Book> {
@Override
public Book mapRow(ResultSet resultSet, int i) throws SQLException {
Book book = new Book();
book.setBookId(resultSet.getLong("book_id"));
book.setBookName(resultSet.getString("book_name"));
book.setAuthor(resultSet.getString("author"));
book.setCreateBy(resultSet.getString("create_by"));
book.setCreateTime(resultSet.getTimestamp("create_time"));
book.setModifyBy(resultSet.getString("modify_by"));
book.setModifyTime(resultSet.getTimestamp("modify_time"));
return book;
}
}
运行之前的测试方法testFindBook(),数据查询成功:
5. 源码及参考
源码地址:https://github.com/zwwhnly/spring-action.git,欢迎下载。
Craig Walls 《Spring实战(第4版)》
原创不易,如果觉得文章能学到东西的话,欢迎点个赞、评个论、关个注,这是我坚持写作的最大动力。
如果有兴趣,欢迎添加我的微信:zwwhnly,等你来聊技术、职场、工作等话题(PS:我是一名奋斗在上海的程序员)。
Spring入门(十五):使用Spring JDBC操作数据库的更多相关文章
- Spring Boot(十五):spring boot+jpa+thymeleaf增删改查示例
Spring Boot(十五):spring boot+jpa+thymeleaf增删改查示例 一.快速上手 1,配置文件 (1)pom包配置 pom包里面添加jpa和thymeleaf的相关包引用 ...
- Spring入门(十四):Spring MVC控制器的2种测试方法
作为一名研发人员,不管你愿不愿意对自己的代码进行测试,都得承认测试对于研发质量保证的重要性,这也就是为什么每个公司的技术部都需要质量控制部的原因,因为越早的发现代码的bug,成本越低,比如说,Dev环 ...
- (转)Spring Boot (十五): Spring Boot + Jpa + Thymeleaf 增删改查示例
http://www.ityouknow.com/springboot/2017/09/23/spring-boot-jpa-thymeleaf-curd.html 这篇文章介绍如何使用 Jpa 和 ...
- Spring Boot (十五): Spring Boot + Jpa + Thymeleaf 增删改查示例
这篇文章介绍如何使用 Jpa 和 Thymeleaf 做一个增删改查的示例. 先和大家聊聊我为什么喜欢写这种脚手架的项目,在我学习一门新技术的时候,总是想快速的搭建起一个 Demo 来试试它的效果,越 ...
- Spring入门(十二):Spring MVC使用讲解
1. Spring MVC介绍 提到MVC,参与过Web应用程序开发的同学都很熟悉,它是展现层(也可以理解成直接展现给用户的那一层)开发的一种架构模式,M全称是Model,指的是数据模型,V全称是Vi ...
- Spring Boot入门系列(十四)使用JdbcTemplate操作数据库,配置多数据源!
前面介绍了Spring Boot 中的整合Mybatis并实现增删改查.如何实现事物控制.不清楚的朋友可以看看之前的文章:https://www.cnblogs.com/zhangweizhong/c ...
- Spring入门(10)-Spring JDBC
Spring入门(10)-Spring JDBC 0. 目录 JdbcTemplate介绍 JdbcTemplate常见方法 代码示例 参考资料 1. JdbcTemplate介绍 JdbcTempl ...
- Spring Boot(十四):spring boot整合shiro-登录认证和权限管理
Spring Boot(十四):spring boot整合shiro-登录认证和权限管理 使用Spring Boot集成Apache Shiro.安全应该是互联网公司的一道生命线,几乎任何的公司都会涉 ...
- Excel VBA入门(五)Excel对象操作
本章是本系列教程的重点.但我觉得应该不是难点.从第零章开始到学完本章,应该可以把VBA用于实战中了. Excel对象主要有4个: 工作薄 Workbook 工作表 Worksheet 单元格区域 Ra ...
随机推荐
- 题解 洛谷P1196 【[NOI2002]银河英雄传说】
题意 有一个划分成n列的星际战场,各列编号为1,2.....n.有n艘战舰,也依次编号1,2.....n,其中第i号战舰位于第i列. 有m条指令,每条指令格式如下 M i j 表示让第i号战舰所在列的 ...
- KubeSphere CI/CD+GitLab+Harbor将Spring Boot项目部署至Kubernetes
上一篇文章分享了如何在 KubeSphere 对公共的代码仓库 GitHub 和镜像仓库 DockerHub 创建流水线,本文将继续使用 KubeSphere,基于 Harbor 和 GitLab 创 ...
- 单细胞转录组测序数据的可变剪接(alternative splicing)分析方法总结
可变剪接(alternative splicing),在真核生物中是一种非常基本的生物学事件.即基因转录后,先产生初始RNA或称作RNA前体,然后再通过可变剪接方式,选择性的把不同的外显子进行重连,从 ...
- c语言实现配置文件的读写
配置文件的格式如下: key1 = value1 key2 = value2 . . . 名值对以一个=链接,一条记录以换行符分割 头文件: #include<stdio.h> #incl ...
- 史上最全 69 道 Spring 面试题和答案
史上最全 69 道 Spring 面试题和答案 目录Spring 概述依赖注入Spring beansSpring注解Spring数据访问Spring面向切面编程(AOP)Spring MVC Spr ...
- Java基础语法(总结篇)
关键字&标识符 关键字的概念与特征 概念:Java关键字是事先定义好的对Java的编译器有特殊的意义,他们用来表示一种数据类型,或者表示程序的结构等,关键字不能用作变量名.方法名.类名.包名和 ...
- Collection,泛型,Map
1.Collection集合 集合和数组的区别? 数组的长度是固定的, 集合的长度是可变的 数组中存储的数据都是同一类型的数据.集合存储的是对象,而且对象的类型可以不一致 集合框架 单列集合 java ...
- Java连载30-方法重载、方法递归
一.方法重载 1.又被称为overload 2.方法重载使用场景 功能类似的时候,尽可能仍方法名相同(但是功能不同或者不相似的时候,方法名尽量不同) 3.什么条件满足之后,可以构成方法重载 (1)在同 ...
- vue-property-decorator vue typescript写法
什么是Typescript TypeScript 是一种由微软开发的自由和开源的编程语言,它是 JavaScript 的一个超集,扩展了 JavaScript 的语法.作者是安德斯大爷, Delphi ...
- Unity3D_10_文件夹目录架构
一:几个特殊文件夹介绍 1.Editor Editor文件夹可以在根目录下,也可以在子目录里,只要名子叫Editor就可以.比如目录:/xxx/xxx/Editor 和 /Editor 是一样的,无论 ...