简单说一下事务
''是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。''
四大特性(ACID)
- 原子性(Atomicity): 指事务不可分割,要么全做,要么都不做。
- 一致性(Consisitency):指事务完成前后,所有数据保持一致状态。譬如转账操作,前后总额不能改后变。
- 隔离性(Isolation):指开启事务后,不受其它事务影响。
- 持久性(Durability):指事务完成后,对数据库记录的修改是永久的。
事务隔离级别
并发的情况下会破坏掉事务的特性,导致操作数据的不一致。情况有以下几种。
-
脏读
当前事务读到另一事务未提交的数据。
我向你转账1元,然后你看到了,给我两个馒头,我拿到馒头走了,接着把事务回滚了。。 -
不可重复读
在一个事务中,多次查询同一记录,结果不一致。原因是别的事务修改了该条记录并提交了。 -
幻读
开启事务后,两次统计数据库记录条数,结果显示不一致。
在做统计时就很为难。
解决办法利用锁机制,常用的是乐观锁,为表中添加一个vision字段;或者直接指定会话的隔离级别。
注:不可重复读和幻读都是读取别人以及提交的事务,只是前者是读取一个数据项,后者则是针对一批。
以往对事务的操作
JDBC中
//JDBC中,事务自动提交,得手动打开,(伪代码)
Connection conn =
DataSourceUtils.getConnection();
//开启事务
conn.setAutoCommit(false);
try {
Object obj =
callback.doInConnection(conn);
conn.commit(); //提交事务
return retVal;
}catch (Exception e) {
conn.rollback();//回滚事务
throw e;
}finally {
conn.close();
}
后来交由SessionFactory管理
Session session = factory.openSession();
try {
session.beginTransaction();
//do something
//session.save(obj);
session.beginTransaction().commit();
} catch (HibernateException e) {
session.beginTransaction().rollback();
e.printStackTrace();
} finally {
if(session != null && session.isOpen()) {
session.close();
}
}
再后来还是得不到满足,因为每个方法都得这样,重复代码过多。而且扩展性很差。于是想到了ThreadLocal模式,即将session放在ThreadLocal变量中。并利用struts2拦截器,自动开启事务,提交事务和关闭事务。但是你需要每次都获取当前线程的session。
@Override
public String intercept(ActionInvocation invocation) throws Exception {
String ret = null;
Session session = null;
try {
session = HibernateUtil.getSession();
session.beginTransaction();
System.out.println("********获取session开启事务***********");
ret = invocation.invoke();//该拦截器负责为这次请求创建一个session并管理业务和关闭session
session.getTransaction().commit();
} catch (Exception e) {
session.getTransaction().rollback();
e.printStackTrace();
} finally {
if(session != null && session.isOpen()) {
System.out.println("********事务结束关闭session***********");
session.close();
}
}
return ret;
}
对的,没错,像下面这样获取session。
//ThreadLocal local
public static Session getSession() {
Session session = local.get();
if(session == null || !session.isOpen()) {
session = factory.openSession();
local.set(session);
}
return session;
}
那么现在,Spring 来了。你不再从应用程序中主动获取资源,你只需要专注你的业务就好。像这种东西,Spring来帮你搞定!
//既然是事务,Spring提供了一个管理事务的类,我们将其注入IoC容器
<!-- 注入事务管理类 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"></property>
<property name="dataSource" ref="dataSource"></property>
</bean>
//上面是通过set方法注入了类依赖的对象,我们也要将其注入
//为类中注入了与数据库连接用到的相关参数
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<constructor-arg name="url" value="jdbc:mysql://127.0.0.1:3306/dbname"></constructor-arg>
<constructor-arg name="username" value="root"></constructor-arg>
<constructor-arg name="password" value="root"></constructor-arg>
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
</bean>
//这个是hibernate自身属性配置
<!-- 配置信息建议查看源代码 -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
<prop key="hibernate.show_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
<!-- mappingResources 配置.hbm.xml
源码解释:Set Hibernate mapping resources to be found in the class path,
like "example.hbm.xml" or "mypackage/example.hbm.xml".
packagesToScan 指定注解类的包,自动扫描,类似spring-component组件
都是数组类型
-->
<property name="mappingResources">
<list>
<value>User.hbm.xml</value>
</list>
</property>
</bean>
接下来我们要使用AOP,相关注释很明显了。
<!-- 定义事务通知,定义了如何实施事务(实施事务的方法名和事务属性),
需要使用事务管理器管理事务,定义了如何选择目标对象的方法及事务的事务属性
-->
<tx:advice id="txAdvice" transaction-manager="transactionManager" >
<tx:attributes>
<tx:method name="get*" read-only="true" />
<tx:method name="list*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!-- 切入点和事务通知,切入点选择实施事务的目标对象 -->
<aop:config>
<aop:pointcut id="tx" expression="execution(* cn.smog.action.*.*(..))"/>
<aop:advisor advice-ref="txAdvice" pointcut-ref="tx" />
</aop:config>
为了方便理解,我决定画张图。