在本系列的之前博客中,我们从没有讲解过操作数据库的方法,但是在实际的工作中,几乎所有的系统都离不开数据的持久化,所以掌握操作数据库的使用方法就非常重要。

在Spring中,操作数据库有很多种方法,我们可以使用JDBC、Hibernate、MyBatis或者其他的数据持久化框架,本篇博客的重点是讲解下在Spring中如何通过JDBC操作数据库。

1. 项目构建失败解决

在讲解JDBC前,我们先解决一个问题,因为本来构建正常的程序在重新构建打包时,竟然报了如下错误:

网上查找资料后,说是依赖的版本有冲突,于是检查了pom.xml中之前添加的Spring的依赖:

  1. <dependency>
  2. <groupId>org.springframework</groupId>
  3. <artifactId>spring-context</artifactId>
  4. <version>4.3.18.RELEASE</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.springframework</groupId>
  8. <artifactId>spring-webmvc</artifactId>
  9. <version>4.3.18.RELEASE</version>
  10. </dependency>
  11. <!--spring aop支持-->
  12. <dependency>
  13. <groupId>org.springframework</groupId>
  14. <artifactId>spring-aop</artifactId>
  15. <version>5.1.8.RELEASE</version>
  16. </dependency>
  17. <dependency>
  18. <groupId>org.springframework</groupId>
  19. <artifactId>spring-test</artifactId>
  20. <version>4.3.18.RELEASE</version>
  21. <scope>test</scope>
  22. </dependency>

其中spring-aop的版本是5.1.8.RELEASE,而其余3个包的版本是4.3.18.RELEASE,将spring-aop版本也修改为4.3.18.RELEASE:

  1. <!--spring aop支持-->
  2. <dependency>
  3. <groupId>org.springframework</groupId>
  4. <artifactId>spring-aop</artifactId>
  5. <version>4.3.18.RELEASE</version>
  6. </dependency>

此时重新构建打包,不再报错,打包成功:

不过上面的依赖还可以简化成下面这样的:

  1. <dependency>
  2. <groupId>org.springframework</groupId>
  3. <artifactId>spring-webmvc</artifactId>
  4. <version>4.3.18.RELEASE</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.springframework</groupId>
  8. <artifactId>spring-test</artifactId>
  9. <version>4.3.18.RELEASE</version>
  10. <scope>test</scope>
  11. </dependency>

因为spring-webmvc包已经包含了spring-context和spring-aop,因此没有必要重复添加这2个依赖:

2. 配置数据源

首先执行如下语句创建MySql数据库spring_action_db:

  1. CREATE DATABASE spring_action_db DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;

然后执行如下语句创建表book:

  1. use spring_action_db;
  2. create table Book
  3. (
  4. book_id bigint auto_increment comment '书籍id',
  5. book_name varchar(50) not null comment '书名',
  6. author varchar(50) not null comment '作者',
  7. create_by varchar(20) not null comment '创建人',
  8. create_time datetime not null comment '创建时间',
  9. modify_by varchar(20) not null comment '修改人',
  10. modify_time datetime not null comment '修改时间',
  11. constraint Book_pk
  12. primary key (book_id)
  13. )
  14. comment '书籍';

准备就绪后,新建配置类配置下数据源:

  1. package chapter10.config;
  2. import org.apache.commons.dbcp2.BasicDataSource;
  3. import org.springframework.context.annotation.Bean;
  4. import org.springframework.context.annotation.ComponentScan;
  5. import org.springframework.context.annotation.Configuration;
  6. @Configuration
  7. @ComponentScan("chapter10")
  8. public class DataSourceConfig {
  9. @Bean
  10. public BasicDataSource dataSource() {
  11. BasicDataSource dataSource = new BasicDataSource();
  12. dataSource.setDriverClassName("com.mysql.jdbc.Driver");
  13. dataSource.setUrl("jdbc:mysql://localhost:3306/spring_action_db");
  14. dataSource.setUsername("root");
  15. dataSource.setPassword("root");
  16. return dataSource;
  17. }
  18. }

因为我们使用的是MySql数据库,所以驱动名称设置的是:com.mysql.jdbc.Driver。

