Java网课基础笔记(17)19-07-30

 

1.事务的四大特性:

  • 原子性:事务包含的所有操作要么全部成功,要么全部失败回滚;成功必须要完全应用到数据库,失败则不能对数据库产生影响;
  • 一致性:事务执行前和执行后必须处于一致性状态

         例:用户A和用户B的前加起来一共是5000; 无论AB用户之间是如何相互转换的,事务结束后两个用户的钱加起来还是5000,这就是  事务的一致性。

  • 隔离性:当多个用户并发访问数据库时,数据库为每一个用户开启的事务,不被其他事务的操作所干扰,多个并发事务之间要相互隔离;

  01:脏读

是指一个事务处理过程里读取了另一个未提交的事务中的数据,然后使用了这个数据;
例:用户A向用户B转账100元,A通知B查看账户,B发现前确实已到账,而之后无论第二条SQL是否执行,只要该事务部提交,则所有操作都将回滚,当B再次查看账户时就会发现前其实并没有到账。

02:不可重复读(更新的时候会发生)
不可重复读是指在一个事务内,多次读取同一个数据,在这个事务还没有结束 ,另一个事务也访问该同一数据,但是由于第二个事务的修改,那么第一个事务两次读取的数据可能不一样,因此称为不可重复读;即同一个事务中原始数据读取不可重复。 注:不可重复读和脏读的区别,脏读是某一个事务读取另一个事务未提交的脏数据; 不可重复读则是读取前一事务提

03:幻读:
当事务不是独立执行时发生的一种现象,例如第一个事务对一个表中的数据进行修改,这种数据涉及到表中的全部数据行,同时,第二个事务也对这个表数据进行修改,这个修改是对表中新增/删除一条数据,那么操作第一个事务的用户发现表中的数据还没有修改的数据行,就好像发生了幻觉一样,这就是发生了幻读。
注:幻读和不可重复读都读取另一条已经提交的事务,所不同的是不可重复读查询的都是同一数据项,而幻读针对的是一批数据整体。

 解决:
数据库提供的四种隔离级别:
01:Read uncommitted(读未提交):最低级别,任何情况都会发生。
02:Read Committed(读已提交):可避免脏读的发生。
03:Repeatable read(可重复读):可避免脏读、不可重复读的发生。
04:Serializable(串行化):避免脏读、不可重复读,幻读的发生。
 

注: 四种隔离级别最高:Seralizable级别,最低的是Read uncommitted级别; 级别越高,执行效率就越低; 隔离级别的设置只对当前链接有效,对JDBC操作数据库来说,一个Connection对象相当于一个链接,只对该Connection对象设置的隔离级别只对该connection对象有效,与其它链接connection对象无关。

01:Mysql的默认隔离级别是:可重复读:Repeatable read;

  • 持久性:一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便在数据库系统遇到故障的情况下也不会丢失事物的操作。

2.Spring 的事务管理是基于 AOP 实现的,而 AOP 是以方法为单位的。Spring 的事务属性分别为传播行为、隔离级别、只读和超时属性,这些属性提供了事务应用的方法和描述策略。

Spring事物管理高层抽象主要包括3个接口,Spring的事物主要是由他们共同完成的:

  • PlatformTransactionManager:事务管理器--主要用于平台相关事务的管理
  • TransactionDefinition:事务定义信息(隔离、传播、超时、只读)--通过配置如何进行事务管理
  • TransactionStatus:事务具体运行状态--事务管理过程中,每个时间点事务的状态信息

PlatformTransactionManager

该接口有三个方法:

  1. commit:提交事务
  2. rollback:回滚事务
  3. getTransaction:获取事务状态

DataSourceTransactionManager针对JdbcTemplate、MyBatis事物控制,使用Connection(连接)进行事务控制:

  • 开启事务 connection.setAutoCommit(false);
  • 提交事务 connection.commit();
  • 回滚事务 connection.rollback();

TransactionDefinition

该方法主要提供的方法:

  • String getName():获取事务对象名称。
  • int getIsolationLevel():获取事务的隔离级别。
  • int getPropagationBehavior():获取事务的传播行为。
  • int getTimeout():获取事务的超时时间(事务的有效期)。
  • boolean isReadOnly():获取事务是否只读(保存、更新、删除--对数据进行操作-变成可读写对,查询-设置这个属性为true,只读不能写),事务管理器能够根据这个返回值进行优化。

在上述五个方法的描述中,事务的传播行为是指在同一个方法中,不同操作前后所使用的事务,解决两个被事务管理的方法相互调用问题。传播行为的种类如表 1 所示。
 

