今年开发的项目XX项目中采用IBatis2.0+RCP +EJB3.0作为项目的架构,其中IBatis作为持久化框架,不采用Hibernate的原因很多,其中系统的高并发,高性能,大数据库量,消息通信类的系统,实时处理系统,偏向数据分析的系统等要求,同时由于hibernate技术的成败取决于开发人员的水平,由于人员水平参差不齐,无法保证对开源项目的实现掌控等原因,可能hibernate的有些逊色,故采用IBatis框架。
不过以前的使用Hibernate的通常做法:针对项目中关系的性能要求低的采用Hibernate框架的常用功能,其他采用Hibernate native sql ,hql或者spring jdbcTemplate 基本上可以满足项目的基本要求。
其中采用IBatis 的优点很多如IBatis框架简单易学,便于dba的测试,便于后期降低的维护成本,提高了开发的进度等。但是IBatis自身存在的问题然后需要改进。如IBatis自身移植性有限,考虑到以后可能数据的移植,进行相应的扩展方言的应用。
(注:以下代码是基于ibatis3 beta4的扩展,ibatis3正式版如果实现改变,将会继续跟进修改)
iBatis3默认使用的分页是基于游标的分页,而这种分页在不同的数据库上性能差异不一致,最好的办法当然是使用类似hibernate的基于方言(Dialect)的物理分页功能。
iBatis3现在提供插件功能,通过插件我们可以编写自己的拦截器来拦截iBatis3的主要执行方法来完成相关功能的扩展。
能够拦截的的类如下:
- Executor
- (update,query,flushStatements,commit,rollback,getTransaction,close,isClosed)
- ParameterHandler
- (getParameterObject,setParameters)
- ResultSetHandler
- (handleResultSets,handleOutputParameters)
- StatementHandler
- (prepare,parameterize,batch,update,query)
但此次我们感兴趣的是Executor.query()方法,查询方法最终都是执行该方法。
该方法完整是:
- Executor.query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;
分页方言的基本实现:
以Mysql数据库示例,即有一个方法 query(sql,offset,limit);
- query("select * from user",5,10);
经过我们的拦截器拦截处理,变成
- query("select * from user limit 5,10",0,0);
1.拦截类实现:
- @Intercepts({@Signature(
- type= Executor.class,
- method = "query",
- args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
- public class OffsetLimitInterceptor implements Interceptor{
- static int MAPPED_STATEMENT_INDEX = 0;
- static int PARAMETER_INDEX = 1;
- static int ROWBOUNDS_INDEX = 2;
- static int RESULT_HANDLER_INDEX = 3;
- Dialect dialect;
- public Object intercept(Invocation invocation) throws Throwable {
- processIntercept(invocation.getArgs());
- return invocation.proceed();
- }
- void processIntercept(final Object[] queryArgs) {
- //queryArgs = query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler)
- MappedStatement ms = (MappedStatement)queryArgs[MAPPED_STATEMENT_INDEX];
- Object parameter = queryArgs[PARAMETER_INDEX];
- final RowBounds rowBounds = (RowBounds)queryArgs[ROWBOUNDS_INDEX];
- int offset = rowBounds.getOffset();
- int limit = rowBounds.getLimit();
- if(dialect.supportsLimit() && (offset != RowBounds.NO_ROW_OFFSET || limit != RowBounds.NO_ROW_LIMIT)) {
- BoundSql boundSql = ms.getBoundSql(parameter);
- String sql = boundSql.getSql().trim();
- if (dialect.supportsLimitOffset()) {
- sql = dialect.getLimitString(sql, offset, limit);
- offset = RowBounds.NO_ROW_OFFSET;
- } else {
- sql = dialect.getLimitString(sql, 0, limit);
- }
- limit = RowBounds.NO_ROW_LIMIT;
- queryArgs[ROWBOUNDS_INDEX] = new RowBounds(offset,limit);
- BoundSql newBoundSql = new BoundSql(sql, boundSql.getParameterMappings(), boundSql.getParameterObject());
- MappedStatement newMs = copyFromMappedStatement(ms, new BoundSqlSqlSource(newBoundSql));
- queryArgs[MAPPED_STATEMENT_INDEX] = newMs;
- }
- }
- private MappedStatement copyFromMappedStatement(MappedStatement ms,SqlSource newSqlSource) {
- Builder builder = new MappedStatement.Builder(ms.getConfiguration(),ms.getId(),newSqlSource,ms.getSqlCommandType());
- builder.resource(ms.getResource());
- builder.fetchSize(ms.getFetchSize());
- builder.statementType(ms.getStatementType());
- builder.keyGenerator(ms.getKeyGenerator());
- builder.keyProperty(ms.getKeyProperty());
- builder.timeout(ms.getTimeout());
- builder.parameterMap(ms.getParameterMap());
- builder.resultMaps(ms.getResultMaps());
- builder.cache(ms.getCache());
- MappedStatement newMs = builder.build();
- return newMs;
- }
- public Object plugin(Object target) {
- return Plugin.wrap(target, this);
- }
- public void setProperties(Properties properties) {
- String dialectClass = new PropertiesHelper(properties).getRequiredString("dialectClass");
- try {
- dialect = (Dialect)Class.forName(dialectClass).newInstance();
- } catch (Exception e) {
- throw new RuntimeException("cannot create dialect instance by dialectClass:"+dialectClass,e);
- }
- System.out.println(OffsetLimitInterceptor.class.getSimpleName()+".dialect="+dialectClass);
- }
- public static class BoundSqlSqlSource implements SqlSource {
- BoundSql boundSql;
- public BoundSqlSqlSource(BoundSql boundSql) {
- this.boundSql = boundSql;
- }
- public BoundSql getBoundSql(Object parameterObject) {
- return boundSql;
- }
- }
- }
2.ibatis3配置文件内容:
- <configuration>
- <plugins>
- <!-- 指定数据库分页方言Dialect, 其它方言:OracleDialect,SQLServerDialect,SybaseDialect,DB2Dialect,PostgreSQLDialect,MySQLDialect,DerbyDialect-->
- <plugin interceptor="cn.org.rapid_framework.ibatis3.plugin.OffsetLimitInterceptor">
- <property name="dialectClass" value="cn.org.rapid_framework.jdbc.dialect.MySQLDialect"/>
- </plugin>
- </plugins>
- <environments default="development">
- <environment id="development">
- <transactionManager type="JDBC" />
- <dataSource type="POOLED">
- <property name="driver" value="com.mysql.jdbc.Driver" />
- <property name="url" value="jdbc:mysql://localhost:3306/test" />
- <property name="username" value="root" />
- <property name="password" value="123456" />
- </dataSource>
- </environment>
- </environments>
- <mappers>
- <mapper resource="com/company/project/model/mapper/BlogMapper.xml" />
- </mappers>
- </configuration>
3.MysqlDialect实现
- public class MySQLDialect extends Dialect{
- public boolean supportsLimitOffset(){
- return true;
- }
- public boolean supportsLimit() {
- return true;
- }
- public String getLimitString(String sql, int offset, int limit) {
- return getLimitString(sql,offset,String.valueOf(offset),limit,String.valueOf(limit));
- }
- public String getLimitString(String sql, int offset,String offsetPlaceholder, int limit, String limitPlaceholder) {
- if (offset > 0) {
- return sql + " limit "+offsetPlaceholder+","+limitPlaceholder;
- } else {
- return sql + " limit "+limitPlaceholder;
- }
- }
- }
其它数据库的Dialect实现请查看:
4.分页使用
直接调用SqlSession.selectList(statement, parameter, new RowBounds(offset,limit))即可使用物理分页
5.存在的问题
现不是使用占位符的方式(即不是“limit ?,?”而是使用“limit 8,20”)使用分页
完整代码可以查看即将发布的rapid-framework 3.0的ibatis3插件
备注:本项目中扩展方言的目的不再于数据库的移植性上,目的仅仅为分页服务,其他方面的暂时没有考虑方言的问题。
在Ibatis中将Dialect注入到SqlExecutor中在数据库的数据操作中使用。
定义方言的接口:
package easyway.tbs.framework.app.pagination;
publicinterface Dialect {
/**
* 是否支持分页
*/
publicboolean supportsLimit();
/**
* 拼装sql语句
*/
public String getLimitString(String sql, int offset, int limit);
publicboolean supportsLimitOffset();
}
针对方言的接口的不同数据库的实现:
针对MySql版本:
package easyway.tbs.framework.app.pagination;
publicclass MySQLDialect implements Dialect {
publicboolean supportsLimitOffset() {
returntrue;
}
publicboolean supportsLimit() {
returntrue;
}
public String getLimitString(String sql, int offset, int limit) {
if (offset > 0) {
System.out.println(sql + " limit " + offset + "," + limit);
return sql + " limit " + offset + "," + limit;
} else {
System.out.println(sql + " limit " + limit);
return sql + " limit " + limit;
}
}
}
针对Oracle:
package easyway.tbs.framework.app.pagination;
publicclass OracleDialect implements Dialect {
publicboolean supportsLimit() {
returntrue;
}
publicboolean supportsLimitOffset() {
returntrue;
}
public String getLimitString(String sql, int offset, int limit) {
sql = sql.trim();
boolean isForUpdate = false;
if (sql.toLowerCase().endsWith(" for update")) {
sql = sql.substring(0, sql.length() - 11);
isForUpdate = true;
}
StringBuffer pagingSelect = new StringBuffer(sql.length() + 100);
if (offset > 0) {
pagingSelect.append("select * from ( select row_.*, rownum rownum_ from ( ");
} else {
pagingSelect.append("select * from ( ");
}
pagingSelect.append(sql);
if (offset > 0) {
int end = offset + limit;
pagingSelect.append(" ) row_ ) where rownum_ <= " + end
+ " and rownum_ > " + offset);
} else {
pagingSelect.append(" ) where rownum <= " + limit);
}
if (isForUpdate) {
pagingSelect.append(" for update");
}
System.out.println(pagingSelect.toString());
return pagingSelect.toString();
}
}
项目建议:实现此方言的定义建议借鉴Spring和Hibernate中各种数据库的方言的封装(Hibernate移植性,可扩展性相对较好)。Dialect主要注册各种数据库特有的函数和各种数据库特有的sql操作如数据库物理分页等。
相关推荐
**iBatis框架**:iBatis是另一种轻量级的ORM框架,它与Hibernate不同,不完全是一个全自动的解决方案。iBatis允许开发者编写自定义的SQL,提供更多的控制权。在SSH2框架中,iBatis可以与Spring集成,通过...
Ibatis 是一个优秀的、开源的 Java 数据访问框架,它提供了 SQL 映射功能,可以将 SQL 语句与 Java 代码分离,从而实现更灵活的数据访问。Ibatis 主要解决的问题是简化 JDBC 的繁琐工作,通过 XML 或注解方式配置 ...
iBatis,另一方面,是一个SQL映射框架。它允许开发者编写自定义的SQL语句,将结果集映射到Java对象,避免了JDBC的繁琐操作。iBatis的核心是SqlMapConfig.xml配置文件,其中定义了数据源、事务管理器以及SQL映射文件...
iBatis框架则提供了一种将SQL语句与Java代码分离的方式,通过XML配置文件或注解来定义SQL语句,使得数据库操作更易于维护和扩展。它的核心概念有SqlSessionFactory、SqlSession和Mapper。SqlSessionFactory创建...
在这个自己编写的Ibatis框架实现中,我们可以看到类似的思路和核心功能。 1. **XML配置文件**: XML文件是Ibatis框架的核心,它存储了SQL语句、参数映射以及结果集映射等信息。在`sqlmap`目录下,可能包含了多个...
Spring MVC 作为Spring框架的一部分,是用于构建前端控制器(DispatcherServlet)的MVC设计模式实现,而iBatis则是一个轻量级的持久层框架,它提供SQL映射功能,简化了数据库操作。 1. **Spring MVC**: - **核心...
这个“struts2+spring+iBatis框架包”集成了这三个框架,使得开发者能够快速构建基于MVC(Model-View-Controller)模式的Web应用。 Struts2作为MVC框架,负责处理应用程序的控制逻辑。它通过Action类和配置文件定义...
3. iBatis框架:iBatis是一个SQL映射框架,它将SQL语句与Java代码分离,提供了一种灵活的方式来执行数据库操作。通过XML或注解方式定义SQL语句,iBatis可以动态地生成和执行SQL,避免了传统的JDBC代码繁琐的模板式...
总的来说,Spring Struts iBatis框架组合提供了一个强大而灵活的开发环境,适合开发大型、复杂的企业级应用。通过熟练掌握这三个框架,开发者可以构建出符合现代开发标准的、易于维护的系统。在学习和使用过程中,...
Struts框架提供了一系列的拦截器(Interceptor),使得开发者可以方便地添加业务逻辑和数据验证,同时,它还支持国际化和主题等功能,增强了应用的可扩展性和可维护性。 **Spring框架**则是一个全面的后端应用管理...
《iBATIS框架源码剖析》这本书,作者任钢深入探讨了iBATIS的核心机制和实现原理,以下是对该框架的一些关键知识点的详细阐述。 1. **动态SQL**:iBATIS的一大亮点是它的动态SQL功能。通过在XML映射文件中使用条件...
iBatis框架: 1. **SQL映射**:iBatis的核心是XML或注解方式定义的SQL语句,将SQL与Java代码分离,增强了可读性和维护性。 2. **动态SQL**:iBatis支持动态SQL,允许在SQL语句中根据条件动态生成不同的查询,提高...
在J2EE开发中,经常将iBATIS与其他框架结合使用,如Struts(一个MVC框架)和Hibernate(一个ORM框架)。Struts处理用户的请求和响应,而iBATIS则作为数据访问层,负责与数据库的交互。Hibernate则可以用于更简单的...
通过对这些组件的源码阅读,我们可以学习到如何设计一个高效、可扩展的持久层框架,同时也可以借鉴Ibatis的优秀实践,比如它的动态SQL支持、缓存机制以及事务管理等。此外,源码中的异常处理、日志记录、单元测试等...
本篇文章将深入探讨如何使用Ibatis框架实现对`student`表的增删改查操作,包括模糊查询以及利用序列自动生成主键。 首先,我们来理解Ibatis的基本概念。Ibatis是由Apache基金会维护的一个开源项目,它是一个SQL映射...
"struts2+spring+ibatis框架实例"是基于这三个框架的一个集成示例,它利用MySQL作为后端数据库。下面我们将详细探讨这三个框架及其在实际项目中的应用。 **Struts2框架** Struts2是MVC(模型-视图-控制器)架构模式...
iBatis 是一个 SQL 映射框架,它允许开发者将 SQL 查询直接写入 XML 文件或注解中,从而与 Java 代码解耦。这使得数据库查询更灵活,避免了传统的 JDBC 编程的繁琐。iBatis 可以与 ORM(对象关系映射)框架如 ...