如果你使用的是其他类型的数据库,需要修改成对应的名称。

因为使用到了MySql驱动,所以我们需要在pom.xml中添加如下依赖,否则在访问数据库时会获取不到连接:

  1. <!-- MySql驱动 -->
  2. <dependency>
  3. <groupId>mysql</groupId>
  4. <artifactId>mysql-connector-java</artifactId>
  5. <version>5.1.46</version>
  6. </dependency>

3. 使用原始的JDBC代码

首先,新建数据库实体类Book:

  1. package chapter10.domain;
  2. import java.util.Date;
  3. public class Book {
  4. private Long bookId;
  5. private String bookName;
  6. private String author;
  7. private String createBy;
  8. private Date createTime;
  9. private String modifyBy;
  10. private Date modifyTime;
  11. public Book(String bookName, String author, String createBy) {
  12. this.bookName = bookName;
  13. this.author = author;
  14. this.createBy = createBy;
  15. this.createTime = new Date();
  16. this.modifyBy=createBy;
  17. this.modifyTime=new Date();
  18. }
  19. public Book(Long bookId, String bookName, String author, String modifyBy) {
  20. this.bookId = bookId;
  21. this.bookName = bookName;
  22. this.author = author;
  23. this.modifyBy = modifyBy;
  24. }
  25. public Book() {
  26. }
  27. // 省略get和set方法
  28. }

然后定义数据访问接口BookRepository,暂时只添加addBook方法:

  1. package chapter10.db;
  2. import chapter10.domain.Book;
  3. public interface BookRepository {
  4. void addBook(Book book);
  5. }

3.1 新增数据

新建数据访问实现类JdbcBookRepository如下所示:

  1. package chapter10.db.jdbc;
  2. import chapter10.db.BookRepository;
  3. import chapter10.domain.Book;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.stereotype.Repository;
  6. import javax.sql.DataSource;
  7. import java.sql.Connection;
  8. import java.sql.PreparedStatement;
  9. import java.sql.SQLException;
  10. import java.sql.Timestamp;
  11. import java.util.Calendar;
  12. import java.util.Date;
  13. @Repository
  14. public class JdbcBookRepository implements BookRepository {
  15. private static final String SQL_INSERT_BOOK =
  16. "INSERT INTO book(book_name, author, create_by, create_time, modify_by, modify_time) VALUES (?,?,?,?,?,?);";
  17. @Autowired
  18. private DataSource dataSource;
  19. @Override
  20. public void addBook(Book book) {
  21. Connection connection = null;
  22. PreparedStatement preparedStatement = null;
  23. try {
  24. Calendar calendar = Calendar.getInstance();
  25. calendar.setTime(new Date());
  26. connection = dataSource.getConnection();
  27. preparedStatement = connection.prepareStatement(SQL_INSERT_BOOK);
  28. preparedStatement.setString(1, book.getBookName());
  29. preparedStatement.setString(2, book.getAuthor());
  30. preparedStatement.setString(3, book.getCreateBy());
  31. preparedStatement.setTimestamp(4, new Timestamp(calendar.getTimeInMillis()));
  32. preparedStatement.setString(5, book.getModifyBy());
  33. preparedStatement.setTimestamp(6, new Timestamp(calendar.getTimeInMillis()));
  34. preparedStatement.execute();
  35. } catch (SQLException e) {
  36. // 异常处理相关代码
  37. } finally {
  38. try {
  39. if (preparedStatement != null) {
  40. preparedStatement.close();
  41. }
  42. if (connection != null) {
  43. connection.close();
  44. }
  45. } catch (SQLException e) {
  46. // 异常处理相关代码
  47. }
  48. }
  49. }
  50. }

注意事项:该类添加了@Repository注解,以便Spring能够扫描到将其注册为bean。

值得注意的是,在这段代码中,我们竟然捕获SQLException捕获了2次,这是因为connection = dataSource.getConnection();preparedStatement.execute();preparedStatement.close();connection.close();都会抛出检查型异常SQLException,所以方法中必须捕获,否则会导致编译不通过:

  1. Connection getConnection() throws SQLException;
  2. boolean execute() throws SQLException;
  3. void close() throws SQLException;
  4. void close() throws SQLException;