传播行为的种类
属性名称描  述
PROPAGATION_REQUIRED(默认)required支持当前事务。如果不存在,就新建一个
PROPAGATION_SUPPORTSsupports支持当前事务。如果不存在,就不使用事务
PROPAGATION_MANDATORYmandatory支持当前事务。如果不存在,则抛出异常
PROPAGATION_REQUIRES_NEWrequires_new如果事务存在,挂起当前事务,创建新的事务
PROPAGATION_NOT_SUPPORTEDnot_supported不支持当前事务,总是以非事务状态执行。如果事务存在,则将其挂起
PROPAGATION_NEVERnever不支持当前事务,如果 事务存在,则抛出异常
PROPAGATION.NESTEDnested嵌套事务,如果当前事务存在,则嵌套事务执行,底层将使用Jdbc的 Savepoint 形成嵌套事务,允许在同一个事务设置保存点,回滚保存点,只对DataSourceTransactionManager起效


在事务管理过程中,传播行为可以控制是否需要创建事务以及如何创建事务。

通常情况下,数据的查询不会改变原数据,所以不需要进行事务管理,而对于数据的增加、修改和删除等操作,必须进行事务管理。如果没有指定事务的传播行为,则 Spring3 默认的传播行为是 required。

【面试题】REQUIRED、REQUIRES_NEW、NESTED区分

REQUIRED:只有一个事务(默认、推荐)

REQUIRES_NEW:存在两个事务,如果事务存在,挂起事务,重新又开启了一个新的事务

NESTED:嵌套事务,事务可以设置保存点,回滚到保存点,选择提交或者回滚

TransactionStatus 

TransactionStatus 接口是事务的状态,它描述了某一时间点上事务的状态信息。其中包含六个操作,具体如表 2 所示。
 

表 2  事务的操作
名称说明
void flush()(hibernate用的)刷新事务
boolean hasSavepoint()获取是否存在保存点
boolean isCompleted()获取事务是否完成
boolean isNewTransaction()获取是否是新事务
boolean isRollbackOnly()获取是否回滚
void setRollbackOnly()设置事务回滚

事务的结束:必须通过commit确认事务提交,rollback作用标记为回滚

数据库操作中,如果只是回滚,后面不操作,数据库在关闭连接的时候,自动发出commit。

try{
    操作
}catch(){
    rollback
}finally{
    commit
}

【三个事务超级接口对象之间的关系】

  1. 首先用户管理事务,需要先配置TransactionDefinition(事务定义信息、事务的管理方案);
  2. 然后根据TransactionDefinition,通过TransactionManager(事务管理器)进行事务管理;
  3. 最后事务运行过程中,每个时刻都可以通过获取TransactionStatus(事务状态)来了解事务的运行状态。

3.Spring事务管理两种方式

Spring支持两种方式事务管理

(1)编程式的事务管理

通过TransactionTemplate手动管理事务,在实际应用中很少使用,原因是要修改原来的代码,加入事务管理代码(入侵性)

(2)使用XML或注释配置声明式事务

  • Spring的声明式事务是通过AOP实现(环绕通知)
  • 开发中经常使用(代码入侵性小)-推荐使用

4.声明式事务管理案例-转账(xml、注解)

xml方式 

在t_account表中

//接口类
package com.account.dao;
public interface IAccountDao {
	public void in(String name,Double money);//存入(转入)
	public void out(String name,Double money);//取出(转出)
}
//实现类
package com.account.dao;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao{
	@Override//存入(转入)
	public void in(String name, Double money) {
		// TODO Auto-generated method stub
		String sql="update t_account set money =money+ ? where name=?";
		super.getJdbcTemplate().update(sql,money,name);
	}
	@Override//取出(转出)
	public void out(String name, Double money) {
		// TODO Auto-generated method stub
		String sql="update t_account set money=money - ? where name=?";
		super.getJdbcTemplate().update(sql,money,name);
	}
}
package com.account.service;
public interface IAccountService {
	void transfer(String oitName,String inName,Double money);
}

package com.account.service;
import com.account.dao.IAccountDao;
public class AccountSerivceImpl implements IAccountService{
	//注入dao
	private IAccountDao accountDao;
	public void setAccountDao(IAccountDao accountDao) {
		this.accountDao=accountDao;
	}
	//转账操作的业务逻辑
	@Override
	public void transfer(String outName, String inName, Double money) {
		// TODO Auto-generated method stub
		//调出dao层
		//先取出
		accountDao.out(outName, money);
		//再转入
		accountDao.in(inName, money);
	}
}
package com.account;
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 com.account.service.IAccountService;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath:applicationContext.xml"})
public class SpringTest {
	//注入测试的service
	@Autowired
	private IAccountService accountService;
	//需求:账号转账,张艺兴账号取出1000元,存放到王一博账号上
	@Test
	public void testTransfer() {
		accountService.transfer("张艺兴", "王一博", 1000d);
		System.out.println("转账成功!");
	}
}
//在applicationContext.xml
<!-- 引入外部属性配置文件 -->
	<context:property-placeholder location="classpath:db.properties"/>
	<!-- 配置内置的数据源bean,使用db.properties -->
	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="${jdbc.driverClass}"></property>
		<property name="url" value="${jdbc.url}"></property>
		<property name="username" value="${jdbc.username}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean> 
