范例代码,可以下载参考:http://download1.youkuaiyun.com/down3/20070613/13193721679.rar
首先,我们使用一个基本的例子进行实现讲解,例子很简单,前面的文章中也讲述过,就是一个最简单的论坛发帖子的实现,共有四个实体对象参与,结构图如下:

我们实现一个很简单的功能,功能有以下步骤:
新增一个用户
这个用户发一篇文章
这个用户再给自己的这篇文章写一个回复
在发文章和回复的时候给用户加分
在进行任何一个操作时,都记录一个日志
在这里除Log对象以外的三个对象在ORM中很有代表性,在后面进行ORM介绍的时候会详细讲解为什么使用这样的对象关系结构,Log对象的出现主要是为了体现多种事务的处理模式。
首先,我们先用比较传统的基于纯SQL的方式编写这个例子,由于不牵扯多种事务类型,这里省却了关于Log对象的操作,只是打印了一些必要的信息
在纯SQL的模式中,基本的实现代码都很相似,我们以新增一个用户为例:
SqlExample.java
(第15行)
//
加入一个用户SQL
public
static
final
String INSERT_USER_SQL
=
"
INSERT INTO USER_INFO VALUES ( ?, ?, ? )
"
;
………………………………
………………………………
(第86行)

try
...
{
//取得连接
conn = getConnection ();
//设置手动事务
conn.setAutoCommit ( false );
//准备声明
PreparedStatement pstmt = conn.prepareStatement ( INSERT_USER_SQL );
//设置用户编号
pstmt.setString ( 1, user_id );
//设置用户名称
pstmt.setString ( 2, userName );
//设置分数
pstmt.setInt ( 3, user_point );
//执行操作
pstmt.executeUpdate ();
//事务提交
conn.commit ();
//关闭声明
pstmt.close ();
//关闭连接
conn.close ();

}
catch
( Exception e )
...
{
//事务回滚
conn.rollback ();
//关闭连接
conn.close ();
//异常继续抛出
throw e;
}
这些代码可以总结为以下几个步骤:
1) 打开数据库连接
2) 建立声明
3) 设置手动事务
4) 执行SQL
5) 提交事务
6) 关闭连接
7) 异常时回滚事务
同样,在其他的方法中,都是同样的步骤,除了第4个步骤(执行SQL)略有不同之外,其他的部分都是一模一样的,这些代码都是重复的代码。
还有一个重复的地方就是同一个功能的SQL语句也会重复,比如在当用户发文章或回复后都加分的部分,重复的SQL语句:
SqlExample.java
(第18行)
//
增加一个帖子SQL
public
static
final
String INSERT_ARTICLE_SQL
=
"
INSERT INTO ARTICLE VALUES ( ?, ?, ? )
"
;
//
增加一个回复
public
static
final
String INSERT_COMMENT_SQL
=
"
INSERT INTO COMMENT VALUES ( ?, ?, ?, ? )
"
;

//
用户加分
public
static
final
String ADD_USER_POINT_SQL
=
"
UPDATE USER_INFO SET POINT = ? WHERE ID = ?
"
;

…………………………
(第157行 方法addArticle中)
//
准备声明
PreparedStatement pstmt
=
conn.prepareStatement ( INSERT_ARTICLE_SQL );
//
设置文章编号
pstmt.setString (
1
, article_id );
//
设置文章内容
pstmt.setString (
2
, article_content );
//
设置作者编号
pstmt.setString (
3
, user_id );
//
执行操作
pstmt.executeUpdate ();
//
关闭声明
pstmt.close ();

/** */
/********给用户加分***********/
//
用户分数
int
point
=
0
;
//
查询用户当前分数
PreparedStatement pstmt2
=
conn.prepareStatement ( QUERY_USER_POINT_SQL );
pstmt2.setString (
1
, user_id );
ResultSet rs
=
pstmt2.executeQuery ();
if
( rs.next () )
point
=
rs.getInt (
1
)
+
1
;
rs.close ();
//
关闭声明
pstmt2.close ();
PreparedStatement pstmt3
=
conn.prepareStatement ( ADD_USER_POINT_SQL );
//
设置分数
pstmt3.setInt (
1
, point );
//
设置用户编号
pstmt3.setString (
2
, user_id );



(第250行 方法addComment中)
//
准备声明
PreparedStatement pstmt
=
conn.prepareStatement ( INSERT_COMMENT_SQL );
//
设置回复编号
pstmt.setString (
1
, comment_id );
//
设置回复内容
pstmt.setString (
2
, comment_content );
//
设置作者编号
pstmt.setString (
3
, user_id );
//
设置作者编号
pstmt.setString (
4
, article_id );
//
执行操作
pstmt.executeUpdate ();
//
关闭声明
pstmt.close ();

/** */
/********给用户加分***********/
//
用户分数
int
point
=
0
;
//
查询用户当前分数
PreparedStatement pstmt2
=
conn.prepareStatement ( QUERY_USER_POINT_SQL );
pstmt2.setString (
1
, user_id );
ResultSet rs
=
pstmt2.executeQuery ();
if
( rs.next () )
point
=
rs.getInt (
1
)
+
1
;
rs.close ();
//
关闭声明
pstmt2.close ();
PreparedStatement pstmt3
=
conn.prepareStatement ( ADD_USER_POINT_SQL );
//
设置分数
pstmt3.setInt (
1
, point );
//
设置用户编号
pstmt3.setString (
2
, user_id );
//
执行操作
pstmt3.executeUpdate ();
//
关闭声明
pstmt3.close ();
//
事务提交
conn.commit ();

从上面的代码中,给用户加分的部分是完全重复的,但是为了保证事务的完整性,又无法将给用户加分的方法放置到其他的方法中去,如果把用户加分的这部分放置到其他方法中进行重用,那么需要将Connection或者Statement对象作为参数传递,将这样的对象到处传递是很危险的,很容器产生链式错误,如果连接对象的状态在一个地方改变了,后继的所有方法都会出错,而且很难查找到错误。
根据上面的分析,在纯SQL的环境中,有两部分的代码总是重复的
1) 对于事务控制的代码
2) 需要重用的SQL代码
在纯SQL环境中,由于很多地方存在着重复的代码,所以代码的维护难度很大,当系统逐渐庞大时,出错概率就会不断的增大。
事务容器的主要作用就是用一个统一的架构避免出现这两种事务控制代码。