最后,新建单元测试类BookRepositoryTest如下所示:

  1. package chapter10;
  2. import chapter10.config.DataSourceConfig;
  3. import chapter10.db.BookRepository;
  4. import chapter10.domain.Book;
  5. import org.junit.Test;
  6. import org.junit.runner.RunWith;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.test.context.ContextConfiguration;
  9. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  10. @RunWith(SpringJUnit4ClassRunner.class)
  11. @ContextConfiguration(classes = DataSourceConfig.class)
  12. public class BookRepositoryTest {
  13. @Autowired
  14. private BookRepository bookRepository;
  15. @Test
  16. public void testAddBook() {
  17. Book book = new Book("Spring实战(第4版)", "Craig Walls", "申城异乡人");
  18. bookRepository.addBook(book);
  19. book = new Book("Java EE开发的颠覆者:Spring Boot实战", "汪云飞", "申城异乡人");
  20. bookRepository.addBook(book);
  21. book = new Book("RabbitMQ实战指南", "朱忠华", "申城异乡人");
  22. bookRepository.addBook(book);
  23. }
  24. }

运行测试方法testAddBook(),数据成功新增到数据库:

3.2 更新数据

首先,在数据访问接口BookRepository中添加更新方法:

  1. void updateBook(Book book);

然后在数据访问实现类JdbcBookRepository中实现该方法:

  1. private static final String SQL_UPDATE_BOOK =
  2. "UPDATE Book SET book_name = ?,author = ?,modify_by = ?,modify_time=? WHERE book_id = ?;";
  3. @Override
  4. public void updateBook(Book book) {
  5. Connection connection = null;
  6. PreparedStatement preparedStatement = null;
  7. try {
  8. Calendar calendar = Calendar.getInstance();
  9. calendar.setTime(new Date());
  10. connection = dataSource.getConnection();
  11. preparedStatement = connection.prepareStatement(SQL_UPDATE_BOOK);
  12. preparedStatement.setString(1, book.getBookName());
  13. preparedStatement.setString(2, book.getAuthor());
  14. preparedStatement.setString(3, book.getModifyBy());
  15. preparedStatement.setTimestamp(4, new Timestamp(calendar.getTimeInMillis()));
  16. preparedStatement.setLong(5, book.getBookId());
  17. preparedStatement.execute();
  18. } catch (SQLException e) {
  19. // 异常处理相关代码
  20. } finally {
  21. try {
  22. if (preparedStatement != null) {
  23. preparedStatement.close();
  24. }
  25. if (connection != null) {
  26. connection.close();
  27. }
  28. } catch (SQLException e) {
  29. // 异常处理相关代码
  30. }
  31. }
  32. }

是不是发现它的代码和之前的新增代码几乎是一样的,而且也不得不对检查型异常SQLException捕获了2次,有代码洁癖的人是不是忍不住想重构,哈哈。

最后,在测试类BookRepositoryTest中添加测试方法testUpdateBook,如下所示:

  1. @Test
  2. public void testUpdateBook() {
  3. Book book = new Book(1L, "Spring实战(第4版)", "Craig Walls", "zwwhnly");
  4. bookRepository.updateBook(book);
  5. book = new Book(2L, "Java EE开发的颠覆者:Spring Boot实战", "汪云飞", "zwwhnly");
  6. bookRepository.updateBook(book);
  7. book = new Book(3L, "RabbitMQ实战指南", "朱忠华", "zwwhnly");
  8. bookRepository.updateBook(book);
  9. }

执行该测试方法,数据更新成功:

3.3 查找数据

首先,在数据访问接口BookRepository中添加更新方法:

  1. Book findBook(long bookId);

