1. 事务是什么
事务就是一项通常包含若干步骤的工作单元,这些步骤必须作为整体来执行,无论成功还是失败。
- 自动事务:针对那些简单的仅由一条语句构成的事务,这种事务不需要显示划定事务边界
- 局部事务:指那种简单的范围较窄的事务,这种事务虽然涉及很多条语句,但只作用于一个数据库
- 全局事务:复杂范围较广的事务,往往涉及许多语句和布置一个数据库
- 定制事务:ibatis支持由用户提供的连接
事务的特性:
- 原子性(atomicity)
- 一致性(consistency)
- 隔离性(isolation):隔离级别(读未提交数据、读已提交数据、可重复读、串行化)
- 持久性(durability)
2. 自动事务
ibatis处理的只有事务,对“自动提交”的支持完全是间接的。
3. 局部事务
仅仅涉及一个应用程序,一种资源,并且一次只能处理一个事务。
局部事务在ibatis的SQLMap配置文件中被配置为一个JDBC类型的事务管理器:
<transactionManager type="JDBC">
<dataSource type="SIMPLE">
<property …/>
<property …/>
<property …/>
</dataSource>
</transactionManager>
public void runStatementsUsingLocalTransactions() {
SqlMapClient sqlMapClient =
SqlMapClientConfig.getSqlMapClient();
try {
sqlMapClient.startTransaction();
Person p =
(Person)sqlMapClient.queryForObject
("getPerson", new Integer(9));
p.setLastName("Smith");
sqlMapClient.update("updatePerson", p);
Department d =
(Department)sqlMapClient.queryForObject
("getDept", new Integer(3));
p.setDepartment(d);
sqlMapClient.update("updatePersonDept", p);
sqlMapClient.commitTransaction();
} finally {
sqlMapClient.endTransaction();
}
}
4. 全局事务
4.1 使用主动或被动事务
主动参与: ibatis会查找全局事务上下文,以某种恰当的方式管理它。
被动参与:ibatis忽略当前应用程序所有关于开始事务、提交事务、结束事务的指令代码。出现错误时,不是主动回滚而是抛出异常。ibatis假设抛出异常就可以引起事务回滚。
<transactionManager type="JTA"><span style="white-space:pre"> </span>//主动参与
<property name="UserTransaction" value="java:/ctx/con/someUserTransaction" />
<dataSource type="JNDI">
<property name="DataSource" value="java:comp/env/jdbc/someDataSource" />
</dataSource>
</transactionManager>
<transactionManager type="EXTERNAL"><span style="white-space:pre"> </span>//被动参与
<dataSource type="JNDI">
<property name="DataSource" value="java:comp/env/jdbc/someDataSource" />
</dataSource>
</transactionManager>
4.2 开始、提交以及结束事务
与局部事务一样
5 定制事务
方法一:使用ibatis某些接口,自己写一个事务管理器,然后把它插入到SQL Map配置文件(12章)
方法二:向ibatis传递一个要使用JDBC Connection实例,从而允许你拥有对连接和事务管理的完全控制权
向ibatis的SqlMapClient实例传递一个Connection实例的方法有两种:
第一种使用SqlMapClient类的setUserConnection(Connection)方法
public void runStatementsUsingSetUserConnection() {
SqlMapClient sqlMapClient = SqlMapClientConfig.getSqlMapClient();
Connection conn = null;
try {
conn = dataSource.getConnection();
conn.setAutoCommit(false);<span style="white-space:pre"> </span>//////
sqlMapClient.setUserConnection(conn);<span style="white-space:pre"> </span>///
Person p = (Person) sqlMapClient.queryForObject("getPerson", new Integer(9));
p.setLastName("Smith");
sqlMapClient.update("updatePerson", p);
Department d = (Department) sqlMapClient.queryForObject("getDept", new Integer(3));
p.setDepartment(d);
sqlMapClient.update("updatePersonDept", p);
conn.commit();<span style="white-space:pre"> </span>///
} finally {
sqlMapClient.setUserConnection(null);<span style="white-space:pre"> </span>///
if (conn != null)
conn.close();<span style="white-space:pre"> </span>///
}
}
第二种使用sqlMapClient类的openSession(Connection)方法。(推荐使用)
public void runStatementsUsingSetUserConnection() {
SqlMapClient sqlMapClient = SqlMapClientConfig.getSqlMapClient();
Connection conn = null;<span style="white-space:pre"> </span>//////
SqlMapSession session = null;<span style="white-space:pre"> </span>//////
try {
conn = dataSource.getConnection();
conn.setAutoCommit(false);<span style="white-space:pre"> </span>//////
session = sqlMapClient.openSession(conn);<span style="white-space:pre"> </span>///
Person p = (Person) session.queryForObject("getPerson", new Integer(9));
p.setLastName("Smith");
session.update("updatePerson", p);
Department d = (Department) session.queryForObject("getDept", new Integer(3));
p.setDepartment(d);
session.update("updatePersonDept", p);
conn.commit();<span style="white-space:pre"> </span>///
} finally {
if (session != null)
session.close();<span style="white-space:pre"> </span>///
if (conn != null)<span style="white-space:pre"> </span>
conn.close();<span style="white-space:pre"> </span>///
}
}
仍然需要定义一个EXTERNAL类型的事务管理器,至少提供一个SIMPLE类型的DataSource,否则,向延迟加载这样的特征就无法正常工作。
6. 事务划界
事务的范围越宽越好,但绝不能超过一次用户动作的范围。
将事务在业务逻辑层划界