对ibatis分页功能的改进

Posted on

对ibatis分页功能的改进

对ibatis**分页功能的改进**

2008-05-26 14:45 今天无意间看到了一篇关于这方面的文章,觉得是网上改进ibatis分页方面比较好的文章,这里转摘一下,希望能让更多的人用的到,也希望别人能把更好的解决方案贡献出来!

使ibatis支持hibernate式的物理分页

一直以来ibatis的分页都是通过滚动ResultSet实现的,应该算是逻辑分页吧。逻辑分页虽然能很干净地独立于特定数据库,但效率在多数情 况下不及特定数据库支持的物理分页,而hibernate的分页则是直接组装sql,充分利用了特定数据库的分页机制,效率相对较高。本文讲述的就是如何 在不重新编译ibatis源码的前提下,为ibatis引入hibernate式的物理分页机制。

基本思路就是找到ibatis执行sql的地方,截获sql并重新组装sql。通过分析ibatis源码知道,最终负责执行sql的类是 com.ibatis.sqlmap.engine.execution.SqlExecutor,此类没有实现任何接口,这多少有点遗憾,因为接口是相 对稳定契约,非大的版本更新,接口一般是不会变的,而类就相对易变一些,所以这里的代码只能保证对当前版本(2.1.7)的ibatis有效。下面是 SqlExecutor执行查询的方法:

/// / Long form of the method to execute a query / / @param request - the request scope / @param conn - the database connection / @param sql - the SQL statement to execute / @param parameters - the parameters for the statement / @param skipResults - the number of results to skip / @param maxResults - the maximum number of results to return / @param callback - the row handler for the query / / @throws SQLException - if the query fails // public void executeQuery(RequestScope request, Connection conn, String sql, Object[] parameters, int skipResults, int maxResults, RowHandlerCallback callback) throws SQLException { ErrorContext errorContext = request.getErrorContext(); errorContext.setActivity("executing query"); errorContext.setObjectId(sql); PreparedStatement ps = null; ResultSet rs = null; try { errorContext.setMoreInfo("Check the SQL Statement (preparation failed)."); Integer rsType = request.getStatement().getResultSetType(); if (rsType != null) { ps = conn.prepareStatement(sql, rsType.intValue(), ResultSet.CONCUR_READ_ONLY); } else { ps = conn.prepareStatement(sql); } Integer fetchSize = request.getStatement().getFetchSize(); if (fetchSize != null) { ps.setFetchSize(fetchSize.intValue()); } errorContext.setMoreInfo("Check the parameters (set parameters failed)."); request.getParameterMap().setParameters(request, ps, parameters); errorContext.setMoreInfo("Check the statement (query failed)."); ps.execute(); rs = getFirstResultSet(ps); if (rs != null) { errorContext.setMoreInfo("Check the results (failed to retrieve results)."); handleResults(request, rs, skipResults, maxResults, callback); } // clear out remaining results while (ps.getMoreResults()); } finally { try { closeResultSet(rs); } finally { closeStatement(ps); } } }

其中handleResults(request, rs, skipResults, maxResults, callback)一句用于处理分页,其实此时查询已经执行完毕,可以不必关心handleResults方法,但为清楚起见,下面来看看 handleResults的实现:

