`
CoderDream
  • 浏览: 477330 次
  • 性别: 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分别与jdbc和hibernate结合的事务控制--案例

    本案例重点探讨了Spring如何与两种流行的数据访问技术——JDBC(Java Database Connectivity)和Hibernate——相结合,进行事务管理。事务控制是确保数据库操作一致性、完整性的关键,尤其在多步骤操作中,它能防止...

    Spring-JDBC,带一小例子

    2. **Transaction Management**: Spring-JDBC支持声明式事务管理,允许开发者使用注解(如@Transactional)来控制事务的边界,无需手动调用commit()和rollback()。这提高了代码的可读性和可维护性。 3. **...

    spring hibernate,spring jdbc事务管理

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

    Spring+Hibernate注解事务实例

    Spring提供了两种事务管理方式:编程式事务管理和声明式事务管理。其中,声明式事务管理通过注解或者XML配置实现,更为简洁且易于维护。 Hibernate是一个流行的Java ORM(对象关系映射)框架,它简化了数据库操作,...

    spring mvc+hibernate 实现事务管理(全注解版)

    2. **Hibernate事务**: Hibernate本身也提供了一种事务管理机制,但在Spring环境中,我们通常使用Spring的事务管理器来协调。当@Transactional注解应用于方法上,Spring会在该方法执行前后自动管理事务的开始和结束...

    hibernate+junit+mysql-jdbc开发核心jar包三合一

    在Java Web开发中,SSH(Spring、Struts、Hibernate)框架是常见的选择,它们提供了强大的功能,用于构建高效、可维护的应用程序。本资源“hibernate+junit+mysql-jdbc开发核心jar包三合一”显然是为了支持SSH框架中...

    spring-boot 集成hibernate

    2. **事务管理**: Spring Boot默认使用`@Transactional`注解进行声明式事务管理。在需要事务操作的方法上添加该注解,Spring会自动管理事务的开始、提交或回滚。 3. **日志**: 开启SQL日志,有助于调试和性能优化。...

    spring-jdbc-dao

    2. 声明式事务管理:利用@Transactional注解在方法级别声明事务,Spring会自动进行事务的开启、提交或回滚。 五、Spring JDBC与其他技术结合 1. MyBatis集成:Spring JDBC与MyBatis结合,可以利用MyBatis的动态SQL...

    spring3,hibernate4 配置声明式事务管理(annotation方式)

    本篇将详细介绍如何在Spring 3和Hibernate 4中通过注解来实现声明式事务管理。 首先,我们需要在项目中引入Spring和Hibernate的依赖库。这通常通过Maven或Gradle等构建工具完成,确保添加了相应的依赖项。 接着,...

    spring3、 hibernate4 配置声明式事务管理(annotation方式)

    本篇将详细介绍如何在Spring 3和Hibernate 4中配置声明式事务管理,采用注解方式。 一、Spring的事务管理 Spring提供了两种事务管理方式:编程式事务管理和声明式事务管理。编程式事务管理需要在代码中显式地调用...

    spring mvc 与JPA/Hibernate的整合示例

    总之,这个示例将指导开发者如何将Spring MVC、JPA和Hibernate整合到同一个项目中,实现一个功能完善的Web应用,同时提供了一种组织和管理代码的方式。通过理解这些组件的协同工作,开发者可以更好地掌握Java Web...

    spring3hibernate4注解是事务管理

    在Java应用中,Spring框架提供了一种声明式事务管理方式,它允许开发者通过在方法级别上添加注解来定义事务边界。 Spring3引入了`@Transactional`注解,这是声明式事务管理的核心。当这个注解应用于一个方法时,...

    struts2-hibernate3-spring整合需要的全部jar包

    它们各自负责不同的职责:Struts2用于控制应用程序的流程,Hibernate3则是优秀的对象关系映射(ORM)框架,Spring则提供全面的依赖注入(DI)和面向切面编程(AOP)功能,以及事务管理等企业级服务。将这三个框架...

    Spring事务管理Demo

    1. 配置事务管理器:在Spring的XML配置文件中,根据数据库类型(如JDBC、Hibernate、MyBatis等)配置相应的事务管理器。 2. 开启事务:使用`@Transactional`注解标记需要在事务中执行的方法。 3. 业务逻辑:在事务中...

    spring3.0两种事务管理配置

    Spring 3.0 提供了两种事务管理配置方法:基于 XML 的事务管理和基于 @Transactional 的事务管理,这两种方法都是为了实现事务管理的目标,分别具有不同的配置方式和优缺点。 基于 XML 的事务管理 这种方法不需要...

    Spring JDBC相关jar包:spring_jdbc_4.0.0.zip

    5. **事务管理**:Spring JDBC模块与Spring的事务管理相集成,可以方便地进行编程式或声明式事务控制。例如,使用`TransactionTemplate`或在配置中定义@Transactional注解,可以实现事务的自动回滚和提交。 6. **...

    JDBC和hibernate事务的详解

    - **易用性**:Hibernate通过面向对象的方式管理事务,相比JDBC的SQL操作,更易于理解和实现。 - **效率**:JDBC直接操作数据库,通常比Hibernate的ORM操作更快,但在事务管理上可能需要更多代码。 - **灵活性**:...

    spring3 hibernate3 整合

    2. **事务管理**:Spring提供了声明式和编程式两种事务管理方式。声明式事务管理通过在服务层方法上添加@Transactional注解,让Spring自动处理事务的开始、提交或回滚。编程式事务管理则需要在代码中手动调用...

    基于注解的Spring MVC+Hibernate简单入门

    ### 基于注解的Spring MVC+Hibernate简单入门 #### 概述 本文主要介绍如何使用基于注解的方式构建Spring MVC与Hibernate相结合的应用程序。这种方式不仅简化了配置过程,而且提高了开发效率。我们将通过一个具体的...

    spring_如何管理事务的

    Spring支持两种类型的事务管理方式:编程式事务管理和声明式事务管理。 #### 二、编程式事务管理 编程式事务管理允许开发人员通过编程的方式直接控制事务的开始、提交或回滚。这种方式相对灵活,但也存在一些缺点...

Global site tag (gtag.js) - Google Analytics