代码仓库: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);
}
}