您的位置:首页 > 编程语言 > Java开发

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

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

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

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

Sql代码


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

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

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

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代码


<?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’处的代码换成:

Java代码


// 帐号1转账1元至帐号3

accountService.transfer(1, 3, 1);//A

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

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

Java代码


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

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代码


<?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:

Java代码


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:

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

*/

public void transfer(Integer fromAccountId, Integer toAccountId, int money) {

accountDAO.subMoney(fromAccountId, money);

accountDAO.addMoney(toAccountId, money);

}

}

代码10:

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);// B

} catch (Exception e) {

System.out.println("转账失败");

}

}

}

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

代码11:增加事务后的 applicationContext.xml

Java代码


<?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

Java代码


<?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层的要使用的方法上声明事务,如:

Java代码


@Transactional(readOnly = false)

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

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

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);

}

}

TransDemo.rar (6 MB)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring 事务