目錄
一、JDBC模板技術概述
1.1 什么是JDBC模板
二、JdbcTemplate使用實戰
2.1?基礎使用(手動創建對象)
2.2?使用Spring管理模板類
2.3 使用開源連接池(Druid)
三、模擬轉賬開發
3.1 基礎實現
3.1.1 Service層
3.1.2?Dao層
3.1.3 配置文件
3.1.4?測試代碼
3.2 使用JdbcDaoSupport實現
3.2.1 DAO層改進
3.2.2 配置文件
3.2.3?測試代碼
四、Spring框架的事務管理
4.1 事務管理相關API
4.1.1 PlatformTransactionManager接口
4.1.2 TransactionDefinition接口
4.2 聲明式事務管理
4.2.1 配置文件方式
4.2.2 配置文件+注解方式
4.2.3 純注解方式
4.3?轉賬案例與事務管理
4.3.1 Service層
4.3.2 Dao層
4.3.3 測試代碼
4.4 事務傳播行為
4.5 事務隔離級別
總結
一、JDBC模板技術概述
1.1 什么是JDBC模板
Spring框架提供的JdbcTemplate類簡化了傳統JDBC開發流程,解決了以下痛點:
-
自動管理連接資源(Connection/Statement/ResultSet)
-
自動處理JDBC異常(將Checked異常轉換為RuntimeException)
-
提供簡潔的API執行SQL語句
-
支持事務管理
二、JdbcTemplate使用實戰
2.1?基礎使用(手動創建對象)
1. 環境搭建(創建 maven 工程,引入坐標依賴)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.qcby</groupId><artifactId>springAOP03</artifactId><version>1.0-SNAPSHOT</version><dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.0.2.RELEASE</version></dependency><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.12</version></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.0.2.RELEASE</version></dependency><dependency><groupId>aopalliance</groupId><artifactId>aopalliance</artifactId><version>1.0</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>5.0.2.RELEASE</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.13</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.25</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-jdbc</artifactId><version>5.0.2.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-tx</artifactId><version>5.0.2.RELEASE</version></dependency><!-- 三、Druid開源的連接池 --><dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.10</version></dependency></dependencies></project>
2. 測試代碼
package com.qcby.demo1;import org.junit.Test;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;import java.util.List;
import java.util.Map;public class Demo1 {/*** 一、使用 new 對象方式完成*/@Testpublic void run1(){// 創建連接池對象,Spring 框架內置了連接池對象DriverManagerDataSource dataSource = new DriverManagerDataSource();// 設置 4 個參數dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");dataSource.setUrl("jdbc:mysql:///spring_db");dataSource.setUsername("root");dataSource.setPassword("12345");try {// 提供模板,創建對象JdbcTemplate template = new JdbcTemplate(dataSource);// 完成數據的增刪改查//1.新增
// template.update("insert into account values (null,?,?)","耶耶 ",1000);
// System.out.println("添加成功!");//2.刪除
// template.update("delete from account where name = ? ","耶耶");
// System.out.println("刪除成功!");//3.更新
// template.update("update account set money=money-? where name = ? ",100.00,"可可");
// System.out.println("更新成功!");//4.查詢List<Map<String, Object>> accounts=template.queryForList("select * from account");//遍歷結果集System.out.println("\n當前賬戶信息:");for (Map<String, Object> account:accounts){System.out.println("ID:"+ account.get("id") +";姓名:" + account.get("name") +";余額:" + account.get("money"));}}catch (Exception e){System.out.println("操作失敗!!!");e.printStackTrace();}}}
2.2?使用Spring管理模板類
1. 將數據源和模板類交給Spring容器管理
<?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"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><!-- 使用Spring框架管理 --><!-- 二、Spring管理內置的連接池 demo1_1.java測試--><!-- 配置連接池 --><bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"><property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql:///spring_db" /><property name="username" value="root" /><property name="password" value="12345" /></bean><!--配置 jdbc 模板 --><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource" /></bean> </beans>
2. 測試代碼
package com.qcby.demo1;import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import java.util.List;
import java.util.Map;@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "classpath:applicationContext_jdbc.xml")
public class Demo1_1 {@Autowiredprivate JdbcTemplate jdbcTemplate;/*** 測試的方式*/@Testpublic void run1(){try {jdbcTemplate.update("insert into account values (null,?,?)","圖圖",500);System.out.println("添加成功!");}catch (Exception e){e.printStackTrace();}}@Testpublic void run2(){try {jdbcTemplate.update("delete from account where name = ? ","圖圖");System.out.println("刪除成功!");}catch (Exception e){e.printStackTrace();}}@Testpublic void run3(){try {jdbcTemplate.update("update account set money=money-? where name = ? ",100.00,"可可");System.out.println("更新成功!");}catch (Exception e){e.printStackTrace();}}@Testpublic void run4(){try {List<Map<String, Object>> accounts=jdbcTemplate.queryForList("select * from account");//遍歷結果集System.out.println("\n當前賬戶信息:");for (Map<String, Object> account:accounts){System.out.println("ID:"+ account.get("id") +";姓名:" + account.get("name") +";余額:" + account.get("money"));}}catch (Exception e){e.printStackTrace();}}}
2.3 使用開源連接池(Druid)
1. 添加Druid依賴
<dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId><version>1.1.10</version>
</dependency>
2. 創建jdbc.properties
jdbc.driverClassName=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql:///spring_db
jdbc.username=root
jdbc.password=12345
3. Spring配置
<?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"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><!-- 三、Spring 框架管理開源的連接池 屬性文件:jdbc.properties--><!-- 第一種寫法 --><!-- 使用開源連接池<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql:///spring_db" /><property name="username" value="root" /><property name="password" value="12345" /></bean>--><!-- 加載屬性文件<bean id="placeholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"><property name="location" value="classpath:jdbc.properties" /></bean>--><!-- 第二種寫法:使用提供標簽的方式 --><!-- 引入屬性文件 --><context:property-placeholder location="classpath:jdbc.properties" /><!-- 加載屬性的文件 配置Druid數據源 --><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${jdbc.driverClassName}" /><property name="url" value="${jdbc.url}" /><property name="username" value="${jdbc.username}" /><property name="password" value="${jdbc.password}" /></bean><!-- 配置 jdbc 模板 --><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><!-- 注入屬性,set方法 --><property name="dataSource" ref="dataSource" /></bean></beans>
4. 測試
package com.qcby.demo1;import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "classpath:applicationContext_jdbc.xml")
public class Demo1_2 {@Autowiredprivate JdbcTemplate jdbcTemplate;/*** 測試的方式*/@Testpublic void run1(){jdbcTemplate.update("insert into account values (null,?,?)","熊四",800);System.out.println("新增成功!");}/*** 修改*/@Testpublic void run2(){jdbcTemplate.update("update account set name = ?,money = ? where id = ?","小鳳",100,7);System.out.println("修改成功!");}/*** 刪除*/@Testpublic void run3(){jdbcTemplate.update("delete from account where id = ?",9);System.out.println("刪除成功!");}/*** 通過 id 查詢*/@Testpublic void run4(){Account account = jdbcTemplate.queryForObject("select * from account where id = ?", new BeanMapper(), 6);System.out.println(account);}/*** 查詢所有的數據*/@Testpublic void run5(){List<Account> list = jdbcTemplate.query("select * from account", new BeanMapper());for (Account account : list) {System.out.println(account);}}
}/*** 實現類,用來進行數據封裝的*/
class BeanMapper implements RowMapper<Account> {/*** 是一行一行進行數據封裝的* @param resultSet* @param i* @return* @throws SQLException*/public Account mapRow(ResultSet resultSet, int i) throws SQLException {Account account = new Account();account.setId(resultSet.getInt("id"));account.setName(resultSet.getString("name"));account.setMoney(resultSet.getDouble("money"));return account;}}
三、模擬轉賬開發
3.1 基礎實現
3.1.1 Service層
package com.qcby.demo2.service;public interface AccountService {/*** 轉賬的方法* @param out 付款人* @param in 收款人* @param money 金額*/public void pay(String out,String in,double money);
}package com.qcby.demo2.service;import com.qcby.demo2.dao.AccountDao;public class AccountServiceImpl implements AccountService{private AccountDao accountDao;public void setAccountDao(AccountDao accountDao) {this.accountDao = accountDao;}/*** 轉賬方法* @param out 付款人* @param in 收款人* @param money 金額*/public void pay(String out, String in, double money) {// 調用 dao 方法accountDao.outMoney(out,money);accountDao.inMoney(in,money);}
}
3.1.2?Dao層
package com.qcby.demo2.dao;public interface AccountDao {/*** 付款* @param out* @param money*/public void outMoney(String out,double money);/*** 收款* @param in* @param money*/public void inMoney(String in,double money);}package com.qcby.demo2.dao;import org.springframework.jdbc.core.JdbcTemplate;public class AccountDaoImpl implements AccountDao{private JdbcTemplate jdbcTemplate;public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {this.jdbcTemplate = jdbcTemplate;}/*** 付款* @param out* @param money*/public void outMoney(String out, double money) {jdbcTemplate.update("update account set money = money - ? where name = ?",money,out);}/*** 收款* @param in* @param money*/public void inMoney(String in, double money) {jdbcTemplate.update("update account set money = money + ? where name = ?",money,in);}
}
3.1.3 配置文件
<?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"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><!--第二種寫法:使用提供標簽的方式--><context:property-placeholder location="classpath:jdbc.properties" /><!--加載屬性的文件--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${jdbc.driverClassName}" /><property name="url" value="${jdbc.url}" /><property name="username" value="${jdbc.username}" /><property name="password" value="${jdbc.password}" /></bean><!--配置 Jdbc 模板類--><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource" /></bean><!--配置 service--><bean id="accountService" class="com.qcby.demo2.service.AccountServiceImpl"><property name="accountDao" ref="accountDao"/></bean><!--配置 service--><bean id="accountDao" class="com.qcby.demo2.dao.AccountDaoImpl"><property name="jdbcTemplate" ref="jdbcTemplate" /></bean></beans>
3.1.4?測試代碼
package com.qcby.demo2;import com.qcby.demo2.service.AccountService;
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(value = "classpath:applicationContext_dao1.xml")
public class Demo2 {@Autowiredprivate AccountService accountService;/*** 測試轉賬的方法*/@Testpublic void testPay(){accountService.pay("熊大","熊二",100);}}
3.2 使用JdbcDaoSupport實現
3.2.1 DAO層改進
package com.qcby.demo2.dao;import org.springframework.jdbc.core.support.JdbcDaoSupport;//extends JdbcDaoSupport
public class AccountDaoImpl1 extends JdbcDaoSupport implements AccountDao{/*** 付款* @param out* @param money*/public void outMoney(String out, double money) {this.getJdbcTemplate().update("update account set money = money - ? where name = ?",money,out);}/*** 收款* @param in* @param money*/public void inMoney(String in, double money) {this.getJdbcTemplate().update("update account set money = money + ? where name = ?",money,in);}
}
3.2.2 配置文件
<?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"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsd"><!--第二種寫法:使用提供標簽的方式--><context:property-placeholder location="classpath:jdbc.properties" /><!--加載屬性的文件--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${jdbc.driverClassName}" /><property name="url" value="${jdbc.url}" /><property name="username" value="${jdbc.username}" /><property name="password" value="${jdbc.password}" /></bean><!--配置 Jdbc 模板類<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource" /></bean>--><!--配置 service--><bean id="accountService" class="com.qcby.demo2.service.AccountServiceImpl"><property name="accountDao" ref="accountDao"/></bean><!--配置 dao<bean id="accountDao" class="com.qcby.demo2.dao.AccountDaoImpl"><property name="jdbcTemplate" ref="jdbcTemplate" /></bean>--><!-- 在accountDaoImpl中extends JdbcDaoSupport --><bean id="accountDao" class="com.qcby.demo2.dao.AccountDaoImpl1"><property name="dataSource" ref="dataSource"/></bean></beans>
3.2.3?測試代碼
package com.qcby.demo2;import com.qcby.demo2.service.AccountService;
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(value = "classpath:applicationContext_dao2.xml")
public class Demo2_1 {@Autowiredprivate AccountService accountService;/*** 測試轉賬的方法*/@Testpublic void testPay(){accountService.pay("熊大","熊二",100);}
}
四、Spring框架的事務管理
4.1 事務管理相關API
4.1.1 PlatformTransactionManager接口
這是Spring事務管理的核心接口,根據不同的持久層框架有不同的實現:
-
DataSourceTransactionManager
:用于JDBC -
HibernateTransactionManager
:用于Hibernate -
JpaTransactionManager
:用于JPA
4.1.2 TransactionDefinition接口
定義事務屬性:
-
事務隔離級別
-
事務傳播行為
-
事務超時時間
-
是否只讀事務
4.2 聲明式事務管理
4.2.1 配置文件方式
<!-- applicationContext_1.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"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd"><!--第二種寫法:使用提供標簽的方式--><context:property-placeholder location="classpath:jdbc.properties" /><!--加載屬性的文件--><bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName" value="${jdbc.driverClassName}" /><property name="url" value="${jdbc.url}" /><property name="username" value="${jdbc.username}" /><property name="password" value="${jdbc.password}" /></bean><!--配置平臺事務管理器--><bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" /></bean><!--配置事務的通知(沒有自己編寫切面類,通知方法也不是自己編寫,Spring框架提供的)--><tx:advice id="txAdvice" transaction-manager="transactionManager"><tx:attributes><!--對 pay 進行增強,設置隔離級別,傳播行為,超時的時間--><tx:method name="pay" isolation="DEFAULT" propagation="REQUIRED" /><tx:method name="find*" read-only="true" /></tx:attributes></tx:advice><!-- 配置 AOP 的增強 --><aop:config><!-- Spring 框架提供系統通知,使用 advisor 標簽 --><aop:advisor advice-ref="txAdvice" pointcut="execution(public * com.qcby.demo3.AccountServiceImpl.pay(..))" /></aop:config><!-- 配置 service --><bean id="accountService" class="com.qcby.demo3.AccountServiceImpl"><property name="accountDao" ref="accountDao"/></bean><!-- 配置 dao --><bean id="accountDao" class="com.qcby.demo3.AccountDaoImpl"><property name="dataSource" ref="dataSource" /></bean></beans>
4.2.2 配置文件+注解方式
<?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"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsdhttp://www.springframework.org/schema/aophttp://www.springframework.org/schema/aop/spring-aop.xsdhttp://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd"><!--開啟注解的掃描--><context:component-scan base-package="com.qcby.demo3" /><!--第二種寫法:使用提供標簽的方式--><context:property-placeholder location="classpath:jdbc.properties" /><!--加載屬性的文件--><bean id="dataSource"class="com.alibaba.druid.pool.DruidDataSource"><property name="driverClassName"value="${jdbc.driverClassName}" /><property name="url" value="${jdbc.url}" /><property name="username" value="${jdbc.username}" /><property name="password" value="${jdbc.password}" /></bean><!--配置平臺事務管理器--><bean id="transactionManager"class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><property name="dataSource" ref="dataSource" /></bean><!--配置 Jdbc 模板類--><bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><property name="dataSource" ref="dataSource" /></bean><!--開啟事務注解的支持--><tx:annotation-driven transaction-manager="transactionManager" /></beans>
package com.qcby.demo3;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;@Service
@Transactional(isolation = Isolation.DEFAULT)
public class AccountServiceImpl implements AccountService{@Autowiredprivate AccountDao accountDao;/*** 轉賬方法* @param out 付款人* @param in 收款人* @param money 金額*/public void pay(String out, String in, double money) {// 調用 dao 方法accountDao.outMoney(out,money);// int a = 10 / 0;accountDao.inMoney(in,money);// 模擬在轉賬過程中發生異常//int a = 10 / 0;}
}
4.2.3 純注解方式
package com.qcby.demo3;import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;import javax.annotation.Resource;
import javax.sql.DataSource;/*** 配置類* @author Administrator*/
@Configuration
@ComponentScan(basePackages="com.qcby.demo3")
@EnableTransactionManagement // 開啟事務注解
public class SpringConfig {/*** @return* @throws Exception*/@Bean(name="dataSource")public DataSource createDataSource() throws Exception{// 創建連接池對象,Spring 框架內置了連接池對象DriverManagerDataSource dataSource = new DriverManagerDataSource();// 設置 4 個參數dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");dataSource.setUrl("jdbc:mysql:///spring_db");dataSource.setUsername("root");dataSource.setPassword("12345");return dataSource;}/*** 創建模板對象* @return*/@Resource(name="dataSource") // 不僅可以作用在屬性上,也可以作用方法上。@Bean(name="jdbcTemplate") // 把 JdbcTemplate 保存到 IOC容器中public JdbcTemplate createJdbcTemplate(DataSource dataSource){JdbcTemplate template = new JdbcTemplate(dataSource);return template;}/*** 創建平臺事務管理器對象* @param dataSource* @return*/@Resource(name="dataSource")@Bean(name="transactionManager")public PlatformTransactionManagercreateTransactionManager(DataSource dataSource){DataSourceTransactionManager manager = new DataSourceTransactionManager(dataSource);return manager;}}
4.3?轉賬案例與事務管理
4.3.1 Service層
package com.qcby.demo3;public interface AccountService {public void pay(String out, String in, double money);
}package com.qcby.demo3;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;@Service
@Transactional(isolation = Isolation.DEFAULT)
public class AccountServiceImpl implements AccountService{@Autowiredprivate AccountDao accountDao;/*** 轉賬方法* @param out 付款人* @param in 收款人* @param money 金額*/public void pay(String out, String in, double money) {// 調用 dao 方法accountDao.outMoney(out,money);// int a = 10 / 0;accountDao.inMoney(in,money);// 模擬在轉賬過程中發生異常//int a = 10 / 0;}
}
4.3.2 Dao層
package com.qcby.demo3;public interface AccountDao {/*** 付款* @param out* @param money*/public void outMoney(String out, double money);/*** 收款* @param in* @param money*/public void inMoney(String in, double money);}package com.qcby.demo3;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;@Repository
public class AccountDaoImpl implements AccountDao {@Autowiredprivate JdbcTemplate jdbcTemplate;/*** 付款* @param out* @param money*/public void outMoney(String out, double money) {jdbcTemplate.update("update account set money = money - ? where name = ?",money,out);}/*** 收款* @param in* @param money*/public void inMoney(String in, double money) {jdbcTemplate.update("update account set money = money + ? where name = ?",money,in);}
}
4.3.3 測試代碼
package com.qcby.demo3;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;// 1.配置文件+注解方式
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "classpath:applicationContext_2.xml")// 2.純注解方式
//@RunWith(SpringJUnit4ClassRunner.class)
//@ContextConfiguration(classes = SpringConfig.class)public class Demo3 {@Autowiredprivate com.qcby.demo3.AccountService accountService;/*** 測試轉賬的方法*/@Testpublic void testPay() {try {accountService.pay("熊大", "熊二", 100);System.out.println("轉賬成功!!!");} catch (Exception e) {e.printStackTrace();System.out.println("轉賬失敗,事務已回滾!");}}
}
4.4 事務傳播行為
Spring定義了7種事務傳播行為:
-
??PROPAGATION_REQUIRED??(默認):如果當前存在事務,則加入該事務;如果當前沒有事務,則創建一個新的事務。
-
??PROPAGATION_SUPPORTS??:如果當前存在事務,則加入該事務;如果當前沒有事務,則以非事務的方式繼續運行。
-
??PROPAGATION_MANDATORY??:如果當前存在事務,則加入該事務;如果當前沒有事務,則拋出異常。
-
??PROPAGATION_REQUIRES_NEW??:創建一個新的事務,如果當前存在事務,則把當前事務掛起。
-
??PROPAGATION_NOT_SUPPORTED??:以非事務方式運行,如果當前存在事務,則把當前事務掛起。
-
??PROPAGATION_NEVER??:以非事務方式運行,如果當前存在事務,則拋出異常。
-
??PROPAGATION_NESTED??:如果當前存在事務,則在嵌套事務內執行;如果當前沒有事務,則創建一個新的事務。
4.5 事務隔離級別
Spring支持以下隔離級別:
-
??ISOLATION_DEFAULT??:使用底層數據庫的默認隔離級別。
-
??ISOLATION_READ_UNCOMMITTED??:允許讀取未提交的數據變更,可能導致臟讀、幻讀或不可重復讀。
-
??ISOLATION_READ_COMMITTED??:只能讀取已提交的數據,防止臟讀,但可能發生幻讀和不可重復讀。
-
??ISOLATION_REPEATABLE_READ??:對同一字段的多次讀取結果都是一致的,除非數據被當前事務修改,防止臟讀和不可重復讀,但可能發生幻讀。
-
??ISOLATION_SERIALIZABLE??:最高的隔離級別,完全服從ACID的隔離級別,確保事務串行執行,防止臟讀、不可重復讀和幻讀。
總結
????????本文詳細介紹了Spring框架中JDBC模板技術的使用方法,Spring的JDBC模板技術大大簡化了數據庫操作代碼,使開發者能夠更專注于業務邏輯而非底層數據庫交互細節。聲明式事務管理則提供了靈活的事務控制方式,確保數據的一致性和完整性。
????????在實際開發中,建議優先使用注解方式配置事務,它更簡潔直觀。對于復雜的事務需求,可以結合使用@Transactional
的各種屬性進行精細化控制。同時,合理選擇事務隔離級別和傳播行為,可以在保證數據安全的前提下提高系統性能。