<!-- 第一步:定义具体的平台事务管理器(DataSource事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<!-- 注入数据源 -->
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<!-- 第二步:定义通知,通知中要处理的就是事务 -->
	<tx:advice id="txAdvice" transaction-manager="transactionManager">
		<!-- 配置事务的属性定义 -->
		<tx:attributes>
		<!-- 配置具体的方法的事务属性
		isolation//事务的隔离级别,默认是按数据库的隔离级别来
		propagation//事务的传播行为,默认是同一个事务
		timeout=“-1”:事务的超时时间,默认值使用数据库的超时时间
		read-only=“false”:事务是否只读,默认课读写
		rollback-for:遇到哪些异常就回滚,其他的都不回滚
		no-rollback-for:遇到哪些异常不回滚,其他的都回滚。和上面互斥
		 -->
		<tx:method name="transfer" isolation="DEFAULT"
		propagation="REQUIRED" timeout="-1" read-only="false"/>
		<!-- 支持通配符
		要求service中方法名字必须符合下面的规则 -->
		<tx:method name="save*"/>
		<tx:method name="update*"/>
		<tx:method name="delete*"/>
		<tx:method name="find*" read-only="true"/>
		</tx:attributes>
	</tx:advice>
	<!-- 第三步:配置切入点,让通知关联切入点,即事务控制业务层的方法 -->
	<aop:config>
		<!-- 切入点 -->
		<aop:pointcut expression="bean(*Service)" id="txPointcut"/>
		<!-- 切面 -->
		<aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
	</aop:config>
	<!-- 配置dao,注入jdbctemplate -->
	<bean id="bookDao" class="com.feng.dao.BookDao">
		<!-- 方案一:在BookDao中提供jdbcTemplate属性,通过set方法注入jdbctemplate -->
		<!-- <property name="jdbcTemplate" ref="jdbcTemplate"></property> -->
		<!-- 方案二:BookDao类继承JdbcDaoSupport,直接注入数据源,就拥有了jdbctemplate对象 -->
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<!-- 创建转账dao -->
	<bean id="accountDao" class="com.account.dao.AccountDaoImpl">
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<!-- 创建转账service -->
	<bean id="accountService" class="com.account.service.AccountSerivceImpl">
		<property name="accountDao" ref="accountDao"></property>
	</bean>

 

如果在转账过程中,即存入或者取出的sql语句任意一个执行不成功,则转账操作也是失败的。(要么都成功要么都失败)

注解方式

//接口类
package com.account.dao;
public interface IAccountDao {
	public void in(String name,Double money);//存入(转入)
	public void out(String name,Double money);//取出(转出)
}
//账户操作持久层
//技术方案
package com.account.dao;
import javax.sql.DataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.support.JdbcDaoSupport;
import org.springframework.stereotype.Repository;
//<bean id="accountDao" class="com.account.dao.AccountDaoImpl">
@Repository("accountDao")
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao{
	//注入数据源
	@Autowired
	public void setDi( DataSource dataSource) {
		// TODO Auto-generated method stub
		super.setDataSource(dataSource);
	}
	@Override//存入(转入)
	public void in(String name, Double money) {
		// TODO Auto-generated method stub
		String sql="update t_account set money =money+ ? where name=?";
		super.getJdbcTemplate().update(sql,money,name);
	}
	@Override//取出(转出)
	public void out(String name, Double money) {
		// TODO Auto-generated method stub
		String sql="update t_account set money=money - ? where name=?";
		super.getJdbcTemplate().update(sql,money,name);
	}
}
package com.account.service;
public interface IAccountService {
	void transfer(String oitName,String inName,Double money);
}

package com.account.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.account.dao.IAccountDao;
//<bean id="accountService" class="com.account.service.AccountSerivceImpl">
@Service("accountService")
@Transactional//添加事务,会对该类中所有对公有方法,自动加上事务--全局对设置,默认可写
public class AccountSerivceImpl implements IAccountService{
	//注入dao
	@Autowired
	private IAccountDao accountDao;
	
	//转账操作的业务逻辑
	@Override
	public void transfer(String outName, String inName, Double money) {
		// TODO Auto-generated method stub
		//调出dao层
		//先取出
		accountDao.out(outName, money);
		//再转入
		accountDao.in(inName, money);
	}
	@Transactional(readOnly = true)//局部覆盖全局
	public void findAcount() {
		System.out.println("查询账户信息");
	}
}
//applicationCOntext.xml
<!-- 引入外部属性配置文件 -->
	<context:property-placeholder location="classpath:db.properties"/>
	<!-- 配置内置的数据源bean,使用db.properties -->
	<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
		<property name="driverClassName" value="${jdbc.driverClass}"></property>
		<property name="url" value="${jdbc.url}"></property>
		<property name="username" value="${jdbc.username}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean> 
<!-- 配置bean组件扫描 -->
	<context:component-scan base-package="com.account"></context:component-scan>
	<!-- 第一步:定义具体的平台事务管理器(DataSource事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<!-- 注入数据源 -->
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	<tx:annotation-driven transaction-manager="transactionManager"/>

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值