基本介紹:通過 Spring 框架可以配置數據源,從而完成對數據表的操作。JdbcTemplate 是 Spring 提供的訪問數據庫的技術。將 JDBC 的常用操作封裝為模板方法
1 JdbcTemplate 使用前需進行如下配置
<dependencies><!--加入c3p0數據源包--><dependency><groupId>com.mchange</groupId><artifactId>c3p0</artifactId><version>0.9.5.2</version></dependency><!--加入mysql連接--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.31</version></dependency><!--加入spring新增的依賴 spring-jdbc,這個依賴中有JdbcTemplate--><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.3.8</version></dependency><!--加入spring開發的基本包--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.8</version></dependency><!--加入spring開發切面編程需要的包--><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.3.8</version></dependency>
</dependencies>
1.2?創建配置文件 resources/jdbc.properties
#配置用戶名
jdbc.userName=root
#密碼
jdbc.password=123456
jdbc.driverClass=com.mysql.cj.jdbc.Driver
#指定要連接的數據庫,這里連接spring數據庫
jdbc.url=jdbc:mysql://localhost:3306/spring
1.3?創建配置文件 resources/JdbcTemplate_ioc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"><!--引入外部的jdbc.properties配置文件--><context:property-placeholder location="classpath:jdbc.properties"/><!--配置數據源對象-DataSource--><bean class="com.mchange.v2.c3p0.ComboPooledDataSource" id="dataSource"><property name="user" value="${jdbc.userName}"/><property name="password" value="${jdbc.password}"/><property name="driverClass" value="${jdbc.driverClass}"/><property name="jdbcUrl" value="${jdbc.url}"/></bean><!--配置JdbcTemplate對象--><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><!--給jdbcTemplate對象配置dataSource屬性--><property name="dataSource" ref="dataSource"/></bean>
</beans>
接下來就可以開始使用JdbcTemplate對象來操作數據庫了!
2??JdbcTemplate對象常用操作
為成功演示以下操作,需提前進行如下準備
執行如下sql語句
-- 創建數據庫
CREATE DATABASE spring;
USE spring
-- 創建表 monster
CREATE TABLE monster(
id INT PRIMARY KEY, `name` VARCHAR(64) NOT NULL DEFAULT '',
skill VARCHAR(64) NOT NULL DEFAULT '' )CHARSET=utf8mb4
INSERT INTO monster VALUES(100, '青牛怪', '吐火');
INSERT INTO monster VALUES(200, '黃袍怪', '吐煙');
INSERT INTO monster VALUES(300, '蜘蛛怪', '吐絲');
創建Monster.java
package com.spring.bean;public class Monster {private Integer monsterId;private String name;private String skill;//全參構造器public Monster(Integer monsterId, String name, String skill) {this.monsterId = monsterId;this.name = name;this.skill = skill;}//無參構造器一定要寫,Spring反射創建對象時,需要使用public Monster() {}public Integer getMonsterId() {return monsterId;}public void setMonsterId(Integer monsterId) {this.monsterId = monsterId;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getSkill() {return skill;}public void setSkill(String skill) {this.skill = skill;}@Overridepublic String toString() {return "Monster{" +"monsterId=" + monsterId +", name='" + name + '\'' +", skill='" + skill + '\'' +'}';}
}
2.1?添加單條數據
2.1.3 方式1:使用 void execute(final String sql) 方法
//測試通過JdbcTemplate對象完成添加數據
@Test
public void addDataByJdbcTemplate(){//獲取到容器ApplicationContext ioc = new ClassPathXmlApplicationContext("jdbcTemplate_ioc.xml");JdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class);//添加方式1String sql = "INSERT INTO monster VALUES(400, '紅孩兒', '槍法')";jdbcTemplate.execute(sql);
}
2.1.3 方式2:使用 int update(String sql, @Nullable Object... args) 方法
//測試通過JdbcTemplate對象完成添加數據@Testpublic void addDataByJdbcTemplate(){//獲取到容器ApplicationContext ioc = new ClassPathXmlApplicationContext("jdbcTemplate_ioc.xml");JdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class);//添加方式2String sql = "INSERT INTO monster VALUES(?, ?, ?)";int affected = jdbcTemplate.update(sql, 500, "紅孩兒2", "槍法2");System.out.println("add ok affected = " + affected);}
2.2 修改數據
方法:public int update(String sql, @Nullable Object... args)
//測試通過JdbcTemplate對象完成修改數據
@Test
public void updateDataByJdbcTemplate(){//獲取到容器ApplicationContext ioc = new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");//獲取JdbcTemplate對象JdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class);//組織sqlString sql = "UPDATE monster SET skill = ? WHERE id = ?";int affected = jdbcTemplate.update(sql, "美人計", 300);System.out.println("update ok affected = " + affected);
}
2.3?批量添加數據
方法:public int[] batchUpdate(String sql, List<Object[]> batchArgs)
//測試通過JdbcTemplate對象完成批量添加數據
@Test
public void addBatchDataByJdbcTemplate(){//獲取到容器ApplicationContext ioc = new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");//獲取JdbcTemplate對象JdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class);String sql = "INSERT INTO monster VALUES(?, ?, ?)";List<Object[]> batchArgs = new ArrayList<>();batchArgs.add(new Object[]{600, "紅孩兒3", "槍法3"});batchArgs.add(new Object[]{700, "紅孩兒4", "槍法4"});batchArgs.add(new Object[]{800, "紅孩兒5", "槍法5"});jdbcTemplate.batchUpdate(sql,batchArgs);System.out.println("batch add ok...");
}
2.4 將單條查詢數據封裝到對象
方法:public <T> T queryForObject(String sql, RowMapper<T> rowMapper, Object... args)
//查詢id=100的monster并封裝到Monster實體對象
@Test
public void selectDataByJdbcTemplate() {ApplicationContext ioc = new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");//得到JdbcTemplate beanJdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class);//組織SQL//通過BeanPropertyRowMapper獲取,rowmapper 是一個接口,可以將查詢的結果,封裝到你指定的Monster對象中.//1. 確定API : queryForObject()//public <T> T queryForObject(String sql, RowMapper<T> rowMapper, @Nullable Object... args)//2.準備參數String sql = "SELECT id AS monsterId, `NAME`, skill FROM monster WHERE id = ?";//使用RowMapper 接口來對返回的數據,進行一個封裝-》底層使用的反射->setter//這里有一個細節: 你查詢的記錄的表的字段需要和 Monster的對象字段名保持一致RowMapper<Monster> rowMapper = new BeanPropertyRowMapper<>(Monster.class);//jdbcTemplateMonster monster = jdbcTemplate.queryForObject(sql, rowMapper, 100);System.out.println("monster= " + monster);System.out.println("查詢ok");
}
2.5 封裝多條查詢數據
方法:public <T> T query(String sql, RowMapper<T> rowMapper, Object... args)
//查詢id>=200的monster并封裝到Monster實體對象
/*** 查詢多條記錄*/
@Test
public void selectMulDataByJdbcTemplate() {ApplicationContext ioc =new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");//得到JdbcTemplate beanJdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class);//組織SQL//通過BeanPropertyRowMapper獲取rowmapper 是一個接口,可以將查詢的結果,封裝到你指定的Monster對象中.//1. 確定API//public <T> T query(String sql, RowMapper<T> rowMapper, Object... args){}//2. 組織參數String sql = "SELECT id AS monsterId, `NAME`, skill FROM monster WHERE id >= ?";RowMapper<Monster> rowMapper = new BeanPropertyRowMapper<>(Monster.class);//3. 調用List<Monster> monsterList = jdbcTemplate.query(sql, rowMapper, 100);for (Monster monster : monsterList) {System.out.println("monster= " + monster);}
}
2.6?查詢返回結果只有一行一列的值
方法:public <T> T queryForObject(String sql, Class<T> requiredType)
//查詢返回結果只有一行一列的值,比如查詢id=100的怪物名
/*** 查詢返回結果只有一行一列的值*/
@Test
public void selectScalarByJdbcTemplate() {ApplicationContext ioc =new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");//得到JdbcTemplate beanJdbcTemplate jdbcTemplate = ioc.getBean(JdbcTemplate.class);//1. 確定API//public <T> T queryForObject(String sql, Class<T> requiredType)//2. 提供參數String sql = "SELECT NAME FROM monster WHERE id = 100";//Class<T> requiredType 表示你返回的單行單列的數據類型String name =jdbcTemplate.queryForObject(sql, String.class);System.out.println("返回name= " + name);
}
2.7 使用具名參數完成操作
在resources/JdbcTemplate_ioc.xml配置文件中增加如下配置
<!--配置NameParameterJdbcTemplate,支持具名參數-->
<bean class="org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate"id="namedParameterJdbcTemplate"><!--這里需要使用構造器關聯數據源--><constructor-arg name="dataSource" ref="dataSource"/>
</bean>
方法:public int update(String sql, Map<String, ?> paramMap)
/*** 使用Map傳入具名參數完成操作,比如添加*/
@Test
public void testDataByNamedParameterJdbcTemplate() {ApplicationContext ioc =new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");//得到NamedParameterJdbcTemplate beanNamedParameterJdbcTemplate namedParameterJdbcTemplate =ioc.getBean(NamedParameterJdbcTemplate.class);//1. 確定使用API//public int update(String sql, Map<String, ?> paramMap)//2. 準備參數 [:my_id, :name, :skill] 要求按照規定的名字來設置參數String sql = "INSERT INTO monster VALUES(:id, :name, :skill)";Map<String, Object> paramMap = new HashMap<>();//給paramMap填寫數據paramMap.put("id", 900);paramMap.put("name", "螞蟻精");paramMap.put("skill", "喜歡打洞");//3. 調用int affected = namedParameterJdbcTemplate.update(sql, paramMap);System.out.println("add ok affected=" + affected);
}
2.8?使用sqlparametersoruce 來封裝具名參數
方法:public int update(String sql, SqlParameterSource paramSource)
//使用sqlparametersoruce 來封裝具名參數,還是添加一個Monster 狐貍精
@Test
public void operDataBySqlparametersoruce() {ApplicationContext ioc =new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");//得到NamedParameterJdbcTemplate beanNamedParameterJdbcTemplate namedParameterJdbcTemplate =ioc.getBean(NamedParameterJdbcTemplate.class);//確定API//public int update(String sql, SqlParameterSource paramSource)//public BeanPropertySqlParameterSource(Object object)//準備參數String sql = "INSERT INTO monster VALUES(:monsterId, :name, :skill)";Monster monster = new Monster(1000, "大象精", "搬運木頭");SqlParameterSource sqlParameterSource =new BeanPropertySqlParameterSource(monster);//調用int affected =namedParameterJdbcTemplate.update(sql, sqlParameterSource);System.out.println("add ok affected= " + affected);
}
2.9?Dao 對象中使用 JdbcTemplate 完成對數據的操作
創建 MonsterDao.java
package com.spring.dao;import com.spring.bean.Monster;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;import javax.annotation.Resource;public class MonsterDao {private JdbcTemplate jdbcTemplate;//完成保存任務public void save(Monster monster){//組織sqlString sql = "INSERT INTO monster VALUES(?,?,?)";jdbcTemplate.update(sql, monster.getMonsterId(), monster.getName(), monster.getSkill());}public JdbcTemplate getJdbcTemplate() {return jdbcTemplate;}public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}
}
在resources/JdbcTemplate_ioc.xml配置文件中增加如下配置
<!--配置monsterDao對象-->
<bean class="com.spring.dao.MonsterDao" id="monsterDao"><property name="jdbcTemplate" ref="jdbcTemplate"/>
</bean>
編寫測試代碼:
//測試MonsterDAO
@Test
public void monsterDaoSave() {ApplicationContext ioc =new ClassPathXmlApplicationContext("JdbcTemplate_ioc.xml");MonsterDao monsterDao = ioc.getBean(MonsterDao.class);Monster monster = new Monster(1100, "小鴨精", "吃魚");monsterDao.save(monster);System.out.println("MonsterDAO保存 ok ..");
}
3 聲明式事務
3.1 聲明式事務的基本使用
(1)在xml文件中加入以下配置
<!--配置事務管理器-對象
1. DataSourceTransactionManager 這個對象是進行事務管理-debug源碼
2. 一定要配置數據源屬性,這樣指定該事務管理器 是對哪個數據源進行事務控制
-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager"><property name="dataSource" ref="dataSource"/>
</bean><!--配置啟動基于注解的聲明式事務管理功能-->
<tx:annotation-driven transaction-manager="transactionManager"/>
(2)在方法上添加注解@Transaction ,即可將該方法作為一個事務來處理(前提是該方法所在的類被注入到了容器中)
/*** @Transactional 注解解讀* 1. 使用@Transactional 可以進行聲明式事務控制* 2. 即將標識的方法中的,對數據庫的操作作為一個事務管理* 3. @Transactional 底層使用的仍然是AOP機制* 4. 底層是使用動態代理對象來調用buyGoodsByTx* 5. 在執行buyGoodsByTx() 方法 先調用 事務管理器的 doBegin() , 調用 buyGoodsByTx()* 如果執行沒有發生異常,則調用 事務管理器的 doCommit(), 如果發生異常 調用事務管理器的 doRollback()進行回調*/
@Transactional
public void buyGoodsByTx(int userId, int goodsId, int amount) {//輸出購買的相關信息System.out.println("用戶購買信息 userId=" + userId+ " goodsId=" + goodsId + " 購買數量=" + amount);//1.得到商品的價格Float price = goodsDao.queryPriceById(userId);//2. 減少用戶的余額goodsDao.updateBalance(userId, price * amount);//3. 減少庫存量goodsDao.updateAmount(goodsId, amount);System.out.println("用戶購買成功~");
}
3.2 事務的傳播機制
當有多個事務處理并存時,可以使用事務的傳播機制去控制,比如用戶去購買兩次商品(使用不同的方法), 每個方法都是一個事務
(1)事務傳播機制種類

- REQUIRED 和 REQUIRED_NEW 傳播機制示意圖
- 事務的傳播機制的設置方法
- REQUIRES_NEW 和 REQUIRED 在處理事務的策略?
3.3 事務隔離級別
3.3.1 事務隔離級別種類(事務相關知識點這篇有詳細介紹mysql基礎知識匯總)
?3.3.2?事務隔離級別的設置
@Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.DEFAULT)
public void buyGoodsByTx2(int userId, int goodsId, int amount) {
在默認情況下 聲明式事務的隔離級別是 REPEATABLE_READ . 我們將buyGoodsByTxISOLATION的隔離級別設置為 Isolation.READ_COMMITTED?,表示只要是提交的數據,在當前事務是可以讀取到最新數據
@Transactional(isolation = Isolation.READ_COMMITTED)
public void buyGoodsByTxISOLATION() {
3.4?事務的超時回滾
介紹:如果一個事務執行的時間超過某個時間限制,就讓該事務回滾。
設置方式如下:
timeout = 2 表示 buyGoods如果執行時間超過了2秒?, 該事務就進行回滾. 如果沒有設置 timeout, 默認是 -1,表示使用事務的默認超時時間, 或者不支持