`
ajoo
  • 浏览: 453106 次
社区版块
存档分类
最新评论

jdbc还是ibatis?

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

每个持久功能的实现都比较类似,大致相当于这样:
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。因为我缺乏说服人的能力。

你怎么看这个问题呢?
分享到:
评论
57 楼 zagile 2007-08-16  
原来的系统跑得好好的,需要很强的理由才能说服当官的决定替换。
56 楼 lszone 2007-08-15  
ibatis麻烦?你用hibernate试试
55 楼 fixopen 2007-08-15  
屁股决定脑袋
因为自己的立场和利益,每个人都会坚持一些从别人看来不可理解的东西,都会时不时的担心一下这个新的外来者是不是会损害自己的利益。
最让你感觉到无能为力的是“我不care”,但这其实也是你最大的攻击点,问问他care什么?告诉他什么能帮助他更好的care这些东西。当然,一定不能忘了工程的投入产出这个最大的砝码。
54 楼 spiritfrog 2007-04-13  
这根本就不是技术问题,都是人的问题,人家固执保守,不肯学习新东西,确实就很无奈。当architect没有他人的拥护看来也是不行的。
53 楼 抛出异常的爱 2007-02-26  
jdbc 不是架构
ibatis是架构

要用什么就看哪个架构省力
如果要升级那就要看是否非要升级所有的文件
原有的有必要修改的代码也可以用重构的方式完成
由于jdbc可以离开架构而存在
所以没必要非把所有的代码都移到新的架构上面
52 楼 ajoo 2007-02-26  
intolong 写道
ajoo不是写过jdbc template么?

http://www.iteye.com/topic/7068

和你同事那个差不多吧,一个继承,一个组合

他这个还不如我的那个。他的这个实现没有处理Error的情况。在Error出现的情况,不会回滚事务。

不管我的还是同事的这个都有若干个问题:
1。不支持cache。还要自己写cache,不如ibatis直接提供的cache支持方便。
2。不支持一个connection多个query或者命令。


51 楼 fuliang 2007-02-26  
我以前做的一个对jdbc的封装(仿照Spring的实现,当然功能弱些,所以代码少很多,容易看)
以下是对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;
       }
 
}
50 楼 intolong 2007-02-25  
ajoo不是写过jdbc template么?

http://www.iteye.com/topic/7068

和你同事那个差不多吧,一个继承,一个组合
49 楼 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层,但需求增加时,不需要修改原来的代码,感觉还是不错的。


既然想到了 何不搞个出来 这段话下面贴个附件带上你的demo 该有多漂亮 呵呵
48 楼 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层,但需求增加时,不需要修改原来的代码,感觉还是不错的。
47 楼 eonhy 2007-02-14  
ibatis在遗留系统改造中使用,它对原有JDBC代码的替换可以说是思路清晰,相当容易上手!

Hibernate用在新设计的系统中,也很容易掌握,难点在于,如果像我所遇到的,表与表之间存在外键,存在多对一和多对多的关系的时候,设计上的不合理就会在运行效率中明显的体现出来,这差点让一个项目在中途进行大规模重构!
46 楼 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是最接近的。


我觉得你还是在从技术层面看问题,还是过于急迫地进入细节。

第一步,不预设任何方案,说服同事选择开源框架,而避免陷入自己开放又一个框架的泥潭;
第二步,你们一起选择一个合适的开源框架。

到底选择哪一个,等解决了第一步以后再说。如果ibatis真的是最适合你们的方案,我相信一旦你们进入第二步,在同事的参与下你们最终会选择ibatis。与其去推销自己的选择,不如引导他作出同样的选择。
45 楼 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是最接近的。
44 楼 aardvark 2007-02-13  
这个问题从一开始就不是一个技术问题。试图从技术的角度来说服同事用ibatis,效果自然不会好。<br/>
<br/>
如果要从技术的角度来说的话,站在同事的立场,ibatis在技术上没有带来显著的好处。ibatis能实现的,或者自己用jdbc也能实现,或者他觉得没有必 要实现。每个人都倾向于选择自己熟悉的方案,因为心里有底。不用ibatis的好处是显而易见的:一、什么也不用学;二、拥有几乎无限度的灵活性。如果将来需要什么功能,凭着对 jdbc的熟悉,可以随时添加必要的功能。<br/>
<br/>
要说服同事,就必须上到更高的台阶来看这个问题:是自己开发框架,还是用现成的。<br/>
<br/>
用jdbc写简单的框架虽然有这样那样的好处,但坏处是致命的:维护成本。现在你们可能还不觉得什么,只是做了个很简单的框架而已。可是今天觉得缺少这个功能,就添加之;明天又觉得那个地方以前没有考虑到,再修改之。时间长了,这个简单的框架就会不再简单,到最后你们就会发现有了个自己的不怎么样的ORM框架。即时你们有意识地控制这个框架的规模,也还是很难。特别是对于技术素质好的人来说,没有办法容忍自己框架的这样那样的不好。结果就是,在这个框架中越陷越深。相对于用现成的开源框架来说,开源框架有一个team在维护,对你们来说就是0成本维护,而且还比自己维护自己框架的效果好。<br/>
<br/>
自己开发框架并不是没有学习成本。开发框架的人是可以不用学,但以后再招人,如果用了现成的框架,可以直接招懂这个框架的人;如果自己开发框架,新招近来的人必须有一个学习的过程,而且更糟的是自己开发的框架还没有什么完善的文档。长远地看用自己的框架的学习成本反而比用现成的要高。<br/>
<br/>
其实只要是用一个现成的框架,不管是ibatis还是hibernate还是spring jdbc template还是什么别的,都比自己开发一个框架强。这样你们可以把精力集中在解决领域问题上,而不是分散在维护自己的框架上。<br/>
<br/>
我在工作中不得不用大量的精力维护我们自己的不怎么样的MVC框架和ORM框架。明天我离开这个公司,后来的人同样要花精力学习和维护这两个框架。<br/>
<br/>
并不是绝对地反对做自己的框架。如果自己有独到的想法,可以实现现有的框架做不到的,完全可以开发一个新的框架。但是,如果真有这个需要,我也宁愿把这个框架开源。一来可以从别人的反馈中更好地发展这个框架,二来也可以有其他的人加入进来,共同发展它。同样是开发框架,比作为一个内部项目来开发就要强得多了。<br/>
<br/>
基于这样的理由,我是坚决反对在公司内部发展自己的框架。建议你先和同事解决“开发自己的框架还是用现成的框架”的问题,然后再具体探讨用哪个框架。<br/>
<br/>
你也不要局限在ibatis上。<br/>
提示一:也许spring jdbc template是一个比较好的折中<br/>
提示二:hibernate也可以用作jdbc框架 <br/>
<a href='http://www.theserverside.com/tt/blogs/showblog.tss?id=Hibernate3_JDBC'>www.theserverside.com/tt/blogs/showblog.tss</a>
43 楼 ajoo 2007-02-13  
cat 写道
题外话:有时候一个程序code没几行,底下XML暗涛汹涌,出点问题还真不好找。可能这也是代码生成的好处之一吧。

不过iBatis看起来还是挺简单,虽然没用过,不过似乎还不至于那么难调。我们用一套存储过程的Wrapper, 是代码生成的,挺方便。

ibatis允许配置DataSourceFactory。
我用dynamic proxy把所有的jdbc call都拦截了,这样调试起来其实更方便。
42 楼 cat 2007-02-11  
题外话:有时候一个程序code没几行,底下XML暗涛汹涌,出点问题还真不好找。可能这也是代码生成的好处之一吧。

不过iBatis看起来还是挺简单,虽然没用过,不过似乎还不至于那么难调。我们用一套存储过程的Wrapper, 是代码生成的,挺方便。
41 楼 lordhong 2007-02-10  
你一个architect连决定用什么技术的权力都没有,还算什么architect啊?

40 楼 TinyJimmy 2007-02-10  
你有两种选择, 说服大家使用新的技术, 要么加入一个能否让你使用新技术的团队.

By the way, 不要总想着改变人, 特别是大多数人, 能改变的是你自己. 包括你的决定和做事情的方法
39 楼 wuyingsong 2007-02-09  
这都要感谢SPRING提供的 HibernateDaoSupport ,它的功能太强大了
38 楼 wuyingsong 2007-02-09  
在SERVICE层直接调用DAO就行了

相关推荐

    iBATIS3与JDBC性能对比

    本篇文章将深入探讨iBATIS3和JDBC在性能上的对比,以帮助开发者更好地理解这两种技术的优劣。 iBATIS3是一个轻量级的持久层框架,它提供了一种将SQL语句与Java代码分离的方式,通过XML或注解来定义SQL映射,增强了...

    springmvc_hibernate_ibatis_jdbc

    标题中的"springmvc_hibernate_ibatis_jdbc"指的是一个整合了SpringMVC、Hibernate、iBatis和JDBC这四种关键技术的Java应用框架。这个框架旨在提供一个全面且强大的解决方案,便于开发人员进行Web应用程序的构建。 ...

    mysql,jdbc详解,与ibatis对比。批量查询,分页处理。

    ### MySQL、JDBC详解及与iBatis对比 #### 一、MySQL基本操作 MySQL作为一款广泛使用的开源关系型数据库管理系统,在IT行业中占有极其重要的地位。对于开发人员来说,掌握MySQL的基本操作至关重要。 ##### 1. 增删...

    ibatis源码,ibatis源码 ibatis源码 ibatis源码

    在源码中,`org.apache.ibatis.transaction.jdbc.JdbcTransaction`和`org.apache.ibatis.transaction.managed.ManagedTransaction`分别对应JDBC和Spring的事务管理。 十、缓存机制 iBatis提供了本地缓存和二级缓存...

    ibatis教程,ibatis帮助文档

    iBATIS是一个由Clinton Begin创建,目前由Apache基金会支持的持久化框架,它专注于数据库查询的简化,尤其在JDBC编程方面提供了极大的便利。相对于全自动化ORM解决方案如Hibernate,iBATIS被称为“半自动化”ORM实现...

    Spring+ibatis 保留ibatis事务的配置

    这里设置事务管理器类型为JDBC,表示ibatis将直接使用底层的JDBC连接进行事务控制,而不是使用Spring提供的事务管理器。 ```xml &lt;transactionManager type="JDBC"&gt; &lt;property value="oracle.jdbc.driver....

    ibatis demo,ibatis例子,ibatis示例

    Ibatis的出现,解决了传统JDBC中手动编写SQL和结果集映射的繁琐工作,提供了更加灵活的数据访问接口。 在"ibatis demo"中,我们通常会看到以下核心概念和知识点: 1. **配置文件**:Ibatis的配置文件(mybatis-...

    Ibatis 入门经典 实例

    相较于传统的 JDBC,Ibatis 提供了更灵活的数据访问层,使得数据库操作更加简洁高效。 二、Ibatis 安装与配置 1. 添加依赖:在 Maven 项目中,需要在 `pom.xml` 文件中添加 Ibatis 的依赖。 2. 配置文件:创建 `...

    Ibatis入门例子,Ibatis教程

    Ibatis,全称为MyBatis,是一个优秀的Java持久层框架,它主要负责SQL映射,使得开发者能够将注意力集中在编写SQL语句上,而无需关注...因此,无论你是初学者还是有经验的开发者,掌握Ibatis都是提升自己技能的好选择。

    IBATISJDBC包

    "IBATISJDBC包"指的是包含了用于支持IBATIS框架运行所需的JDBC(Java Database Connectivity)驱动及相关依赖的集合。在MYECLIPSE这样的集成开发环境中,这些JAR包是必不可少的,因为它们使得开发者能够无缝地将...

    ibatis实现数据的操作

    与传统的JDBC相比,Ibatis提供了一种更方便、更易于维护的方式来处理数据库交互。 在使用Ibatis时,首先需要配置数据源,这是通过DataSource接口实现的,常见的实现有Apache的DBCP和C3P0等。接着,我们需要创建...

    SPRING IBATIS 保留IBATIS事务的配置方式

    这样的配置意味着iBatis将使用JDBC的方式进行事务管理,即所有的事务控制逻辑都由iBatis负责,而不是由Spring的事务管理器来处理。 ### 手动控制事务 在实际的应用开发中,如果希望保留iBatis的事务管理机制并能够...

    Spring与iBATIS的集成

    最后,还讨论了iBATIS的优缺点,及为什么是这样的一种方式,即所谓的“在完全OR与JDBC之间的一个幽雅的折衷。” &lt;br&gt;就象Developer's Notebook 系列中所有的书一样,你将由直接的且实用的方式获得信息,这些信息...

    Ibatis3手册 Ibatis3参考手册

    ### Ibatis3手册知识点概述 Ibatis3作为一款流行的持久层框架,在软件开发领域具有重要的地位。本篇文章基于“Ibatis3手册 Ibatis3参考手册”的标题及描述,深入解析Ibatis3的核心概念、架构特点以及如何进行实际...

    iBatis文档\ibatis.doc

    iBatis 是一款轻量级的Java持久层框架,它主要负责将数据库操作与业务逻辑解耦,使得开发者可以更加专注于SQL和业务代码的编写。...无论是简单还是复杂的数据库操作,iBatis都能够提供有效的解决方案。

    springmvc集成ibatis

    而 iBATIS 是一个持久层框架,它封装了 JDBC,使得 SQL 查询操作更加简洁。 **SpringMVC 知识点** 1. **DispatcherServlet**: SpringMVC 的核心组件,作为前端控制器接收请求并分发到对应的处理器。 2. **Model-...

    ibatis乱码解决方法(ibatis独立)

    3. **JDBC驱动设置**: - 在数据库连接时,通过`Connection`对象的`setCharacterEncoding()`方法设置字符集,确保与数据库保持一致。 4. **SQL语句处理**: - 如果在动态SQL中涉及到字符串拼接,务必确保字符串...

    springmvc整合ibatis

    4. **连接池**:C3P0 是一个开源的 JDBC 连接池,它实现了数据源和 JNDI 绑定,提供了性能优化功能。在整合中,我们需要配置 C3P0 数据源,以便为应用程序提供高效的数据库连接管理。 5. **配置整合**:在 ...

    Ibatis

    Ibatis 是一个优秀的开源持久层框架,它允许开发者将 SQL 查询与 Java 代码分离,从而避免了传统的 JDBC 编程中的大量模板代码。作为一个轻量级的框架,Ibatis 提供了灵活的映射机制,使得 XML 或注解方式的 SQL ...

    ibatis2.0+sqlserver2005环境搭建

    3. SQL Server JDBC驱动:Ibatis通过JDBC与数据库交互,因此需要对应的驱动jar包。对于SQL Server 2005,你需要`sqljdbc4.jar`。 接下来,我们逐步进行环境配置: **1. 创建项目结构** 创建一个新的Java项目,并...

Global site tag (gtag.js) - Google Analytics