数据库事务(Transaction) 指做为单个逻辑工做单元执行的一系列操做。将一组相关操做组合为一个要么所有成功要么所有失败的单元,使应用程序更加可靠。java
事务必须知足ACID:mysql
可清楚定义事务边界,实现细粒度的事务控制,好比可经过程序代码来控制事务什么时候开始、什么时候结束等,可实现细粒度事务控制。spring
如不须要细粒度的事务控制,可以使用声明式事务,只需在Spring配置文件作一些配置便可将操做归入到事务管理中,解除与代码的耦合, 对应用代码影响最小。当不需事务管理时,可直接从Spring配置文件中移除该设置。sql
spring不直接管理事务,而将管理事务责任委托给JTA或相应持久性机制提供的特定平台的事务实现数据库
事务管理器实现 | 场合 |
---|---|
org.springframework.jdbc.datasource.DataSourceTransactionManagerexpress |
在单一JDBC Datasource中管理事务apache |
org.springframework.orm.hibernate3.HibernateTransactionManager编程 |
持久化机制为hibernate后端 |
org.springframework.jdo.JdoTransactionManager并发 |
持久化机制为Jdo |
org.springframework.transaction.jta.JtaTransactionManager |
使用一个JTA实现来管理事务。一个事务跨越多个资源时必须使用 |
org.springframework.orm.ojb.PersistenceBrokerTransactionManager |
持久化机制为apache的ojb |
传播行为:定义关于客户端和被调用方法的事务边界
传播行为 |
含 义 |
---|---|
REQUIRED |
业务方法需在一个事务中运行。如方法运行时已处在一事务中,则加入到该事务,不然为本身建立一个新事务 |
NOT_SUPPORTED |
声明方法不需事务。如方法没有关联到一事务,容器不会为它开启事务;如方法在一事务中被调用,该事务被挂起,在方法调用结束后,原事务恢复执行 |
REQUIRESNEW |
不论是否存在事务,业务方法总会为本身发起一新事务。如方法已运行在一个事务中,则原有事务会被挂起,新的事务会被建立,直到方法执行结束,新事务才算结束,原事务才恢复执行 |
MANDATORY |
指定业务方法只能在一个已存在事务中执行,业务方法不能发起本身事务。如业务方法在没有事务的环境调用,容器抛出异常。 |
SUPPORTS |
如业务方法在某个事务范围内被调用,则方法成为该事务的一部分。如业务方法在事务范围外被调用,则方法在没有事务的环境下执行 |
Never |
指定业务方法绝对不能在事务范围内执行,如业务方法在某个事务中执行,容器抛出异常,只有业务方法没有关联到任何事务才能正常执行 |
NESTED |
如一活动事务存在,则运行在一嵌套事务中. 如没有活动事务, 则按REQUIRED属性执行。使用一单独的事务, 该事务拥有多个可回滚保存点。内部事务回滚不会对外部事务形成影响。只对DataSourceTransactionManager事务管理器起效 |
隔离级别 |
含义 |
---|---|
DEFAULT |
使用后端数据库默认的隔离级别 |
READ_UNCOMMITED |
容许读取还未提交的已改变数据,可能致使脏、幻、不可重复读 |
READ_COMMITTED |
容许在并发事务已经提交后读取。可防止脏读,但幻读和 不可重复读仍可发生 |
REPEATABLE_READ |
对相同字段的屡次读取一致,除非数据被事务自己改变。可防止脏、不可重复读,但幻读仍可能发生。 |
SERIALIZABLE |
彻底服从ACID的隔离级别,确保不发生脏、幻、不可重复读。在全部隔离级别中最慢的,典型的经过彻底锁定事务涉及的数据表。 |
注意:随着隔离级别的提升并发性能下降,具体使用那种隔离级别须要视状况而定。
补充:
表示该方法是否只读,默认是false(可写)。对数据库执行增、删、改,事务必定要定义可写,对数据库执行查询的时候,此时能够定为只读(true)
咱们采用的是声明式事务管理,配置事务时,需在xml配置文件引入声明事务的tx标签
事务的配置方式:
参考代码以下:
<?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-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> <!-- 链接池基本配置信息 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/springdb?useUnicode=true&characterEncoding=utf8"></property> <property name="username" value="root"></property> <property name="password" value="liuxin950326"></property> <!-- 链接池启动时的初始值 --> <property name="initialSize" value="10"></property> <!-- 链接池的最大值 --> <property name="maxActive" value="80"></property> <!-- 最大空闲值.通过一个高峰时间,链接池可将已用不到链接慢慢释放一部分,一直减小到maxIdle为止 --> <property name="maxIdle" value="5"></property> <property name="minIdle" value="2"></property> </bean> <!-- 配置JDBC模板 --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 声明式事务管理_xml配置_用通知管理事务 --> <tx:advice id="txAdvice" transaction-manager="transactionManager"> <!-- 定义事务关联属性 --> <tx:attributes> <!-- 定义事务关联的方法 transfer* 表示transfer开头的方法 isolation(隔离性) propagation(传播性) read-only(只读性) --> <tx:method name="transfer*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/> <tx:method name="*" read-only="true"/> </tx:attributes> </tx:advice> <aop:config> <!-- 定义切入点 --> <aop:pointcut expression="execution(* www.enfp.lx_04_jdbc.lx_01_transaction_xml..*.*(..))" id="pointcutTransfer"/> <!-- 切入点与(通知形式)的事务相关联 --> <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcutTransfer"/> </aop:config> <bean id="bankAccountDao" class="www.enfp.lx_04_jdbc.lx_01_transaction_xml.dao.BankAccountDaoImpl"> <property name="jdbcTemplate" ref="jdbcTemplate"></property> </bean> <bean id="bankAccountService" class="www.enfp.lx_04_jdbc.lx_01_transaction_xml.service.BankAccountServiceImpl"> <property name="bankAccountDao" ref="bankAccountDao"></property> </bean> </beans>
参考代码以下:
beans.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-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd"> <!-- 链接池基本配置信息 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/springdb?useUnicode=true&characterEncoding=utf8"></property> <property name="username" value="root"></property> <property name="password" value="liuxin950326"></property> <!-- 链接池启动时的初始值 --> <property name="initialSize" value="10"></property> <!-- 链接池的最大值 --> <property name="maxActive" value="80"></property> <!-- 最大空闲值.通过一个高峰时间,链接池可将已用不到链接慢慢释放一部分,一直减小到maxIdle为止 --> <property name="maxIdle" value="5"></property> <property name="minIdle" value="2"></property> </bean> <!-- 注释组件自动扫描 --> <context:component-scan base-package="www.enfp.lx_04_jdbc.lx_01_transaction_annotation"></context:component-scan> <!-- 配置JDBC模板 --> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 配置事务管理器 --> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 启动以注解的方式配置事务管理器 --> <tx:annotation-driven transaction-manager="transactionManager"/> </beans>
BankAccountServiceImpl.java
package www.enfp.lx_04_jdbc.lx_01_transaction_annotation.service; import javax.annotation.Resource; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Isolation; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import www.enfp.lx_04_jdbc.lx_01_transaction_xml.dao.IBankAccountDao; import www.enfp.lx_04_jdbc.lx_01_transaction_xml.domain.BankAccount; @Service("bankAccountService") @Transactional(readOnly = true) public class BankAccountServiceImpl implements IBankAccountService { @Resource(name = "bankAccountDao") private IBankAccountDao bankAccountDao = null; public IBankAccountDao getBankAccountDao() { return bankAccountDao; } public void setBankAccountDao(IBankAccountDao bankAccountDao) { this.bankAccountDao = bankAccountDao; } @Override @Transactional(isolation = Isolation.DEFAULT, propagation = Propagation.REQUIRED, readOnly = false) public boolean transferBankAccount(String firstBankAccountName, String secondBankAccountName, double money) { try { if (money < 0) { throw new RuntimeException("转帐金额必须为正数"); } BankAccount accountA = this.bankAccountDao .queryBankAccountByAccountName(firstBankAccountName); if (accountA.getBalance() < money) { throw new RuntimeException("余额不足"); } BankAccount accountB = this.bankAccountDao .queryBankAccountByAccountName(secondBankAccountName); accountA.setBalance(accountA.getBalance() - money); accountB.setBalance(accountB.getBalance() + money); this.bankAccountDao.updateBankAccount(accountA); this.bankAccountDao.updateBankAccount(accountB); return true; } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } return false; } }
未完待续。。。。。