什么是事务

这个问题比较大,按照我的理解就是,一个事务内的n个操作,要么全部完成,一旦有一个操作有问题,那么所有的操作都全部回滚。

Jdbc的事务

首先,大家已经知道了,事务说白了就是一个词----统一,要么全部OK,要么都不做。

在jdbc中,默认情况下,一个sql就是一个事务,一个事务也仅仅只有一个sql。AutoCommit=true

那么我们正常使用的时候,肯定是想把若干个sql绑到一起,看做一个事务。

那么我们第一步就是先告诉connection,你别一个sql一个sql提交了,整体来。即AutoCommit=false

我们看下面的例子

public int delete(int sID) {
  dbc = new DataBaseConnection();
  Connection con = dbc.getConnection();
  try {
   con.setAutoCommit(false);   // 更改JDBC事务的默认提交方式
   dbc.executeUpdate("delete from xiao where ID=" + sID);
   dbc.executeUpdate("delete from xiao_content where ID=" + sID);
   dbc.executeUpdate("delete from xiao_affix where bylawid=" + sID);
   con.commit();//提交JDBC事务
   con.setAutoCommit(true);    // 恢复JDBC事务的默认提交方式
   dbc.close();
   return 1;
  }
  catch (Exception exc) {
   con.rollBack();//回滚JDBC事务
   exc.printStackTrace();
   dbc.close();
   return -1;
  }
}

jta事务

不懂,我目前没用到这个东西。

hibernate中的事务

Hibernate 是JDBC 的轻量级封装,本身并不具备事务管理能力。在事务管理层, Hibernate将其委托给底层的JDBC或者JTA,以实现事务管理和调度功能。 

Hibernate的默认事务处理机制基于JDBC Transaction。我们也可以通过配置文件设定采用JTA作为事务管理实现:

</pre><pre name="code" class="html"><hibernate-configuration>
	<session-factory>
		……
		<property name="hibernate.transaction.factory_class">
		net.sf.hibernate.transaction.JTATransactionFactory
		<!--net.sf.hibernate.transaction.JDBCTransactionFactory-->
		</property>
		……
	</session-factory>
</hibernate-configuration>  

单纯的使用hibernate,对于事务的处理是很简单的,例如

	Date date = new Date();
		SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
		Teacher s = new Teacher();
		s.setName("zhangsan");
		s.setAge(232);
		s.setDate(sdf.format(date));

		SessionFactory sessionFactory = new AnnotationConfiguration()
				.configure().buildSessionFactory();
		Session session = sessionFactory.getCurrentSession();

		session.beginTransaction();
		session.save(s);
		session.getTransaction().commit();
		session.close();
		sessionFactory.close();

抽象的来说,

session = sessionFactory.openSession();
Transaction tx = session.beginTransaction();
……
tx.commit();  

在jdbc层面上就相当于:

Connection dbconn = getConnection();
dbconn.setAutoCommit(false);
……
dbconn.commit();  

另一方面,在我们获得session的时候,hibernate会初始化数据库连接,把AutoCommit设置为false,后面在beginTransaction会再次检查AutoCommit的值是否未false(防止用户更改)。

所以

session = sessionFactory.openSession();
session.save(user);
session.close();  

上面的代码不会对数据库产生任何影响。你没提交嘛!

使用spring替hibernate管理事务

首先,我们为什么要让spring去替hibernate管理事务?

一 如果单纯的用hibernate,每次对数据库做一次操作,我都得beginTransaction然后getTransaction.commit。你不烦呀。

二 粒度的问题,事务其实在更高的层次上看是一个逻辑概念,它是几个操作的集合。而默认情况下,hibernate只管理对数据库最低层次的操作。(这个,具体的咱们在后面再说)





OK,我们已经知道了用spring管理事务的必要性,再看看用spring管理事务的方式。

有两种。

一种是使用xml,一种是使用Annotation

/////////////////////////////////以下为9月30日的补充内容

其实更精确的说,还有一种------编程式事务管理,只不过相对应后面介绍的声明式与注解式,编程式就显得很low了#

我写个简单的例子:

public class BankServiceImpl implements BankService {
	private BankDao bankDao;
	private TransactionDefinition txDefinition;
	private PlatformTransactionManager txManager;

