代碼倉庫:https://gitee.com/DerekAndroid/SpringTransaction.git
spring的聲明式事務AOP-maven版-代碼倉庫:https://gitee.com/DerekAndroid/springaction.git
關係圖
一: spring的 jdbcTemplate
一: spring的 jdbcTemplate
jdbcTemplate是spring提供的dao層用來和數據庫數據交互的技術
回顧dao層操作數據庫數據的技術:
1 jdbc+c3p0 任何代碼要想操作數據庫的數據都得遵循jdbc規範
2 dbutils apache組織提供的對jdbc+c3p0的封裝
3 hibernate 對jdbc+c3p0的封裝
4 jdbctemplate spring對jdbc+c3p0的封裝
5 hibernateTemplate spring對hibernate又封裝一次
6 mybatis 對jdbc+c3p0的封裝
7 SqlMapClientTemplate sping對mybatis又封裝一次
A mybatis
B dbutils
c hibernateTemplate
jdbctemplate -------- dbutils
dbutils: apache
QueryRunner qr=new QueryRunner();
qr.setDataSource(連接池)
String sql="crud"
qr.update()
qr.query()
jdbctemplate: spring
jdbctemplate qr=new jdbctemplate();
qr.setDataSource(連接池)
String sql="crud"
qr.update()
qr.query()
jdbctemplate的開發步驟:
1 導包
spring-jdbc.jar
spring-tx.jar
2 對數據庫的數據進行crud操作
3 作業: 在dao層使用jdbctempate對數據庫數據進行crud操作
寫2遍即可
jdbctemplate在dao層有2種注入方式:
set方式注入 能使用註解
繼承的方式 讓jdbctemplate繼承JdbcDaoSupport 不能使用註解
hibernateTempalte也有這2中方式
二: spring的聲明式事務
回顧事務: 面試題
事務有什麼特性: ACID
原子性: 一件完成的事情,要不全部成功 要不全部失敗
轉賬: 加錢 減錢
一致性: 事務的前後,數據總數不發生變化
jack 1000 rose 1000 2000
jack 500 rose 1500 2000
持久性: 只要事務已完成(提交),數據就會到數據庫中
隔離性: 事務具備隔離性,如果沒有隔離性,會發送讀取數據的問題
不具備隔離性的話,會發生什麼問題:
會發送讀取數據的問題
髒讀: 在一個事務中,讀取到了另一個事務還沒有提交的數據 必須杜絕的
ps:所有的數據庫都自動避免的髒讀
重複讀:在一個事務中,2次讀取到的數據內容不一致(update) 可以允許
虛讀/幻讀:在一個事務中,2次讀取到的數據內容不一致(insert) 可以允許
可以通過調整數據庫的隔離級別,避免以上問題的發生:
設置隔離級別
read uncommitted 效率最高 安全最低
read committed oracle
repeatable read mysql
serializable 安全最高 效率最低
事務的編寫:
1 獲取連接
2 通過連接開啓事務
con.setAutoCommit(false)
con.commit()
con.rollback()
開啓事務(spring提供的方法) 增強的方法
save() 切入點
提交事務(spring提供的方法) 增強的方法
我得讓spring提供的事務方法在指定的save之前執行,在save之後執行 ------AOP
以前的事務都得自己來編寫操作,那spring給我們提供好了一套操作事務的封裝,只要拿過來用即可
spring給我們提供了4種操作事務的方式:(掌握xml+註解)
API的方式
硬編碼方式
,可以將提供好的API以代碼的方式進行事務的控制 (沒人用) 純編碼方式
package cn.itcast.jdbctempalte;
import java.beans.PropertyVetoException;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import com.mchange.v2.c3p0.ComboPooledDataSource;
public class Demo
{
@Test // 硬編碼的方式
public void test1() throws Exception
{
// c3p0
ComboPooledDataSource ds = new ComboPooledDataSource(); //ioc
ds.setDriverClass("com.mysql.jdbc.Driver");
ds.setJdbcUrl("jdbc:mysql://localhost:3306/spring04");
ds.setUser("root"); // di
ds.setPassword("1234");
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(ds);
String sql="insert into account values(?,?)";
jdbcTemplate.update(sql, "jack",1000);
}
@Test // ioc+di
public void test2()
{
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
JdbcTemplate jdbcTemplate =(JdbcTemplate)context.getBean("jdbcTemplate");
String sql="insert into account values(?,?)";
jdbcTemplate.update(sql, "rose",1000);
}
}
純註解方式
PlatformTransactionManager: 平臺事務管理器 spring提供接口 封裝事務的方法
提交方法
回滾方法
我們要用只能找這個接口的實現類來用:
DataSourceTransactionManager: dbutils jdbcTempalte connnection
切面類 提交方法 回滾方法 通知/增強
HibernateTransactionManager: hibernate hibernateTemplate session
具體實現代碼:
package cn.itcast.springconfig;
import java.beans.PropertyVetoException;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Qualifier;
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.transaction.annotation.EnableTransactionManagement;
import com.mchange.v2.c3p0.ComboPooledDataSource;
@Configuration
@ComponentScan(basePackages="cn.itcast")
@EnableTransactionManagement //<tx:annotation-driven transaction-manager="transactionManager"/>
public class SpringConfig
{
// 創建出來c3p0 給spring
@Bean(name="c3p0")
public DataSource createDataSourceC3p0() throws PropertyVetoException
{
ComboPooledDataSource ds = new ComboPooledDataSource(); //ioc
ds.setDriverClass("com.mysql.jdbc.Driver");
ds.setJdbcUrl("jdbc:mysql://localhost:3306/spring04");
ds.setUser("root"); // di
ds.setPassword("1234");
return ds;
}
@Bean(name="jdbcTemplate")
public JdbcTemplate createJdbcTemplate(@Qualifier("c3p0") DataSource ds) // 使用註解問spring要
{
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(ds);
return jdbcTemplate;
}
@Bean(name="transactionManager")
public DataSourceTransactionManager createDataSourceTransactionManager(@Qualifier("c3p0") DataSource ds) // 使用註解問spring要
{
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(ds);
return dataSourceTransactionManager;
}
}
xml方式 (重點掌握)
底層就是API的封裝 直接以xml方式告訴給sping即可 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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- spring加載src下的properties文件 -->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!-- c3p0 -->
<bean id="c3p0" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- jdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="c3p0"></property>
</bean>
<!-- tranFerDao -->
<bean id="tranFerDao" class="cn.itcast.daoimpl.TranFerDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<!-- tranFerService 目標類 -->
<bean id="tranFerService" class="cn.itcast.serviceimpl.TranFerServiceImpl">
<property name="tranFerDao" ref="tranFerDao"></property>
</bean>
<!-- 切面類 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="c3p0"></property>
</bean>
<!-- 配置DataSourceTransactionManager裏面事務方法的一些參數
不寫 該方法使用的事務參數都是默認值
-->
<tx:advice transaction-manager="transactionManager" id="txadvice">
<tx:attributes>
<tx:method name="tranfer"/>
</tx:attributes>
</tx:advice>
<!-- 織入 -->
<aop:config>
<aop:pointcut expression="execution(* cn.itcast.serviceimpl.TranFerServiceImpl.tranfer(..))" id="pointcut"/>
<!-- 針對事務的配置標籤 -->
<aop:advisor advice-ref="txadvice" pointcut-ref="pointcut"/>
</aop:config>
</beans>
註解配置: xml+註解(企業開發)
別人的 用xml
自己的 用註解
<context:component-scan base-package="cn.itcast"></context:component-scan>
事務的註解2步:
指定開啓事務的註解 告訴spirng使用的事務方法是誰的方法
<tx:annotation-driven transaction-manager="PlatformTransactionManager"/>
在方法上或則是類上配置:
@Transactional
<?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:tx="http://www.springframework.org/schema/tx"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 別人的類 -->
<!-- spring加載src下的properties文件 -->
<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
<!-- c3p0 -->
<bean id="c3p0" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- jdbcTemplate -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="c3p0"></property>
</bean>
<!-- 切面類 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="c3p0"></property>
</bean>
<!-- 自己的類 -->
<!-- 開啓註解掃描器 -->
<context:component-scan base-package="cn.itcast"></context:component-scan>
<!-- 開啓事務的註解配置 告訴使用的是哪個類下的事務 -->
<tx:annotation-driven transaction-manager="transactionManager"/>
</beans>
在需要用到的類上配置
package cn.itcast.serviceimpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import cn.itcast.dao.TranFerDao;
import cn.itcast.service.TranFerService;
@Service("tranFerService")
@Transactional
public class TranFerServiceImpl implements TranFerService
{
@Autowired
private TranFerDao tranFerDao;
public void tranfer(String toUser,String inUser,double money)
{
// 減錢
tranFerDao.toMoney(toUser,money);
int i=1/0;
// 加錢
tranFerDao.inMoney(inUser,money);
}
}
junit測試
ps:junit報錯,idea需要同事引入junit-4.12.jar+hamcrest-core-1.3.jar
package cn.itcast.test;
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;
import cn.itcast.service.TranFerService;
@ContextConfiguration("classpath:applicationContext.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringJunit
{
@Autowired
private TranFerService tranFerService;
@Test
public void test()
{
tranFerService.tranfer("jack", "rose", 500);
}
}