然后在数据访问实现类JdbcBookRepository中实现该方法:

  1. private static final String SQL_SELECT_BOOK =
  2. "SELECT book_id,book_name,author,create_by,create_time,modify_by,modify_time FROM book WHERE book_id = ?;";
  3. @Override
  4. public Book findBook(long bookId) {
  5. Connection connection = null;
  6. PreparedStatement preparedStatement = null;
  7. ResultSet resultSet = null;
  8. Book book = null;
  9. try {
  10. connection = dataSource.getConnection();
  11. preparedStatement = connection.prepareStatement(SQL_SELECT_BOOK);
  12. preparedStatement.setLong(1, bookId);
  13. resultSet = preparedStatement.executeQuery();
  14. if (resultSet.next()) {
  15. book = new Book();
  16. book.setBookId(resultSet.getLong("book_id"));
  17. book.setBookName(resultSet.getString("book_name"));
  18. book.setAuthor(resultSet.getString("author"));
  19. book.setCreateBy(resultSet.getString("create_by"));
  20. book.setCreateTime(resultSet.getTimestamp("create_time"));
  21. book.setModifyBy(resultSet.getString("modify_by"));
  22. book.setModifyTime(resultSet.getTimestamp("modify_time"));
  23. }
  24. } catch (SQLException e) {
  25. // 异常处理相关代码
  26. } finally {
  27. try {
  28. if (resultSet != null) {
  29. resultSet.close();
  30. }
  31. if (preparedStatement != null) {
  32. preparedStatement.close();
  33. }
  34. if (connection != null) {
  35. connection.close();
  36. }
  37. } catch (SQLException e) {
  38. // 异常处理相关代码
  39. }
  40. }
  41. return book;
  42. }

是不是发现它的代码和之前的新增、更新代码大部分是一样的,而且也不得不对检查型异常SQLException捕获了2次,有代码洁癖的人是不是已经开始动手重构了,哈哈。

最后,在测试类BookRepositoryTest中添加测试方法testFindBook,如下所示:

  1. @Test
  2. public void testFindBook() {
  3. Book book = bookRepository.findBook(1L);
  4. Assert.assertNotNull(book);
  5. Assert.assertEquals(book.getBookName(), "Spring实战(第4版)");
  6. }

执行该测试方法,数据查询成功:

4. 使用JDBC模板

使用了原始的JDBC操作数据库后,好多有代码洁癖的同学都忍不住开始重构了,因为大部分代码都是样板代码,只有少部分才和业务逻辑相关,好消息是Spring已经帮我们重构过了,Spring将数据访问的样板代码抽象到模板类之中,我们可以直接使用模板类,从而简化了JDBC代码。

4.1 新增数据

首先,在配置类DataSourceConfig中添加如下配置:

  1. @Bean
  2. public JdbcTemplate jdbcTemplate(DataSource dataSource) {
  3. return new JdbcTemplate(dataSource);
  4. }

然后将之前新建的类JdbcBookRepository上的@Repository注解移除掉。

接着,新建数据访问实现类JdbcTemplateBookRepository如下所示:

  1. package chapter10.db.jdbc;
  2. import chapter10.db.BookRepository;
  3. import chapter10.domain.Book;
  4. import org.springframework.beans.factory.annotation.Autowired;
  5. import org.springframework.jdbc.core.JdbcOperations;
  6. import org.springframework.stereotype.Repository;
  7. import java.sql.Date;
  8. @Repository
  9. public class JdbcTemplateBookRepository implements BookRepository {
  10. private static final String SQL_INSERT_BOOK =
  11. "INSERT INTO book(book_name, author, create_by, create_time, modify_by, modify_time) VALUES (?,?,?,?,?,?);";
  12. @Autowired
  13. private JdbcOperations jdbcOperations;
  14. @Override
  15. public void addBook(Book book) {
  16. jdbcOperations.update(SQL_INSERT_BOOK, book.getBookName(),
  17. book.getAuthor(),
  18. book.getCreateBy(),
  19. new Date(System.currentTimeMillis()),
  20. book.getModifyBy(),
  21. new Date(System.currentTimeMillis()));
  22. }
  23. }

注意事项:该类添加了@Repository注解,以便Spring能够扫描到将其注册为bean。

很简洁有没有,从之前的代码优化到现在的代码,有代码洁癖的同学估计开心死了。

因为之前测试类BookRepositoryTest中,我们注入的是接口,所以我们不需要修改测试类的代码,即可直接访问到新建的JdbcTemplateBookRepository类的实现方法:

  1. @Autowired
  2. private BookRepository bookRepository;

