`
zxlaiye
  • 浏览: 96091 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

MyBatis笔记

    博客分类:
  • java
阅读更多
●悲催的Bug
与spring结合时,org.mybatis.spring.mapper.MapperScannerConfigurer不支持 <context:property-placeholder />
(mybatis 3.1.1, mybatis-spring 1.1.1)

●结合Spring的配置
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/>
    <property name="typeAliasesPackage" value="xx.xx.xx.entity" />
    <property name="plugins">
       <list>
          <!-- 配置自己实现的分页插件 -->
          <bean class="xx.xx.xx.mybatis.PagingPlugin">
            <property name="dialect" value="mysql"/>
          </bean>
        </list>
    </property>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <property name="basePackage" value="xx.xx.xx.dao" />
</bean>


●传递多个参数
//使用@Param来注解,例如:
List queryXX(@Param("arg1") int arg1, @Param("arg2") String arg2);


●分页插件。 有以下缺点:
  • 暂时只实现mysql和oracle
  • oracle未测试
  • 未考虑取总数的性能
  • 未考虑排序
  • 查找的结果集未能自动放到分页对象(Page)中

Page.java
public class Page{
	private int limit = 20; //每页显示条数
	private int start = 0;  //起始行号
	private long total = -1; //总数
	private List result = new ArrayList(); //结果集
	//---- 省略get set ----//
}

PagingPlugin.java
/**
 * Mybatis的分页查询插件,通过拦截StatementHandler的prepare方法来实现。
 * 只有在参数列表中包括Page类型的参数时才进行分页查询。
 * 在多参数的情况下,只对第一个Page类型的参数生效。
 * 另外,在参数列表中,Page类型的参数无需用@Param来标注
 * @author linzongxue 2012-1-16(修改)
 *
 */
@Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class})}) 
public class PagingPlugin implements Interceptor {
	private String dialect;

	@SuppressWarnings("unchecked")
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		if(!(invocation.getTarget() instanceof RoutingStatementHandler)) 
			return invocation.proceed();
		
		RoutingStatementHandler statementHandler = (RoutingStatementHandler)invocation.getTarget();
        BoundSql boundSql = statementHandler.getBoundSql();
        //分析是否含有分页参数,如果没有则不是分页查询
        //注意:在多参数的情况下,只处理第一个分页参数
        Page page = null;
        Object paramObj = boundSql.getParameterObject();
        if (paramObj instanceof Page){ //只有一个参数的情况
        	page = (Page)paramObj;
        }
        else if (paramObj instanceof Map){ //多参数的情况,找到第一个Page的参数
        	for (Map.Entry<String, Object> e : ((Map<String, Object>)paramObj).entrySet()){
        		if (e.getValue() instanceof Page){
        			page = (Page)e.getValue();
        			break;
        		}
        	}
        }
        
        if (page == null) return invocation.proceed();
        
        //查找总记录数,并设置Page的相关参数
        long total = this.getTotal(invocation);
        page.setTotal(total);
        //生成分页SQL
        String pageSql = generatePageSql(boundSql.getSql(), page);
        //强制修改最终要执行的SQL
        setFieldValue(boundSql, "sql", pageSql);
		return invocation.proceed();
	}
	
	/**
	 * 获取记录总数
	 */
	@SuppressWarnings("unchecked")
	private long getTotal(Invocation invocation) throws Exception{
		RoutingStatementHandler statementHandler = (RoutingStatementHandler)invocation.getTarget();
        BoundSql boundSql = statementHandler.getBoundSql();
		/*
		 * 为了设置查找总数SQL的参数,必须借助MappedStatement、Configuration等这些类,
		 * 但statementHandler并没有开放相应的API,所以只好用反射来强行获取。
		 */
        BaseStatementHandler delegate = (BaseStatementHandler)getFieldValue(statementHandler, "delegate");
        MappedStatement mappedStatement = (MappedStatement)getFieldValue(delegate, "mappedStatement");
        Configuration configuration = mappedStatement.getConfiguration();
        TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        Object param = boundSql.getParameterObject();
        MetaObject metaObject = configuration.newMetaObject(param);
        
		long total = 0;
        String sql = boundSql.getSql();
        String countSql = "select count(1) from (" + sql+ ") as t"; //记录统计  (mysql要求必须添加 最后的as t)
        try{
            Connection conn = (Connection)invocation.getArgs()[0];
	        PreparedStatement ps = conn.prepareStatement(countSql);
	        int i = 1;
	        for (ParameterMapping pm : boundSql.getParameterMappings()) {
	        	Object value = null;
	        	String propertyName = pm.getProperty();
	            PropertyTokenizer prop = new PropertyTokenizer(propertyName);
	        	if (typeHandlerRegistry.hasTypeHandler(param.getClass())) {
	                value = param;  
	            }
	            else if (boundSql.hasAdditionalParameter(propertyName)) {
	                value = boundSql.getAdditionalParameter(propertyName);  
	            }
	            else if (propertyName.startsWith(ForEachSqlNode.ITEM_PREFIX)&& boundSql.hasAdditionalParameter(prop.getName())) {  
	                value = boundSql.getAdditionalParameter(prop.getName());
	                if (value != null) {  
	                    value = configuration.newMetaObject(value).getValue(propertyName.substring(prop.getName().length()));  
	                }
	            } else {  
	                value = metaObject.getValue(propertyName);
	            }

	        	pm.getTypeHandler().setParameter(ps, i++, value, pm.getJdbcType());
	        }
	        ResultSet rs = ps.executeQuery();
	        rs.next();
	        total = rs.getLong(1);
	        rs.close();  
	        ps.close();
        }
        catch (Exception e){
        	throw new RuntimeException("分页查询无法获取总记录数", e);
        }
        return total;
	}
	
	/**
	 * 生成分页SQL
	 */
	private String generatePageSql(String sql, Page page){
		StringBuilder pageSql = new StringBuilder();
        if("mysql".equals(dialect)){
            pageSql.append(sql);
            pageSql.append(" limit ").append(page.getStart()).append(",").append(page.getLimit());  
        }
        else if("oracle".equals(dialect)){
            pageSql.append("select * from (select t.*, ROWNUM num from (")
            	.append(sql).append(") as t where ROWNUM <= ")
	            .append(page.getStart() + page.getLimit())
	            .append(") where num > ").append(page.getStart());
        }
        else{
        	throw new RuntimeException("分页插件还不支持数据库类型:" + dialect);
        }
        return pageSql.toString();
    }
	
	/**
	 * 用反射取对象的属性值
	 */
	private Object getFieldValue(Object obj, String fieldName) throws Exception{        
        for (Class<?> superClass = obj.getClass(); superClass != Object.class; superClass = superClass.getSuperclass()) {
			try{
	        	Field field = superClass.getDeclaredField(fieldName);
				field.setAccessible(true);
				return field.get(obj);
			}
			catch(Exception e){}
		}
        return null;
	}

	/**
	 * 用反射设置对象的属性值
	 */
	private void setFieldValue(Object obj, String fieldName, Object fieldValue) throws Exception{
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, fieldValue);
	}
	
	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}
	
	@Override
	public void setProperties(Properties props) {
		
	}
	
	public void setDialect(String dialect){
		this.dialect = dialect.toLowerCase();
	}
	
	public String getDialect(){
		return this.dialect;
	}
}
分享到:
评论
2 楼 estn_h 2013-03-27  
1 楼 zouhuiqun 2012-04-05  
 

相关推荐

    mybatis笔记.zip

    MyBatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC ...通过阅读这份“mybatis笔记”,你可以更深入地了解如何使用 MyBatis 进行数据访问,提升你的开发效率。

    mybatis笔记

    mybatis笔记

    传智播客mybatis笔记

    ### MyBatis基础知识与应用详解 #### 一、MyBatis概述 MyBatis是一个优秀的持久层框架,它简化了Java应用程序与关系型数据库交互的复杂性。原名为iBatis,起初作为Apache的一个开源项目,后于2010年迁移到Google ...

    【狂神说】MyBatis笔记(全).zip

    【狂神说】MyBatis笔记,全部,完整,格式为md

    狂神说Mybatis笔记,md格式的

    本笔记基于狂神说的讲解,将全面解析Mybatis的核心概念、使用方法以及实战技巧。 1. **Mybatis概述** Mybatis 是一个轻量级的ORM(Object-Relational Mapping)框架,它允许开发者用XML或注解来配置和映射原生信息...

    mybatis笔记一对应代码

    【标题】"mybatis笔记一对应代码"涵盖了MyBatis框架的基础使用和核心概念,这是一份关于如何在实际开发中应用MyBatis的详细笔记。MyBatis是一款优秀的持久层框架,它允许开发者将SQL语句直接写在XML配置文件或者注解...

    吴天雄-Mybatis笔记.doc

    Mybatis 是一款流行的开源持久层框架,源自 iBatis,主要功能是对 JDBC 的封装,提供半自动化的 ORM(对象关系映射)支持。这个框架允许开发者编写 SQL 命令来处理数据库操作,同时减轻了编写传统 JDBC 代码的负担。...

    MyBatis笔记.md

    MyBatis笔记.md

    MyBatis笔记(狂神说java)

    【MyBatis笔记(狂神说java)】是针对Java开发者的一份详尽的MyBatis学习资料,它深入浅出地介绍了MyBatis这一流行持久层框架的使用与实践。MyBatis是一个轻量级的Java ORM(对象关系映射)框架,它解决了在Java开发...

    mybatis笔记(二)

    【标题】"mybatis笔记(二)" 涉及到的是MyBatis框架在使用时的一些深入探讨,尤其是与MySQL数据库连接以及Java 1.7版本的兼容性问题。MyBatis是一个优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。这个...

    mybatis笔记.rar

    在提供的压缩包`02mybatis笔记`中,很可能是对MyBatis的进一步学习和实践总结,包括了如何创建和使用Mapper,如何编写动态SQL,以及如何处理输入输出参数等内容。这份笔记对于初学者来说是非常有价值的参考资料,...

    狂神说java MyBatis 笔记

    【狂神说java MyBatis 笔记】 MyBatis 是一个优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解...

    java-Mybatis笔记.docx

    "java-Mybatis笔记.docx" 从标题和描述中,我们可以看出这篇笔记是关于java和Mybatis的相关知识点,下面我们将对这些知识点进行详细的解释和分析。 首先,笔记中提到使用jdbc操作数据库的痛苦经历,包括加载驱动、...

    Mybatis笔记.pages

    Mybatis笔记.pages

    MyBatis笔记珍藏版

    MyBatis笔记珍藏版 很容易学的 请用oneNote查看 相信对你的帮助不是一个积分就能比拟的

Global site tag (gtag.js) - Google Analytics