小弟 刚开始学习spring。一直没有用到过spring,但是知道spring很强大,没有学会。心理一直虚得慌。今天有时间看了看,记录下我自己的一些学习内容。在spring中进行事务的管理非常的方便这是我比较深刻的感受,在我现在项目中纯jdbc编程的过程中,手动控制事务都是非常的痛苦的,我经验不足更是经常忘了什么时候该去控制事务。经常会导致脏数据的产生。
在使用spring事务管理之前,先配置自己的数据源。我所看到的好像都是现在配置文件applicationContext.xml中,配置自己的DataSource。spring默认使用的数据库连接池是dbcp 所以在使用的时候要导入相应的包:在xml文件中加入:
<bean id="theDataSource" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/usermanager" />
<property name="username" value="root" />
<property name="password" value="root" />
<property name="initialSize" value="2" />
<property name="maxActive" value="100" />
<property name="maxIdle" value="2" />
<property name="minIdle" value="1" />
</bean>
定义完了数据源之后,我们就要对这个数据源进行事务的控制,
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="theDataSource"></property>
</bean>
jta的事务我就不考虑了,那个我更不懂了,我所了解到的是:jta事务依赖容器,就是web服务器,应用jta事务的是分布式的事务管理,所以我们在容器的进行控制,容器控制事务就必须定义jndi。spring通过读取容器的jndi进行jta事务的控制(我感觉是不对,如果有哪位看到了这篇烂文章 给小弟指点指点,谢谢)。
资源同步的事务:
有了上面这些 我们就知道了事务管理器是怎么创建的,以及在什么数据源上采用什么样的事务控制,这里有个问题就是我们知道这样配置了,在我们使用持久化的代码时:如何确保通过相关的 PlatformTransactionManager
来恰当地获取并操作资源,来满足事务同步,这些操作包括:创建、复用、清理 和 触发(可能没有)。
这里spring给提供了高层次解决方案:
首选的方法是使用Spring的高层持久化集成API。这种方式不会替换原始的API,而是在内部封装了资源创建、复用、清理、事务同步以及异常映射等功能,这样用户的数据访问代码就不必关心这些,而集中精力于自己的持久化逻辑。通常,对所有持久化API都采用这种 模板 方法,包括 JdbcTemplate
、HibernateTemplate
和JdoTemplate
类(这些在这份参考文档后面的章节中详细叙述)。
这里我就看了下JdbcTemplate >> 我们可以这么写:
private JdbcTemplate jdbcTemplate;
//通过配置文件settter注入方式把刚在上面定义的dataSource注入进来
public void setDataSource(DataSource source) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
用注入的dataSource实例化JdbcTemplate对象
JdbcTemplate后台是怎么实现的:
setDataSource(dataSource);
afterPropertiesSet();
}
//jdbcTemplate extends JdbcAccessor 调用JdbcAccessor的
//setDataSource(dataSource);
/**
* Set the JDBC DataSource to obtain connections from.
*/
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
把datasource设置进去,
/**
* Eagerly initialize the exception translator,
* creating a default one for the specified DataSource if none set.
*/
public void afterPropertiesSet() {
if (getDataSource() == null) {
throw new IllegalArgumentException("dataSource is required");
}
if (!isLazyInit()) {
getExceptionTranslator();
}
}
jdbcTemplate也提供了设置是否延迟加载的 方法 这里不是我关注的重点,就不列出来了。
throws DataAccessException {
Assert.notNull(psc, "PreparedStatementCreator must not be null");
Assert.notNull(action, "Callback object must not be null");
//通过数据源工作 获得连接
Connection con = DataSourceUtils.getConnection(getDataSource());
PreparedStatement ps = null;
try {
Connection conToUse = con;
if (this.nativeJdbcExtractor != null &&
this.nativeJdbcExtractor.isNativeConnectionNecessaryForNativePreparedStatements()) {
conToUse = this.nativeJdbcExtractor.getNativeConnection(con);
}
ps = psc.createPreparedStatement(conToUse);
applyStatementSettings(ps);
PreparedStatement psToUse = ps;
if (this.nativeJdbcExtractor != null) {
psToUse = this.nativeJdbcExtractor.getNativePreparedStatement(ps);
}
Object result = action.doInPreparedStatement(psToUse);
SQLWarning warning = ps.getWarnings();
throwExceptionOnWarningIfNotIgnoringWarnings(warning);
return result;
}
catch (SQLException ex) {
// Release Connection early, to avoid potential connection pool deadlock
// in the case when the exception translator hasn't been initialized yet.
if (psc instanceof ParameterDisposer) {
((ParameterDisposer) psc).cleanupParameters();
}
String sql = getSql(psc);
psc = null;
JdbcUtils.closeStatement(ps);
ps = null;
DataSourceUtils.releaseConnection(con, getDataSource());
con = null;
throw getExceptionTranslator().translate("PreparedStatementCallback", sql, ex);
}
finally {
if (psc instanceof ParameterDisposer) {
((ParameterDisposer) psc).cleanupParameters();
}
JdbcUtils.closeStatement(ps);
DataSourceUtils.releaseConnection(con, getDataSource());
}
}
JdbcTemplate 增删改查的方法 最后都会调用这个execute方法,在这个方法里面自己获得数据库连接 自己关闭,对我们隐藏了这些信息。
在JDBC环境下,你不再使用传统的调用 DataSource
的 getConnection()
方法的方式,而是使用Spring的 org.springframework.jdbc.datasource.DataSourceUtils
,像这样:
Connection conn = DataSourceUtils.getConnection(dataSource);
如果已有一个事务及与之关联的connection存在,该实例将被返回。否则,该方法调用将触发起一个新的connection的创建动作,该connection(可选地)被同步到任何现有的事务,并可以在同一事务范围内被后续的调用复用。正如上面提到的,这个过程有一个额外的好处,就是任何 SQLException
将被包装为Spring框架的 CannotGetJdbcConnectionException
,该类是Spring框架的unchecked的DataAccessExceptions层次体系中的一员。这将给你比从 SQLException
中简单所得更多的信息,而且保证了跨数据库——甚至其他持久化技术——的移植性。
应该指出的是,这些类同样可以在没有Spring事务管理的环境中工作良好(事务同步能力是可选的),所以无论你是否使用Spring的事务管理,你都可以使用这些类。