运行之前的测试方法testAddBook(),数据成功新增到数据库:

4.2 更新数据

在数据访问实现类JdbcTemplateBookRepository中添加如下代码:

  1. private static final String SQL_UPDATE_BOOK =
  2. "UPDATE Book SET book_name = ?,author = ?,modify_by = ?,modify_time=? WHERE book_id = ?;";
  3. @Override
  4. public void updateBook(Book book) {
  5. jdbcOperations.update(SQL_UPDATE_BOOK, book.getBookName(),
  6. book.getAuthor(),
  7. book.getModifyBy(),
  8. new Timestamp(System.currentTimeMillis()),
  9. book.getBookId());
  10. }

然后简单修改下之前的测试方法testUpdateBook():

  1. @Test
  2. public void testUpdateBook() {
  3. Book book = new Book(4L, "Spring实战(第4版)", "Craig Walls", "zwwhnly");
  4. bookRepository.updateBook(book);
  5. book = new Book(5L, "Java EE开发的颠覆者:Spring Boot实战", "汪云飞", "zwwhnly");
  6. bookRepository.updateBook(book);
  7. book = new Book(6L, "RabbitMQ实战指南", "朱忠华", "zwwhnly");
  8. bookRepository.updateBook(book);
  9. }

运行之前的测试方法testUpdateBook(),数据更新成功:

4.3 查找数据

在数据访问实现类JdbcTemplateBookRepository中添加如下代码:

  1. private static final String SQL_SELECT_BOOK =
  2. "SELECT book_id,book_name,author,create_by,create_time,modify_by,modify_time FROM book WHERE book_id = ?;";
  3. @Override
  4. public Book findBook(long bookId) {
  5. return jdbcOperations.queryForObject(SQL_SELECT_BOOK, new BookRowMapper(), bookId);
  6. }
  7. private static final class BookRowMapper implements RowMapper<Book> {
  8. @Override
  9. public Book mapRow(ResultSet resultSet, int i) throws SQLException {
  10. Book book = new Book();
  11. book.setBookId(resultSet.getLong("book_id"));
  12. book.setBookName(resultSet.getString("book_name"));
  13. book.setAuthor(resultSet.getString("author"));
  14. book.setCreateBy(resultSet.getString("create_by"));
  15. book.setCreateTime(resultSet.getTimestamp("create_time"));
  16. book.setModifyBy(resultSet.getString("modify_by"));
  17. book.setModifyTime(resultSet.getTimestamp("modify_time"));
  18. return book;
  19. }
  20. }

运行之前的测试方法testFindBook(),数据查询成功:

5. 源码及参考

源码地址:https://github.com/zwwhnly/spring-action.git,欢迎下载。

Craig Walls 《Spring实战(第4版)》

原创不易,如果觉得文章能学到东西的话,欢迎点个赞、评个论、关个注,这是我坚持写作的最大动力。

如果有兴趣,欢迎添加我的微信:zwwhnly,等你来聊技术、职场、工作等话题(PS:我是一名奋斗在上海的程序员)。

