1.事物:事物是一系列的动做,他们综合在一块儿才是一个完整的单元,这些动做必须所有完成,若是有一个失败的话,事物就会回滚到最初始的状态,仿佛什么都没有发生过。java
事物有四个特性:spring
2.spring中事物有5个隔离级别sql
隔离级别是指若干个并发事物之间的隔离水平,TransactionDefinition介绍了五种隔离级别数据库
1)ISOLATION_DEFAULT:这是默认值,表示采用使用的数据库的默认的隔离级别编程
2)ISOLACTION_COMMITTED(读已提交):容许读取其余并发事物已经提交的更新(防止脏读)并发
3)ISOLACTION_UNCOMMITTED(读未已提交):容许读取其余并发事物还未提交的更新,会致使事物之间的3个缺陷发生,这是速度最快的一个隔离级别,但也是隔离级别最低的一个。app
4)ISOLACTION_REPEATABLE_READ(重复读):除非事物自身修改了数据,不然规定事物屡次重复读取数据必须相同,(防此脏读,不可重复读) 框架
5)ISOLACTION_SERIALIZABLE(串行化):这是最高的隔离级别,它能够防止脏读、不可重复读和幻读。性能
注意:并非全部的资源管理器都支持全部的隔离级别,可针对不一样的资源管理使用以上的隔离级别。 测试
3.事物的只读性
在对数据库的操做中,查询是使用最频繁的操做,每次执行查询时都要从数据库中从新读取数据,有时屡次读取的数据都是相同的,这样的数据操做不只浪费了系统资源,还影响了系统速度。对访问量大的程序来讲,节省这部分资源能够大大提 升系统速度。
若是将事物声明为只读的,那么数据库能够根据事物的特性优化事物的读取操做,事物的只读性须要配合事物的传播行为共同设置
<prop key="query">PROPAGATION_REQUIRED,readOnly<prop>
4.事物的超时性
这个属性和事务的只读属性同样须要搭配事务的传播行为共同设置,它设置了事务的超时时间,事务自己可能会因某种缘由很长没有回应,在这期间事务可能锁定了数据库的表格,这样会出现严重的性能问题。经过设置事务的超时时间,从开始执行事务起,在规定的超时时间内若是没有事务就将它回滚。事务的超时属性以timeout_为前缀和一个整型数字定义,例如:
<prop key="query*">PROPAGATION_REGUIRED,timeout_5,readOnly</prop>
5.编程式事物和声明式事物
编程式事物: Transaction tx = con.beginTransaction(); tx.commit();
声明式事物:用框架去维护事物对象,和事物的提交和回滚
Spring特有的事物传播行为,spring支持7种传播行为,肯定客户端和被调用端的事物边界(说的通俗一点就是多个具备事物控制的service的相互调用时所造成的复杂的事物边界控制)。
传播行为 | 含义 |
PROPAGATION_REQUIRED(XML文件中为REQUIRED) | 表示当前方法必须在一个具备事务的上下文中运行,若有客户端有事务在进行,那么被调用端将在该事务中运行,不然的话从新开启一个事务。(若是被调用端发生异常,那么调用端和被调用端事务都将回滚) |
PROPAGATION_SUPPORTS(XML文件中为SUPPORTS) | 表示当前方法没必要须要具备一个事务上下文,可是若是有一个事务的话,它也能够在这个事务中运行 |
PROPAGATION_MANDATORY(XML文件中为MANDATORY) | 表示当前方法必须在一个事务中运行,若是没有事务,将抛出异常 |
PROPAGATION_NESTED(XML文件中为NESTED) | 表示若是当前方法正有一个事务在运行中,则该方法应该运行在一个嵌套事务中,被嵌套的事务能够独立于被封装的事务中进行提交或者回滚。若是封装事务存在,而且外层事务抛出异常回滚,那么内层事务必须回滚,反之,内层事务并不影响外层事务。若是封装事务不存在,则同PROPAGATION_REQUIRED的同样 |
PROPAGATION_NEVER(XML文件中为NEVER) | 表示当方法务不该该在一个事务中运行,若是存在一个事务,则抛出异常 |
PROPAGATION_REQUIRES_NEW(XML文件中为REQUIRES_NEW) | 表示当前方法必须运行在它本身的事务中。一个新的事务将启动,并且若是有一个现有的事务在运行的话,则这个方法将在运行期被挂起,直到新的事务提交或者回滚才恢复执行。 |
PROPAGATION_NOT_SUPPORTED(XML文件中为NOT_SUPPORTED) | 表示该方法不该该在一个事务中运行。若是有一个事务正在运行,他将在运行期被挂起,直到这个事务提交或者回滚才恢复执行 |
6.在Spring的事物管理中主要涉及下面三个接口,platformTransactionManager,TransactionDefinition,TransactionStatus,其中platformTransactionManager抽取了事物管理过程当中的整个流程中的最顶层的操做接口。
7.Spring对事物的提交仍是回滚:异常类型是编译时异常,默认是提交的
异常类型是运行时异常,默认是回滚的
8.事物的并发会产生什么问题
1)第一类丢失更新:在没有事物隔离的状况下,两个事物同时更新一行数据,可是第二个事物却中途失败退出,致使对数据的两个修改都失效了。
例如:张三的工资为5000元,事物A中获取工资为5000,事物B获取工资为5000,汇入100,并提交数据库,工资变为5100,随后,事物A发生异常,回滚了,恢复张三的工资为5000元,这样就致使了事物B的跟新丢失了
2)脏读:脏读就是指当一个事物正在访问数据,而且对数据进行了修改,而这种修改尚未提交到数据库中,这时,另外一个事物也访问这个数据,而后使用了这个数据。
例如:张三的工资为5000,事物A中把他的工资为改成8000,但事物A还没有提交。于此同时,事物B正在读取张三的工资,读取到张三的工资为8000。随后,事物A发生异常,而回滚了事物。张三的工资又回滚到5000。最后,事物B读取到的张三工资为8000的数据即为脏数据,事物B作了一次脏读。
3)不可重复读:是指在一个事物内,屡次读同一数据。在这个事物尚未结束时,另外一个事物也访问该同一数据,那么,在第一个事物中的两次读数据之间,因为第二个事物的修改,那么第一个事物中两次读取数据之间,因为第二个事物的修改,那么第一个事物两次读到的数据多是不同的。这样就发生了在同一个事物内两次读到的数据是不同的,所以称为是不可重复度的。
例如:在事物A中,读取到张三的工资为5000,操做没有完成,事物还没提交。于此同时,事物B把张三的工资改成8000,并提交了事物。随后,在事物A中,在次读取到张三的工资,此时工资变为8000.在一个事物中先后两次读取到的结果并不一致,致使了不可重复度。
4)第二类丢失更新:不可重复读的特利。有两个并发事物同时读取同一行数据,而后其中一个对它进行修改提交,而另外一个也进行了修改提交,这就会形成第一次读写操做失效。
例如:在事物A中,读取到张三的存款为5000,操做没有完成,事物还没提交。于此同时,事物B,存储1000,把张三的存款改成6000,并提交了事物,这样事物A的更新覆盖了事物B的更新。
5)幻读:是指当前事物不是独立执行时发生的一种现象,例如第一个事物对表中的数据进行了修改,这种修改涉及到表中的所有数据行,同时,第二个事物也修改了这个表中的数据,这种修改是向表中插入一行新的数据,那么,之后就会发生操做第一个事物的用户发向表中还有没有修改的数据行,就好像发生了幻觉同样。
例如:目前工资为5000的员工有10人,事物A读取全部工资为5000的人数为10人。此时,事物B插入一条工资也为5000的记录,这时,事物A再次读取工资为5000的员工,记录为11人,此时产生了幻读。
提示:不可重复读的重点是修改,一样的条件,你读取过的数据,再次读取出来的发现值不同了。
幻读的重点在于新增或者删除,一样的条件,第一次和第二次读出来的记录数不同
9.事物的实例
买股票的事件
01.建立实体类
package cn.happy.entity; /* * 银行帐户 * */ public class Account { private Integer aid; //账户id private String aname; //账户名 private double blance; //账户余额 public Integer getAid() { return aid; } public void setAid(Integer aid) { this.aid = aid; } public String getAname() { return aname; } public void setAname(String aname) { this.aname = aname; } public double getBlance() { return blance; } public void setBlance(double blance) { this.blance = blance; } }
Stock类
package cn.happy.entity; /* * 股票类 * */ public class Stock { private Integer sid; //股票id private String sname; //股票名 private Integer count; //持股数 public Integer getSid() { return sid; } public void setSid(Integer sid) { this.sid = sid; } public String getSname() { return sname; } public void setSname(String sname) { this.sname = sname; } public Integer getCount() { return count; } public void setCount(Integer count) { this.count = count; } }
异常类
StockException
/* * 编译时异常 spring默认 自动提交事物 * 运行时异常 spring默认 自动回滚事物 * */ public class StockException extends Exception { public StockException() { super(); } public StockException(String message) { super(message); } }
02.建立dao层
public interface IAccountDao { //核心业务 public void updateAccount(int aid,double money,boolean isBuy); }
public interface IStockDao { public void updateStock(int sid,int count,boolean isBuy); }
03.建立dao的实现类
import cn.happy.dao.IAccountDao; import org.springframework.jdbc.core.support.JdbcDaoSupport; public class IAccountDaoImpl extends JdbcDaoSupport implements IAccountDao { public void updateAccount(int aid, double money, boolean isBuy) { String sql = null; if(isBuy){
//购买股票 sql = "update account set blance=blance-? where aid=?"; }else{
//抛出股票 sql = "update account set blance=blance+? where aid=?"; } this.getJdbcTemplate().update(sql,money,aid); } }
import cn.happy.dao.IStockDao; import org.springframework.jdbc.core.support.JdbcDaoSupport; public class IStockDaoImpl extends JdbcDaoSupport implements IStockDao { String sql = null; public void updateStock(int sid, int count, boolean isBuy) { if(isBuy){ //购买股票 sql="update stock set count=count+? where sid=?"; }else{ //抛出股票 sql="update stock set count=count-? where sid=?"; } //对数据进行增 删 改 都用update this.getJdbcTemplate().update(sql, count, sid); } }
04. 建立service层
public interface IStockService { public void buyStock(int sid,int counts,int aid,double money); }
05.建立service实现类
import cn.happy.dao.IAccountDao; import cn.happy.dao.StockDao; import cn.happy.entity.StockException; public class AccoutServiceImpl implements IAccountService { //植入AccountDao private IAccountDao accountDao; //植入StockDao private StockDao stockDao; public void buyStock(int sid, int count, int aid, int money) throws StockException { boolean isBuy = true; //购买股票 accountDao.updateAccount(aid,money,isBuy); //手动制造异常 if(1==1) { throw new StockException(); } stockDao.updateStock(sid,count,isBuy); } public IAccountDao getAccountDao() { return accountDao; } public void setAccountDao(IAccountDao accountDao) { this.accountDao = accountDao; } public StockDao getStockDao() { return stockDao; } public void setStockDao(StockDao stockDao) { this.stockDao = stockDao; } }
06.编写配置文件
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd "> <!--1.识别jdbc.properties文件--> <context:property-placeholder location="jdbc.properties"></context:property-placeholder> <!--2.创建数据远,${} spring内置的数据源 DriverMangerDateSource--> <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName" value="${jdbc.driver}"></property> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> </bean> <!--4.dao的配置--> <bean id="AccountDao" class="cn.happy.dao.impl.IAccountDaoImpl"> <property name="dataSource" ref="dataSource"></property> </bean> <bean id="StockDao" class="cn.happy.dao.impl.IStockDaoImpl"> <property name="dataSource" ref="dataSource"></property> </bean> <!--5.service的配置--> <bean id="stockService" class="cn.happy.service.StockServiceImpl"> <property name="accountDao" ref="AccountDao"></property> <property name="stockDao" ref="StockDao"></property> </bean> <!--事物管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!--配置事物,拦截业务方法--> <bean id="accountServiceProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <!--目标类型(要加强的类)--> <property name="target" ref="stockService"></property> <property name="transactionManager" ref="transactionManager"></property> <!--加强--> <property name="transactionAttributes"> <props> <prop key="buyStock">ISOLATION_DEFAULT,PROPAGATION_REQUIRED,-StockException</prop> </props> </property> </bean> </beans>
07.编写测试类
public class TestSW { @Test public void test01(){ ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); IStockService stockService = (IStockService)context.getBean("stockService"); stockService.buyStock(34,10,3,1000); } }
在发生异常时,余额和股票数都不会变。