	public boolean transfer(Long fromId, Long toId, double amount) {
		TransactionStatus txStatus = txManager.getTransaction(txDefinition);
		boolean result = false;
		try {
				result = bankDao.transfer(fromId, toId, amount);
				txManager.commit(txStatus);
			} catch (Exception e) {
				result = false;
				txManager.rollback(txStatus);
				System.out.println("Transfer Error!");
			}
		return result;
	}
}

TransactionDefinition,PlatformTransactionManager都是spring注入的#

上面的bankDao.transfer(fromId, toId, amount)就是把tromid的amount块钱转移到toid上去#

感觉是和jdbc的每什么区别,还都得提交#,我们应该关注于业务本身,对于事务的提交与回滚应该交给系统#

既然说到了jdbc,那么大家就肯定会想到jdbctemplate#那么既然有jdbctebmplage,那为什么就不能有transactionTemplate呢?

我们看下面的例子

public class BankServiceImpl implements BankService {
	private BankDao bankDao;
	private TransactionTemplate transactionTemplate;

	public boolean transfer(final Long fromId, final Long toId, final double amount) {
	return (Boolean) transactionTemplate.execute(new TransactionCallback(){
	public Object doInTransaction(TransactionStatus status) {
		Object result;
		try {
			result = bankDao.transfer(fromId, toId, amount);
		} catch (Exception e) {
			status.setRollbackOnly();
		        result = false;
		       System.out.println("Transfer Error!");
		}
		return result;
		}
		});
	}
}

这样一来,我们就能关注单纯的业务逻辑了。(只不过一旦出错了,我们还得status.setRollbackOnly())

关于硬编码这块,大家参见

全面分析 Spring 的编程式事务管理及声明式事务管理

只不过,大家请记着,除非我们是接手了一个遗留系统,否则还是不要用编程式事务管理了。

当然,虽然不鼓励大家主动去用这个东西,但是我们至少得会用,看到这个这些代码得知道是什么意思,更进一步的,如果我们还能知道里面的实现过程,那对我们的编程技术,或者说架构能力都是有帮助的。

总结一下:

基于 TransactionDefinition、PlatformTransactionManager、TransactionStatus 编程式事务管理是 Spring 提供的最原始的方式,通常我们不会这么写,但是了解这种方式对理解 Spring
事务管理的本质有很大作用。

基于 TransactionTemplate 的编程式事务管理是对上一种方式的封装,使得编码更简单、清晰。

/////////////////////////////////以上为9月30日的补充内容

Annotation

我们先说使用Annotation。

第一步

在spring的xml里面加上

一般情况下,JDBC(iBATIS) 使用 DataSourceTransactionManager,hibernate使用HibernateTransactionManager

	<bean id="txManager"
		class="org.springframework.orm.hibernate3.HibernateTransactionManager">
		<property name="sessionFactory" ref="sessionFactory" />
	</bean>

	<tx:annotation-driven transaction-manager="txManager"/>

当然,如果xml里面没有tx的命名空间,还得加上

xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/tx
           http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"

这一步没什么说的,大家记死就OK(我说的记死,不是说让大家记得上面的每一字母;只要知道有这两个配置,需要用的时候,知道去哪找就OK)。

第二步就是加事务了

这一步更简单

@Transaction

哪个方法需要spring的事务管理,就给那个方法加上 @Transaction

我给大家一个实例,向数据库里添加一个user,然后再添加一条日志记录。

说到这,先看代码。

package com.bjsxt.service;

import javax.annotation.Resource;

import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.bjsxt.dao.LogDAO;
import com.bjsxt.dao.UserDAO;
import com.bjsxt.model.Log;
import com.bjsxt.model.User;

@Component("userService")
public class UserService {

        @Resource
	private UserDAO userDAO;
	@Resource
	private LogDAO logDAO;

	public User getUser(int id) {
		return null;
	}

	public void add(User user) {
		userDAO.save(user);
		Log log = new Log();
		log.setMsg("a user saved!");
		logDAO.save(log);
	}
        //省略getset
}

package com.bjsxt.dao.impl;

import javax.annotation.Resource;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.stereotype.Component;

