Spring JDBC的使用詳解
JDBC介紹
從這篇文章開始,我們將會介紹SpringBoot另外一個核心的技術(shù),即數(shù)據(jù)庫訪問技術(shù),提到數(shù)據(jù)訪問,學(xué)習(xí)Java的同學(xué)瞬間能就想起JDBC技術(shù),JDBC 是 Java Database Connectivity 的全稱,是Java語言中用來規(guī)范客戶端程序如何來訪問數(shù)據(jù)庫的應(yīng)用程序接口,提供了諸如查詢和更新數(shù)據(jù)庫中數(shù)據(jù)的一套標(biāo)準(zhǔn)的API,這套標(biāo)準(zhǔn)不同的數(shù)據(jù)庫廠家之間共同準(zhǔn)守,并提供各自的具體實現(xiàn)。如圖所示:

這樣設(shè)計的好處,就是Java程序只需要和JDBC API交互,從而屏蔽了訪問數(shù)據(jù)庫的復(fù)雜的實現(xiàn),大大降低了Java程序訪問數(shù)據(jù)庫的復(fù)雜度。對于日常開發(fā)而言,我們只需要掌握J(rèn)DBC API 規(guī)范中的幾個核心編程對象即可,這些對象包括DriverManger、Connection、Statement及ResultSet。
DriverManager
DriverManager主要負(fù)責(zé)加載不同數(shù)據(jù)庫廠家提供的驅(qū)動程序包(Driver),并且根據(jù)不同的請求向Java程序返回數(shù)據(jù)庫連接(Connection)對象,先看下Driver接口的定義:
public interface Driver {
//獲取數(shù)據(jù)庫連接
Connection connect(String url, java.util.Properties info)
throws SQLException;
boolean acceptsURL(String url) throws SQLException;
DriverPropertyInfo[] getPropertyInfo(String url, java.util.Properties info)
throws SQLException;
int getMajorVersion();
int getMinorVersion();
boolean jdbcCompliant();
public Logger getParentLogger() throws SQLFeatureNotSupportedException;
}
Driver中有個重要的方法 connect,來提供Connection對象
不同的數(shù)據(jù)庫對Driver,有具體的實現(xiàn),以MySql為例:
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
// 通過 DriverManager 注冊 Driver
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
…
}
這里用到了DriverManager,DriverManager通過 registerDriver來注冊不同數(shù)據(jù)庫的Driver,并且還提供了getConnection返回數(shù)據(jù)庫連接對象。
Connection
通過DriverManager可以獲取Connetion對象,Connection對象可以理解與數(shù)據(jù)庫連接的一種會話(Session),一個Connection對象代表一個數(shù)據(jù)庫的連接,負(fù)責(zé)完成與數(shù)據(jù)庫底層的通訊。
Connection對象提供了一組重載的方法來創(chuàng)建Statement和PreparedStatement,Statement和PreparedStatement是SQL執(zhí)行的載體,另外Connection對象還會涉及事務(wù)相關(guān)的操作。
Connection對象最核心的幾個方法如下:
public interface Connection extends Wrapper, AutoCloseable {
//創(chuàng)建 Statement
Statement createStatement() throws SQLException;
//創(chuàng)建 PreparedStatement
PreparedStatement prepareStatement(String sql) throws SQLException;
//提交
void commit() throws SQLException;
//回滾
void rollback() throws SQLException;
//關(guān)閉連接
void close() throws SQLException;
}
Statement/PreparedStatement
Statement和PreparedStatement是由Connection對象來創(chuàng)建的,用來執(zhí)行靜態(tài)的SQL語句并且返回生成的結(jié)果集對象,這里存在兩種類型,一種是普通的Statement,另外一種支持預(yù)編譯的PreparedStatement。
所謂預(yù)編譯,是指數(shù)據(jù)庫的編譯器會對 SQL 語句提前編譯,然后將預(yù)編譯的結(jié)果緩存到數(shù)據(jù)庫中,下次執(zhí)行時就可以通過替換參數(shù)并直接使用編譯過的語句,從而大大提高 SQL 的執(zhí)行效率。
以Statement為例,看下Statement最核心的方法:
public interface Statement extends Wrapper, AutoCloseable {
//執(zhí)行查詢語句
ResultSet executeQuery(String sql) throws SQLException;
//執(zhí)行更新語句
int executeUpdate(String sql) throws SQLException;
//執(zhí)行 SQL 語句
boolean execute(String sql) throws SQLException;
//執(zhí)行批處理
int[] executeBatch() throws SQLException;
}
ResultSet
通過Statement或PreparedStatement執(zhí)行SQL語句,我們引出了另外一個對象即為ResultSet對象,代碼如下:
public interface ResultSet extends Wrapper, AutoCloseable {
//獲取下一個結(jié)果
boolean next() throws SQLException;
//獲取某一個類型的結(jié)果值
Value getXXX(int columnIndex) throws SQLException;
…
}
ResultSet對象提供了next()方法,用來對整個結(jié)果集遍歷操作,如果next()方法返回為true,說明還有下一條記錄,
我們可以調(diào)用 ResultSet 對象的一系列 getXXX() 方法來取得對應(yīng)的結(jié)果值。
JDBC訪問數(shù)據(jù)庫流程
對于開發(fā)人員而言,通過JDBC的API是Java訪問數(shù)據(jù)庫的主要途徑,下面用代碼來展示下訪問數(shù)據(jù)庫的一個整體流程:
String url = "jdbc:mysql://localhost:3306/test" ;
String username = "root" ;
String password = "root" ;
//1.通過DriverManager獲取connection連接
Connection connection = DriverManager.getConnection(url,username,password);
//2.創(chuàng)建preparedStatement
PreparedStatement preparedStatement = connection.prepareStatement("select * from user");
//3.執(zhí)行SQL返回ResultSet
ResultSet resultSet = preparedStatement.executeQuery();
//4.遍歷resultSet結(jié)果集
while (resultSet.next()){
//resultSet.getString("1");
}
//5.釋放資源
resultSet.close();
preparedStatement.close();
connection.close();
配置數(shù)據(jù)源
上面我們在介紹JDBC的時候,Connection對象是通過DriverManager獲取,Connection對象代表著和數(shù)據(jù)庫的連接,每次通過DriverManager獲取比較耗時,影響了系統(tǒng)的性能。那有沒有辦法能夠復(fù)用Connection對象呢,答案是肯定的
JDBC給我們提供了DataSource接口來實現(xiàn)Connection的復(fù)用,核心代碼如下:
public interface DataSource extends CommonDataSource, Wrapper {
Connection getConnection() throws SQLException;
Connection getConnection(String username, String password)
throws SQLException;
}
作為一種基礎(chǔ)組件,不需要開發(fā)人員自己實現(xiàn) DataSource,因為業(yè)界已經(jīng)存在了很多優(yōu)秀的實現(xiàn)方案,如 DBCP、C3P0 、Druid 、Hikari等
SpringBoot默認(rèn)HikariDataSource作為DataSource的實現(xiàn),現(xiàn)在我們SpringBoot為例,看下SpringBoot如何通過JDBC來操作數(shù)據(jù)庫的,在進(jìn)行數(shù)據(jù)庫操作之前,我們首先需要先配置DataSource,SpringBoot配置DataSource非常簡單,只需要在配置文件中添加DataSource的配置:
spring:
# datasource 數(shù)據(jù)源配置內(nèi)容
datasource:
url: jdbc:mysql://localhost:3306/test?useSSL=false&useUnicode=true&characterEncoding=UTF-8
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: root
使用JDBC操縱數(shù)據(jù)庫
DataSource配好后,我們在本地的數(shù)據(jù)庫服務(wù)中,創(chuàng)建一個test數(shù)據(jù)庫,并且執(zhí)行以下DDL創(chuàng)建user表
CREATE TABLE `user` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主鍵', `username` varchar(64) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '用戶名', `password` varchar(32) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密碼', `create_time` datetime DEFAULT NULL COMMENT '創(chuàng)建時間', PRIMARY KEY (`id`), UNIQUE KEY `idx_username` (`username`) ) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
接下來我們創(chuàng)建一個實體類,實體類中屬性和user表中字段一一對應(yīng)
@Data
public class User {
/**
* 主鍵
*/
private Integer id;
/**
* 用戶名
*/
private String username;
/**
* 密碼
*/
private String password;
/**
* 創(chuàng)建時間
*/
private Date createTime;
}
注意:這里使用了Lombok的@Data注解來生成get/set方法。
我們再定義一個UserDao接口,
public interface UserDao {
/**
* 新增
* @param user
* @return
*/
Integer insert(User user);
/**
* 根據(jù)ID查詢
* @param id
* @return
*/
User selectById(Integer id);
/**
* 根據(jù)ID更新
* @param user
* @return
*/
Integer updateById(User user);
/**
* 根據(jù)ID刪除
* @param id
* @return
*/
Integer deleteById(Integer id);
}
這里之所以要抽離出一個UserDao一層有兩個原因:第一UserDao只封裝了對use表的數(shù)據(jù)庫操作,代碼易于維護和管理,第二我們可以基于UserDao接口提供不同的實現(xiàn)來訪問數(shù)據(jù)庫,比如我們可以提供基于原生JDBC的實現(xiàn),也可以用JDBCTemplate實現(xiàn)數(shù)據(jù)庫的訪問,還可以通過Mybatis等
接下來將通過代碼形式來展示下SpringBoot是如何通過JDBC API對數(shù)據(jù)庫進(jìn)行CRUD操作的。我們來定義UserDao的具體實現(xiàn)類命名為:UserRawJdbcDao實現(xiàn)以下方法:
新增數(shù)據(jù)
@Override
public Integer insert(User user) {
final String SQL_INSERT = "INSERT INTO user(username, password, create_time) VALUES(?, ?, ?)";
Connection connection = null;
PreparedStatement statement = null;
ResultSet rs = null;
Integer count = 0;
try{
connection = dataSource.getConnection();
statement = connection.prepareStatement(SQL_INSERT, Statement.RETURN_GENERATED_KEYS);
statement.setString(1,user.getUsername());
statement.setString(2,user.getPassword());
statement.setTimestamp(3,new Timestamp(user.getCreateTime().getTime()));
count = statement.executeUpdate();
rs = statement.getGeneratedKeys();
if(rs.next()){
user.setId(rs.getInt(1));
}
}catch (SQLException e){
e.printStackTrace();
}finally {
try {
if(rs != null){
rs.close();
}
if(statement != null){
statement.close();
}
if(connection != null){
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
return count;
}
查詢數(shù)據(jù)
@Override
public User selectById(Integer id) {
final String SQL_SELECT_ID = "SELECT id,username,password,create_time FROM user WHERE id = ?";
Connection connection = null;
PreparedStatement statement = null;
ResultSet rs = null;
User user = null;
try{
connection = dataSource.getConnection();
statement = connection.prepareStatement(SQL_SELECT_ID);
statement.setInt(1, id);
rs = statement.executeQuery();
if(rs.next()){
user = new User();
user.setId(rs.getInt("id"));
user.setUsername(rs.getString("username"));
user.setPassword(rs.getString("password"));
user.setCreateTime(rs.getTimestamp("create_time"));
}
}catch (SQLException e){
e.printStackTrace();
}finally {
try {
if(rs != null){
rs.close();
}
if(statement != null){
statement.close();
}
if(connection != null){
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
return user;
}
更新數(shù)據(jù)
@Override
public Integer updateById(User user) {
final String SQL_UPDATE = "UPDATE user SET username = ?, password = ? WHERE id = ?";
Connection connection = null;
PreparedStatement statement = null;
ResultSet rs = null;
Integer count = 0;
try{
connection = dataSource.getConnection();
statement = connection.prepareStatement(SQL_UPDATE);
statement.setString(1,user.getUsername());
statement.setString(2,user.getPassword());
statement.setInt(3,user.getId());
count = statement.executeUpdate();
}catch (SQLException e){
e.printStackTrace();
}finally {
try {
if(rs != null){
rs.close();
}
if(statement != null){
statement.close();
}
if(connection != null){
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
return count;
}
刪除數(shù)據(jù)
@Override
public Integer deleteById(Integer id) {
final String SQL_DELETE = "DELETE FROM user WHERE id = ?";
Connection connection = null;
PreparedStatement statement = null;
ResultSet rs = null;
Integer count = 0;
try{
connection = dataSource.getConnection();
statement = connection.prepareStatement(SQL_DELETE);
statement.setInt(1,id);
count = statement.executeUpdate();
}catch (SQLException e){
e.printStackTrace();
}finally {
try {
if(rs != null){
rs.close();
}
if(statement != null){
statement.close();
}
if(connection != null){
connection.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
return count;
}
到此,SpringBoot通過調(diào)用原生的JDBC的API完成對user表的CRUD操作,這代碼對有代碼潔癖的同學(xué)簡直不能忍,有大量共性的代碼,如創(chuàng)建Connection、Statement、ResultSet、資源的釋放和異常的處理。這部分封裝和優(yōu)化SpringBoot已經(jīng)處理過了,SpringBoot提供了JdbcTemplate模板工具類實現(xiàn)數(shù)據(jù)訪問,它簡化了JDBC API的使用方法。
使用JdbcTemplate操縱數(shù)據(jù)庫
同UserRawJdbcDao,我們再定義UserDao的另外一套實現(xiàn)類命名為:UserJdbcDao,這套實現(xiàn)類是通過JdbcTemplate完成對數(shù)據(jù)庫的操作,完成接口定義的方法如下:
新增數(shù)據(jù)
@Override
public Integer insert(User user){
// 創(chuàng)建 KeyHolder 對象,設(shè)置返回的主鍵 ID
KeyHolder keyHolder = new GeneratedKeyHolder();
int count = jdbcTemplate.update(INSERT_PREPARED_STATEMENT_CREATOR_FACTORY.newPreparedStatementCreator(
Arrays.asList(user.getUsername(),user.getPassword(),user.getCreateTime())),keyHolder);
// 設(shè)置 ID 主鍵到 entity 實體中
if (keyHolder.getKey() != null) {
user.setId(keyHolder.getKey().intValue());
}
// 返回影響行數(shù)
return count;
}
查詢數(shù)據(jù)
@Override
public User selectById(Integer id){
User result = jdbcTemplate.queryForObject("SELECT id, username, password, create_time FROM user WHERE id=?",
new BeanPropertyRowMapper<>(User.class), id);
return result;
}
更新數(shù)據(jù)
@Override
public Integer updateById(User user) {
return jdbcTemplate.update("UPDATE user SET username = ?, password = ? WHERE id = ?",
user.getUsername(),user.getPassword(),user.getId());
}
刪除數(shù)據(jù)
@Override
public Integer deleteById(Integer id){
return jdbcTemplate.update("DELETE FROM user WHERE id = ?", id);
}
小結(jié)
通過對比我們發(fā)現(xiàn)使用JdbcTemplate模板工具類可以大大減少JDBC訪問數(shù)據(jù)庫的代碼復(fù)雜度,作為開發(fā)人員我們應(yīng)該只關(guān)心業(yè)務(wù)邏輯的具體實現(xiàn)過程,對JDBC底層對象的創(chuàng)建,資源的釋放,異常的捕獲,應(yīng)該交給框架統(tǒng)一維護和管理。
雖然JdbcTemplate減少的我們訪問數(shù)據(jù)庫的代碼量,不過使用也有一些問題,比如:新增數(shù)據(jù)的時候默認(rèn)無法返回生成主鍵的id,將SQL硬編碼到Java代碼中,如果SQL修改,需要重新編譯Java代碼,不利于系統(tǒng)的維護等。這時我們需要另外一個框架,它就是大名鼎鼎的Mybatis,下一篇我將會介紹SpringBoot如何整合Mybatis。
項目源碼
github:github.com/dragon8844/…
以上就是Spring JDBC的使用詳解的詳細(xì)內(nèi)容,更多關(guān)于Spring JDBC的使用的資料請關(guān)注腳本之家其它相關(guān)文章!
- Java編程中使用JDBC API連接數(shù)據(jù)庫和創(chuàng)建程序的方法
- JDBC編程實現(xiàn)文件、圖片的存儲方法
- JDBC 使用說明(流程、架構(gòu)、編程)
- Java Spring5學(xué)習(xí)之JdbcTemplate詳解
- jdbc使用PreparedStatement批量插入數(shù)據(jù)的方法
- 將原生JDBC封裝實現(xiàn)CRUD的案例
- JavaWeb實戰(zhàn)之用Servlet+JDBC實現(xiàn)用戶登錄與注冊
- SpringBoot多數(shù)據(jù)源配置詳細(xì)教程(JdbcTemplate、mybatis)
- Java構(gòu)建JDBC應(yīng)用程序的實例操作
- JDBC編程的詳細(xì)步驟
相關(guān)文章
SpringBoot使用Validation包進(jìn)行輸入?yún)?shù)校驗
Spring Boot 自帶的 spring-boot-starter-validation 包支持以標(biāo)準(zhǔn)注解的方式進(jìn)行輸入?yún)?shù)校驗,本文即關(guān)注 spring-boot-starter-validation 包所涵蓋的標(biāo)準(zhǔn)注解的使用、校驗異常的捕獲與展示、分組校驗功能的使用,以及自定義校驗器的使用,需要的朋友可以參考下2024-05-05
Netty事件循環(huán)主邏輯NioEventLoop的run方法分析
這篇文章主要介紹了Netty事件循環(huán)主邏輯NioEventLoop的run方法分析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-03-03
IDEA 創(chuàng)建一個Mybatis Maven項目的方法步驟(圖文)
這篇文章主要介紹了IDEA 創(chuàng)建一個Mybatis Maven項目的方法步驟(圖文),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03

