参考文章:原文链接
事务隔离级别:
隔离级别所要解决的问题是在应用程序中,存在多个事务同时在运行时,需要解决和处理好的问题!
脏读(dirty read)
一个事物更新了数据库中的某些数据,另一个事物读取了这些数据,这时前一个事物由于某些原因回滚了,那么第二个事物读取的数据就是“脏数据”
不可重复读(non-repeatable read)
一个事物两次查询同一数据,但两次查询中间可能有另外一个事物更改了这个数据,导致前一个事物两次读出的数据不一致。
幻读 (phantom read)
一个事物两次查询同一个表,但两次查询中间可能有另外一个事物又向这个表中插入了一些新数据,导致前一个事物的两次查询不一致
Spring中@Transactional中Isolation有具备的值:
DEFAULT 使用各个数据库默认的隔离级别
Read Uncommited :读未提交数据( 会出现脏读,不可重复读,幻读)
Read Commited :读已提交的数据(会出现不可重复读,幻读)
Repeatable Read :可重复读(会出现幻读)
Serializable :串行化
注意:
如果开启了bin-log日志,配置Read Uncommited或者Read Commited会报错:
Caused by: java.sql.SQLException: Cannot execute statement: impossible to write to binary log since BINLOG_FORMAT = STATEMENT
and at least one table uses a storage engine limited to row-based logging.
InnoDB is limited to row-logging when transaction isolation level is READ COMMITTED or READ UNCOMMITTED.
原因:mysql默认的binlog_format是STATEMENT,而在READ COMMITTED或READ UNCOMMITTED隔离级别下,innodb只能使用的binlog_format是ROW
事务的配置:
web.xml中:
<!-- Spring为我们提供的OpenSessionInViewFilter过滤器,主要功能是用来把一个Hibernate Session和一次完整的请求过程对应的线程相绑定
目的是为了实现"Open Session in View"的模式。例如: 它允许在事务提交之后延迟加载显示所需要的对象-->
<filter>
<filter-name>Spring OpenSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate5.support.OpenSessionInViewFilter</filter-class>
<init-param>
<!-- 指定org.springframework.orm.hibernate3.LocalSessionFactoryBean在spring配置文件中的名称,默认值为sessionFactory
如果LocalSessionFactoryBean在spring中的名称不是sessionFactory,该参数一定要指定,否则会出现找不到sessionFactory的例外 -->
<param-name>sessionFactoryBeanName</param-name>
<param-value>sessionFactory</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>Spring OpenSessionInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
ApplicationContext.xml配置:
<!-- =============================== 事务管理 ================================== -->
<!--配置事务管理器-->
<bean id ="transactionManager" class="org.springframework.orm.hibernate5.HibernateTransactionManager">
<!-- 注入sessionFactory-->
<property name="sessionFactory" ref="sessionFactory"></property>
</bean>
<!--开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"/>
重点以及感悟:
1、使用误区:
无论在service层或者dao层调用,session对象获得都是sessionFactory.getCurrentSession();
userDao调用不同的方法,如:userDao.save(user); userDao.update(user); 用到的session对象是同一个(得到的session用hashcode打印出来,可以看出是一个对象)
事务起不起作用,一个注意点:
用@Transaction的方法必须抛出运行时异常。这样方法里面的代码才可能被回滚。
如果方面里面被try catch处理了,将不能抛出异常,那么即使有运行时异常也被catch处理了,所以就不能回滚了,
错误实例:
@Transactional
@Override
public void batchAdd() throws Exception {
try {
User user = new User();
user.setName("dhh");
user.setPassword("xxxxxx");
user.setSex("男");
user.setTimetag(new Date());
this.save(user); //写库处理,和下面的逻辑要事务处理
Thread.sleep(5000);
int i = 5/0; //另外的写库处理,模拟异常
System.out.println(i);
} catch (Exception e) { //异常被catch了,不能抛出异常,将不能回滚
e.printStackTrace();
}
}
个人思路:事务操作最好放到dao层处理,异常集中到service层处理,所以service最好别抛抛异常给controller
2、@Transactional之value
value这里主要用来指定不同的事务管理器;主要用来满足在同一个系统中,存在不同的事务管理器。
比如在Spring中,声明了两种事务管理器txManager1, txManager2.
然后,用户可以根据这个参数来根据需要指定特定的txManager.
那有同学会问什么情况下会存在多个事务管理器的情况呢?
比如在一个系统中,需要访问多个数据源或者多个数据库,则必然会配置多个事务管理器的。
3、Propagation支持7种不同的传播机制:
REQUIRED
业务方法需要在一个事务中运行,如果方法运行时,已处在一个事务中,那么就加入该事务,否则自己创建一个新的事务.这是spring默认的传播行为.。
SUPPORTS:
如果业务方法在某个事务范围内被调用,则方法成为该事务的一部分,如果业务方法在事务范围外被调用,则方法在没有事务的环境下执行。
MANDATORY:
只能在一个已存在事务中执行,业务方法不能发起自己的事务,如果业务方法在没有事务的环境下调用,就抛异常
REQUIRES_NEW
业务方法总是会为自己发起一个新的事务,如果方法已运行在一个事务中,则原有事务被挂起,新的事务被创建,直到方法结束,新事务才结束,原先的事务才会恢复执行.
NOT_SUPPORTED
声明方法需要事务,如果方法没有关联到一个事务,容器不会为它开启事务.如果方法在一个事务中被调用,该事务会被挂起,在方法调用结束后,原先的事务便会恢复执行.
NEVER:
声明方法绝对不能在事务范围内执行,如果方法在某个事务范围内执行,容器就抛异常.只有没关联到事务,才正常执行.
NESTED:
如果一个活动的事务存在,则运行在一个嵌套的事务中.如果没有活动的事务,则按REQUIRED属性执行.它使用了一个单独的事务,
这个事务拥有多个可以回滚的保证点.内部事务回滚不会对外部事务造成影响, 它只对DataSourceTransactionManager 事务管理器起效.
3、使用范围:
@Transactional写在类前,那么所有的方法,都具有事务特性,
如果某个方法不需要propagation=Propagation.NOT_SUPPORTED
@Transactional(value="transactionManager",propagation=Propagation.NOT_SUPPORTED,isolation=Isolation.REPEATABLE_READ)
@Override
public void batchAdd() throws Exception {
}
具体Transaction有哪些参数,以及值怎么填写,打开源码就一清二楚了!!!
4、想法验证:
我之前想在dao的基类中定义@Repository
那么继承这个基类的dao方法都继承这个变量,经过检验,是行不通的
同理,在接口上定义@Repository
也是行不通的