Spring入门(十五):使用Spring JDBC操作数据库的更多相关文章

  1. Spring Boot(十五):spring boot+jpa+thymeleaf增删改查示例

    Spring Boot(十五):spring boot+jpa+thymeleaf增删改查示例 一.快速上手 1,配置文件 (1)pom包配置 pom包里面添加jpa和thymeleaf的相关包引用 ...

  2. Spring入门(十四):Spring MVC控制器的2种测试方法

    作为一名研发人员,不管你愿不愿意对自己的代码进行测试,都得承认测试对于研发质量保证的重要性,这也就是为什么每个公司的技术部都需要质量控制部的原因,因为越早的发现代码的bug,成本越低,比如说,Dev环 ...

  3. (转)Spring Boot (十五): Spring Boot + Jpa + Thymeleaf 增删改查示例

    http://www.ityouknow.com/springboot/2017/09/23/spring-boot-jpa-thymeleaf-curd.html 这篇文章介绍如何使用 Jpa 和 ...

  4. Spring Boot (十五): Spring Boot + Jpa + Thymeleaf 增删改查示例

    这篇文章介绍如何使用 Jpa 和 Thymeleaf 做一个增删改查的示例. 先和大家聊聊我为什么喜欢写这种脚手架的项目,在我学习一门新技术的时候,总是想快速的搭建起一个 Demo 来试试它的效果,越 ...

  5. Spring入门(十二):Spring MVC使用讲解

    1. Spring MVC介绍 提到MVC,参与过Web应用程序开发的同学都很熟悉,它是展现层(也可以理解成直接展现给用户的那一层)开发的一种架构模式,M全称是Model,指的是数据模型,V全称是Vi ...

  6. Spring Boot入门系列(十四)使用JdbcTemplate操作数据库,配置多数据源!

    前面介绍了Spring Boot 中的整合Mybatis并实现增删改查.如何实现事物控制.不清楚的朋友可以看看之前的文章:https://www.cnblogs.com/zhangweizhong/c ...

  7. Spring入门(10)-Spring JDBC

    Spring入门(10)-Spring JDBC 0. 目录 JdbcTemplate介绍 JdbcTemplate常见方法 代码示例 参考资料 1. JdbcTemplate介绍 JdbcTempl ...

  8. Spring Boot(十四):spring boot整合shiro-登录认证和权限管理

    Spring Boot(十四):spring boot整合shiro-登录认证和权限管理 使用Spring Boot集成Apache Shiro.安全应该是互联网公司的一道生命线,几乎任何的公司都会涉 ...

  9. Excel VBA入门(五)Excel对象操作

    本章是本系列教程的重点.但我觉得应该不是难点.从第零章开始到学完本章,应该可以把VBA用于实战中了. Excel对象主要有4个: 工作薄 Workbook 工作表 Worksheet 单元格区域 Ra ...

随机推荐

  1. C#开发BIMFACE系列21 服务端API之获取模型数据6:获取单模型的楼层信息

    系列目录     [已更新最新开发文章,点击查看详细] 一个文件/模型中可能包含多个楼层信息,获取楼层信息对于前端页面的动态展示非常有帮助.本篇介绍获取一个文件/模型中可能包含多个楼层信息的详细方法. ...

  2. 环境变量_JAVA_LAUNCHER_DEBUG,它能给你更多的JVM信息

    关于环境: 本文中的实战都是在docker容器中进行的,容器的出处请参照<在docker上编译openjdk8>一文,里面详细的说明了如何构造镜像和启动容器. 在上一篇文章<修改,编 ...

  3. mybatis转义

    SELECT * FROM test WHERE 1 = 1 AND start_date <= CURRENT_DATE AND end_date >= CURRENT_DATE 在执行 ...

  4. 【LeetCode】79-单词搜索

    题目描述 给定一个二维网格和一个单词,找出该单词是否存在于网格中. 单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中 "相邻" 单元格是那些水平相邻或垂直相邻的单元格.同一 ...

  5. git-基础命令使用

    1. 创建版本库     什么是版本库你?版本库有名仓库,英文名repository,你可以简单理解成一个目录,这个目录里面的所有文件都可以被git管理起来,每个文件的修改.删除.git都能跟踪,以便 ...

  6. Python二元操作符

    def quiz_message(grade): outcome = 'failed' if grade<50 else 'passid' print ('grade', grade, 'out ...

  7. KafkaProducer源码分析

    Kafka常用术语 Broker:Kafka的服务端即Kafka实例,Kafka集群由一个或多个Broker组成,主要负责接收和处理客户端的请求 Topic:主题,Kafka承载消息的逻辑容器,每条发 ...

  8. 固定定位下的div水平居中

    发现了一个之前未留意的知识点,做个笔记. 当一个块级元素的父元素开启了flex布局后,我们可以很轻松的将这个元素居中对齐,可以在父元素上加 justify-content: center; align ...

  9. Net基础篇_学习笔记_第九天_数组_三个练习

    练习一: using System; using System.Collections.Generic; using System.Linq; using System.Text; using Sys ...

  10. Guava的RateLimiter实现接口限流

    最近开发需求中有需要对后台接口进行限流处理,整理了一下基本使用方法. 首先添加guava依赖: <dependency> <groupId>com.google.guava&l ...