import com.bjsxt.dao.UserDAO;
import com.bjsxt.model.User;

@Component("userDAO")
public class UserDAOImpl implements UserDAO {

	private SessionFactory sessionFactory;

	public SessionFactory getSessionFactory() {
		return sessionFactory;
	}

	@Resource
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
	}

	public void save(User user) {
		Session s = sessionFactory.getCurrentSession();
		s.save(user);
	}
}

省略LogDAO的接口及实现。

现在的问题是我把 @Transaction加到UserDAOImpl的sava方法上(当然还有LogDAOImpl的save方法)还是加到UserService的add方法上。

我的代码都已经写出来了,大家就是用大腿想,应该也能想出来,加到整体的业务逻辑上。

这个观点提升一下就是,我们应该在service层做事务管理





OK,我们仔细看看这 @Transaction

下图是 @Transaction的doc文档

我们其实只有看两个:

readOnly,我们应该能猜出来,如果某个方法的 @transaction加了readonly,那么方法内部就不能对数据库有增删改的行为。

为什么会有这个属性呢,spring为对readOnly的transaction的方法做优化。

因此,如果你肯定某个方法是不会修改数据库,那就给他加上readOnly=true吧,另一方面,从设计上来讲,readOnly也可以看做一种检查,看某个不应该出现更改数据库的地方出现了更改操作。





第二个属性是propagation,我们能看出他的选值是Propagation,而Propagation是一个枚举类。

Propagation的说明如下:





我们最经常使用的,而且也是spring默认的就是REQUIRED

REQUIRED就是,如果当前方法没有事务,那就新产生一个事务,并且如果此方法有了事务,那么方法内部的方法调用也会使用这个事务。

至于别的几个参数,大家就都忘了吧。





如果把 @Transaction加到某个类上,就等于给这个类的所有方法都加上了 @Transaction

@Transaction标签不可继承。

前面我们已经说了,让spring管理事务有两种方式,第一是annotation,上面我们已经说了,下面我们说说使用xml。

XML(声明式事务管理)

在spring的xml中加入如下内容

<bean id="txManager"
		class="org.springframework.orm.hibernate3.HibernateTransactionManager">
		<property name="sessionFactory" ref="sessionFactory" />
	</bean>

	<aop:config>
		<aop:pointcut id="bussinessService"
			expression="execution(public * com.bjsxt.service..*.*(..))" />
		<aop:advisor pointcut-ref="bussinessService"
			advice-ref="txAdvice" />
	</aop:config>

	<tx:advice id="txAdvice" transaction-manager="txManager">
		<tx:attributes>
			<tx:method name="getUser" read-only="true" />
			<tx:method name="add*" propagation="REQUIRED"/>
		</tx:attributes>
	</tx:advice>

当然,要去掉使用annotation的

<tx:annotation-driven transaction-manager="txManager"/>

我解释一下上面的定义。

在com.bjsxt.service包及其子包下的所有类的所有public的方法都加上事务管理

具体的事务设置是,如果方法名是getUser那么设置read-only为true,如果方法是以add开头的,那么设置propagation为REQUIRED(这个其实不用设,因为是默认的)









现在有个问题,到底是用annotation那,还是xml呢?

回答是看情况。

你觉得哪个方便用哪个。





另外,上面的代码使用的是spring3 hibernate3

如果使用spring4 hibernate4

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:aop="http://www.springframework.org/schema/aop"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
           http://www.springframework.org/schema/tx
           http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
	<context:annotation-config />
	<context:component-scan base-package="com.bjsxt" />

	<bean
		class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
		<property name="locations">
			<value>classpath:jdbc.properties</value>
		</property>
	</bean>

	<bean id="dataSource" destroy-method="close"
		class="org.apache.commons.dbcp2.BasicDataSource">
		<property name="driverClassName"
			value="${jdbc.driverClassName}" />
		<property name="url" value="${jdbc.url}" />
		<property name="username" value="${jdbc.username}" />
		<property name="password" value="${jdbc.password}" />
	</bean>

	<bean id="sessionFactory"
        class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
		<property name="annotatedClasses">
			<list>
				<value>com.bjsxt.model.User</value>
				<value>com.bjsxt.model.Log</value>
			</list>
		</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>
	</bean>

	<bean id="txManager"
		class="org.springframework.orm.hibernate4.HibernateTransactionManager">
		<property name="sessionFactory" ref="sessionFactory" />
	</bean>

	<aop:config>
		<aop:pointcut id="bussinessService"
			expression="execution(public * com.bjsxt.service..*.*(..))" />
		<aop:advisor pointcut-ref="bussinessService"
			advice-ref="txAdvice" />
	</aop:config>

	<tx:advice id="txAdvice" transaction-manager="txManager">
		<tx:attributes>
			<tx:method name="getUser" read-only="true" />
			<tx:method name="add*" propagation="REQUIRED"/>
		</tx:attributes>
	</tx:advice>

