2007-02-06
jdbc还是ibatis?
公司的一个大系统的持久层一直是直接使用jdbc。在jdbc的基础上,又自制了一个简陋的cache。
每个持久功能的实现都比较类似,大致相当于这样:
当然,各个功能的实现不完全一样,有的有事务,有的没有;有的忘了关闭statement,有的忘了checkIn connection;有的在出现Error的时候忘了rollback。等等等等。
dao层的代码就是调用这些不同的jdbc代码,然后再包上一层HashMap做cache:
当然,还要自己实现cloneAccount()。
所有对Account, Contribution, Plan之类的cache代码也类似。
后来鉴于偶尔出现资源泄漏问题,一个程序员写了一个jdbc模板,长成这个样子:
然后上面的代码可以简化为仅仅重载这四个抽象函数:
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的集成长成这个样子:
这样,除非用户代码调用openSession(),其它的函数都自动处理了Session的关闭。事务处理用一个boolean参数来控制也相当简单。
上面的那么多jdbc代码和cache代码最终就可以直接变成:
那么同事对这个东西的意见在哪里呢?
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。因为我缺乏说服人的能力。
你怎么看这个问题呢?
每个持久功能的实现都比较类似,大致相当于这样:
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);
}
当然,各个功能的实现不完全一样,有的有事务,有的没有;有的忘了关闭statement,有的忘了checkIn connection;有的在出现Error的时候忘了rollback。等等等等。
dao层的代码就是调用这些不同的jdbc代码,然后再包上一层HashMap做cache:
Object cacheKey = ...;
synchronized(cache) {
Account acct = (Account)cache.get(cacheKey);
if(acct == null) {
acct = runJdbcForAccount(...);
cache.put(cacheKey, acct);
}
return acct.cloneAccount();
}
当然,还要自己实现cloneAccount()。
所有对Account, Contribution, Plan之类的cache代码也类似。
后来鉴于偶尔出现资源泄漏问题,一个程序员写了一个jdbc模板,长成这个样子:
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);
}
}
}
然后上面的代码可以简化为仅仅重载这四个抽象函数:
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的集成长成这个样子:
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);
}
这样,除非用户代码调用openSession(),其它的函数都自动处理了Session的关闭。事务处理用一个boolean参数来控制也相当简单。
上面的那么多jdbc代码和cache代码最终就可以直接变成:
Accunt acct = persistence.queryForObject("getAccountById", accountId);
那么同事对这个东西的意见在哪里呢?
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。因为我缺乏说服人的能力。
你怎么看这个问题呢?
- 03:59
- 浏览 (22786)
- 评论 (57)
- 分类: I disagree
- 进入论坛
- 相关推荐
评论
zagile
2007-08-16
原来的系统跑得好好的,需要很强的理由才能说服当官的决定替换。
lszone
2007-08-15
ibatis麻烦?你用hibernate试试
fixopen
2007-08-15
屁股决定脑袋。
因为自己的立场和利益,每个人都会坚持一些从别人看来不可理解的东西,都会时不时的担心一下这个新的外来者是不是会损害自己的利益。
最让你感觉到无能为力的是“我不care”,但这其实也是你最大的攻击点,问问他care什么?告诉他什么能帮助他更好的care这些东西。当然,一定不能忘了工程的投入产出这个最大的砝码。
因为自己的立场和利益,每个人都会坚持一些从别人看来不可理解的东西,都会时不时的担心一下这个新的外来者是不是会损害自己的利益。
最让你感觉到无能为力的是“我不care”,但这其实也是你最大的攻击点,问问他care什么?告诉他什么能帮助他更好的care这些东西。当然,一定不能忘了工程的投入产出这个最大的砝码。
spiritfrog
2007-04-13
这根本就不是技术问题,都是人的问题,人家固执保守,不肯学习新东西,确实就很无奈。当architect没有他人的拥护看来也是不行的。
抛出异常的爱
2007-02-26
jdbc 不是架构
ibatis是架构
要用什么就看哪个架构省力
如果要升级那就要看是否非要升级所有的文件
原有的有必要修改的代码也可以用重构的方式完成
由于jdbc可以离开架构而存在
所以没必要非把所有的代码都移到新的架构上面
ibatis是架构
要用什么就看哪个架构省力
如果要升级那就要看是否非要升级所有的文件
原有的有必要修改的代码也可以用重构的方式完成
由于jdbc可以离开架构而存在
所以没必要非把所有的代码都移到新的架构上面
ajoo
2007-02-26
intolong 写道
ajoo不是写过jdbc template么?
http://www.javaeye.com/topic/7068
和你同事那个差不多吧,一个继承,一个组合
http://www.javaeye.com/topic/7068
和你同事那个差不多吧,一个继承,一个组合
他这个还不如我的那个。他的这个实现没有处理Error的情况。在Error出现的情况,不会回滚事务。
不管我的还是同事的这个都有若干个问题:
1。不支持cache。还要自己写cache,不如ibatis直接提供的cache支持方便。
2。不支持一个connection多个query或者命令。
fuliang
2007-02-26
我以前做的一个对jdbc的封装(仿照Spring的实现,当然功能弱些,所以代码少很多,容易看)
以下是对jdbc进行了简单的封装:使用Template mothod模式和回调接口大大减少了对jdbc的低级操作。其中JdbcTemplate类提供了使用jdbc访问数据库的模板,提供了queryForObject和queryForList方法来分别获取查询到的对象和对象的聚集,execute方法来更新、插入、删除操作。提供了RowCallBackHandler接口,可以通过实现该接口来提供对查询一行的回调处理,使用PreparedStatementCallBack回调接口,来提供对PreparedStatement参数的设置,提供了ORMaping接口,通过实现该接口可以很容易实现关系数据库与domain对象的映射,并产生可重用的逻辑
以下是代码包含对JDBC的封装和Dao的实现:
以下是对jdbc进行了简单的封装:使用Template mothod模式和回调接口大大减少了对jdbc的低级操作。其中JdbcTemplate类提供了使用jdbc访问数据库的模板,提供了queryForObject和queryForList方法来分别获取查询到的对象和对象的聚集,execute方法来更新、插入、删除操作。提供了RowCallBackHandler接口,可以通过实现该接口来提供对查询一行的回调处理,使用PreparedStatementCallBack回调接口,来提供对PreparedStatement参数的设置,提供了ORMaping接口,通过实现该接口可以很容易实现关系数据库与domain对象的映射,并产生可重用的逻辑
以下是代码包含对JDBC的封装和Dao的实现:
package edu.jlu.fuliang.library.jdbc;
import java.util.ArrayList;
import java.util.List;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
public class JdbcTemplate {
public List query(String sql,RowCallBackHandler handler){
Connection connection = JdbcUtils.createConnection();
PreparedStatement ps = JdbcUtils.createPreparedStatement(sql,connection);
ResultSet rs = null;
List list = new ArrayList();
try {
rs = ps.executeQuery();
while(rs.next()){
list.add(handler.processRow(rs));
}
} catch (SQLException e) {
e.printStackTrace();
}finally{
JdbcUtils.closeStatement(ps);
JdbcUtils.relaseConnection(connection);
}
return list;
}
public Object queryForObject(String sql,RowCallBackHandler handler){
Connection connection = JdbcUtils.createConnection();
PreparedStatement ps = JdbcUtils.createPreparedStatement(sql,connection);
ResultSet rs = null;
Object o = null;
try {
rs = ps.executeQuery();
rs.next();
o = handler.processRow(rs);
} catch (SQLException e) {
e.printStackTrace();
}finally{
JdbcUtils.closeStatement(ps);
JdbcUtils.relaseConnection(connection);
}
return o;
}
public void executeUpdate(String sql,PreparedStatementCallBack pareparedStatementCallBack){
Connection connection = JdbcUtils.createConnection();
PreparedStatement ps = JdbcUtils.createPreparedStatement(sql,connection);
try {
pareparedStatementCallBack.doInStatement(ps);
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}finally{
JdbcUtils.closeStatement(ps);
JdbcUtils.relaseConnection(connection);
}
}
}
//回调接口RowCallBackHandler
package edu.jlu.fuliang.library.jdbc;
import java.sql.ResultSet;
public interface RowCallBackHandler {
public Object processRow(ResultSet rs);
}
///回调接口PreparedStatementCallBack
package edu.jlu.fuliang.library.jdbc;
import java.sql.PreparedStatement;
import java.sql.SQLException;
public interface PreparedStatementCallBack {
public void doInStatement(PreparedStatement stmt)throws SQLException;
}
//ORMaping接口
package edu.jlu.fuliang.library.jdbc;
import java.sql.ResultSet;
public interface ORMaping {
public Object mapping(ResultSet rs,Object o);
}
//JdbcUtils.java
package edu.jlu.fuliang.library.jdbc;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
public class JdbcUtils {
public static Connection createConnection(){//为了简单,进行了硬编码
String user = "root";
String passwd = "123456";
String url = "jdbc:mysql://localhost/test?useUnicode=true&characterEncoding=gb2312";
Connection connection = null;
try{
Class.forName("com.mysql.jdbc.Driver").newInstance();
connection = DriverManager.getConnection(url,user,passwd);
}
catch(Exception e){
e.printStackTrace();
}
return connection;
}
public static PreparedStatement createPreparedStatement(String sql,Connection connection){
PreparedStatement stmt = null;
try{
stmt = connection.prepareStatement(sql);
}catch(Exception e){
e.printStackTrace();
}
return stmt;
}
public static void closeStatement(Statement s){
try {
s.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
public static void relaseConnection(Connection connection){
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//以下是数据访问对象:
//BorrowerDao.java
package edu.jlu.fuliang.library.dao;
import edu.jlu.fuliang.library.domain.Borrower;
public interface BorrowerDao {
public Borrower queryByLastName(String lastName);
public void updateBorrower(Borrower borrower);
public void insertBorrower(Borrower borrower);
public void deleteBorrowerByLastName(String lastName);
}
// BookDao.java
package edu.jlu.fuliang.library.dao;
import java.util.List;
import edu.jlu.fuliang.library.domain.Book;
public interface BookDao {
public Book queryByTitle(String title);
public List queryByAuthor(String author);
public List queryByBorrowerLastName(String firstName);
public void updateBook(Book book);
public void insertBook(Book book);
public void deleteBookByTile(String title);
}
// BorrowOrReturnDao.java
package edu.jlu.fuliang.library.dao;
public interface BorrowOrReturnDao {
public void borrowBook(String borrowerLastName,String bookTile);
public void returnBook(String borrowerLastName,String bookTile);
}
//抽象工厂:
package edu.jlu.fuliang.library.dao;
public interface DaoFactory {
public BookDao createBookDao();
public BorrowerDao createBorrowerDao();
public BorrowOrReturnDao createBorrowOrReturnDao();
}
//具体工厂:
package edu.jlu.fuliang.library.dao;
import edu.jlu.fuliang.library.dao.mysql.BookDaoImpl;
import edu.jlu.fuliang.library.dao.mysql.BorrowerDaoImpl;
public class MysqlDaoFactory implements DaoFactory{
public BookDao createBookDao() {
return new BookDaoImpl();
}
public BorrowerDao createBorrowerDao() {
return new BorrowerDaoImpl();
}
public BorrowOrReturnDao createBorrowOrReturnDao() {
return new BorrowOrReturnDaoImpl();
}
}
//BookDao的具体实现:
//BookDaoImpl.java
package edu.jlu.fuliang.library.dao.mysql;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
import edu.jlu.fuliang.library.dao.BookDao;
import edu.jlu.fuliang.library.domain.Book;
import edu.jlu.fuliang.library.jdbc.*;
public class BookDaoImpl implements BookDao{
private JdbcTemplate jdbcTemplate = new JdbcTemplate();
public Book queryByTitle(String title) {
String sql = "select * from book where title = "+title;
return (Book)jdbcTemplate.queryForObject(sql,new RowCallBackHandler(){
public Object processRow(ResultSet rs) {
BookMaping map = new BookMaping();
Book book = (Book)map.mapping(rs,new Book());
return book;
}
});
}
public List queryByAuthor(String author) {
String sql = "select * from book where author = "+author;
return jdbcTemplate.query(sql,new RowCallBackHandler(){
public Object processRow(ResultSet rs) {
BookMaping map = new BookMaping();
Book book = (Book)map.mapping(rs,new Book());
return book;
}
});
}
//Borrower的具体实现:
//BookDaoImpl.java
package edu.jlu.fuliang.library.dao.mysql;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import edu.jlu.fuliang.library.dao.BorrowerDao;
import edu.jlu.fuliang.library.domain.Borrower;
import edu.jlu.fuliang.library.jdbc.JdbcTemplate;
import edu.jlu.fuliang.library.jdbc.PreparedStatementCallBack;
import edu.jlu.fuliang.library.jdbc.RowCallBackHandler;
public class BorrowerDaoImpl implements BorrowerDao{
private JdbcTemplate jdbcTemplate = new JdbcTemplate();
public Borrower queryByLastName(String lastName) {
String sql = "select * from borrower where lastName = " + lastName;
return (Borrower)jdbcTemplate.queryForObject(sql,new RowCallBackHandler(){
public Object processRow(ResultSet rs) {
BorrowerMaping borrowerMaping = new BorrowerMaping();
return borrowerMaping.mapping(rs,new Borrower());
}
});
}
public void updateBorrower(final Borrower borrower) {
String sql = "update borrower set lastname = ?,firstname=?" +
",address =?,city =?,zip=?,state=? where lastname = ?";
jdbcTemplate.executeUpdate(sql,new PreparedStatementCallBack(){
public void doInStatement(PreparedStatement stmt) throws SQLException {
stmt.setString(1,borrower.getLastName());
stmt.setString(2,borrower.getFirstName());
stmt.setString(3,borrower.getAddress());
stmt.setString(4,borrower.getCity());
stmt.setString(5,borrower.getZip());
stmt.setString(6,borrower.getState());
stmt.setString(7,borrower.getLastName());
}
});
}
public void insertBorrower(final Borrower borrower) {
String sql = "insert into borrower values(?,?,?,?,?,?)";
jdbcTemplate.executeUpdate(sql,new PreparedStatementCallBack(){
public void doInStatement(PreparedStatement stmt) throws SQLException {
stmt.setString(1,borrower.getLastName());
stmt.setString(2,borrower.getFirstName());
stmt.setString(3,borrower.getAddress());
stmt.setString(4,borrower.getCity());
stmt.setString(5,borrower.getZip());
stmt.setString(6,borrower.getState());
}
});
}
public void deleteBorrowerByLastName(final String lastName) {
String sql = "delete from borrower where lastName = ?";
jdbcTemplate.executeUpdate(sql,new PreparedStatementCallBack(){
public void doInStatement(PreparedStatement stmt) throws SQLException {
stmt.setString(1,lastName);
}
});
}
}
// BorrowOrReturnDaoImpl.java
package edu.jlu.fuliang.library.dao.mysql;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import edu.jlu.fuliang.library.dao.BorrowOrReturnDao;
import edu.jlu.fuliang.library.jdbc.JdbcTemplate;
import edu.jlu.fuliang.library.jdbc.PreparedStatementCallBack;
public class BorrowOrReturnDaoImpl implements BorrowOrReturnDao{
private JdbcTemplate jdbcTemplate = new JdbcTemplate();
public void borrowBook(final String borrowerLastName, final String bookTitle) {
String sql = "insert into book_borrower values(?,?)";
jdbcTemplate.executeUpdate(sql,new PreparedStatementCallBack(){
public void doInStatement(PreparedStatement stmt) {
try{
stmt.setString(1,borrowerLastName);
stmt.setString(2,bookTitle);
}catch(SQLException e){
e.printStackTrace();
}
}
});
}
public void returnBook(final String borrowerLastName, final String bookTitle) {
String sql = "delete from book_borrower where lastname = ?,title = ?";
jdbcTemplate.executeUpdate(sql,new PreparedStatementCallBack(){
public void doInStatement(PreparedStatement stmt) {
try{
stmt.setString(1,borrowerLastName);
stmt.setString(2,bookTitle);
}catch(SQLException e){
e.printStackTrace();
}
}
});
}
}
//book的ORMaping的实现:
// BookMaping.java
package edu.jlu.fuliang.library.dao.mysql;
import java.sql.ResultSet;
import java.sql.SQLException;
import edu.jlu.fuliang.library.domain.Book;
import edu.jlu.fuliang.library.jdbc.ORMaping;
public class BookMaping implements ORMaping{
public Object mapping(ResultSet rs, Object o) {
Book book = (Book)o;
try {
book.setTitleName(rs.getString("titleName"));
book.setAuthor(rs.getString("author"));
book.setISBN(rs.getString("isbn"));
book.setType(rs.getInt("type"));
book.setItemsAvailable(rs.getInt("itemAvailable"));
} catch (SQLException e) {
e.printStackTrace();
}
return book;
}
}
////borrower的ORMaping的实现:
// BorrowerMaping.java
package edu.jlu.fuliang.library.dao.mysql;
import java.sql.ResultSet;
import java.sql.SQLException;
import edu.jlu.fuliang.library.domain.Borrower;
import edu.jlu.fuliang.library.jdbc.ORMaping;
public class BorrowerMaping implements ORMaping{
public Object mapping(ResultSet rs, Object o) {
Borrower borrower = (Borrower)o;
try {
borrower.setLastName(rs.getString(1));
borrower.setFirstName(rs.getString(2));
borrower.setAddress(rs.getString(3));
borrower.setCity(rs.getString(4));
borrower.setZip(rs.getString(5));
borrower.setState(rs.getString(6));
} catch (SQLException e) {
e.printStackTrace();
}
return borrower;
}
}
intolong
2007-02-25
ajoo不是写过jdbc template么?
http://www.javaeye.com/topic/7068
和你同事那个差不多吧,一个继承,一个组合
http://www.javaeye.com/topic/7068
和你同事那个差不多吧,一个继承,一个组合
jianfeng008cn
2007-02-25
fuliang 写道
使用jdbc,可以使用Spring对jdbc的封装,感觉还是比较好用的,
如果你不打算使用Spring,也可以使用Template method模式和
回调方法来封装对jdbc的低级操作(具体可参见Spring的实现)。
当然使用ibatis可以大大减少许多低级操作,而且学习非常容易,
感觉还是有必要在公司推广。当然hibernate是不错的选择,不过
学习难度比ibatis大点。
不过我一直对持久层技术存在一些疑惑:
无论是jdbc、ibatis、hibernate,当使用Dao模式进行封装分离出
持久化层时,当业务需求增加或变化时,dao往往也许要发生变化,例
如查询文章,以前根据title,现在又需要根据id,这就需要增加新的
方法,觉得很不爽。如果能够直接设置Example的状态,根据这个对象
的非null属性来查询(当然需要支持复杂的情况,如title满足简单的
正则表达式,整数的范围等)。只需要一个方法,传递一个对象就可以查
询满足的对象。rails的ActiveRecord,能够动态的产生查询方法(
find_by_id(),find_by_title()),没有了dao层,但需求增加时,不需要修改原来的代码,感觉还是不错的。
如果你不打算使用Spring,也可以使用Template method模式和
回调方法来封装对jdbc的低级操作(具体可参见Spring的实现)。
当然使用ibatis可以大大减少许多低级操作,而且学习非常容易,
感觉还是有必要在公司推广。当然hibernate是不错的选择,不过
学习难度比ibatis大点。
不过我一直对持久层技术存在一些疑惑:
无论是jdbc、ibatis、hibernate,当使用Dao模式进行封装分离出
持久化层时,当业务需求增加或变化时,dao往往也许要发生变化,例
如查询文章,以前根据title,现在又需要根据id,这就需要增加新的
方法,觉得很不爽。如果能够直接设置Example的状态,根据这个对象
的非null属性来查询(当然需要支持复杂的情况,如title满足简单的
正则表达式,整数的范围等)。只需要一个方法,传递一个对象就可以查
询满足的对象。rails的ActiveRecord,能够动态的产生查询方法(
find_by_id(),find_by_title()),没有了dao层,但需求增加时,不需要修改原来的代码,感觉还是不错的。
既然想到了 何不搞个出来 这段话下面贴个附件带上你的demo 该有多漂亮 呵呵
fuliang
2007-02-24
使用jdbc,可以使用Spring对jdbc的封装,感觉还是比较好用的,
如果你不打算使用Spring,也可以使用Template method模式和
回调方法来封装对jdbc的低级操作(具体可参见Spring的实现)。
当然使用ibatis可以大大减少许多低级操作,而且学习非常容易,
感觉还是有必要在公司推广。当然hibernate是不错的选择,不过
学习难度比ibatis大点。
不过我一直对持久层技术存在一些疑惑:
无论是jdbc、ibatis、hibernate,当使用Dao模式进行封装分离出
持久化层时,当业务需求增加或变化时,dao往往也许要发生变化,例
如查询文章,以前根据title,现在又需要根据id,这就需要增加新的
方法,觉得很不爽。如果能够直接设置Example的状态,根据这个对象
的非null属性来查询(当然需要支持复杂的情况,如title满足简单的
正则表达式,整数的范围等)。只需要一个方法,传递一个对象就可以查
询满足的对象。rails的ActiveRecord,能够动态的产生查询方法(
find_by_id(),find_by_title()),没有了dao层,但需求增加时,不需要修改原来的代码,感觉还是不错的。
如果你不打算使用Spring,也可以使用Template method模式和
回调方法来封装对jdbc的低级操作(具体可参见Spring的实现)。
当然使用ibatis可以大大减少许多低级操作,而且学习非常容易,
感觉还是有必要在公司推广。当然hibernate是不错的选择,不过
学习难度比ibatis大点。
不过我一直对持久层技术存在一些疑惑:
无论是jdbc、ibatis、hibernate,当使用Dao模式进行封装分离出
持久化层时,当业务需求增加或变化时,dao往往也许要发生变化,例
如查询文章,以前根据title,现在又需要根据id,这就需要增加新的
方法,觉得很不爽。如果能够直接设置Example的状态,根据这个对象
的非null属性来查询(当然需要支持复杂的情况,如title满足简单的
正则表达式,整数的范围等)。只需要一个方法,传递一个对象就可以查
询满足的对象。rails的ActiveRecord,能够动态的产生查询方法(
find_by_id(),find_by_title()),没有了dao层,但需求增加时,不需要修改原来的代码,感觉还是不错的。
eonhy
2007-02-14
ibatis在遗留系统改造中使用,它对原有JDBC代码的替换可以说是思路清晰,相当容易上手!
Hibernate用在新设计的系统中,也很容易掌握,难点在于,如果像我所遇到的,表与表之间存在外键,存在多对一和多对多的关系的时候,设计上的不合理就会在运行效率中明显的体现出来,这差点让一个项目在中途进行大规模重构!
Hibernate用在新设计的系统中,也很容易掌握,难点在于,如果像我所遇到的,表与表之间存在外键,存在多对一和多对多的关系的时候,设计上的不合理就会在运行效率中明显的体现出来,这差点让一个项目在中途进行大规模重构!
aardvark
2007-02-13
ajoo 写道
我其实也反对开发自己的“框架”。自己的库还成。Framework嘛,现在一听这个词就头疼。
可惜,我们现在自己开发的Framework不管好不好,都顶着一个“公司标准,一致性”的大帽子,你不用都不行。
这个遗留系统里面自己的框架多了,有mvc, property, ioc, persistence。都是作的比较古怪,灵活性,可用性和简单性都比较差,跟业界标准也不那么兼容。
比如,persistence框架里自己弄一个MyConnection,而不是java.sql.Connection。
property框架自己内部通过某种逻辑决定ClassLoader,而这个ClassLoader从外界无法得到,所以也无法和别的开源框架兼容。自己弄的MyProperties不实现java.util.Map。
等等等等。
不过,软件这东西,真是公说公有理的事情。就一个“一致性”就几乎可以枪毙一切改进措施了。
spring template我们暂时还是不用。不太想引入spring的依赖。毕竟我们还不到替换自己的ioc的时候。
commons dbutil倒是轻量的多。不过和我们的自制persistence不太兼容,也不能帮我们做cache。
hibernate嘛,要的改动就大得多了,学习曲线也陡,我自己都不能说服自己采用。毕竟现在的persistence框架上和概念上和ibatis是最接近的。
可惜,我们现在自己开发的Framework不管好不好,都顶着一个“公司标准,一致性”的大帽子,你不用都不行。
这个遗留系统里面自己的框架多了,有mvc, property, ioc, persistence。都是作的比较古怪,灵活性,可用性和简单性都比较差,跟业界标准也不那么兼容。
比如,persistence框架里自己弄一个MyConnection,而不是java.sql.Connection。
property框架自己内部通过某种逻辑决定ClassLoader,而这个ClassLoader从外界无法得到,所以也无法和别的开源框架兼容。自己弄的MyProperties不实现java.util.Map。
等等等等。
不过,软件这东西,真是公说公有理的事情。就一个“一致性”就几乎可以枪毙一切改进措施了。
spring template我们暂时还是不用。不太想引入spring的依赖。毕竟我们还不到替换自己的ioc的时候。
commons dbutil倒是轻量的多。不过和我们的自制persistence不太兼容,也不能帮我们做cache。
hibernate嘛,要的改动就大得多了,学习曲线也陡,我自己都不能说服自己采用。毕竟现在的persistence框架上和概念上和ibatis是最接近的。
我觉得你还是在从技术层面看问题,还是过于急迫地进入细节。
第一步,不预设任何方案,说服同事选择开源框架,而避免陷入自己开放又一个框架的泥潭;
第二步,你们一起选择一个合适的开源框架。
到底选择哪一个,等解决了第一步以后再说。如果ibatis真的是最适合你们的方案,我相信一旦你们进入第二步,在同事的参与下你们最终会选择ibatis。与其去推销自己的选择,不如引导他作出同样的选择。
ajoo
2007-02-13
我其实也反对开发自己的“框架”。自己的库还成。Framework嘛,现在一听这个词就头疼。
可惜,我们现在自己开发的Framework不管好不好,都顶着一个“公司标准,一致性”的大帽子,你不用都不行。
这个遗留系统里面自己的框架多了,有mvc, property, ioc, persistence。都是作的比较古怪,灵活性,可用性和简单性都比较差,跟业界标准也不那么兼容。
比如,persistence框架里自己弄一个MyConnection,而不是java.sql.Connection。
property框架自己内部通过某种逻辑决定ClassLoader,而这个ClassLoader从外界无法得到,所以也无法和别的开源框架兼容。自己弄的MyProperties不实现java.util.Map。
等等等等。
不过,软件这东西,真是公说公有理的事情。就一个“一致性”就几乎可以枪毙一切改进措施了。
spring template我们暂时还是不用。不太想引入spring的依赖。毕竟我们还不到替换自己的ioc的时候。
commons dbutil倒是轻量的多。不过和我们的自制persistence不太兼容,也不能帮我们做cache。
hibernate嘛,要的改动就大得多了,学习曲线也陡,我自己都不能说服自己采用。毕竟现在的persistence框架上和概念上和ibatis是最接近的。
可惜,我们现在自己开发的Framework不管好不好,都顶着一个“公司标准,一致性”的大帽子,你不用都不行。
这个遗留系统里面自己的框架多了,有mvc, property, ioc, persistence。都是作的比较古怪,灵活性,可用性和简单性都比较差,跟业界标准也不那么兼容。
比如,persistence框架里自己弄一个MyConnection,而不是java.sql.Connection。
property框架自己内部通过某种逻辑决定ClassLoader,而这个ClassLoader从外界无法得到,所以也无法和别的开源框架兼容。自己弄的MyProperties不实现java.util.Map。
等等等等。
不过,软件这东西,真是公说公有理的事情。就一个“一致性”就几乎可以枪毙一切改进措施了。
spring template我们暂时还是不用。不太想引入spring的依赖。毕竟我们还不到替换自己的ioc的时候。
commons dbutil倒是轻量的多。不过和我们的自制persistence不太兼容,也不能帮我们做cache。
hibernate嘛,要的改动就大得多了,学习曲线也陡,我自己都不能说服自己采用。毕竟现在的persistence框架上和概念上和ibatis是最接近的。
aardvark
2007-02-13
这个问题从一开始就不是一个技术问题。试图从技术的角度来说服同事用ibatis,效果自然不会好。
如果要从技术的角度来说的话,站在同事的立场,ibatis在技术上没有带来显著的好处。ibatis能实现的,或者自己用jdbc也能实现,或者他觉得没有必 要实现。每个人都倾向于选择自己熟悉的方案,因为心里有底。不用ibatis的好处是显而易见的:一、什么也不用学;二、拥有几乎无限度的灵活性。如果将来需要什么功能,凭着对 jdbc的熟悉,可以随时添加必要的功能。
要说服同事,就必须上到更高的台阶来看这个问题:是自己开发框架,还是用现成的。
用jdbc写简单的框架虽然有这样那样的好处,但坏处是致命的:维护成本。现在你们可能还不觉得什么,只是做了个很简单的框架而已。可是今天觉得缺少这个功能,就添加之;明天又觉得那个地方以前没有考虑到,再修改之。时间长了,这个简单的框架就会不再简单,到最后你们就会发现有了个自己的不怎么样的ORM框架。即时你们有意识地控制这个框架的规模,也还是很难。特别是对于技术素质好的人来说,没有办法容忍自己框架的这样那样的不好。结果就是,在这个框架中越陷越深。相对于用现成的开源框架来说,开源框架有一个team在维护,对你们来说就是0成本维护,而且还比自己维护自己框架的效果好。
自己开发框架并不是没有学习成本。开发框架的人是可以不用学,但以后再招人,如果用了现成的框架,可以直接招懂这个框架的人;如果自己开发框架,新招近来的人必须有一个学习的过程,而且更糟的是自己开发的框架还没有什么完善的文档。长远地看用自己的框架的学习成本反而比用现成的要高。
其实只要是用一个现成的框架,不管是ibatis还是hibernate还是spring jdbc template还是什么别的,都比自己开发一个框架强。这样你们可以把精力集中在解决领域问题上,而不是分散在维护自己的框架上。
我在工作中不得不用大量的精力维护我们自己的不怎么样的MVC框架和ORM框架。明天我离开这个公司,后来的人同样要花精力学习和维护这两个框架。
并不是绝对地反对做自己的框架。如果自己有独到的想法,可以实现现有的框架做不到的,完全可以开发一个新的框架。但是,如果真有这个需要,我也宁愿把这个框架开源。一来可以从别人的反馈中更好地发展这个框架,二来也可以有其他的人加入进来,共同发展它。同样是开发框架,比作为一个内部项目来开发就要强得多了。
基于这样的理由,我是坚决反对在公司内部发展自己的框架。建议你先和同事解决“开发自己的框架还是用现成的框架”的问题,然后再具体探讨用哪个框架。
你也不要局限在ibatis上。
提示一:也许spring jdbc template是一个比较好的折中
提示二:hibernate也可以用作jdbc框架
www.theserverside.com/tt/blogs/showblog.tss
如果要从技术的角度来说的话,站在同事的立场,ibatis在技术上没有带来显著的好处。ibatis能实现的,或者自己用jdbc也能实现,或者他觉得没有必 要实现。每个人都倾向于选择自己熟悉的方案,因为心里有底。不用ibatis的好处是显而易见的:一、什么也不用学;二、拥有几乎无限度的灵活性。如果将来需要什么功能,凭着对 jdbc的熟悉,可以随时添加必要的功能。
要说服同事,就必须上到更高的台阶来看这个问题:是自己开发框架,还是用现成的。
用jdbc写简单的框架虽然有这样那样的好处,但坏处是致命的:维护成本。现在你们可能还不觉得什么,只是做了个很简单的框架而已。可是今天觉得缺少这个功能,就添加之;明天又觉得那个地方以前没有考虑到,再修改之。时间长了,这个简单的框架就会不再简单,到最后你们就会发现有了个自己的不怎么样的ORM框架。即时你们有意识地控制这个框架的规模,也还是很难。特别是对于技术素质好的人来说,没有办法容忍自己框架的这样那样的不好。结果就是,在这个框架中越陷越深。相对于用现成的开源框架来说,开源框架有一个team在维护,对你们来说就是0成本维护,而且还比自己维护自己框架的效果好。
自己开发框架并不是没有学习成本。开发框架的人是可以不用学,但以后再招人,如果用了现成的框架,可以直接招懂这个框架的人;如果自己开发框架,新招近来的人必须有一个学习的过程,而且更糟的是自己开发的框架还没有什么完善的文档。长远地看用自己的框架的学习成本反而比用现成的要高。
其实只要是用一个现成的框架,不管是ibatis还是hibernate还是spring jdbc template还是什么别的,都比自己开发一个框架强。这样你们可以把精力集中在解决领域问题上,而不是分散在维护自己的框架上。
我在工作中不得不用大量的精力维护我们自己的不怎么样的MVC框架和ORM框架。明天我离开这个公司,后来的人同样要花精力学习和维护这两个框架。
并不是绝对地反对做自己的框架。如果自己有独到的想法,可以实现现有的框架做不到的,完全可以开发一个新的框架。但是,如果真有这个需要,我也宁愿把这个框架开源。一来可以从别人的反馈中更好地发展这个框架,二来也可以有其他的人加入进来,共同发展它。同样是开发框架,比作为一个内部项目来开发就要强得多了。
基于这样的理由,我是坚决反对在公司内部发展自己的框架。建议你先和同事解决“开发自己的框架还是用现成的框架”的问题,然后再具体探讨用哪个框架。
你也不要局限在ibatis上。
提示一:也许spring jdbc template是一个比较好的折中
提示二:hibernate也可以用作jdbc框架
www.theserverside.com/tt/blogs/showblog.tss
ajoo
2007-02-13
cat 写道
题外话:有时候一个程序code没几行,底下XML暗涛汹涌,出点问题还真不好找。可能这也是代码生成的好处之一吧。
不过iBatis看起来还是挺简单,虽然没用过,不过似乎还不至于那么难调。我们用一套存储过程的Wrapper, 是代码生成的,挺方便。
不过iBatis看起来还是挺简单,虽然没用过,不过似乎还不至于那么难调。我们用一套存储过程的Wrapper, 是代码生成的,挺方便。
ibatis允许配置DataSourceFactory。
我用dynamic proxy把所有的jdbc call都拦截了,这样调试起来其实更方便。
cat
2007-02-11
题外话:有时候一个程序code没几行,底下XML暗涛汹涌,出点问题还真不好找。可能这也是代码生成的好处之一吧。
不过iBatis看起来还是挺简单,虽然没用过,不过似乎还不至于那么难调。我们用一套存储过程的Wrapper, 是代码生成的,挺方便。
不过iBatis看起来还是挺简单,虽然没用过,不过似乎还不至于那么难调。我们用一套存储过程的Wrapper, 是代码生成的,挺方便。
lordhong
2007-02-10
你一个architect连决定用什么技术的权力都没有,还算什么architect啊?
TinyJimmy
2007-02-10
你有两种选择, 说服大家使用新的技术, 要么加入一个能否让你使用新技术的团队.
By the way, 不要总想着改变人, 特别是大多数人, 能改变的是你自己. 包括你的决定和做事情的方法
By the way, 不要总想着改变人, 特别是大多数人, 能改变的是你自己. 包括你的决定和做事情的方法
wuyingsong
2007-02-09
这都要感谢SPRING提供的 HibernateDaoSupport ,它的功能太强大了
wuyingsong
2007-02-09
在SERVICE层直接调用DAO就行了
- 浏览: 182196 次

- 详细资料
搜索本博客
最近加入圈子
最新评论
-
SQL 小技巧
第三个问题,先写出代码来吧。等有点时间再解释一下。第四个问题其实可以照猫画虎的: ...
-- by ajoo -
SQL 小技巧
第一个问题是我在维护一个金融分析软件的时候碰到的。原来的那位老兄正儿八经地用一个 ...
-- by ajoo -
SQL 小技巧
效率没问题。实际上一般的query效率都在查询上,至于对查询结果的计算,代价基本 ...
-- by ajoo -
Not Convinced about Java ...
最讨厌所谓的魔法了,调试的时候能让人吐血。
-- by aninfeel -
SQL 小技巧
ajoo 写道Readonly 写道问题一,经过google得到一用sum,lo ...
-- by Readonly






评论排行榜