private void handleResults(RequestScope request, ResultSet rs, int skipResults, int maxResults, RowHandlerCallback callback) throws SQLException { try { request.setResultSet(rs); ResultMap resultMap = request.getResultMap(); if (resultMap != null) { // Skip Results if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) { if (skipResults > 0) { rs.absolute(skipResults); } } else { for (int i = 0; i < skipResults; i++) { if (!rs.next()) { break; } } } // Get Results int resultsFetched = 0; while ((maxResults == SqlExecutor.NO_MAXIMUM_RESULTS || resultsFetched < maxResults) && rs.next()) { Object[] columnValues = resultMap.resolveSubMap(request, rs).getResults(request, rs); callback.handleResultObject(request, columnValues, rs); resultsFetched++; } } } finally { request.setResultSet(null); } }

此处优先使用的是ResultSet的absolute方法定位记录,是否支持absolute取决于具体数据库驱动,但一般当前版本的数据库都支 持该方法,如果不支持则逐条跳过前面的记录。由此可以看出如果数据库支持absolute,则ibatis内置的分页策略与特定数据库的物理分页效率差距 就在于物理分页查询与不分页查询在数据库中的执行效率的差距了。因为查询执行后读取数据前数据库并未把结果全部返回到内存,所以本身在存储占用上应该差距 不大,如果都使用索引,估计执行速度也差不太多。

继续我们的话题。其实只要在executeQuery执行前组装sql,然后将其传给executeQuery,并告诉handleResults 我们不需要逻辑分页即可。拦截executeQuery可以采用aop动态实现,也可直接继承SqlExecutor覆盖executeQuery来静态 地实现,相比之下后者要简单许多,而且由于SqlExecutor没有实现任何接口,比较易变,动态拦截反到增加了维护的工作量,所以我们下面来覆盖 executeQuery:

package com.aladdin.dao.ibatis.ext; import java.sql.Connection; import java.sql.SQLException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.aladdin.dao.dialect.Dialect; import com.ibatis.sqlmap.engine.execution.SqlExecutor; import com.ibatis.sqlmap.engine.mapping.statement.RowHandlerCallback; import com.ibatis.sqlmap.engine.scope.RequestScope; public class LimitSqlExecutor extends SqlExecutor { private static final Log logger = LogFactory.getLog(LimitSqlExecutor.class);

private Dialect dialect;
private boolean enableLimit = true;
public Dialect getDialect() {
    return dialect;
 }
public void setDialect(Dialect dialect) {
    this.dialect = dialect;
 }
public boolean isEnableLimit() {
    return enableLimit;
 }
public void setEnableLimit(boolean enableLimit) {
    this.enableLimit = enableLimit;
 }
 @Override
public void executeQuery(RequestScope request, Connection conn, String sql,
         Object[] parameters, int skipResults, int maxResults,
         RowHandlerCallback callback) throws SQLException {
    if ((skipResults != NO_SKIPPED_RESULTS || maxResults != NO_MAXIMUM_RESULTS)
            && supportsLimit()) {
         sql = dialect.getLimitString(sql, skipResults, maxResults);
        if(logger.isDebugEnabled()){
             logger.debug(sql);
         }
         skipResults = NO_SKIPPED_RESULTS;
         maxResults = NO_MAXIMUM_RESULTS;            
     }
    super.executeQuery(request, conn, sql, parameters, skipResults,
             maxResults, callback);
 }
public boolean supportsLimit() {
    if (enableLimit && dialect != null) {
        return dialect.supportsLimit();
     }
    return false;
 }

}

其中:

skipResults = NO_SKIPPED_RESULTS; maxResults = NO_MAXIMUM_RESULTS;

告诉handleResults不分页(我们组装的sql已经使查询结果是分页后的结果了),此处引入了类似hibenate中的数据库方言接口Dialect,其代码如下:

package com.aladdin.dao.dialect; public interface Dialect {

public boolean supportsLimit();
public String getLimitString(String sql, boolean hasOffset);
public String getLimitString(String sql, int offset, int limit);

}

下面为Dialect接口的MySQL实现:

package com.aladdin.dao.dialect; public class MySQLDialect implements Dialect { protected static final String SQL_END_DELIMITER = ";"; public String getLimitString(String sql, boolean hasOffset) { return new StringBuffer(sql.length() + 20).append(trim(sql)).append( hasOffset ? " limit ?,?" : " limit ?") .append(SQL_END_DELIMITER).toString(); } public String getLimitString(String sql, int offset, int limit) { sql = trim(sql); StringBuffer sb = new StringBuffer(sql.length() + 20); sb.append(sql); if (offset > 0) { sb.append(" limit ").append(offset).append(',').append(limit) .append(SQL_END_DELIMITER); } else { sb.append(" limit ").append(limit).append(SQL_END_DELIMITER); } return sb.toString(); } public boolean supportsLimit() { return true; } private String trim(String sql) { sql = sql.trim(); if (sql.endsWith(SQL_END_DELIMITER)) { sql = sql.substring(0, sql.length() - 1

                - SQL_END_DELIMITER.length());
     }
    return sql;
 }

}

接下来的工作就是把LimitSqlExecutor注入ibatis中。我们是通过spring来使用ibatis的,所以在我们的dao基类中执行注入,代码如下:

package com.aladdin.dao.ibatis; import java.io.Serializable; import java.util.List; import org.springframework.orm.ObjectRetrievalFailureException; import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport; import com.aladdin.dao.ibatis.ext.LimitSqlExecutor; import com.aladdin.domain.BaseObject; import com.aladdin.util.ReflectUtil; import com.ibatis.sqlmap.client.SqlMapClient; import com.ibatis.sqlmap.engine.execution.SqlExecutor; import com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient; public abstract class BaseDaoiBatis extends SqlMapClientDaoSupport { private SqlExecutor sqlExecutor; public SqlExecutor getSqlExecutor() { return sqlExecutor; } public void setSqlExecutor(SqlExecutor sqlExecutor) { this.sqlExecutor = sqlExecutor; } public void setEnableLimit(boolean enableLimit) { if (sqlExecutor instanceof LimitSqlExecutor) { ((LimitSqlExecutor) sqlExecutor).setEnableLimit(enableLimit); } } public void initialize() throws Exception { if (sqlExecutor != null) { SqlMapClient sqlMapClient = getSqlMapClientTemplate() .getSqlMapClient(); if (sqlMapClient instanceof ExtendedSqlMapClient) { ReflectUtil.setFieldValue(((ExtendedSqlMapClient) sqlMapClient) .getDelegate(), "sqlExecutor", SqlExecutor.class, sqlExecutor); } } } ... }


对ibatis分页功能的改进

Posted on

对ibatis分页功能的改进 - newpeter - ITeye技术网站

首页 资讯 论坛 问答 博客 群组 更多 ▼

招聘 搜索

您还未登录 ! 我的应用 登录 注册

newpeter

永久域名 http://newpeter.iteye.com/

当前比较全的Linux各种版本简介 | 常用正则表达式

2009-02-04

对ibatis分页功能的改进 **

iBATISSQLHibernateDAOEXT

今天无意间看到了一篇关于这方面的文章,觉得是网上改进ibatis分页方面比较好的文章,这里转摘一下,希望能让更多的人用的到,也希望别人能把更好的解决方案贡献出来! 使ibatis支持hibernate式的物理分页

一直以来ibatis的分页都是通过滚动ResultSet实现的,应该算是逻辑分页吧。逻辑分页虽然能很干净地独立于特定数据库,但效率在多数情 况下不及特定数据库支持的物理分页,而hibernate的分页则是直接组装sql,充分利用了特定数据库的分页机制,效率相对较高。本文讲述的就是如何 在不重新编译ibatis源码的前提下,为ibatis引入hibernate式的物理分页机制。

基本思路就是找到ibatis执行sql的地方,截获sql并重新组装sql。通过分析ibatis源码知道,最终负责执行sql的类是 com.ibatis.sqlmap.engine.execution.SqlExecutor,此类没有实现任何接口,这多少有点遗憾,因为接口是相 对稳定契约,非大的版本更新,接口一般是不会变的,而类就相对易变一些,所以这里的代码只能保证对当前版本(2.1.7)的ibatis有效。下面是 SqlExecutor执行查询的方法:

/// / Long form of the method to execute a query / / @param request - the request scope / @param conn - the database connection / @param sql - the SQL statement to execute / @param parameters - the parameters for the statement / @param skipResults - the number of results to skip / @param maxResults - the maximum number of results to return / @param callback - the row handler for the query / / @throws SQLException - if the query fails // public void executeQuery(RequestScope request, Connection conn, String sql, Object[] parameters, int skipResults, int maxResults, RowHandlerCallback callback) throws SQLException { ErrorContext errorContext = request.getErrorContext(); errorContext.setActivity("executing query"); errorContext.setObjectId(sql); PreparedStatement ps = null; ResultSet rs = null; try { errorContext.setMoreInfo("Check the SQL Statement (preparation failed)."); Integer rsType = request.getStatement().getResultSetType(); if (rsType != null) { ps = conn.prepareStatement(sql, rsType.intValue(), ResultSet.CONCUR_READ_ONLY); } else { ps = conn.prepareStatement(sql); } Integer fetchSize = request.getStatement().getFetchSize(); if (fetchSize != null) { ps.setFetchSize(fetchSize.intValue()); } errorContext.setMoreInfo("Check the parameters (set parameters failed)."); request.getParameterMap().setParameters(request, ps, parameters); errorContext.setMoreInfo("Check the statement (query failed)."); ps.execute(); rs = getFirstResultSet(ps); if (rs != null) { errorContext.setMoreInfo("Check the results (failed to retrieve results)."); handleResults(request, rs, skipResults, maxResults, callback); } // clear out remaining results while (ps.getMoreResults()); } finally { try { closeResultSet(rs); } finally { closeStatement(ps); } } }

其中handleResults(request, rs, skipResults, maxResults, callback)一句用于处理分页,其实此时查询已经执行完毕,可以不必关心handleResults方法,但为清楚起见,下面来看看 handleResults的实现:

private void handleResults(RequestScope request, ResultSet rs, int skipResults, int maxResults, RowHandlerCallback callback) throws SQLException { try { request.setResultSet(rs); ResultMap resultMap = request.getResultMap(); if (resultMap != null) { // Skip Results if (rs.getType() != ResultSet.TYPE_FORWARD_ONLY) { if (skipResults > 0) { rs.absolute(skipResults); } } else { for (int i = 0; i < skipResults; i++) { if (!rs.next()) { break; } } } // Get Results int resultsFetched = 0; while ((maxResults == SqlExecutor.NO_MAXIMUM_RESULTS || resultsFetched < maxResults) && rs.next()) { Object[] columnValues = resultMap.resolveSubMap(request, rs).getResults(request, rs); callback.handleResultObject(request, columnValues, rs); resultsFetched++; } } } finally { request.setResultSet(null); } }

此处优先使用的是ResultSet的absolute方法定位记录,是否支持absolute取决于具体数据库驱动,但一般当前版本的数据库都支 持该方法,如果不支持则逐条跳过前面的记录。由此可以看出如果数据库支持absolute,则ibatis内置的分页策略与特定数据库的物理分页效率差距 就在于物理分页查询与不分页查询在数据库中的执行效率的差距了。因为查询执行后读取数据前数据库并未把结果全部返回到内存,所以本身在存储占用上应该差距 不大,如果都使用索引,估计执行速度也差不太多。

继续我们的话题。其实只要在executeQuery执行前组装sql,然后将其传给executeQuery,并告诉handleResults 我们不需要逻辑分页即可。拦截executeQuery可以采用aop动态实现,也可直接继承SqlExecutor覆盖executeQuery来静态 地实现,相比之下后者要简单许多,而且由于SqlExecutor没有实现任何接口,比较易变,动态拦截反到增加了维护的工作量,所以我们下面来覆盖 executeQuery:

package com.aladdin.dao.ibatis.ext; import java.sql.Connection; import java.sql.SQLException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.aladdin.dao.dialect.Dialect; import com.ibatis.sqlmap.engine.execution.SqlExecutor; import com.ibatis.sqlmap.engine.mapping.statement.RowHandlerCallback; import com.ibatis.sqlmap.engine.scope.RequestScope; public class LimitSqlExecutor extends SqlExecutor { private static final Log logger = LogFactory.getLog(LimitSqlExecutor.class); private Dialect dialect; private boolean enableLimit = true; public Dialect getDialect() { return dialect; } public void setDialect(Dialect dialect) { this.dialect = dialect; } public boolean isEnableLimit() { return enableLimit; } public void setEnableLimit(boolean enableLimit) { this.enableLimit = enableLimit; } @Override public void executeQuery(RequestScope request, Connection conn, String sql, Object[] parameters, int skipResults, int maxResults, RowHandlerCallback callback) throws SQLException { if ((skipResults != NO_SKIPPED_RESULTS || maxResults != NO_MAXIMUM_RESULTS) && supportsLimit()) { sql = dialect.getLimitString(sql, skipResults, maxResults); if(logger.isDebugEnabled()){ logger.debug(sql); } skipResults = NO_SKIPPED_RESULTS; maxResults = NO_MAXIMUM_RESULTS; } super.executeQuery(request, conn, sql, parameters, skipResults, maxResults, callback); } public boolean supportsLimit() { if (enableLimit && dialect != null) { return dialect.supportsLimit(); } return false; } }

其中:

skipResults = NO_SKIPPED_RESULTS; maxResults = NO_MAXIMUM_RESULTS;

告诉handleResults不分页(我们组装的sql已经使查询结果是分页后的结果了),此处引入了类似hibenate中的数据库方言接口Dialect,其代码如下:

package com.aladdin.dao.dialect; public interface Dialect { public boolean supportsLimit(); public String getLimitString(String sql, boolean hasOffset); public String getLimitString(String sql, int offset, int limit); }

下面为Dialect接口的MySQL实现:package com.aladdin.dao.ibatis;

import java.io.Serializable; import java.util.List; import org.springframework.orm.ObjectRetrievalFailureException; import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport; import com.aladdin.dao.ibatis.ext.LimitSqlExecutor; import com.aladdin.domain.BaseObject; import com.aladdin.util.ReflectUtil; import com.ibatis.sqlmap.client.SqlMapClient; import com.ibatis.sqlmap.engine.execution.SqlExecutor; import com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient; public abstract class BaseDaoiBatis extends SqlMapClientDaoSupport { private SqlExecutor sqlExecutor; public SqlExecutor getSqlExecutor() { return sqlExecutor; } public void setSqlExecutor(SqlExecutor sqlExecutor) { this.sqlExecutor = sqlExecutor; } public void setEnableLimit(boolean enableLimit) { if (sqlExecutor instanceof LimitSqlExecutor) { ((LimitSqlExecutor) sqlExecutor).setEnableLimit(enableLimit); } } public void initialize() throws Exception { if (sqlExecutor != null) { SqlMapClient sqlMapClient = getSqlMapClientTemplate() .getSqlMapClient(); if (sqlMapClient instanceof ExtendedSqlMapClient) { ReflectUtil.setFieldValue(((ExtendedSqlMapClient) sqlMapClient) .getDelegate(), "sqlExecutor", SqlExecutor.class, sqlExecutor); } } } ... }

分享到: 当前比较全的Linux各种版本简介 | 常用正则表达式

1 楼 vasuer 2010-08-29 引用

te

发表评论

您还没有登录,请登录后发表评论(快捷键 Alt+S / Ctrl+Enter)

newpeter的博客

newpeter

搜索本博客

最近访客 >>更多访客

ankonlcy的博客

ankonlcy

amazingstar的博客

amazingstar woozyangel的博客

woozyangel

jdTangYin的博客

jdTangYin

博客分类

其他分类

存档

  • 2009-02 (13)
  • 2009-01 (1)
  • 2007-05 (2)
  • 更多存档...
  • Rss
  • Rss_google 声明:ITeye文章版权属于作者,受法律保护。没有作者书面许可不得转载。若作者同意转载,必须以超链接形式标明文章原始出处和作者。 © 2003-2011 ITeye.com. All rights reserved. [ 京ICP证110151号 ]

使用ibatis操作数据库的封装

Posted on

使用ibatis操作数据库的封装

使用ibatis操作数据库的封装**

近期刚进入公司,也是本人的第一份正式工作,公司使用的ORM框架是ibatis,下面代码是对batis dao的一个封装,主要继承自spring的SqlMapClientDaoSupport,负责为单个Entity 提供CRUD操作的IBatis DAO基类,使用该基类,可以减少不少代码量。

view plaincopy to clipboardprint?

  1. package com.nfschina.utils.dao.ibatis

  2. import java.io.Serializable;

  3. import java.sql.Connection;

  4. import java.sql.ResultSet;

  5. import java.sql.SQLException;

  6. import java.util.HashMap;

  7. import java.util.List;

  8. import java.util.Map;

  9. import org.apache.commons.beanutils.PropertyUtils;

  10. import org.apache.commons.lang.StringUtils;

  11. import org.springframework.util.Assert;

  12. import com.ibatis.sqlmap.engine.impl.SqlMapClientImpl;

  13. import com.ibatis.sqlmap.engine.mapping.sql.stat.StaticSql;

  14. import com.ibatis.sqlmap.engine.mapping.statement.MappedStatement;

  15. importcom.nfschina.utils.BaseException;

  16. import com.nfschina.utils.DataPage;

  17. import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport

  18. ///

  19. /* IBatis Dao的泛型基类.

  20. /* 继承于Spring的SqlMapClientDaoSupport,提供分页函数和若干便捷查询方法,并对返回值作了泛型类型转换.

  21. /*/

  22. @SuppressWarnings("unchecked")

  23. public class IBatisGenericDao extends SqlMapClientDaoSupport {

  24. public static final String POSTFIX_INSERT = ".insert";

  25. public static final String POSTFIX_UPDATE = ".update";

  26. public static final String POSTFIX_DELETE = ".delete";

  27. public static final String POSTFIX_DELETE_PRIAMARYKEY = ".deleteByPrimaryKey";

  28. public static final String POSTFIX_SELECT = ".select";

  29. public static final String POSTFIX_SELECTMAP = ".selectByMap";

  30. public static final String POSTFIX_SELECTSQL = ".selectBySql";

  31. public static final String POSTFIX_COUNT = ".count";

  32. ///

  33. /* 根据ID获取对象

  34. /*

  35. /* @throws BaseException

  36. /* @throws SQLException

  37. /*/

  38. public T get(Class entityClass, Serializable id) throws BaseException, SQLException {

  39. T o = (T) getSqlMapClient().queryForObject(entityClass.getName() + POSTFIX_SELECT, id);

  40. if (o == null)

  41. throw new BaseException(BaseException.DATA_NOTFOUND, "未找到实体: " + id);

  42. return o;

  43. }

  44. ///

  45. /* 获取全部对象

  46. /* @throws SQLException

  47. /*/

  48. public List getAll(Class entityClass) throws SQLException {

  49. return getSqlMapClient().queryForList(entityClass.getName() + POSTFIX_SELECT, null);

  50. }

  51. ///

  52. /* 新增对象

  53. /* @throws SQLException

  54. /*/

  55. public void insert(Object o) throws SQLException {

  56. getSqlMapClient().insert(o.getClass().getName() + POSTFIX_INSERT, o);

  57. }

  58. ///

  59. /* 保存对象

  60. /* @throws SQLException

  61. /*/

  62. public int update(Object o) throws SQLException {

  63. return getSqlMapClient().update(o.getClass().getName() + POSTFIX_UPDATE, o);

  64. }

  65. ///

  66. /* 删除对象

  67. /* @throws SQLException

  68. /*/

  69. public int remove(Object o) throws SQLException {

  70. return getSqlMapClient().delete(o.getClass().getName() + POSTFIX_DELETE, o);

  71. }

  72. ///

  73. /* 根据ID删除对象

  74. /* @throws SQLException

  75. /*/

  76. public int removeById(Class entityClass, Serializable id) throws SQLException {

  77. return getSqlMapClient().delete(entityClass.getName() + POSTFIX_DELETE_PRIAMARYKEY, id);

  78. }

  79. ///

  80. /* map查询.

  81. /*

  82. /* @param map

  83. /* 包含各种属性的查询

  84. /* @throws SQLException

  85. /*/

  86. public List find(Class entityClass, Map map) throws SQLException {

  87. if (map == null)

  88. return this.getSqlMapClient().queryForList(entityClass.getName() + POSTFIX_SELECT, null);

  89. else {

  90. map.put("findBy", "True");

  91. return this.getSqlMapClient()

  92. .queryForList(entityClass.getName() + POSTFIX_SELECTMAP, map);

  93. }

  94. }

  95. ///

  96. /* sql 查询.

  97. /*

  98. /* @param sql

  99. /* 直接sql的语句(需要防止注入式攻击)

  100. /* @throws SQLException

  101. /*/

  102. public List find(Class entityClass, String sql) throws SQLException {

  103. Assert.hasText(sql);

  104. if (StringUtils.isEmpty(sql))

  105. return this.getSqlMapClient().queryForList(entityClass.getName() + POSTFIX_SELECT, null);

  106. else

  107. return this.getSqlMapClient()

  108. .queryForList(entityClass.getName() + POSTFIX_SELECTSQL, sql);

  109. }

  110. ///

  111. /* 根据属性名和属性值查询对象.

  112. /*

  113. /* @return 符合条件的对象列表

  114. /* @throws SQLException

  115. /*/

  116. public List findBy(Class entityClass, String name, Object value) throws SQLException {

  117. Assert.hasText(name);

  118. Map map = new HashMap();

  119. map.put(name, value);

  120. return find(entityClass, map);

  121. }

  122. ///

  123. /* 根据属性名和属性值查询对象.

  124. /*

  125. /* @return 符合条件的唯一对象

  126. /*/

  127. public T findUniqueBy(Class entityClass, String name, Object value) {

  128. Assert.hasText(name);

  129. Map map = new HashMap();

  130. try {

  131. PropertyUtils.getProperty(entityClass.newInstance(), name);

  132. map.put(name, value);

  133. map.put("findUniqueBy", "True");

  134. return (T) getSqlMapClient().queryForObject(entityClass.getName() + POSTFIX_SELECTMAP,

  135. map);

  136. } catch (Exception e) {

  137. logger.error("Error when propertie on entity," + e.getMessage(), e.getCause());

  138. return null;

  139. }

  140. }

  141. ///

  142. /* 根据属性名和属性值以Like AnyWhere方式查询对象.

  143. /* @throws SQLException

  144. /*/

  145. public List findByLike(Class entityClass, String name, String value) throws SQLException {

  146. Assert.hasText(name);

  147. Map map = new HashMap();

  148. map.put(name, value);

  149. map.put("findLikeBy", "True");

  150. return getSqlMapClient().queryForList(entityClass.getName() + POSTFIX_SELECTMAP, map);

  151. }

  152. ///

  153. /* 判断对象某些属性的值在数据库中不存在重复

  154. /*

  155. /* @param tableName

  156. /* 数据表名字

  157. /* @param names

  158. /* 在POJO里不能重复的属性列表,以逗号分割 如"name,loginid,password"

  159. /* FIXME how about in different schema?

  160. /*/

  161. public boolean isNotUnique(Object entity, String tableName, String names) {

  162. try {

  163. String primarykey;

  164. Connection con = getSqlMapClient().getCurrentConnection();

  165. ResultSet dbMetaData = con.getMetaData().getPrimaryKeys(con.getCatalog(), null, tableName);

  166. dbMetaData.next();

  167. if (dbMetaData.getRow() > 0) {

  168. primarykey = dbMetaData.getString(4);

  169. if (names.indexOf(primarykey) > -1)

  170. return false;

  171. } else {

  172. return true;

  173. }

  174. } catch (SQLException e) {

  175. logger.error(e.getMessage(), e);

  176. return false;

  177. }

  178. return false;

  179. }

  180. ///

  181. /* 分页查询函数,使用PaginatedList.

  182. /*

  183. /* @param pageNo

  184. /* 页号,从0开始.

  185. /* @throws SQLException

  186. /*/

  187. @SuppressWarnings("rawtypes")

  188. public DataPage pagedQuery(String sqlName, HashMap hashMap, Integer pageNo, Integer pageSize)

  189. throws SQLException {

  190. if (pageNo == null || pageSize == null) {

  191. List list = getSqlMapClient().queryForList(sqlName, hashMap);

  192. if (list == null || list.size() == 0) {

  193. return new DataPage();

  194. } else {

  195. return new DataPage(0, list.size(), list.size(), list);

  196. }

  197. } else {

  198. Assert.hasText(sqlName);

  199. Assert.isTrue(pageNo >= 1, "pageNo should start from 1");

  200. // Count查询

  201. Integer totalCount = (Integer) getSqlMapClient().queryForObject(sqlName + ".Count", hashMap);

  202. if (totalCount < 1) {

  203. return new DataPage();

  204. }

  205. // 实际查询返回分页对象

  206. int startIndex = DataPage.getStartOfPage(pageNo, pageSize);

  207. hashMap.put("startIndex", startIndex);

  208. hashMap.put("pageSize", pageSize);

  209. List list = getSqlMapClient().queryForList(sqlName, hashMap);

  210. return new DataPage(startIndex, totalCount, pageSize, list);

  211. }

  212. }

  213. public String getMappedSQL(String sqlName) {

  214. String sql = null;

  215. SqlMapClientImpl sqlmap = (SqlMapClientImpl) getSqlMapClient();

  216. MappedStatement stmt = sqlmap.getMappedStatement(sqlName);

  217. StaticSql staticSql = (StaticSql) stmt.getSql();

  218. sql = staticSql.getSql(null, null);

  219. return sql;

  220. }

  221. }

package com.nfschina.utils.dao.ibatis import java.io.Serializable; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.beanutils.PropertyUtils; import org.apache.commons.lang.StringUtils; import org.springframework.util.Assert; import com.ibatis.sqlmap.engine.impl.SqlMapClientImpl; import com.ibatis.sqlmap.engine.mapping.sql.stat.StaticSql; import com.ibatis.sqlmap.engine.mapping.statement.MappedStatement; importcom.nfschina.utils.BaseException; import com.nfschina.utils.DataPage; import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport /// / IBatis Dao的泛型基类. / 继承于Spring的SqlMapClientDaoSupport,提供分页函数和若干便捷查询方法,并对返回值作了泛型类型转换. // @SuppressWarnings("unchecked") public class IBatisGenericDao extends SqlMapClientDaoSupport { public static final String POSTFIX_INSERT = ".insert"; public static final String POSTFIX_UPDATE = ".update"; public static final String POSTFIX_DELETE = ".delete"; public static final String POSTFIX_DELETE_PRIAMARYKEY = ".deleteByPrimaryKey"; public static final String POSTFIX_SELECT = ".select"; public static final String POSTFIX_SELECTMAP = ".selectByMap"; public static final String POSTFIX_SELECTSQL = ".selectBySql"; public static final String POSTFIX_COUNT = ".count"; /// / 根据ID获取对象 / / @throws BaseException / @throws SQLException // public T get(Class entityClass, Serializable id) throws BaseException, SQLException { T o = (T) getSqlMapClient().queryForObject(entityClass.getName() + POSTFIX_SELECT, id); if (o == null) throw new BaseException(BaseException.DATA_NOTFOUND, "未找到实体: " + id); return o; } /// / 获取全部对象 / @throws SQLException // public List getAll(Class entityClass) throws SQLException { return getSqlMapClient().queryForList(entityClass.getName() + POSTFIX_SELECT, null); } /// / 新增对象 / @throws SQLException // public void insert(Object o) throws SQLException { getSqlMapClient().insert(o.getClass().getName() + POSTFIX_INSERT, o); } /// / 保存对象 / @throws SQLException // public int update(Object o) throws SQLException { return getSqlMapClient().update(o.getClass().getName() + POSTFIX_UPDATE, o); } /// / 删除对象 / @throws SQLException // public int remove(Object o) throws SQLException { return getSqlMapClient().delete(o.getClass().getName() + POSTFIX_DELETE, o); } /// / 根据ID删除对象 / @throws SQLException // public int removeById(Class entityClass, Serializable id) throws SQLException { return getSqlMapClient().delete(entityClass.getName() + POSTFIX_DELETE_PRIAMARYKEY, id); } /// / map查询. / / @param map / 包含各种属性的查询 / @throws SQLException // public List find(Class entityClass, Map map) throws SQLException { if (map == null) return this.getSqlMapClient().queryForList(entityClass.getName() + POSTFIX_SELECT, null); else { map.put("findBy", "True"); return this.getSqlMapClient() .queryForList(entityClass.getName() + POSTFIX_SELECTMAP, map); } } /// / sql 查询. / / @param sql / 直接sql的语句(需要防止注入式攻击) / @throws SQLException // public List find(Class entityClass, String sql) throws SQLException { Assert.hasText(sql); if (StringUtils.isEmpty(sql)) return this.getSqlMapClient().queryForList(entityClass.getName() + POSTFIX_SELECT, null); else return this.getSqlMapClient() .queryForList(entityClass.getName() + POSTFIX_SELECTSQL, sql); } /// / 根据属性名和属性值查询对象. / / @return 符合条件的对象列表 / @throws SQLException // public List findBy(Class entityClass, String name, Object value) throws SQLException { Assert.hasText(name); Map map = new HashMap(); map.put(name, value); return find(entityClass, map); } /// / 根据属性名和属性值查询对象. / / @return 符合条件的唯一对象 // public T findUniqueBy(Class entityClass, String name, Object value) { Assert.hasText(name); Map map = new HashMap(); try { PropertyUtils.getProperty(entityClass.newInstance(), name); map.put(name, value); map.put("findUniqueBy", "True"); return (T) getSqlMapClient().queryForObject(entityClass.getName() + POSTFIX_SELECTMAP, map); } catch (Exception e) { logger.error("Error when propertie on entity," + e.getMessage(), e.getCause()); return null; } } /// / 根据属性名和属性值以Like AnyWhere方式查询对象. / @throws SQLException // public List findByLike(Class entityClass, String name, String value) throws SQLException { Assert.hasText(name); Map map = new HashMap(); map.put(name, value); map.put("findLikeBy", "True"); return getSqlMapClient().queryForList(entityClass.getName() + POSTFIX_SELECTMAP, map); } /// / 判断对象某些属性的值在数据库中不存在重复 / / @param tableName / 数据表名字 / @param names / 在POJO里不能重复的属性列表,以逗号分割 如"name,loginid,password"
/
FIXME how about in different schema? // public boolean isNotUnique(Object entity, String tableName, String names) { try { String primarykey; Connection con = getSqlMapClient().getCurrentConnection(); ResultSet dbMetaData = con.getMetaData().getPrimaryKeys(con.getCatalog(), null, tableName); dbMetaData.next(); if (dbMetaData.getRow() > 0) { primarykey = dbMetaData.getString(4); if (names.indexOf(primarykey) > -1) return false; } else { return true; } } catch (SQLException e) { logger.error(e.getMessage(), e); return false; } return false; } /// / 分页查询函数,使用PaginatedList. / / @param pageNo / 页号,从0开始. / @throws SQLException /*/ @SuppressWarnings("rawtypes") public DataPage pagedQuery(String sqlName, HashMap hashMap, Integer pageNo, Integer pageSize) throws SQLException { if (pageNo == null || pageSize == null) { List list = getSqlMapClient().queryForList(sqlName, hashMap); if (list == null || list.size() == 0) { return new DataPage(); } else { return new DataPage(0, list.size(), list.size(), list); } } else { Assert.hasText(sqlName); Assert.isTrue(pageNo >= 1, "pageNo should start from 1"); // Count查询 Integer totalCount = (Integer) getSqlMapClient().queryForObject(sqlName + ".Count", hashMap); if (totalCount < 1) { return new DataPage(); } // 实际查询返回分页对象 int startIndex = DataPage.getStartOfPage(pageNo, pageSize); hashMap.put("startIndex", startIndex); hashMap.put("pageSize", pageSize); List list = getSqlMapClient().queryForList(sqlName, hashMap); return new DataPage(startIndex, totalCount, pageSize, list); } } public String getMappedSQL(String sqlName) { String sql = null; SqlMapClientImpl sqlmap = (SqlMapClientImpl) getSqlMapClient(); MappedStatement stmt = sqlmap.getMappedStatement(sqlName); StaticSql staticSql = (StaticSql) stmt.getSql(); sql = staticSql.getSql(null, null); return sql; } }

view plaincopy to clipboardprint?

  1. package com.nfschina.utils.dao.ibatis;

  2. import java.io.Serializable;

  3. import java.lang.reflect.InvocationTargetException;

  4. import java.sql.SQLException;

  5. import java.util.List;

  6. import java.util.Map;

  7. import org.apache.commons.beanutils.PropertyUtils;

  8. import org.apache.commons.lang.StringUtils;

  9. import org.springframework.orm.ObjectRetrievalFailureException;

  10. import com.nfschina.utils.BaseException;

  11. import com.nfschina.utils.GenericsUtils;

  12. import

  13. ///

  14. /* 负责为单个Entity 提供CRUD操作的IBatis DAO基类.

  15. /*

  16. /* 子类只要在类定义时指定所管理Entity的Class, 即拥有对单个Entity对象的CRUD操作.

  17. /*

  18. /*

     

  19. /* public class UserManagerIbatis extends IBatisEntityDao {

  20. /* }

  21. /*

  22. /*/

  23. public class IBatisEntityDao extends IBatisGenericDao {

  24. ///

  25. /* DAO所管理的Entity类型.

  26. /*/

  27. protected Class entityClass;

  28. protected String primaryKeyName;

  29. ///

  30. /* 在构造函数中将泛型T.class赋给entityClass.

  31. /*/

  32. @SuppressWarnings("unchecked")

  33. public IBatisEntityDao() {

  34. entityClass = (Class) GenericsUtils.getSuperClassGenricType(getClass());

  35. }

  36. ///

  37. /* 根据属性名和属性值查询对象.

  38. /*

  39. /* @return 符合条件的对象列表

  40. /* @throws SQLException

  41. /*/

  42. public List findBy(String name, Object value) throws SQLException {

  43. return findBy(getEntityClass(), name, value);

  44. }

  45. ///

  46. /* 根据属性的名值对查询对象

  47. /* @param map

  48. /* @return

  49. /* @throws SQLException

  50. /*/

  51. public List find(Map map) throws SQLException{

  52. return find(getEntityClass(), map);

  53. }

  54. ///

  55. /* 根据属性的名值对查询唯一对象

  56. /* @param map

  57. /* @return

  58. /* @throws SQLException

  59. /*/

  60. public T findUniqueByMap(Map map) throws SQLException{

  61. List list = find(getEntityClass(), map);

  62. if(list == null || list.size() <= 0){

  63. return null;

  64. }

  65. return list.get(0);

  66. }

  67. ///

  68. /* 根据属性名和属性值以Like AnyWhere方式查询对象.

  69. /* @throws SQLException

  70. /*/

  71. public List findByLike(String name, String value) throws SQLException {

  72. return findByLike(getEntityClass(), name, value);

  73. }

  74. ///

  75. /* 根据属性名和属性值查询单个对象.

  76. /*

  77. /* @return 符合条件的唯一对象

  78. /*/

  79. public T findUniqueBy(String name, Object value) {

  80. return findUniqueBy(getEntityClass(), name, value);

  81. }

  82. ///

  83. /* 根据ID获取对象.

  84. /*

  85. /* @throws BaseException

  86. /* @throws SQLException

  87. /*/

  88. public T get(Serializable id) throws BaseException, SQLException {

  89. return get(getEntityClass(), id);

  90. }

  91. ///

  92. /* 获取全部对象.

  93. /* @throws SQLException

  94. /*/

  95. public List getAll() throws SQLException {

  96. return getAll(getEntityClass());

  97. }

  98. ///

  99. /* 取得entityClass.

  100. /*

  101. /* JDK1.4不支持泛型的子类可以抛开Class entityClass,重载此函数达到相同效果。

  102. /*/

  103. protected Class getEntityClass() {

  104. return entityClass;

  105. }

  106. public String getPrimaryKeyName() {

  107. if (StringUtils.isEmpty(primaryKeyName))

  108. primaryKeyName = "id";

  109. return primaryKeyName;

  110. }

  111. protected Object getPrimaryKeyValue(Object o) throws NoSuchMethodException, IllegalAccessException,

  112. InvocationTargetException, InstantiationException {

  113. return PropertyUtils.getProperty(entityClass.newInstance(), getPrimaryKeyName());

  114. }

  115. ///

  116. /* 根据ID移除对象.

  117. /* @throws SQLException

  118. /*/

  119. public int removeById(Serializable id) throws SQLException {

  120. return removeById(getEntityClass(), id);

  121. }

  122. ///

  123. /* 保存对象. 为了实现IEntityDao 我在内部使用了insert和upate 2个方法.

  124. /* @throws SQLException

  125. /*/

  126. public void saveOrUpdate(Object o) throws SQLException {

  127. Object primaryKey;

  128. try {

  129. primaryKey = getPrimaryKeyValue(o);

  130. } catch (Exception e) {

  131. throw new ObjectRetrievalFailureException(entityClass, e);

  132. }

  133. if (primaryKey == null)

  134. insert(o);

  135. else

  136. update(o);

  137. }

  138. public void setPrimaryKeyName(String primaryKeyName) {

  139. this.primaryKeyName = primaryKeyName;

  140. }

  141. public String getIdName(Class<?> clazz) {

  142. return "id";

  143. }

  144. }

package com.nfschina.utils.dao.ibatis; import java.io.Serializable; import java.lang.reflect.InvocationTargetException; import java.sql.SQLException; import java.util.List; import java.util.Map; import org.apache.commons.beanutils.PropertyUtils; import org.apache.commons.lang.StringUtils; import org.springframework.orm.ObjectRetrievalFailureException; import com.nfschina.utils.BaseException; import com.nfschina.utils.GenericsUtils; import /// / 负责为单个Entity 提供CRUD操作的IBatis DAO基类. /

/ 子类只要在类定义时指定所管理Entity的Class, 即拥有对单个Entity对象的CRUD操作. / /

 / public class UserManagerIbatis extends IBatisEntityDao { / } / 
// public class IBatisEntityDao extends IBatisGenericDao { /// / DAO所管理的Entity类型. // protected Class entityClass; protected String primaryKeyName; /// / 在构造函数中将泛型T.class赋给entityClass. // @SuppressWarnings("unchecked") public IBatisEntityDao() { entityClass = (Class) GenericsUtils.getSuperClassGenricType(getClass()); } /// / 根据属性名和属性值查询对象. / / @return 符合条件的对象列表 / @throws SQLException // public List findBy(String name, Object value) throws SQLException { return findBy(getEntityClass(), name, value); } /// / 根据属性的名值对查询对象 / @param map / @return / @throws SQLException // public List find(Map map) throws SQLException{ return find(getEntityClass(), map); } /// / 根据属性的名值对查询唯一对象 / @param map / @return / @throws SQLException // public T findUniqueByMap(Map map) throws SQLException{ List list = find(getEntityClass(), map); if(list == null || list.size() <= 0){ return null; } return list.get(0); } /// / 根据属性名和属性值以Like AnyWhere方式查询对象. / @throws SQLException // public List findByLike(String name, String value) throws SQLException { return findByLike(getEntityClass(), name, value); } /// / 根据属性名和属性值查询单个对象. / / @return 符合条件的唯一对象 // public T findUniqueBy(String name, Object value) { return findUniqueBy(getEntityClass(), name, value); } /// / 根据ID获取对象. / / @throws BaseException / @throws SQLException // public T get(Serializable id) throws BaseException, SQLException { return get(getEntityClass(), id); } /// / 获取全部对象. / @throws SQLException // public List getAll() throws SQLException { return getAll(getEntityClass()); } /// / 取得entityClass. /

/ JDK1.4不支持泛型的子类可以抛开Class entityClass,重载此函数达到相同效果。 // protected Class getEntityClass() { return entityClass; } public String getPrimaryKeyName() { if (StringUtils.isEmpty(primaryKeyName)) primaryKeyName = "id"; return primaryKeyName; } protected Object getPrimaryKeyValue(Object o) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { return PropertyUtils.getProperty(entityClass.newInstance(), getPrimaryKeyName()); } /// / 根据ID移除对象. / @throws SQLException // public int removeById(Serializable id) throws SQLException { return removeById(getEntityClass(), id); } /// / 保存对象. 为了实现IEntityDao 我在内部使用了insert和upate 2个方法. / @throws SQLException /*/ public void saveOrUpdate(Object o) throws SQLException { Object primaryKey; try { primaryKey = getPrimaryKeyValue(o); } catch (Exception e) { throw new ObjectRetrievalFailureException(entityClass, e); } if (primaryKey == null) insert(o); else update(o); } public void setPrimaryKeyName(String primaryKeyName) { this.primaryKeyName = primaryKeyName; } public String getIdName(Class<?> clazz) { return "id"; } }

ibatis学习(三)

Posted on

ibatis学习(三)---ibatis与spring的整合 - joe --专注java,开源,架构,项目管理 - BlogJava

joe --专注java,开源,架构,项目管理

STANDING ON THE SHOULDERS OF GIANTS

posts - 339, comments - 292, trackbacks - 0, articles - 1 BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合 :: 管理 ## ibatis学习(三)---ibatis与spring的整合

Posted on 2007-12-07 18:26 @joe 阅读(13593) 评论(11) 编辑 收藏

Spring通过DAO模式,提供了对iBATIS的良好支持。SqlMapClient对象是iBATIS中的主要对象,我们可以通过配置让spring来管理SqlMapClient对象的创建。

与hibernate类似,Spring 提供了SqlMapClientDaoSupport对象,我们的DAO可以继承这个类,通过它所提供的SqlMapClientTemplate对象来操纵数据库。看起来这些概念都与hibernate类似。

通过SqlMapClientTemplate来操纵数据库的CRUD是没有问题的,这里面关键的问题是事务处理。Spring提供了强大的声明式事务处理的功能,我们已经清楚hibernate中如何配置声明式的事务,那么在iBATIS中如何获得声明式事务的能力呢?

第一,我们需要了解的是spring通过AOP来拦截方法的调用,从而在这些方法上面添加声明式事务处理的能力。典型配置如下:applicationContext-common.xml

<tx:advice id="txAdvice" transaction-manager="*事务管理器名称*">

    <tx:attributes>

       <tx:method name="add/*" propagation="REQUIRED"/>

       <tx:method name="del/*" propagation="REQUIRED"/>

       <tx:method name="update/*" propagation="REQUIRED"/>

       <tx:method name="/*" read-only="true"/>

   </tx:attributes>

</tx:advice>



<!-- 配置哪些类的方法需要进行事务管理 -->

<aop:config>

   <aop:pointcut id="allManagerMethod" expression="execution(/* com.ibatis.manager./*./*(..))"/>

   <aop:advisor advice-ref="txAdvice" pointcut-ref="allManagerMethod"/>

</aop:config>

这些事务都是声明在业务逻辑层的对象上的。

第二,我们需要一个事务管理器,对事务进行管理。

<property name="dataSource" ref="dataSource"/>

</bean>

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">

    <property name="driverClassName" value="com.mysql.jdbc.Driver"/>

    <property name="url" value="jdbc:mysql://127.0.0.1/ibatis"/>

    <property name="username" value="root"/>

    <property name="password" value="mysql"/>

</bean>

此后,我们需要让spring来管理SqlMapClient对象:

<bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean">

   <property name="configLocation"><value>classpath:sqlMapConfig.xml</value></property>

</bean>

我们的sqlMapConfig.xml就可以简写为:

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE sqlMapConfig

PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"     

"http://ibatis.apache.org/dtd/sql-map-config-2.dtd">

User.xml:如下

<?xml version="1.0" encoding="UTF-8" ?>

<!DOCTYPE sqlMap

PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"     

"http://ibatis.apache.org/dtd/sql-map-2.dtd">
insert into t_user values ( null,/#username/#,/#password/# ) update t_user set username = /#username/#,password=/#password/# where id=/#id/# delete from t_user where id=/#id/#

我们的DAO的编写: package com.iabtis.dao.impl.ibatis;

import java.util.List;

import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;

import com.ibatis.dao.UserDAO;

import com.ibatis.crm.model.User;

public class UserDAOImpl extends SqlMapClientDaoSupport implements UserDAO {

public void select(User user) {

          getSqlMapClientTemplate().delete("selectUser ",user.getId());

   }

public List findAll() {

          return getSqlMapClientTemplate().queryForList("selectAllUsers ");

   }

   public void delete(User user) {

          getSqlMapClientTemplate().delete("deleteUser ",user.getId());

   }

   public void save(User user) {

          getSqlMapClientTemplate().insert("insertUser ",user);

   }

   public void update(User user) {

          getSqlMapClientTemplate().update("updateUser ",user);

   }

}

继承SqlMapClientDaoSupport,要求我们注入SqlMapClient对象,因此,需要有如下的DAO配置:

这就是所有需要注意的问题了,此后就可以在业务逻辑层调用DAO对象了!

评论

/# re: ibatis学习(三)---ibatis与spring的整合 回复 更多评论

2007-12-08 09:42 by laocat

豁然开朗 !!

/# re: ibatis学习(三)---ibatis与spring的整合 回复 更多评论

2008-03-24 16:08 by 屹砾

整合的问题现在终于清楚了, 对于advice和aop还有点不清楚。

/# re: ibatis学习(三)---ibatis与spring的整合[未登录] 回复 更多评论

2008-06-13 17:26 by 冷漠大神o.../)

真是不错的文章啊 如果在在可以把源码提供下载 那就更完美了 :-) 呵呵 是不是有点贪心

/# re: ibatis学习(三)---ibatis与spring的整合[未登录] 回复 更多评论

2008-06-13 17:34 by 冷漠大神o.../)

有没有代码下载啊?

/# re: ibatis学习(三)---ibatis与spring的整合 回复 更多评论

2008-08-25 11:35 by 379548695qq

UserDAO类里面的内容是什么?

/# re: ibatis学习(三)---ibatis与spring的整合[未登录] 回复 更多评论

2009-03-19 10:17 by 虫子

正在烦恼中,google到了你的方案!呵呵

/# re: ibatis学习(三)---ibatis与spring的整合 回复 更多评论

2009-08-13 16:32 by jadmin

很好,学习了

/# re: ibatis学习(三)---ibatis与spring的整合 回复 更多评论

2009-08-15 23:15 by 匹马单枪

好帖, 学习了!

/# re: ibatis学习(三)---ibatis与spring的整合 回复 更多评论

2009-09-01 13:07 by 2

为什么我的出错了

/# re: ibatis学习(三)---ibatis与spring的整合[未登录] 回复 更多评论

2010-04-28 11:10 by test

autocommit 如果不设置为false, 事务有用么

/# re: ibatis学习(三)---ibatis与spring的整合 回复 更多评论

2010-11-01 13:42 by rr

sqlMapClient里应该注入dataSource

新用户注册 刷新评论列表
找优秀程序员,就在博客园 IT新闻: · 婚恋网世纪佳缘乱象重生 成一夜情猎场 · 帝国时代 Online 已发布,可免费下载 · 小米发布会中的亮点与尿点:“狗日的 1999” · 315投诉网疑被关后改名重开张 新网站否认 · 团购网站融资冷却裁员求生 行业洗牌危机并存 博客园 博问 IT新闻 Java程序员招聘 标题 请输入标题 姓名 请输入你的姓名 主页 请输入验证码 验证码 /* 内容(请不要发表任何与政治相关的内容) 请输入评论内容 Remember Me? 登录 [使用Ctrl+Enter键可以直接提交] 推荐职位: · 广州ASP.NET程序员(广州丹霄信息技术) · 上海 .NET软件工程师(上海苏秦网络) · 上海.NET软件开发工程师(东方财富信息) · 北京 SQL数据库开发工程师(圣特尔科技) · 北京高新诚聘 ASP.NET 程序员(盈科融通软件) · 北京 .NET软件工程师(北京科胜永昌) · 北京.NET 研发工程师 (北京捷报数据) · 北京C/#开发工程师 B/S方向(圣特尔科技)

博客园首页随笔: · C/#设计模式——命令模式(Command Pattern) · 老系统维护 · 使用单例模式实现自己的HttpClient工具类 · Spread for Windows Forms高级主题(2)---理解单元格类型 · ERP/MIS开发 菜单设计器(Menu Designer)及其B/S,C/S双重实现(B/S开源) 知识库: · IT项目管理的六种错误思维 · 我的10个开发原则 · 大数据下的数据分析平台架构 · 为什么编程是独一无二的职业 · 分享8年开发经验,浅谈个人发展经历,明确自己发展方向 最简洁阅读版式: ibatis学习(三)---ibatis与spring的整合 网站导航:

博客园 IT新闻 知识库 博客生活 IT博客网 C++博客 博问 管理 Powered by: BlogJava Copyright © @joe

公告

老婆的淘宝,欢迎选购

留言簿(2)

随笔分类

相册

搜索

积分与排名

  • 积分 - 249820
  • 排名 - 69

最新评论

阅读排行榜

评论排行榜

60天内阅读排行

加速你的Hibernate引擎(上)

Posted on

加速你的Hibernate引擎(上)

您还未登录!|登录|注册|帮助

简约设计の艺术

一个自由程序员的琐碎

加速你的Hibernate引擎(上)

分类: ORM Hibernate Framework Java Persistence API JavaEE 2010-11-03 09:55 5540人阅读 评论(8) 收藏 举报 目录(?)[+]

  1. 引言
  2. Hibernate性能调优
  3. 监控和剖析

  4. 监控SQL生成

  5. 查看Hibernate统计
  6. 剖析

  7. 调优技术

  8. 业务规则与设计调优

  9. 继承映射调优

  10. 每个类层次一张表

  11. 每个子类一张表
  12. 每个具体类一张表
  13. 使用隐式多态实现每个具体类一张表

  14. 领域对象调优

  15. POJO调优

  16. POJO之间关联的调优

  17. 连接池调优

  18. 事务和并发的调优

Hibernate LogoInfoQ Logo

Hibernate是 最流行的对象关系映射(ORM)引擎之一,它提供了数据持久化和查询服务。在你的项目中引入Hibernate并让它跑起来是很容易的。但是,要让它跑得 好却是需要很多时间和经验的。通过我们的使用Hibernate 3.3.1和Oracle 9i的能源项目中的一些例子,本文涵盖了很多Hibernate调优技术。其中还提供了一些掌握Hibernate调优技术所必需的数据库知识。

我们假设读者对Hibernate有一个基本的了解。如果一个调优方法在Hibernate 参考文档(下文简称HRD)或其他调优文章中有详细描述,我们仅提供一个对该文档的引用并从不同角度对其做简单说明。我们关注于那些行之有效,但又缺乏文档的调优方法。

作者 Yongjun Jiao and Stewart Clark 译者丁雪丰 发布于 2010年10月26日 上午12时0分

1.引言

Hibernate是最流行的对象关系映射(ORM)引擎之一,它提供了数据持久化和查询服务。在你的项目中引入Hibernate并让它跑起来是很容易的。但是,要让它跑得好却是需要很多时间和经验的。通过我们的使用Hibernate 3.3.1和Oracle 9i的能源项目中的一些例子,本文涵盖了很多Hibernate调优技术。其中还提供了一些掌握Hibernate调优技术所必需的数据库知识。

我们假设读者对Hibernate有一个基本的了解。如果一个调优方法在Hibernate 参考文档(下文简称HRD)或其他调优文章中有详细描述,我们仅提供一个对该文档的引用并从不同角度对其做简单说明。我们关注于那些行之有效,但又缺乏文档的调优方法。

2.Hibernate性能调优

调优是一个迭代的、持续进行的过程,涉及软件开发生命周期(SDLC)的所有阶段。在一个典型的使用Hibernate进行持久化的Java EE应用程序中,调优会涉及以下几个方面:

  • 业务规则调优
  • 设计调优
  • Hibernate调优
  • Java GC调优
  • 应用程序容器调优
  • 底层系统调优,包括数据库和OS。

没有一套精心设计的方案就去进行以上调优是非常耗时的,而且很可能收效甚微。好的调优方法的重要部分是为调优内容划分优先级。可以用Pareto定律(又称“80/20法则”)来解释这一点,即通常80%的应用程序性能改善源自头20%的性能问题[5]。

相比基于磁盘和网络的访问,基于内存和CPU的访问能提供更低的延迟和更高的吞吐量。这种基于IO的Hibernate调优与底层系统IO部分的调优应该优先于基于CPU和内存的底层系统GC、CPU和内存部分的调优。 范例1

我们调优了一个选择电流的HQL查询,把它从30秒降到了1秒以内。如果我们在垃圾回收方面下功夫,可能收效甚微——也许只有几毫秒或者最多几秒,相比HQL的改进,GC方面的改善可以忽略不计。

好的调优方法的另一个重要部分是决定何时优化[4]。

积极优化的提倡者主张开始时就进行调优,例如在业务规则和设计阶段,在整个SDLC都持续进行优化,因为他们认为后期改变业务规则和重新设计代价太大。

另一派人提倡在SDLC末期进行调优,因为他们抱怨前期调优经常会让设计和编码变得复杂。他们经常引用Donald Knuth的名言“过早优化是万恶之源”[6]。

为了平衡调优和编码需要一些权衡。根据笔者的经验,适当的前期调优能带来更明智的设计和细致的编码。很多项目就失败在应用程序调优上,因为上面提到的“过早优化”阶段在被引用时脱离了上下文,而且相应的调优不是被推迟得太晚就是投入资源过少。

但是,要做很多前期调优也不太可能,因为没有经过剖析,你并不能确定应用程序的瓶颈究竟在何处,应用程序一般都是这样演化的。

对我们的多线程企业级应用程序的剖析也表现出大多数应用程序平均只有20-50%的CPU使用率。剩余的CPU开销只是在等待数据库和网络相关的IO。

基于上述分析,我们得出这样一个结论,结合业务规则和设计的Hibernate调优在Pareto定律中20%的那个部分,相应的它们的优先级更高。

一种比较实际的做法是:

  1. 识别出主要瓶颈,可以预见其中多数是Hibernate、业务规则和设计方面的(其数量视你的调优目标而定;但三到五个是不错的开端)。
  2. 修改应用程序以便消除这些瓶颈。
  3. 测试应用程序,然后重复步骤1,直到达到你的调优目标为止。

你能在Jack Shirazi的《Java Performance Tuning》 [7]一书中找到更多关于性能调优阶段的常见建议。

下面的章节中,我们会按照调优的大致顺序(列在前面的通常影响最大)去解释一些特定的调优技术。

3. 监控和剖析

没有对Hibernate应用程序的有效监控和剖析,你无法得知性能瓶颈以及何处需要调优。

3.1.1 监控SQL生成

尽管使用Hibernate的主要目的是将你从直接使用SQL的痛苦中解救出来,为了对应用程序进行调优,你必须知道Hibernate生成了哪些 SQL。JoeSplosky在他的《The Law of Leaky Abstractions》一文中详细描述了这个问题。

你可以在log4j中将org.hibernate.SQL包的日志级别设为DEBUG,这样便能看到生成的所有SQL。你还可以将其他包的日志级别设为DEBUG,甚至TRACE来定位一些性能问题。

3.1.2 查看Hibernate统计

如果开启hibernate.generate.statistics,Hibernate会导出实体、集合、会话、二级缓存、查询和会话工厂的统计信息,这对通过SessionFactory.getStatistics()进行的调优很有帮助。为了简单起见,Hibernate还可以使用MBean“org.hibernate.jmx.StatisticsService”通过JMX来导出统计信息。你可以在这个网站找到配置范例 。

3.1.3 剖析

一个好的剖析工具不仅有利于Hibernate调优,还能为应用程序的其他部分带来好处。然而,大多数商业工具(例如JProbe [10])都很昂贵。幸运的是Sun/Oracle的JDK1.6自带了一个名为“Java VisualVM” [11]的调试接口。虽然比起那些商业竞争对手,它还相当基础,但它提供了很多调试和调优信息。

4. 调优技术

4.1 业务规则与设计调优

尽管业务规则和设计调优并不属于Hibernate调优的范畴,但此处的决定对后面Hibernate的调优有很大影响。因此我们特意指出一些与Hibernate调优有关的点。

在业务需求收集与调优过程中,你需要知道:

  • 数据获取特性包括引用数据(reference data)、只读数据、读分组(read group)、读取大小、搜索条件以及数据分组和聚合。
  • 数据修改特性包括数据变更、变更组、变更大小、无效修改补偿、数据库(所有变更都在一个数据库中或在多个数据库中)、变更频率和并发性,以及变更响应和吞吐量要求。
  • 数据关系,例如关联(association)、泛化(generalization)、实现(realization)和依赖(dependency)。

基于业务需求,你会得到一个最优设计,其中决定了应用程序类型(是OLTP还是数据仓库,亦或者与其中某一种比较接近)和分层结构(将持久层和服务 层分离还是合并),创建领域对象(通常是POJO),决定数据聚合的地方(在数据库中进行聚合能利用强大的数据库功能,节省网络带宽;但是除了像 COUNT、SUM、AVG、MIN和MAX这样的标准聚合,其他的聚合通常不具有移植性。在应用服务器上进行聚合允许你应用更复杂的业务逻辑;但你需要 先在应用程序中载入详细的数据)。 范例2

分析员需要查看一个取自大数据表的电流ISO(Independent System Operator)聚合列表。最开始他们想要显示大多数字段,尽管数据库能在1分钟内做出响应,应用程序也要花30分钟将1百万行数据加载到前端UI。经 过重新分析,分析员保留了14个字段。因为去掉了很多可选的高聚合度字段,从剩下的字段中进行聚合分组返回的数据要少很多,而且大多数情况下的数据加载时 间也缩小到了可接受的范围内。

范例3

过24个“非标准”(shaped,表示每小时都可以有自己的电量和价格;如果所有24小时的电量和价格相同,我们称之为“标准”)小时会修改小时电流交易,其中包括2个属性:每小时电量和价格。起初我们使用Hibernate的select-before-update特性,就是更新24行数据需要24次选择。因为我们只需要2个属性,而且如果不修改电量或价格的话也没有业务规则禁止无效修改,我们就关闭了select-before-update特性,避免了24次选择。

4.2继承映射调优

尽管继承映射是领域对象的一部分,出于它的重要性我们将它单独出来。HRD [1]中的第9章“继承映射”已经说得很清楚了,所以我们将关注SQL生成和针对每个策略的调优建议。

以下是HRD中范例的类图:

4.2.1 每个类层次一张表

只需要一张表,一条多态查询生成的SQL大概是这样的: select id, payment_type, amount, currency, rtn, credit_card_type from payment

针对具体子类(例如CashPayment)的查询生成的SQL是这样的:

select id, amount, currency from payment where payment_type=’CASH’

这样做的优点包括只有一张表、查询简单以及容易与其他表进行关联。第二个查询中不需要包含其他子类中的属性。所有这些特性让该策略的性能调优要比其他策略容易得多。这种方法通常比较适合数据仓库系统,因为所有数据都在一张表里,不需要做表连接。

主要的缺点整个类层次中的所有属性都挤在一张大表里,如果有很多子类特有的属性,数据库中就会有太多字段的取值为null,这为当前基于行的数据库 (使用基于列的DBMS的数据仓库处理这个会更好些)的SQL调优增加了难度。除非进行分区,否则唯一的数据表会成为热点,OLTP系统通常在这方面都不 太好。

4.2.2每个子类一张表

需要4张表,多态查询生成的SQL如下: select id, payment_type, amount, currency, rtn, credit_card type, case when c.payment_id is not null then 1 when ck.payment_id is not null then 2 when cc.payment_id is not null then 3 when p.id is not null then 0 end as clazz from payment p left join cash_payment c on p.id=c.payment_id left join cheque_payment ck on p.id=ck.payment_id left join credit_payment cc on p.id=cc.payment_id;

针对具体子类(例如CashPayment)的查询生成的SQL是这样的:

select id, payment_type, amount, currency from payment p left join cash_payment c on p.id=c.payment_id;

优点包括数据表比较紧凑(没有不需要的可空字段),数据跨三个子类的表进行分区,容易使用超类的表与其他表进行关联。紧凑的数据表可以针对基于行的 数据库做存储块优化,让SQL执行得更好。数据分区增加了数据修改的并发性(除了超类,没有热点),OLTP系统通常会更好些。

同样的,第二个查询不需要包含其他子类的属性。

缺点是在所有策略中它使用的表和表连接最多,SQL语句稍显复杂(看看Hibernate动态鉴别器的长CASE子句)。相比单张表,数据库要花更多时间调优数据表连接,数据仓库在使用该策略时通常不太理想。

因为不能跨超类和子类的字段来建立复合索引,如果需要按这些列进行查询,性能会受影响。任何子类数据的修改都涉及两张表:超类的表和子类的表。

4.2.3每个具体类一张表

涉及三张或更多的表,多态查询生成的SQL是这样的: select p.id, p.amount, p.currency, p.rtn, p. credit_card_type, p.clazz from (select id, amount, currency, null as rtn,null as credit_card type, 1 as clazz from cash_payment union all select id, amount, null as currency, rtn,null as credit_card type, 2 as clazz from cheque_payment union all select id, amount, null as currency, null as rtn,credit_card type, 3 as clazz from credit_payment) p;

针对具体子类(例如CashPayment)的查询生成的SQL是这样的:

select id, payment_type, amount, currency from cash_payment;

优点和上面的“每个子类一张表”策略相似。因为超类通常是抽象的,所以具体的三张表是必须的[开头处说的3张或更多的表是必须的],任何子类的数据修改只涉及一张表,运行起来更快。

缺点是SQL(from子句和union all子查询)太复杂。但是大多数数据库对此类SQL的调优都很好。

如果一个类想和Payment超类关联,数据库无法使用引用完整性(referential integrity)来实现它;必须使用触发器来实现它。这对数据库性能有些影响。

4.2.4使用隐式多态实现每个具体类一张表

只需要三张表。对于Payment的多态查询生成三条独立的SQL语句,每个对应一个子类。Hibernate引擎通过Java反射找出Payment的所有三个子类。

具体子类的查询只生成该子类的SQL。这些SQL语句都很简单,这里就不再阐述了。

它的优点和上节类似:紧凑数据表、跨三个具体子类的数据分区以及对子类任意数据的修改都只涉及一张表。

缺点是用三条独立的SQL语句代替了一条联合SQL,这会带来更多网络IO。Java反射也需要时间。假设如果你有一大堆领域对象,你从最上层的Object类进行隐式选择查询,那该需要多长时间啊!

根据你的映射策略制定合理的选择查询并非易事;这需要你仔细调优业务需求,基于特定的数据场景制定合理的设计决策。

以下是一些建议:

  • 设计细粒度的类层次和粗粒度的数据表。细粒度的数据表意味着更多数据表连接,相应的查询也会更复杂。
  • 如非必要,不要使用多态查询。正如上文所示,对具体类的查询只选择需要的数据,没有不必要的表连接和联合。
  • “每个类层次一张表”对有高并发、简单查询并且没有共享列的OLTP系统来说是个不错的选择。如果你想用数据库的引用完整性来做关联,那它也是个合适的选择。
  • “每个具体类一张表”对有高并发、复杂查询并且没有共享列的OLTP系统来说是个不错的选择。当然你不得不牺牲超类与其他类之间的关联。
  • 采用混合策略,例如“每个类层次一张表”中嵌入“每个子类一张表”,这样可以利用不同策略的优势。随着你项目的进化,如果你要反复重新映射,那你可能也会采用该策略。
  • “使用隐式多态实现每个具体类一张表”这种做法并不推荐,因为其配置过于繁缛、使用“any”元素的复杂关联语法和隐式查询的潜在危险性。 范例4

下面是一个交易描述应用程序的部分领域类图:

开始时,项目只有GasDeal和少数用户,它使用“每个类层次一张表”。

OilDeal和ElectricityDeal是后期产生更多业务需求后加入的。没有改变映射策略。但是ElectricityDeal有太多自己的属性,因此有很多电相关的可空字段加入了Deal表。因为用户量也在增长,数据修改变得越来越慢。

重新设计时我们使用了两张单独的表,分别针对气/油和电相关的属性。新的映射混合了“每个类层次一张表”和“每个子类一张表”。我们还重新设计了查询,以便允许针对具体交易子类进行选择,消除不必要的列和表连接。

4.3 领域对象调优

基于4.1**节**中对业务规则和设计的调优,你得到了一个用POJO来表示的领域对象的类图。我们建议:

4.3.1 POJO调优

  • 从读写数据中将类似引用这样的只读数据和以读为主的数据分离出来。 只读数据的二级缓存是最有效的,其次是以读为主的数据的非严格读写。将只读POJO标识为不可更改的(immutable)也是一个调优点。如果一个服务层方法只处理只读数据,可以将它的事务标为只读,这是优化Hibernate和底层JDBC驱动的一个方法。
  • 细粒度的POJO和粗粒度的数据表。 基于数据的修改并发量和频率等内容来分解大的POJO。尽管你可以定义一个粒度非常细的对象模型,但粒度过细的表会导致大量表连接,这对数据仓库来说是不能接受的。
  • 优先使用非final的类。 Hibernate只会针对非final的类使用CGLIB代理来实现延时关联获取。如果被关联的类是final的,Hibernate会一次加载所有内容,这对性能会有影响。
  • 使用业务键为分离(detached)实例实现equals()和hashCode()方法。 在多层系统中,经常可以在分离对象上使用乐观锁来提升系统并发性,达到更高的性能。
  • 定义一个版本或时间戳属性。 乐观锁需要这个字段来实现长对话(应用程序事务)[译注:session译为会话,conversion译为对话,以示区别]。
  • 优先使用组合POJO。 你的前端UI经常需要来自多个不同POJO的数据。你应该向UI传递一个组合POJO而不是独立的POJO以获得更好的网络性能。 有两种方式在服务层构建组合POJO。一种是在开始时加3.2载所有需要的独立POJO,随后抽取需要的属性放入组合POJO;另一种是使用HQL投影,直接从数据库中选择需要的属性。 如果其他地方也要查找这些独立POJO,可以把它们放进二级缓存以便共享,这时第一种方式更好;其他情况下第二种方式更好。

4.3.2 POJO之间关联的调优

  • 如果可以用one-to-one、one-to-many或many-to-one的关联,就不要使用many-to-many。
  • many-to-many关联需要额外的映射表。 尽管你的Java代码只需要处理两端的POJO,但查询时,数据库需要额外地关联映射表,修改时需要额外的删除和插入。
  • 单向关联优先于双向关联。 由于many-to-many的特性,在双向关联的一端加载对象会触发另一端的加载,这会进一步触发原始端加载更多的数据,等等。 one-to-many和many-to-one的双向关联也是类似的,当你从多端(子实体)定位到一端(父实体)。 这样的来回加载很耗时,而且可能也不是你所期望的。
  • 不要为了关联而定义关联;只在你需要一起加载它们时才这么做,这应该由你的业务规则和设计来决定(见范例**5**)。 另外,你要么不定义任何关联,要么在子POJO中定义一个值类型的属性来表示父POJO的ID(另一个方向也是类似的)。
  • 集合调优 如果集合排序逻辑能由底层数据库实现,就使用“order-by”属性来代替“sort”,因为通常数据库在这方面做得比你好。 集合可以是值类型的(元素或组合元素),也可以是实体引用类型的(one-to-many或many-to-many关联)。对引用类型集合的调优主要是调优获取策略。对于值类型集合的调优,HRD [1]中的20.5节“理解集合性能”已经做了很好的阐述。
  • 获取策略调优。请见4.7**节的范例**5范例5

我们有一个名为ElectricityDeals的核心POJO用于描述电的交易。从业务角度来看,它有很多many-to-one关联,例如和 Portfolio、Strategy和Trader等的关联。因为引用数据十分稳定,它们被缓存在前端,能基于其ID属性快速定位到它们。

为了有好的加载性能,ElectricityDeal只映射元数据,即那些引用POJO的值类型ID属性,因为在需要时,可以在前端通过portfolioKey从缓存中快速查找Portfolio: name="portfolioKey" column="PORTFOLIO_ID"type="integer"/>

这种隐式关联避免了数据库表连接和额外的字段选择,降低了数据传输的大小。

4.4 连接池调优

由于创建物理数据库连接非常耗时,你应该始终使用连接池,而且应该始终使用生产级连接池而非Hibernate内置的基本连接池算法。

通常会为Hibernate提供一个有连接池功能的数据源。Apache DBCP的BasicDataSource[13]是一个流行的开源生产级数据源。大多数数据库厂商也实现了自己的兼容JDBC 3.0的连接池。举例来说,你也可以使用Oracle ReaApplication Cluster [15]提供的JDBC连接池[14]以获得连接的负载均衡和失败转移。

不用多说,你在网上能找到很多关于连接池调优的技术,因此我们只讨论那些大多数连接池所共有的通用调优参数:

  • 最小池大小:连接池中可保持的最小连接数。
  • 最大池大小:连接池中可以分配的最大连接数。 如果应用程序有高并发,而最大池大小又太小,连接池就会经常等待。相反,如果最小池大小太大,又会分配不需要的连接。
  • 最大空闲时间:连接池中的连接被物理关闭前能保持空闲的最大时间。
  • 最大等待时间:连接池等待连接返回的最大时间。该参数可以预防失控事务(runaway transaction)。
  • 验证查询:在将连接返回给调用方前用于验证连接的SQL查询。这是因为一些数据库被配置为会杀掉长时间空闲的连接,网络或数据库相关的异常也可能会杀死连接。为了减少此类开销,连接池在空闲时会运行该验证。

4.5事务和并发的调优

短数据库事务对任何高性能、高可扩展性的应用程序来说都是必不可少的。你使用表示对话请求的会话来处理单个工作单元,以此来处理事务。

考虑到工作单元的范围和事务边界的划分,有3中模式:

  • 每次操作一个会话。每次数据库调用需要一个新会话和事务。因为真实的业务事务通常包含多个此类操作和大量小事务,这一般会引起更多数据库活动(主要是数据库每次提交需要将变更刷新到磁盘上),影响应用程序性能。这是一种反模式,不该使用它。
  • 使用分离对象,每次请求一个会话。每次客户端请求有一个新会话和一个事务,使用Hibernate的“当前会话”特性将两者关联起来。 在一个多层系统中,用户通常会发起长对话(或应用程序事务)。大多数时间我们使用Hibernate的自动版本和分离对象来实现乐观并发控制和高性能。
  • 带扩展(或长)会话的每次对话一会话。在一个也许会跨多个事务的长对话中保持会话开启。尽管这能把你从重新关联中解脱出来,但会话可能会内存溢出,在高并发系统中可能会有旧数据。

你还应该注意以下几点。

  • 如果不需要JTA就用本地事务,因为JTA需要更多资源,比本地事务更慢。就算你有多个数据源,除非有跨多个数据库的事务,否则也不需要 JTA。在最后的一个场景下,可以考虑在每个数据源中使用本地事务,使用一种类似“Last Resource Commit Optimization”[16]的技术(见下面的范例**6**)。
  • 如果不涉及数据变更,将事务标记为只读的,就像4.3.1**节**提到的那样。
  • 总是设置默认事务超时。保证在没有响应返回给用户时,没有行为不当的事务会完全占有资源。这对本地事务也同样有效。
  • 如果Hibernate不是独占数据库用户,乐观锁会失效,除非创建数据库触发器为其他应用程序对相同数据的变更增加版本字段值。 范例6

我们的应用程序有多个在大多数情况下只和数据库“A”打交道的服务层方法;它们偶尔也会从数据库“B”中获取只读数据。因为数据库“B”只提供只读数据,我们对这些方法在这两个数据库上仍然使用本地事务。

服务层上有一个方法设计在两个数据库上执行数据变更。以下是伪代码: //Make sure a local transaction on database A exists @Transactional (readOnly=false, propagation=Propagation.REQUIRED) public void saveIsoBids() { //it participates in the above annotated local transaction insertBidsInDatabaseA(); //it runs in its own local transaction on database B insertBidRequestsInDatabaseB(); //must be the last operation

因为insertBidRequestsInDatabaseB()是saveIsoBids ()中的最后一个方法,所以只有下面的场景会造成数据不一致:

在saveIsoBids()执行返回时,数据库“A”的本地事务提交失败。

但是,就算saveIsoBids()使用JTA,在两阶段提交(2PC)的第二个提交阶段失败的时候,你还是会碰到数据不一致。因此如果你能处理好上述的数据不一致性,而且不想为了一个或少数几个方法引入JTA的复杂性,你应该使用本地事务。

(未完待续)

关于作者

Yongjun Jiao是SunGard Consulting Services的技术主管。过去10年中他一直是专业软件开发者,他的专长包括Java SE、Java EE、Oracle和应用程序调优。他最近的关注点是高性能计算,包括内存数据网格、并行计算和网格计算。

Stewart Clark是SunGard Consulting Services的负责人。过去15年中他一直是专业软件开发者和项目经理,他的专长包括Java核心编程、Oracle和能源交易。

[译注:由于原文较长,中译版分两次发布]

查看英文原文:Revving Up Your Hibernate Engine

转自:http://www.infoq.com/cn/articles/hibernate_tuning 本文是使用 B3log Solo简约设计の艺术 进行同步发布的

原文地址:http://b3log-88250.appspot.com/articles/2010/11/03/1288788936673.html 分享到:

8楼 pettery2 2010-12-01 22:41发表 [回复] [引用] [举报][e01]7楼 Duke147 2010-11-09 19:05发表 [回复] [引用] [举报][e01]6楼 tangweiwei0000 2010-11-08 09:53发表 [回复] [引用] [举报][e01]5楼 TAOCLEE 2010-11-07 14:29发表 [回复] [引用] [举报]路漫漫其修远喜4楼 codeshuo 2010-11-06 22:17发表 [回复] [引用] [举报][e01] 但我看不太懂啊。。。还得加把劲。3楼 lupengji 2010-11-06 18:28发表 [回复] [引用] [举报][e01]2楼 liuwu513 2010-11-05 16:01发表 [回复] [引用] [举报][e01] 这一部分太重要了1楼 skytalemcc 2010-11-05 09:47发表 [回复] [引用] [举报][e01] 您还没有登录,请[登录][注册]

/* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场 TOP

个人资料

88250

  • 访问:4219337次
  • 积分:41528分
  • 排名:第20名

  • 原创:1195篇

  • 转载:326篇
  • 译文:42篇
  • 评论:2834条

文章搜索

文章分类

展开

阅读排行

推荐文章 最新评论

wilder2000: mark

喝冰开水: 把源码发给我还吗?690236414@qq.com谢谢了,最好是我能加你好友,好相互交流

低调小一: 多谢了,转载了,楼主辛苦

ccssddnnbbookkee: 很详细,谢谢

hooqee: 不错!

起飞---为梦想而飞: 我一本都没看过,需要好好看看

logoc: 咱能,一天到晚不转来转去吗。还他妈的专家

bubble: 顶b3blog 对搜索引擎优化方面做的如何

簡約_Billy: 想您致敬,

簡約_Billy:

Code snips

Linux/Ubuntu

My projects

Technologies

公司简介|招贤纳士|广告服务|银行汇款帐号|联系方式|版权声明|法律顾问|问题报告QQ客服 微博客服 论坛反馈 联系邮箱:webmaster@csdn.net 服务热线:400-600-2320京 ICP 证 070598 号北京创新乐知信息技术有限公司 版权所有世纪乐知(北京)网络技术有限公司 提供技术支持江苏乐知网络技术有限公司 提供商务支持Copyright © 1999-2012, CSDN.NET, All Rights Reserved GongshangLogo