</beans>

如果在一个项目里,我既有通过hibernate来访问数据库,也有使用jdbc访问数据库,那么transactionmanager选哪个呢?(如果不清楚transactionmanager是干什么的,请参阅 全面分析 Spring 的编程式事务管理及声明式事务管理  )

到底用哪个呢?

答案是采用HibernateTransactionManager

它既可以管hibernate,也可以管jdbc

参见: spring 同时配置hibernate and jdbc 事务

/////////////////////////////////以下为9月30日的补充内容

其实声明式事务管理,除了上面说的,我们还可以看看历史,我们看看在spring3.0之前,还有什么方式

怎么说呢,虽然之前的事务管理方式都已经很out了,而且我们也没太大的必要对之前的实现方法做多么深的理解,但是不是有那么已经老话嘛:温故而知新,可以为师矣。

我们只有知道了之前是怎么样的,才能真正的体会,为什么事务管理会是今天这个样子。之前是现在的基础。

之前的方法有这么几种基于TransactionInterceptor,基于TransactionProxyFactoryBean再之后就是我们上面讲的:基于 <tx> 和 <aop> 命名空间的声明式事务管理,和基于 @Transactional 的方式。

总结一下:

基于 TransactionInterceptor 的声明式事务是 Spring 声明式事务的基础,通常也不建议使用这种方式,但是与前面一样,了解这种方式对理解 Spring 声明式事务有很大作用。

基于 TransactionProxyFactoryBean 的声明式事务是上中方式的改进版本,简化的配置文件的书写,这是 Spring 早期推荐的声明式事务管理方式,但是在 Spring 2.0 中已经不推荐了。

基于 <tx> 和 <aop> 命名空间的声明式事务管理是目前推荐的方式,其最大特点是与 Spring AOP 结合紧密,可以充分利用切点表达式的强大支持,使得管理事务更加灵活。

基于 @Transactional 的方式将声明式事务管理简化到了极致。开发人员只需在配置文件中加上一行启用相关后处理 Bean 的配置,然后在需要实施事务管理的方法或者类上使用 @Transactional 指定事务规则即可实现事务管理,而且功能也不必其他方式逊色。

关于TransactionInterceptor和TransactionProxyFactoryBean 大家请参考 全面分析 Spring 的编程式事务管理及声明式事务管理

/////////////////////////////////以上为9月30日的补充内容

参考资料

北京尚学堂 马士兵 spring3讲解

http://www.iteye.com/topic/177988

http://blog.sina.com.cn/s/blog_4b5bc0110100h0wz.html

spring 同时配置hibernate and jdbc 事务

全面分析 Spring 的编程式事务管理及声明式事务管理

