在本系列的上一篇文章中,我们看到了一个典型的事务处理失败的案例,其主要原因在于,service层和各个DAO所使用的Connection是不一样的,而JDBC中事务处理的作用对象正是Connection对象,所以不同DAO中的操作不在同一个事务里面,从而导致事务失败。从中我们得出了教训:要避免这种失败,我们可以使所有操作共享一个Connection对象,这样应该就没有问题了。

在本篇文章中,我们将看到一个成功的,但是丑陋的事务处理方案,它的基本思路是:在service层创建Connection对象,再将该Connection传给各个DAO类,这样就完成了Connection共享的目的。

修改两个DAO类,使他们都接受一个Connection对象,定义UglyBankDao类如下:

package davenkin.step2_ugly;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException; public class UglyBankDao
{
public void withdraw(int bankId, int amount, Connection connection) throws SQLException
{
PreparedStatement selectStatement = connection.prepareStatement("SELECT BANK_AMOUNT FROM BANK_ACCOUNT WHERE BANK_ID = ?");
selectStatement.setInt(1, bankId);
ResultSet resultSet = selectStatement.executeQuery();
resultSet.next();
int previousAmount = resultSet.getInt(1);
resultSet.close();
selectStatement.close(); int newAmount = previousAmount - amount;
PreparedStatement updateStatement = connection.prepareStatement("UPDATE BANK_ACCOUNT SET BANK_AMOUNT = ? WHERE BANK_ID = ?");
updateStatement.setInt(1, newAmount);
updateStatement.setInt(2, bankId);
updateStatement.execute(); updateStatement.close();
}
}

使用同样的方法,定义UglyInsuranceDao类:

package davenkin.step2_ugly;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException; public class UglyInsuranceDao
{
public void deposit(int insuranceId, int amount, Connection connection) throws SQLException
{
PreparedStatement selectStatement = connection.prepareStatement("SELECT INSURANCE_AMOUNT FROM INSURANCE_ACCOUNT WHERE INSURANCE_ID = ?");
selectStatement.setInt(1, insuranceId);
ResultSet resultSet = selectStatement.executeQuery();
resultSet.next();
int previousAmount = resultSet.getInt(1);
resultSet.close();
selectStatement.close(); int newAmount = previousAmount + amount;
PreparedStatement updateStatement = connection.prepareStatement("UPDATE INSURANCE_ACCOUNT SET INSURANCE_AMOUNT = ? WHERE INSURANCE_ID = ?");
updateStatement.setInt(1, newAmount);
updateStatement.setInt(2, insuranceId);
updateStatement.execute(); updateStatement.close();
}
}

然后修改Service类,在UglyBankService类的transfer方法中,首先创建一个Connection对象,然后在将该对象依次传给UglyBankDao的withdraw方法和UglyInsuranceDao类的deposit方法,这样service层和DAO层使用相同的Connection对象。定义UglyBankService类如下:

package davenkin.step2_ugly;

import davenkin.BankService;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException; public class UglyBankService implements BankService
{
private DataSource dataSource;
private UglyBankDao uglyBankDao;
private UglyInsuranceDao uglyInsuranceDao; public UglyBankService(DataSource dataSource)
{
this.dataSource = dataSource;
} public void transfer(int fromId, int toId, int amount)
{
Connection connection = null;
try
{
connection = dataSource.getConnection();
connection.setAutoCommit(false); uglyBankDao.withdraw(fromId, amount, connection);
uglyInsuranceDao.deposit(toId, amount, connection); connection.commit();
} catch (Exception e)
{
try
{
assert connection != null;
connection.rollback();
} catch (SQLException e1)
{
e1.printStackTrace();
}
} finally
{
try
{
assert connection != null;
connection.close();
} catch (SQLException e)
{
e.printStackTrace();
}
}
} public void setUglyBankDao(UglyBankDao uglyBankDao)
{
this.uglyBankDao = uglyBankDao;
} public void setUglyInsuranceDao(UglyInsuranceDao uglyInsuranceDao)
{
this.uglyInsuranceDao = uglyInsuranceDao;
}
}

通过上面共享Connection对象的方法虽然可以完成事务处理的目的,但是这样做法是丑陋的,原因在于:为了完成事务处理的目的,我们需要将一个底层的Connection类在service层和DAO层之间进行传递,而DAO层的方法也要接受这个Connection对象,这种做法显然是不好的,这就是典型的API污染。

在下一篇博文中,我们将讲到如何在不传递Connection对象的情况下完成和本文相同的事务处理功能。

 
 

