在本系列的以前博客中,咱們從沒有講解過操做數據庫的方法,可是在實際的工做中,幾乎全部的系統都離不開數據的持久化,因此掌握操做數據庫的使用方法就很是重要。java
在Spring中,操做數據庫有不少種方法,咱們可使用JDBC、Hibernate、MyBatis或者其餘的數據持久化框架,本篇博客的重點是講解下在Spring中如何經過JDBC操做數據庫。mysql
在講解JDBC前,咱們先解決一個問題,由於原本構建正常的程序在從新構建打包時,居然報了以下錯誤:git
網上查找資料後,說是依賴的版本有衝突,因而檢查了pom.xml中以前添加的Spring的依賴:github
<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:web
<!--spring aop支持-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
複製代碼
此時從新構建打包,再也不報錯,打包成功:spring
不過上面的依賴還能夠簡化成下面這樣的:sql
<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個依賴:數據庫
首先執行以下語句建立MySql數據庫spring_action_db:apache
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>
複製代碼
首先,新建數據庫實體類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);
}
複製代碼
新建數據訪問實現類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(),數據成功新增到數據庫:
首先,在數據訪問接口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);
}
複製代碼
執行該測試方法,數據更新成功:
首先,在數據訪問接口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版)");
}
複製代碼
執行該測試方法,數據查詢成功:
使用了原始的JDBC操做數據庫後,好多有代碼潔癖的同窗都忍不住開始重構了,由於大部分代碼都是樣板代碼,只有少部分才和業務邏輯相關,好消息是Spring已經幫咱們重構過了,Spring將數據訪問的樣板代碼抽象到模板類之中,咱們能夠直接使用模板類,從而簡化了JDBC代碼。
首先,在配置類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(),數據成功新增到數據庫:
在數據訪問實現類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(),數據更新成功:
在數據訪問實現類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(),數據查詢成功:
源碼地址:github.com/zwwhnly/spr…,歡迎下載。
Craig Walls 《Spring實戰(第4版)》
最後,歡迎關注個人微信公衆號:「申城異鄉人」,全部博客會同步更新。