Hibernate与Spring的事务管理的更多相关文章

  1. Spring第13篇—–Spring整合Hibernate之声明式事务管理

    不容置疑的我们可以知道Spring的事务管理是通过AOP(AOP把我们的事务管理织入到我们的业务逻辑里面了)的方式来实现的,因为事务方面的代码与spring的绑定并以一种样板式结构使用.(面向切面编程 ...

  2. Spring应用——事务管理

    事务基础:请参看:http://www.cnblogs.com/solverpeng/p/5720306.html 一.Spring 事务管理 1.前提:事务管理器 在使用 Spring 声明式事务管 ...

  3. spring,mybatis事务管理配置与@Transactional注解使用[转]

    spring,mybatis事务管理配置与@Transactional注解使用[转] spring,mybatis事务管理配置与@Transactional注解使用 概述事务管理对于企业应用来说是至关 ...

  4. Spring高级事务管理难点剖析

    1Spring事务传播行为 所谓事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播.Spring支持7种事务传播行为 PROPAGATION_REQUIRED(加入已有事务) 如果当前没 ...

  5. Spring Boot事务管理(上)

    摘要 本文主要介绍基于Spring Boot的事务管理,尤其是@Transactional注解详细用法.首先,简要介绍Spring Boot中如何开启事务管理:其次,介绍在Spring,Spring ...

  6. 3.1.4 Spring的事务管理

    四.Spring的事务管理 事务原本是数据库中的概念, 在Dao层. 但一般情况下, 需要将事务提升到 业务层, 即Service层. 这样做是为了 能够使用事务的特性来管理具体的业务. 1. Spr ...

  7. Spring的事务管理1

    事务的回顾: 事务:逻辑上的一组操作,组成这组事务的各个单元,要么全部成功,要么全部失败 事务的特性:ACID 原子性(Atomicity):事务不可分割 一致性(Consistency):事务执行前 ...

  8. Spring 对事务管理的支持

    1.Spring对事务管理的支持 Spring为事务管理提供了一致的编程模板,在高层次建立了统一的事务抽象.也就是说,不管选择Spring JDBC.Hibernate .JPA 还是iBatis,S ...

  9. Spring Boot事务管理(中)

    在上一篇 Spring Boot事务管理(上)的基础上介绍Spring Boot事务属性和事务回滚规则 . 4 Spring Boot事务属性 什么是事务属性呢?事务属性可以理解成事务的一些基本配置, ...

随机推荐

  1. 【python标准库模块一】时间模块time学习

    本文介绍python的标准库模块time的常见用法 时间模块time 导入时间模块 import time 得到时间戳,这是统计从1970年1月1日0点0分到现在经过了多少秒,一般用于加减法一起用,比 ...

  2. Docker 编辑网络配置文件

    Docker 1.2.0 开始支持在运行中的容器里编辑 /etc/hosts, /etc/hostname 和 /etc/resolve.conf 文件. 但是这些修改是临时的,只在运行的容器中保留, ...

  3. Android Studio精彩案例(三)《模仿微信ViewPage+Fragment实现方式二》

    转载本专栏文章,请注明出处,尊重原创 .文章博客地址:道龙的博客 写在前面的话:此专栏是博主在工作之余所写,每一篇文章尽可能写的思路清晰一些,属于博主的"精华"部分,不同于以往专栏 ...

  4. Android源码浅析(六)——SecureCRT远程连接Linux,配置端点和字节码

    Android源码浅析(六)--SecureCRT远程连接Linux,配置端点和字节码 需要编译源码的同学,一般都是win+虚拟机吧,但是再虚拟机里体验并不是很好,所有市面上有很多的软件能够做到在wi ...

  5. 用Python递归解决阿拉伯数字转为中文财务数字格式的问题(2)--打开思路的一种方法

    几天前自己写了个将阿拉伯数字转为中文财务数字的程序.用的递归,不幸的是它是树形递归. 虽然实际过程中不太可能出现金额数字大到让Python递归栈溢出,但是始终是一块心病,这玩意终究在理论上是受限制的. ...

  6. C#删除WebBrowser控件的Session

    因最近做一个成绩查询导出的程序,用到webbrowser控件,该查询的网站限制一个会话只能查询3次成绩,而我要查询4000多人的成绩. using System.Runtime.InteropServ ...

  7. Apache shiro集群实现 (三)shiro身份认证(Shiro Authentication)

    Apache shiro集群实现 (一) shiro入门介绍 Apache shiro集群实现 (二) shiro 的INI配置 Apache shiro集群实现 (三)shiro身份认证(Shiro ...

  8. System startup files

    System startup files When you log in, the shell defines your user environment after reading the init ...

  9. UNIX环境高级编程——环境变量表读取/添加/修改/删除

    #include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char* pVal ...

  10. EBS总账模块与其他模块数据关联关系

    表名:GL_IMPORT_REFERENCES 说明:总账导入附加信息表 用途:用来追溯从子模块传入总账模块的明细,对于报表开发很有帮助 SQL 语句: select * from gl_je_hea ...