jdbc还是ibatis?

公司的一个大系统的持久层一直是直接使用jdbc。在jdbc的基础上,又自制了一个简陋的cache。

每个持久功能的实现都比较类似,大致相当于这样:
[code]
MyProprietaryConnection conn = ConnectionManager.checkOut(Database.DB_NAME);
try {
PreparedStatement stmt = conn.getPreparedStatement("some statement id, identifying a sql statement in an xml file");
stmt.setString(1, "param 1");
stmt.setInt(2, param2);
...
try {
ResultSet resultSet = stmt.executeQuery();
try{
while(resultSet.next()) {
...
}
}
finally {
resultSet.close();
}
}
finally {
stmt.close();
}
}
finally {
ConnectionManager.checkIn(conn);
}
[/code]
当然,各个功能的实现不完全一样,有的有事务,有的没有;有的忘了关闭statement,有的忘了checkIn connection;有的在出现Error的时候忘了rollback。等等等等。

dao层的代码就是调用这些不同的jdbc代码,然后再包上一层HashMap做cache:
[code]
Object cacheKey = ...;
synchronized(cache) {
Account acct = (Account)cache.get(cacheKey);
if(acct == null) {
acct = runJdbcForAccount(...);
cache.put(cacheKey, acct);
}
return acct.cloneAccount();
}
[/code]
当然,还要自己实现cloneAccount()。
所有对Account, Contribution, Plan之类的cache代码也类似。

后来鉴于偶尔出现资源泄漏问题,一个程序员写了一个jdbc模板,长成这个样子:
[code]
abstract class PersisterCommand {
protected abstract void populateStatement(PreparedStatement stmt);
protected abstract Object processResult(ResultSet resultSet);
protected abstract boolean isTransactional();
protected abstract PreparedStatement getStatement();
public Object run() {
MyProprietaryConnection conn = ConnectionManager.checkOut(Database.DB_NAME);
try {
PreparedStatement stmt = getStatement();
populateStatement(stmt);
...
try {
if(isTransactional()) {
conn.startTransaction();
}
ResultSet resultSet = stmt.executeQuery();
try{
Object result = processResult(resultSet);
if(isTransactional()) {
conn.commitTransaction();
}
return result;
}
catch(Exception e){
if(isTransactional()) conn.rollbackTransaction();
throw e;
}
finally {
resultSet.close();
}
}
finally {
stmt.close();
}
}
finally {
ConnectionManager.checkIn(conn);
}
}
}
[/code]
然后上面的代码可以简化为仅仅重载这四个抽象函数:
getStatement负责取得某个特定的sql statement;populateStatement负责填充参数;processResult负责把ResultSet转换成domain object;isTransactional制定是否使用事务。

介绍了这么多背景情况,希望你已经看到了,原来的直接jdbc方法是非常繁琐,容易出错,代码量大,而且重复很多。
这个PersisterCommand也有很多局限:
1。它只能处理一个connection一个statement,不能做batch。
2。它在Error出现的时候没有rollback。
3。子类仍然要针对jdbc api写一些有重复味道的代码。
4。代码不容易单元测试。因为ConnectionManager.checkOut()和ConnectionManager.checkIn()都是写死的。


另外,这个自制的cache也是一个重复代码的生产者。

针对这种情况,我本来想继续重构,弄出一个CacheManager和更灵活的jdbc模板。但是后来一想,倒还不如直接用ibatis来得好。毕竟ibatis已经是被业界广泛使用的工具,总比自己制造轮子强。而且,相比于hibernate,ibatis也有更贴近我们现在的模型和使用习惯的优势。


我的一个同事(公司的元老),开始是对ibatis很感兴趣的。

可惜的是,当我完成了ibatis的集成,他试用了一下之后就改变了主意。这个同事在项目组甚至整个公司说话都是很有分量的,不说服他,推广ibatis就面临夭折的可能。

我的ibatis的集成长成这个样子:
[code]
public interface Action {
Object run(SqlMapSession session);
}
public class IbatisPersistence {
public SqlMapSession openSession();
public Object queryForObject(String key);
public List queryForList(String key);
public int update(String key, boolean useTransaction);
public int delete(String key, boolean useTransaction);
public int update(String key);
public int delete(String key);
public Object run(Action action);
}
[/code]

这样,除非用户代码调用openSession(),其它的函数都自动处理了Session的关闭。事务处理用一个boolean参数来控制也相当简单。

上面的那么多jdbc代码和cache代码最终就可以直接变成:
[code]
Accunt acct = persistence.queryForObject("getAccountById", accountId);
[/code]

那么同事对这个东西的意见在哪里呢?
1。他和另外一个同事为调试一个使用了ibatis的程序bug花了一天时间。后来把ibatis删掉,直接用jdbc就修好了。
当时我在休假,回来后一看,这个bug首先是一个stored proc的bug。他们花很多时间在ibatis里面找问题其实都是瞎耽误工夫;其次,在他们到处找ibatis的问题的时候,注释掉了两行关键代码,后来忘了放回来,所以才发生stored proc修好后,ibatis代码还是不工作,直到换了jdbc才修好。
虽然我解释了原因,同事坚持认为ibatis过于复杂。如果它花了他这么长时间来debug,别人也有可能因为种种原因花很多时间来debug别的问题。
2。ibatis只支持一个参数。这个我也解释了,你可以用java bean或者Map。可是同事认为这也是ibatis的学习曲线问题。如果采用,就要求大家都去学ibatis doc才行。
3。同事原来期待的是象Active Record那样的革命性的提高和简化。象ibatis这样还是要进行手工mapping的,对他来说就是没什么太大意义。他不觉得在java里面做这个mapping有什么不好。(我想,也许Hibernate对他更有吸引力。不过把这个系统转换为Hibernate这工作量可大多了)
4。当我说ibatis可以节省很多资源管理的重复代码时,同事说他可以用PersisterCommand。我说PersisterCommand的这些局限性的时候,他说,他不在乎。大不了直接写jdbc。
5。一致性问题。如果同时用jdbc和ibatis,大家就要学两个东西,造成混淆。而如果要把所有东西都换成ibatis,工作量是一个方面,所有的人都要学习ibatis这个代价也是很大的。
6。同事认为cache的那点重复代码无所谓。即使有一些降低cache hit ratio的bug也不是什么大不了的。

最后无法达成统一意见。因为你说什么优点的时候,他只要一句“我不在乎”你就无话可说了。


在这些论点里面,我也认可ibatis的学习曲线和一致性问题。可是,总不能就永远任由这个持久层代码这么滥下去吧?在java这个领域里,我几乎完全相信不可能出现Active Record等价的东西的。而无论Hibernate还是jpa,只怕都是有不下于ibatis的学习曲线和更高的从遗留系统移植的代价吧?

越来越感觉自己不是一个合格的architect。因为我缺乏说服人的能力。

你怎么看这个问题呢?
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值