Java事务处理全解析(三)——丑陋的案例的更多相关文章

  1. Java事务处理全解析(二)——失败的案例

    在本系列的上一篇文章中,我们讲到了Java事务处理的基本问题,并且讲到了Service层和DAO层,在本篇文章中,我们将以BankService为例学习一个事务处理失败的案例. BankService ...

  2. Java事务处理全解析(一)——Java事务处理的基本问题

    Java中的事务处理有多简单?在使用EJB时,事务在我们几乎察觉不到的情况下发挥着作用:而在使用Spring时,也只需要配置一个TransactionManager,然后在需要事务的方法上加上Tran ...

  3. Java事务处理全解析(七)—— 像Spring一样使用Transactional注解(Annotation)

    在本系列的上一篇文章中,我们讲到了使用动态代理的方式完成事务处理,这种方式将service层的所有public方法都加入到事务中,这显然不是我们需要的,需要代理的只是那些需要操作数据库的方法.在本篇中 ...

  4. Java事务处理全解析(六)—— 使用动态代理(Dynamic Proxy)完成事务

    在本系列的上一篇文章中,我们讲到了使用Template模式进行事务管理,这固然是一种很好的方法,但是不那么完美的地方在于我们依然需要在service层中编写和事务处理相关的代码,即我们需要在servi ...

  5. Java事务处理全解析(四)—— 成功的案例(自己实现一个线程安全的TransactionManager)

    在本系列的上一篇文章中我们讲到,要实现在同一个事务中使用相同的Connection对象,我们可以通过传递Connection对象的方式达到共享的目的,但是这种做法是丑陋的.在本篇文章中,我们将引入另外 ...

  6. Java事务处理全解析(八)——分布式事务入门例子(Spring+JTA+Atomikos+Hibernate+JMS)

    在本系列先前的文章中,我们主要讲解了JDBC对本地事务的处理,本篇文章将讲到一个分布式事务的例子. 请通过以下方式下载github源代码: git clone https://github.com/d ...

  7. Java事务处理全解析(五)—— Template模式

    在本系列的上一篇文章中,我们讲到了使用TransactionManger和ConnectionHolder完成线程安全的事务管理,在本篇中,我们将在此基础上引入Template模式进行事务管理. Te ...

  8. java事务处理全解析

    http://blog.csdn.net/huilangeliuxin/article/details/43446177

  9. 《Java面试全解析》505道面试题详解

    <Java面试全解析>是我在 GitChat 发布的一门电子书,全书总共有 15 万字和 505 道 Java 面试题解析,目前来说应该是最实用和最全的 Java 面试题解析了. 我本人是 ...

随机推荐

  1. 使用 rqt_console 和 roslaunch---8

    使用 rqt_console 和 roslaunch Description: 本教程介绍如何使用rqt_console和rqt_logger_level进行调试,以及如何使用roslaunch同时运 ...

  2. psp0

    周活动总结表 姓名:苗堃                                                                                         ...

  3. spark新能优化之提高并行度

    实际上Spark集群的资源并不一定会被充分利用到,所以要尽量设置合理的并行度,来充分地利用集群的资源.才能充分提高Spark应用程序的性能. Spark会自动设置以文件作为输入源的RDD的并行度,依据 ...

  4. 越狱Season 1-Episode 15: By the Skin and the Teeth

    Season 1, Episode 15: By the Skin and the Teeth -Pope: doctor...you can leave. 医生你得离开 -Burrows: It's ...

  5. 【P1303】苹果二叉树

    树归入门题 原题: 有一棵苹果树,如果树枝有分叉,一定是分2叉(就是说没有只有1个儿子的结点).这棵树共有N个结点(叶子点或者树枝分叉点),编号为1-N,树根编号一定是1.我们用一根树枝两端连接的结点 ...

  6. Java——IO(输入输出流)

     *  * 想要知道开发时用到哪些数据对象,只要通过四个明确即可:  * 1.明确源,和目的:  * 源: InputStream reader  * 目的: OutPutStream Write ...

  7. SpringMVC @Value取值(取properties属性文件的属性值)

    @Controller @RequestMapping("/reg") public class RegController extends BaseController { @V ...

  8. C#实现图片文件到数据流再到图片文件的转换 --转

    /----引入必要的命名空间 using System.IO; using System.Drawing.Imaging; //----代码部分----// private byte[] photo; ...

  9. Python列表操作——模拟实现栈和队列

    1.实现栈: stack=[] def pushit(): stack.append(raw_input('Enter New String:').strip()) def popit(): if l ...

  10. C# 大于屏幕的窗体

    1.使用SetWindowPos就可以做到这一点,只是最后一个参数要选对. RECT windowRect = new RECT(); User32.GetWindowRect(MyForm2.Han ...