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版)》
6. 最後
歡迎掃碼關注微信公眾號:「申城異鄉人」,定期分享Java技術乾貨,讓我們一起進步。
相關推薦
Spring入門(十五):使用Spring JDBC操作資料庫
在本系列的之前部落格中,我們從沒有講解過操作資料庫的方法,但是在實際的工作中,幾乎所有的系統都離不開資料的持久化,所以掌握操作資料庫的使用方法就非常重要。 在Spring中,操作資料庫有很多種方法,我們可以使用JDBC、Hibernate、MyBatis或者其他的資料持久化框架,本篇部落格的重點是講解下在Sp
Spring Boot(十五):spring boot+jpa+thymeleaf增刪改查示例
app 配置文件 quest 重啟 fin nbu 生產 prot html Spring Boot(十五):spring boot+jpa+thymeleaf增刪改查示例 一、快速上手 1,配置文件 (1)pom包配置 pom包裏面添加jpa和thymeleaf的相關包引
Spring入門(十一):Spring AOP使用進階
在上篇部落格中,我們瞭解了什麼是AOP以及在Spring中如何使用AOP,本篇部落格繼續深入講解下AOP的高階用法。 1. 宣告帶引數的切點 假設我們有一個介面CompactDisc和它的實現類BlankDisc: package chapter04.soundsystem; /** * 光碟 */ p
Spring入門(十二):Spring MVC使用講解
1. Spring MVC介紹 提到MVC,參與過Web應用程式開發的同學都很熟悉,它是展現層(也可以理解成直接展現給使用者的那一層)開發的一種架構模式,M全稱是Model,指的是資料模型,V全稱是View,指的是檢視頁面,如JSP、Thymeleaf等,C全稱是Controller,指的是控制器,用來處理使
Spring入門(十四):Spring MVC控制器的2種測試方法
作為一名研發人員,不管你願不願意對自己的程式碼進行測試,都得承認測試對於研發質量保證的重要性,這也就是為什麼每個公司的技術部都需要質量控制部的原因,因為越早的發現程式碼的bug,成本越低,比如說,Dev環境發現bug的成本要低於QA環境,QA環境發現bug的成本要低於Prod環境,Prod環境發現bug的成本
十五:Spring Cloud 之Eureka服務註冊中心(HA版)
1. Eureka簡介 2. 程式碼實現 2.1涉及的模組 eureka-server-ha:通過profiles指定不同的埠來模擬多服務例項。 eureka-service:服務提供者 2.2
springcloud(十五):Spring Cloud 終於按捺不住推出了自己的服務閘道器 Gateway
Spring 官方最終還是按捺不住推出了自己的閘道器元件:Spring Cloud Gateway ,相比之前我們使用的 Zuul(1.x) 它有哪些優勢呢?Zuul(1.x) 基於 Servlet,使用阻塞 API,它不支援任何長連線,如 WebSockets,Spring Cloud Gateway 使用
Spring Security原始碼分析十五:Spring Security 頁面許可權控制
Spring Security是一個能夠為基於Spring的企業應用系統提供宣告式的安全訪問控制解決方案的安全框架。它提供了一組可以在Spring應用上下文中配置的Bean,充分利用了Spring IoC,DI(控制反轉Inversion of Contr
Spring Boot2(十五):Shiro記住我rememberMe、驗證碼Kaptcha
接著上次學習的《Spring Boot2(十二):手摸手教你搭建Shiro安全框架》,實現了Shiro的認證和授權。今天繼續在這個基礎上學習Shiro實現功能記住我rememberMe,以及登入時驗證碼Kaptcha。 Remember Me記住我:使用者的登入狀態會不會因為瀏覽器的關閉而失效,直到Cooki
Spring Cloud (十四):Spring Cloud 開源軟體都有哪些?
學習一門新的技術如果有優秀的開源專案,對初學者的學習將會是事半功倍,通過研究和學習優秀的開源專案,可以快速的瞭解此技術的相關應用場景和應用示例,參考優秀開源專案會降低將此技術引入到專案中的成本。為此抽了一些時間為大家尋找了一些非常優秀的 Spring Cloud 開源軟體供大家學習參考。 上次寫了一篇文章Sp
【QT】QT從零入門教程(五):影象檔案操作 [新建開啟儲存]
前文已經講解了選單欄、工具欄、工作列的實現方法,下面我們對程式進行完善。實現功能為:為軟體新增“檔案”選單,並在下拉列表上新增”新建”、 ”開啟”、 ”儲存”、 ”另存為”、 ”關閉”,並在工具欄加上“新建”、“開啟”、“儲存”圖示。 程式碼 /
Spring Boot(十一):Spring Boot中MongoDB的使用
Spring Boot(十一):Spring Boot中MongoDB的使用 mongodb是最早熱門非關係資料庫的之一,使用也比較普遍,一般會用做離線資料分析來使用,放到內網的居多。由於很多公司使用了雲服務,伺服器預設都開放了外網地址,導致前一陣子大批 MongoDB 因配置漏洞被攻擊,資料被刪,引起了人
《partner4java 講述Spring入門》之:spring cache支援(spring3.1如何使用cache 快取)
(以下內容參照自官方文件;p4jorm下載地址http://blog.csdn.net/partner4java/article/details/8629578;cache demo下載地址http://download.csdn.net/detail/partner4ja
Spring入門Blog[五、Spring容器組建註解@Component和Resouces實現完全註解配置]
@Resource和@Component實現零XML配置 1、@Resource的註解: @Resource是J2EE的註解。意思是說在容器裡面找相應的資源。也可以通過name屬性指定它name的資源。可以註解到field或者setter上面 public class
jdbc篇第2課:使用jdbc操作資料庫
上節課我們成功的連線了資料庫,這節課我們試試來用java操作資料庫 想要操作資料庫,必定要藉助sql,java.sql包裡提供了一些類和介面來方便我們執行sql語句 Demo 01: package jdbc; imp
Spring Boot入門教程(四十八):初始化操作 CommandLineRunner和ApplicationRunner
CommandLineRunner和ApplicationRunner在SpringApplication.run()之前,在所有的beans載入完成之後執行,用於執行一些初始化操作(如載入快取、讀取配置檔案、建立執行緒池等) CommandLineRunner和Applicatio
Spring Cloud 入門教程(五): Ribbon實現客戶端的負載均衡
接上節,假如我們的Hello world服務的訪問量劇增,用一個服務已經無法承載, 我們可以把Hello World服務做成一個叢集。 很簡單,我們只需要複製Hello world服務,同時將原來的埠8762修改為8763。然後啟動這兩個Spring Boot應用, 就可
Spring Boot教程十五:多執行緒
實際的開發應該開發過程中,經常需要使用到多執行緒,而且大多時候需要獲取到每個執行緒執行的結果,然後再執行剩下的業務邏輯。具體實現如下; pom檔案引用: <dependency> <groupId>com.go
spring cloud 入門系列五:使用Feign 實現宣告式服務呼叫
一、Spring Cloud Feign概念引入通過前面的隨筆,我們瞭解如何通過Spring Cloud ribbon進行負責均衡,如何通過Spring Cloud Hystrix進行服務斷路保護,兩者作為基礎工具類框架應用在各種基礎設施類微服務和業務類微服務中,並且成對存在,那麼有沒有更高層的封裝,將兩者的
Spring入門(五):Spring Bean Scope講解
1. 前情回顧 Spring入門(一):建立Spring專案 Spring入門(二):自動化裝配bean Spring入門(三):通過JavaConfig裝配bean Spring入門(四):使用Maven管理Spring專案 2. 什麼是Bean的Scope? Scope描述的是Spring容器是如何新