事务是什么
事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消。也就是事务具有原子性,一个事务中的一系列的操作要么全部成功,要么一个都不做。
事务的7种传播级别:
1) PROPAGATION_REQUIRED ,默认的spring事务传播级别,使用该级别的特点是,如果上下文中已经存在事务,那么就加入到事务中执行,如果当前上下文中不存在事务,则新建事务执行。所以这个级别通常能满足处理大多数的业务场景。
2)PROPAGATION_SUPPORTS ,从字面意思就知道,supports,支持,该传播级别的特点是,如果上下文存在事务,则支持事务加入事务,如果没有事务,则使用非事务的方式执行。所以说,并非所有的包在transactionTemplate.execute中的代码都会有事务支持。这个通常是用来处理那些并非原子性的非核心业务逻辑操作。应用场景较少。
3)PROPAGATION_MANDATORY , 该级别的事务要求上下文中必须要存在事务,否则就会抛出异常!配置该方式的传播级别是有效的控制上下文调用代码遗漏添加事务控制的保证手段。比如一段代码不能单独被调用执行,但是一旦被调用,就必须有事务包含的情况,就可以使用这个传播级别。
4)PROPAGATION_REQUIRES_NEW ,从字面即可知道,new,每次都要一个新事务,该传播级别的特点是,每次都会新建一个事务,并且同时将上下文中的事务挂起,执行当前新建事务完成以后,上下文事务恢复再执行。
这是一个很有用的传播级别,举一个应用场景:现在有一个发送100个红包的操作,在发送之前,要做一些系统的初始化、验证、数据记录操作,然后发送100封红包,然后再记录发送日志,发送日志要求100%的准确,如果日志不准确,那么整个父事务逻辑需要回滚。
怎么处理整个业务需求呢?就是通过这个PROPAGATION_REQUIRES_NEW 级别的事务传播控制就可以完成。发送红包的子事务不会直接影响到父事务的提交和回滚。
5)PROPAGATION_NOT_SUPPORTED ,这个也可以从字面得知,not supported ,不支持,当前级别的特点就是上下文中存在事务,则挂起事务,执行当前逻辑,结束后恢复上下文的事务。
这个级别有什么好处?可以帮助你将事务极可能的缩小。我们知道一个事务越大,它存在的风险也就越多。所以在处理事务的过程中,要保证尽可能的缩小范围。比如一段代码,是每次逻辑操作都必须调用的,比如循环1000次的某个非核心业务逻辑操作。这样的代码如果包在事务中,势必造成事务太大,导致出现一些难以考虑周全的异常情况。所以这个事务这个级别的传播级别就派上用场了。用当前级别的事务模板抱起来就可以了。
6)PROPAGATION_NEVER ,该事务更严格,上面一个事务传播级别只是不支持而已,有事务就挂起,而PROPAGATION_NEVER传播级别要求上下文中不能存在事务,一旦有事务,就抛出runtime异常,强制停止执行!这个级别上辈子跟事务有仇。
7)PROPAGATION_NESTED ,字面也可知道,nested,嵌套级别事务。该传播级别特征是,如果上下文中存在事务,则嵌套事务执行,如果不存在事务,则新建事务。
那么什么是嵌套事务呢?很多人都不理解,我看过一些博客,都是有些理解偏差。
脏读、不可重复读、幻读
脏读 :所谓的脏读,其实就是读到了别的事务回滚前的脏数据。比如事务B执行过程中修改了数据X,在未提交前,事务A读取了X,而事务B却回滚了,这样事务A就形成了脏读。
不可重复读 :不可重复读字面含义已经很明了了,比如事务A首先读取了一条数据,然后执行逻辑的时候,事务B将这条数据改变了,然后事务A再次读取的时候,发现数据不匹配了,就是所谓的不可重复读了。
幻读 :小的时候数手指,第一次数十10个,第二次数是11个,怎么回事?产生幻觉了?
幻读也是这样子,事务A首先根据条件索引得到10条数据,然后事务B改变了数据库一条数据,导致也符合事务A当时的搜索条件,这样事务A再次搜索发现有11条数据了,就产生了幻读。
事务的四大特性
数据库事务 transanction 正确执行的四个基本要素。ACID,原子性(Atomicity)、一致性(Correspondence)、隔离性(Isolation)、持久性(Durability)。
**原子性:**整个事务中的所有操作,要么全部完成,要么全部不完成,不可能停滞在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前的状态,就像这个事务从来没有执行过一样。
**一致性:**在事务开始之前和事务结束以后,数据库的完整性约束没有被破坏。
**隔离性:**隔离状态执行事务,使它们好像是系统在给定时间内执行的唯一操作。如果有两个事务,运行在相同的时间内,执行 相同的功能,事务的隔离性将确保每一事务在系统中认为只有该事务在使用系统。这种属性有时称为串行化,为了防止事务操作间的混淆, 必须串行化或序列化请 求,使得在同一时间仅有一个请求用于同一数据。
**持久性:**在事务完成以后,该事务所对数据库所作的更改便持久的保存在数据库之中,并不会被回滚
示例.1
public class Account implements Serializable {
private Integer accountId;
private String accountName;
private double balance;
public Integer getAccountId() {
return accountId;
}
public void setAccountId(Integer accountId) {
this.accountId = accountId;
}
public String getAccountName() {
return accountName;
}
public void setAccountName(String accountName) {
this.accountName = accountName;
}
public double getBalance() {
return balance;
}
public void setBalance(double balance) {
this.balance = balance;
}
}
dao
public interface AccountDao {
//查询
public List<Account> getAll();
//删除
public int delAccount(int account);
//添加
public int INSERT(Account account);
//修改
void addMoney(double balance,Integer id);//加钱方法
void subMonet(double balance,Integer id);//减钱方法。
}
daoimpl
@Repository
public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao {
/*@Resource
private JdbcTemplate jdbcTemplate;*/
@Override
public List<Account> getAll() {
/* List<Account> query = this.getJdbcTemplate().query("select * from account", new RowMapper<Account>() {
@Override
public Account mapRow(ResultSet rs, int RowNum) throws SQLException {
Account account = new Account();
account.setAccountId(rs.getInt("accountid"));
account.setAccountName(rs.getString("accountname"));
account.setBalance(rs.getDouble("balance"));
return account;
}
});*/
RowMapper<Account> ac = new BeanPropertyRowMapper<>(Account.class);
List<Account> query = getJdbcTemplate().query("select * from account", ac);
return query;
}
@Override
public int delAccount(int account) {
String sql="delete from account where accountid=?";
int update = getJdbcTemplate().update(sql, account);
return update;
}
@Override
public int INSERT(Account account) {
String sql="INSERT INTO account (accountid,accountname,balance) VALUE(?,?,?)";
int insert = getJdbcTemplate().update(sql, account.getAccountId(),account.getAccountName(), account.getBalance());
return insert;
}
@Override
public void addMoney(double balance, Integer id) {
String sql="update account set balance=balance+? where accountid=?";
this.getJdbcTemplate().update(sql,new Object[]{balance,id});
}
@Override
public void subMonet(double balance, Integer id) {
String sql="update account set balance=balance-? where accountid=?";
this.getJdbcTemplate().update(sql,new Object[]{balance,id});
}
}
service
public interface AccountService {
public List<Account> getAll();
//删除
public int delAccount(int account);
//添加
public int INSERT(Account account);
//修改
void transferMonet();
}
serviceimpl
@Service("accountServiceImpl")
public class AccountServiceImpl implements AccountService {
@Resource
private AccountDao dao;
public void setDao(AccountDao dao) {
this.dao = dao;
}
@Override
public List<Account> getAll() {
return dao.getAll();
}
@Override
public int delAccount(int account) {
return dao.delAccount(account);
}
@Override
public int INSERT(Account account) {
return dao.INSERT(account);
}
//注解方式
@Transactional(isolation = Isolation.REPEATABLE_READ,propagation = Propagation.REQUIRED)
@Override
public void transferMonet() {
dao.addMoney(100,1);
//手动设置异常
if(true)
throw new RuntimeException("----------异常--------------");
dao.subMonet(100,2);
}
}
ApplicationContext.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">
<context:component-scan base-package="com.jdbcTempalate"/>
<context:property-placeholder location="classpath:database.properties"/>
<!--数据源-->
<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>
<property name="password" value="${jdbc.password}"></property>
</bean>
<bean id="jdbcTempalate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--<!–找到daoimpl–>-->
<bean id="accountDaoImpl" class="com.jdbcTempalate.dao.impl.AccountDaoImpl">
<property name="jdbcTemplate" ref="jdbcTempalate"></property>
</bean>
<bean id="accountServiceImpl" class="com.jdbcTempalate.Service.impl.AccountServiceImpl">
<property name="dao" ref="accountDaoImpl"></property>
</bean>
<!--配置spring事务配置-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!--注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置增强的的代理工厂baen-->
<bean id="transactionProxyFactoryBean" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager" ref="transactionManager"></property>
<!--业务类对象-->
<property name="target" ref="accountServiceImpl"></property>
<property name="transactionAttributes">
<props>
<!-- 事务的传播行为和隔离级别-->
<prop key="transferMonet">ISOLATION_READ_COMMITTED,PROPAGATION_REQUIRED</prop>
</props>
</property>
</bean>
<!--AOP事务-->
<!--<tx:advice id="transactionadvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="transferMoney" isolation="READ_COMMITTED" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* com.jdbcTempalate.Service.impl.*.*(..))"/>
<aop:advisor advice-ref="transactionadvice" pointcut-ref="pointcut"></aop:advisor>
</aop:config>-->
<!--Spring支持注解式事务配置-->
<!--<tx:annotation-driven/>-->
</beans>
test
public class test {
public static void main(String[] args) {
ApplicationContext ac=new ClassPathXmlApplicationContext("ApplicationContext.xml");
AccountService c=(AccountService) ac.getBean("transactionProxyFactoryBean");
//删除
int i = c.delAccount(6);
//添加
Account account=new Account();
account.setAccountId(10);
account.setAccountName("宋小宝");
account.setBalance(1111.1);
c.INSERT(account);
//修改
c.transferMonet();
//查询
List<Account> all = c.getAll();
for (Account a:all
) {
System.out.println(a.getAccountName());
}
}
}
Aop和注解方式可以查看ApplicationContext.xml类