写在前面
如果直接用JDBC来访问数据库,会有大量重复相似的代码,而且这样的工程逻辑也不清晰,难以维护,spring中对数据访问做了优化。
JDBC
用JDBC访问数据库的流程大致如下:
try{
Connection connection = getDataSource().getConnection();
Statement statement = connection.createStatement();
// ...
statement.executeUpdate("....");
statement.close();
} finally {
statement.close();
connection.close();
}
JdbcTemplate
既然有这么多相似的代码,那么最直接的优化的方法就是将访问的过程进行封装,从而让业务代码专心地去做业务:
public class JdbcTemplate {
public Object execute(StatementCallback action) throws DataAccessException {
// 将连接数据库等操作抽取出来
}
}
然后直接用它来执行对应的sql就可以了:
jdbcTemplate.execute(new StatementCallback(){
public Object doInStatement(Statement stmt) throws SQLException {
stmt.execute("your sql"); return ret;
}
}
但是这样赤裸裸地把SQL和Java代码混在一起也不怎么好,还是有点乱。
iBatis
ORM是的对数据库的操作更加炉火纯青,甚至不需要写sql,不过这里要介绍的是iBatis(原因是用这个比较多)。iBatis实际上应该算是半个ORM吧,因为我们还得去编写sql。基本用法如下:
Map parameters = new HashMap();
parameters.put("param1", value);
// ..
Object ret = sqlMapClient.queryForObject("sql_id", parameters);
还有:
protected void batchInsert(final List beans) {
sqlMapClientTemplate.execute(new SqlMapClientCallback() {
public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
executor.startBatch();
Interator iter = beans.iterator();
while(iter.hasNext()) {
Bean bean = (Bean) iter.next();
executor.insert("insert_name", bean);
}
executor.executeBatch();
return null;
}
});
}
事务
当然,直接用JDBC也可以做事务,在失败的时候直接调用就可以了:
void rollback() throws SQLException;
但是就像上面讲的,这样太不方便了,下面来看TransactionTemplate的方式:
Boolean executeResult = (Boolean) transactionTemplate.execute(new TransactionCallback() {
@Override
public Object doInTransaction(TransactionStatus status) {
try {
// 操作数据库
return true;
} catch (Exception e) {
status.setRollbackOnly();
return false;
}
}
}
用template的方式比较灵活了,重复的代码也比较少了。但是代码调整起来还是有点大,比如将原来不是事务的东西变成是一个事务就有点麻烦了。下面来看用配置的方式:
<bean id="plantDao" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="transactionManager">
<ref bean="transactionManager" />
</property>
<property name="target">
<ref bean="plantDaoTarget" />
</property>
<property name="proxyInterfaces">
<value>com.*.spring.dao.PlantDao</value>
</property>
</bean>
其他配置的方式(没有实践过,备用):
- ProxyFactory + TransactionInterceptor
- BeanNameAutoProxyCreator
- Spring 2.x申明事务配置方式
在整个上面的实现中,为了实现的方便必须将connection相关的操作“丢掉”,而要丢掉比较好的办法是用ThreadLocal,这样就可以随用随取。
---------- ---------- ---------- ---------- ---------- 分割线 ---------- ---------- ---------- ---------- ----------
接下来收集一些大家可能遇到的问题。有时候需要用iBatis来执行一个SQL,可以用如下配置:
<sqlMap namespace="SQL">
<statement id="sqlExecute" parameterClass="java.util.Map">
$sql$
</statement>
</sqlMap>
用$sql$的原因是##会默认添加"",同时需要设置remapResults=true(默认为false),这样iBatis会根据每次的查询结果设置元数据,保证返回正确结果,但是会造成一定的性能损失,在这里有具体讨论。