`
CoderDream
  • 浏览: 470573 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
社区版块
存档分类
最新评论

事务管理入门-JDBC/Hibernate事务管理器/Spring注解 3种方式

阅读更多

在软件开发过程中,经常会遇到事务问题,下面我们来看看最简单的JDBC和Spring分别如何处理事务。

 

关于事务控制的场景当然是转账,我们使用的数据库是MySQL。

打开test数据库后,运行下面的数据库脚本:

DROP TABLE IF EXISTS account;
CREATE TABLE account (
accountId int primary key auto_increment,
accountname varchar(20),
money int not null
);

INSERT INTO ACCOUNT(ACCOUNTNAME,MONEY) VALUES('zhangsan',100);
INSERT INTO ACCOUNT(ACCOUNTNAME,MONEY) VALUES('lisi',100);
 

1、JDBC中的事务控制

 

代码1:AccountDAO.java

package com.coderdream;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class AccountDAO {

	public void transfer(Integer fromAccountId, Integer toAccountId, int money) {
		try {
			// 1. 注册驱动
			Class.forName("com.mysql.jdbc.Driver");

			// 2. 获取数据库的连接
			Connection conn = DriverManager.getConnection(
					"jdbc:mysql://localhost/test", "root", "1234");

			// 3. 获取表达式
			Statement stmt1 = conn.createStatement();
			Statement stmt2 = conn.createStatement();
			Statement stmt3 = conn.createStatement();
			Statement stmt4 = conn.createStatement();

			// 执行插入数据的 SQL
			ResultSet rs1 = stmt1
					.executeQuery("SELECT MONEY FROM ACCOUNT WHERE ACCOUNTID="
							+ fromAccountId);
			// 5. 显示结果集里面的数据
			int money1 = 0;
			while (rs1.next()) {
				System.out.println(rs1.getInt(1));
				money1 = rs1.getInt(1);
			}

			// 修改
			money1 -= money;
			System.out.println("money1: " + money1);
			stmt2.executeUpdate("UPDATE ACCOUNT SET MONEY=" + money1
					+ " WHERE ACCOUNTID=" + fromAccountId);

			// 执行插入数据的 SQL
			ResultSet rs2 = stmt3
					.executeQuery("SELECT MONEY FROM ACCOUNT WHERE ACCOUNTID="
							+ toAccountId);
			// 5. 显示结果集里面的数据
			int money2 = 0;
			while (rs2.next()) {
				System.out.println(rs2.getInt(1));
				money2 = rs2.getInt(1);
			}

			// 修改
			money2 += money;
			System.out.println("money2: " + money2);
			stmt2.executeUpdate("UPDATE ACCOUNT SET MONEY=" + money2
					+ " WHERE ACCOUNTID=" + toAccountId);

			// 6. 释放资源
			rs1.close();
			rs2.close();
			stmt1.close();
			stmt2.close();
			stmt3.close();
			stmt4.close();
			conn.close();
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		} catch (SQLException e) {
			e.printStackTrace();
		}

	}
}
 

代码2:AccountService.java

package com.coderdream;

public class AccountService {

	private AccountDAO accountDAO;

	/**
	 * 通过 Spring 向 Service ͨszh注入 Dao
	 * 
	 * @param accountDAO
	 */
	public void setAccountDAO(AccountDAO accountDAO) {
		this.accountDAO = accountDAO;
	}

	/**
	 * 转账
	 * 
	 * @param fromAccountId
	 *            转出帐号
	 * @param toAccountId
	 *            转入帐号
	 * @param money
	 *            转账金额
	 */
	public void transfer(Integer fromAccountId, Integer toAccountId, int money) {
		accountDAO.transfer(fromAccountId, toAccountId, money);
	}
}

 

代码3:Main.java

package com.coderdream;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class Main {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		ApplicationContext act = new FileSystemXmlApplicationContext(
				"src/applicationContext.xml");
		AccountService accountService = (AccountService) act
				.getBean("accountService");
		try {
			// 帐号1转账1元至帐号2
			accountService.transfer(1, 2, 1);//A
               } catch (Exception e) {
			System.out.println("转账失败!");
		}
	}
}
 

代码4: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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">

	<bean id="accountDAO" class="com.coderdream.AccountDAO" />
	
	<bean id="accountService" class="com.coderdream.AccountService">
		<property name="accountDAO" ref="accountDAO"></property>
	</bean>
</beans>

 

上面的代码是没有加事务控制的,如果把‘A’处的代码换成:

			// 帐号1转账1元至帐号3
			accountService.transfer(1, 3, 1);//A

 

则由于帐号3不存在,所以会出现问题,帐号1的金额会减1,而帐号2的金额不会变,转账的1元就“不翼而飞”了,所以必须在Dao层加入事务控制。

 

代码5:加入事务控制后的AccountDAO

public class AccountDAO {

	public void transfer(Integer fromAccountId, Integer toAccountId, int money) {
		Connection conn = null;
		ResultSet rs1 = null;
		Integer rs2 = null;
		ResultSet rs3 = null;
		Integer rs4 = null;
		Statement stmt1 = null;
		Statement stmt2 = null;
		Statement stmt3 = null;
		Statement stmt4 = null;
		// 1. 注册驱动
		try {
			Class.forName("com.mysql.jdbc.Driver");

			// 2. 获取数据库的连接
			conn = DriverManager.getConnection("jdbc:mysql://localhost/test",
					"root", "1234");

			conn.setAutoCommit(false);

			// 3. 获取表达式
			stmt1 = conn.createStatement();
			stmt2 = conn.createStatement();
			stmt3 = conn.createStatement();
			stmt4 = conn.createStatement();

			// 执行插入数据的 SQL
			rs1 = stmt1
					.executeQuery("SELECT MONEY FROM ACCOUNT WHERE ACCOUNTID="
							+ fromAccountId);
			// 5. 显示结果集里面的数据
			int money1 = 0;
			while (rs1.next()) {
				System.out.println(rs1.getInt(1));
				money1 = rs1.getInt(1);
			}

			// 修改
			money1 -= money;
			System.out.println("money1: " + money1);
			rs2 = stmt2.executeUpdate("UPDATE ACCOUNT SET MONEY=" + money1
					+ " WHERE ACCOUNTID=" + fromAccountId);
			if (1 != rs2) {
				throw new Exception(" 转出失败,帐号: " + fromAccountId);
			}

			// 执行插入数据的 SQL
			rs3 = stmt3
					.executeQuery("SELECT MONEY FROM ACCOUNT WHERE ACCOUNTID="
							+ toAccountId);
			// 5. 显示结果集里面的数据
			int money2 = 0;
			while (rs3.next()) {
				System.out.println(rs3.getInt(1));
				money2 = rs3.getInt(1);
			}

			// 修改
			money2 += money;
			System.out.println("money2: " + money2);
			rs4 = stmt2.executeUpdate("UPDATE ACCOUNT SET MONEY=" + money2
					+ " WHERE ACCOUNTID=" + toAccountId);
			if (1 != rs4) {
				throw new Exception(" 转入失败,帐号: " + toAccountId);
			}

			conn.commit();
			System.out.println("转帐成功!");
		} catch (Exception e) {
			try {
				conn.rollback();
			} catch (Exception e1) {
				e1.printStackTrace();
			}
			e.printStackTrace();
		}
		// 6. 释放资源
		finally {
			try {
				if (rs1 != null) {
					rs1.close();
				}
				if (rs3 != null) {
					rs3.close();
				}
				if (stmt1 != null) {
					stmt1.close();
				}
				if (stmt2 != null) {
					stmt2.close();
				}
				if (stmt3 != null) {
					stmt3.close();
				}
				if (stmt4 != null) {
					stmt4.close();
				}
				if (conn != null) {
					conn.close();
				}
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
	}
}
 

2、Spring中的事务控制方式一:Hibernate的事务管理器托管

我们先来看看通过Spring+Hibernate来操作数据库的简单示例。

先通过 MyEclipse 生成Hibernate 需要的 Bean 及 hbm.xml文件:

 

代码6:Account.java

package com.coderdream;

/**
 * Account entity.
 * 
 * @author MyEclipse Persistence Tools
 */
public class Account implements java.io.Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 909891879728703117L;

	private Integer accountId;
	private String accountname;
	private Integer money;

	// Property accessors

	public Integer getAccountId() {
		return this.accountId;
	}

	public void setAccountId(Integer accountId) {
		this.accountId = accountId;
	}

	public String getAccountname() {
		return this.accountname;
	}

	public void setAccountname(String accountname) {
		this.accountname = accountname;
	}

	public Integer getMoney() {
		return this.money;
	}

	public void setMoney(Integer money) {
		this.money = money;
	}

	// Constructors

	/** default constructor */
	public Account() {
	}

	/** full constructor */
	public Account(String accountname, Integer money) {
		this.accountname = accountname;
		this.money = money;
	}

}
 

代码7:Account.hbm.xml

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- 
    Mapping file autogenerated by MyEclipse Persistence Tools
-->
<hibernate-mapping>
    <class name="com.coderdream.Account" table="account" catalog="test">
        <id name="accountId" type="java.lang.Integer">
            <column name="accountId" />
            <generator class="native" />
        </id>
        <property name="accountname" type="java.lang.String">
            <column name="accountname" length="20" />
        </property>
        <property name="money" type="java.lang.Integer">
            <column name="money" length="20" />
        </property>
    </class>
</hibernate-mapping>
 

代码8:

package com.coderdream;

import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

public class AccountDAO extends HibernateDaoSupport {

	public void addMoney(Integer accountId, int money) {
		Account account = (Account) getHibernateTemplate().get(Account.class,
				accountId);
		account.setMoney(account.getMoney() + money);
		getHibernateTemplate().saveOrUpdate(account);
	}

	public void subMoney(Integer accountId, int money) {
		Account account = (Account) getHibernateTemplate().get(Account.class,
				accountId);
		account.setMoney(account.getMoney() - money);
		getHibernateTemplate().saveOrUpdate(account);
	}
}
 

代码9:

package com.coderdream;

public class AccountService {

	private AccountDAO accountDAO;

	/**
	 * 通过 Spring 将 DAO 注入到 Service
	 * 
	 * @param accountDAO
	 */
	public void setAccountDAO(AccountDAO accountDAO) {
		this.accountDAO = accountDAO;
	}

	/**
	 * 转账方法包括两个原子方法:转出方法和转入方法
	 * 
	 * @param fromAccountId
	 * @param toAccountId
	 * @param money
	 */
	public void transfer(Integer fromAccountId, Integer toAccountId, int money) {
		accountDAO.subMoney(fromAccountId, money);
		accountDAO.addMoney(toAccountId, money);
	}
}
 

代码10:

package com.coderdream;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class Main {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		ApplicationContext act = new FileSystemXmlApplicationContext(
				"src/applicationContext.xml");
		AccountService accountService = (AccountService) act
				.getBean("accountService");
		try {
			// 帐号1转账1元至帐号2
			accountService.transfer(1, 2, 1);// B
		} catch (Exception e) {
			System.out.println("转账失败");
		}
	}
}
 

上面的代码同样没有加入事务控制,如果在‘B’处将转入的帐号设置为不存在的帐号3,同样会有问题,下面我们来加入事务控制,我们需要修改 applicationContext.xml 文件:

 

代码11:增加事务后的 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"
	xsi:schemaLocation="http://www.springframework.org/schema/beans 
	http://www.springframework.org/schema/beans/spring-beans-2.5.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/test"></property>
		<property name="username" value="root"></property>
		<property name="password" value="1234"></property>
	</bean>

	<bean id="sessionFactory"
		class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
		<property name="dataSource">
			<ref bean="dataSource" />
		</property>
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">
					org.hibernate.dialect.MySQLDialect
				</prop>
				<prop key="hibernate.show_sql">true</prop>
			</props>
		</property>
		<property name="mappingResources">
			<list>
				<value>com/coderdream/Account.hbm.xml</value>
			</list>
		</property>
	</bean>

	<!-- 引用Hibernate的事务管理器 -->
	<bean id="transactionManager"
		class="org.springframework.orm.hibernate3.HibernateTransactionManager">
		<property name="sessionFactory" ref="sessionFactory"></property>
	</bean>

	<bean id="accountDAO" class="com.coderdream.AccountDAO">
		<property name="sessionFactory" ref="sessionFactory"></property>
	</bean>

	<!-- 通过事务管理器来管理Service -->
	<bean id="accountService"
		class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
		<property name="transactionManager" ref="transactionManager"></property>
		<property name="target">
			<bean class="com.coderdream.AccountService">
				<property name="accountDAO" ref="accountDAO"></property>
			</bean>
		</property>
		<property name="transactionAttributes">
			<props>
				<prop key="transfer">PROPAGATION_REQUIRED</prop>
			</props>
		</property>
	</bean>
</beans>
 

3、Spring中的事务控制方式二:注解方式

当然,我们还可以通过注解的方式加入事务的控制。

我们需要先在 applicationContext.xml 声明事务控制器和注解:

 

代码12: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:jee="http://www.springframework.org/schema/jee"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:context="http://www.springframework.org/schema/context"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.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/test"></property>
		<property name="username" value="root"></property>
		<property name="password" value="1234"></property>
	</bean>

	<bean id="sessionFactory"
		class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
		<property name="dataSource">
			<ref bean="dataSource" />
		</property>
		<property name="hibernateProperties">
			<props>
				<prop key="hibernate.dialect">
					org.hibernate.dialect.MySQLDialect
				</prop>
				<prop key="hibernate.show_sql">true</prop>
			</props>
		</property>
		<property name="mappingResources">
			<list>
				<value>com/coderdream/Account.hbm.xml</value>
			</list>
		</property>
	</bean>

	<!-- 引用Hibernate的事务管理器 -->
	<bean id="transactionManager"
		class="org.springframework.orm.hibernate3.HibernateTransactionManager">
		<property name="sessionFactory" ref="sessionFactory"></property>
	</bean>

	<!-- 使用annotation定义事务 -->
	<tx:annotation-driven transaction-manager="transactionManager" />

	<bean id="accountDAO" class="com.coderdream.AccountDAO">
		<property name="sessionFactory" ref="sessionFactory"></property>
	</bean>

	<bean id="accountService" class="com.coderdream.AccountService">
		<property name="accountDAO" ref="accountDAO"></property>
	</bean>
</beans>
 

同时需要在Service层的要使用的方法上声明事务,如:

@Transactional(readOnly = false)

 如果只是对数据库进行查询操作,这里的“readOnly = true“,如果是”增/删/改“操作,则为 false。

 

代码13:在方法上加入“注解式”事务控制后的 AccountService.java

package com.coderdream;

public class AccountService {

	private AccountDAO accountDAO;

	/**
	 * 通过 Spring 将 DAO 注入到 Service
	 * 
	 * @param accountDAO
	 */
	public void setAccountDAO(AccountDAO accountDAO) {
		this.accountDAO = accountDAO;
	}

	/**
	 * 转账方法包括两个原子方法:转出方法和转入方法
	 * 
	 * @param fromAccountId
	 * @param toAccountId
	 * @param money
	 */
	@Transactional(readOnly = false)
	public void transfer(Integer fromAccountId, Integer toAccountId, int money) {
		accountDAO.subMoney(fromAccountId, money);
		accountDAO.addMoney(toAccountId, money);
	}
}

 

5
2
分享到:
评论
1 楼 suntine 2011-04-26  
呵呵 不错

相关推荐

    spring hibernate,spring jdbc事务管理

    两个项目,一个项目是基于spring jdbc实现的分布式事务,一个是基于spring hibernate的分布式事务,hibernate项目里的applicationContext2.xml是基于mysql和mssql, applicationContext3.xml基于两个mssql, ...

    passwd002-spring-hibernate应用

    这是一个Dynamic Web Project,主要使用hibernate和spring框架,其中事务管理使用的是注解。 hibernate的版本:hibernate-distribution-3.6.6.Final; spring的版本:spring-framework-3.1.0.M2 。 注意:此项目没有...

    spring3.2+strut2+hibernate4

    spring3.2+strut2+hibernate4 注解方式。 spring.xml &lt;beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi=...

    spring mvc + spring + hibernate 全注解整合开发视频教程 11

    spring mvc + spring + hibernate 全注解整合开发视频教程 11

    SpringMVC+Hibernate全注解整合

    &lt;bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"&gt; &lt;property name="dataSource" ref="dataSource" /&gt; &lt;value&gt;...

    spring-jdbc-orm:基于spring-jdbc 写的一个小型ORM

    spring-jdbc-orm基于spring-jdbc 写的一个小型ORM设计初衷由于公司现有的代码工程经历了无数人的手,原来的初衷已经变了模样,可以说乱的让我不能接受,代码中大部分都使用了Map封装实体信息,有伤大雅,大部分业务...

    spring2.5学习PPT 传智博客

    使用Spring注解方式管理事务与传播行为详解 24.使用Spring配置文件实现事务管理 25.搭建和配置Spring与Hibernate整合的环境 26.Spring集成的Hibernate编码与测试 27.Struts与Spring集成方案1(Struts集成Spring) ...

    ssh框架所需整合的所有42个jar包

    struts2:struts2-convention-plugin-2.3.1.2.jar ---注解开发 struts2-spring-plugin-2.3.1.2.jar---用于整合spring hibernate:hibernate3.jar---核心包 hibernate-release-4.3.1.Final\lib\required\*.jar ...

    OA项目SSH整合框架

    2,配置声明式事务(使用基于注解的方式) 1,配置 &lt;!-- 配置事务管理器 --&gt; &lt;bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"&gt; ...

    spring的ppt

    开始spring之旅;装配bean;创建切面;使用Aspectj进行AOP开发;使用pojo+xml进行AOP开发;征服数据库-jdbc;征服数据库-集成hibernate;...事务管理-spring2.5配置方式;事务管理-注解驱动事务等等。

    Spring-Reference_zh_CN(Spring中文参考手册)

    9.9.1. 对一个特定的 DataSource 使用错误的事务管理器 9.10. 更多的资源 10. DAO支持 10.1. 简介 10.2. 一致的异常层次 10.3. 一致的DAO支持抽象类 11. 使用JDBC进行数据访问 11.1. 简介 11.1.1. Spring JDBC包结构...

    spring_MVC源码

    弃用了struts,用spring mvc框架做了几个项目,感觉都不错,而且使用了注解方式,可以省掉一大堆配置文件。本文主要介绍使用注解方式配置的spring mvc,之前写的spring3.0 mvc和rest小例子没有介绍到数据层的内容,...

    Spring.3.x企业应用开发实战(完整版).part2

    10.5.2 Hibernate+Spring JDBC混合框架的事务管理 10.6 特殊方法成漏网之鱼 10.6.1 哪些方法不能实施Spring AOP事务 10.6.2 事务增强遗漏实例 10.7 数据连接泄漏 10.7.1 底层连接资源的访问问题 10.7.2 Spring JDBC...

    06丨20%的业务代码的Spring声明式事务,可能都没处理正确

    Java Transaction API (JTA)、JDBC、Hibernate 和 Java Persistence API (JPA) 等事务 API,实现了一致的编程模型,而 Spring 的声明式事务功能更是提供了极其方便的事务配置方式,配合 Spring Boot 的自动配置,...

    Spring中文帮助文档

    9.9.1. 对一个特定的 DataSource 使用了错误的事务管理器 9.10. 更多的资源 10. DAO支持 10.1. 简介 10.2. 一致的异常层次 10.3. 一致的DAO支持抽象类 11. 使用JDBC进行数据访问 11.1. 简介 11.1.1. 选择一...

    Spring API

    9.9.1. 对一个特定的 DataSource 使用了错误的事务管理器 9.10. 更多的资源 10. DAO支持 10.1. 简介 10.2. 一致的异常层次 10.3. 一致的DAO支持抽象类 11. 使用JDBC进行数据访问 11.1. 简介 11.1.1. 选择一种...

    SpringMVC-SSH全注解

    &lt;bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"&gt; &lt;property name="dataSource" ref="dataSource" /&gt; &lt;value&gt;...

    Spring+3.x企业应用开发实战光盘源码(全)

     第9章:介绍了Spring事务管理的工作机制,通过XML、注解等方式进行事务管理配置,同时还讲解了JTA事务配置知识。  第10章:对实际应用中Spring事务管理各种疑难问题进行透彻的剖析,让读者对Spring事务管理不再有...

    Spring面试题含答案.pdf

    45. 使用 Spring 通过什么方式访问 Hibernate? 46. Spring 支持的 ORM 47.如何通过 HibernateDaoSupport 将 Spring 和 Hibernate 结合起来? 48. Spring 支持的事务管理类型 49. Spring 框架的事务管理有哪些优点?...

Global site tag (gtag.js) - Google Analytics