<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>绿色记忆 &#187; Mybatis</title>
	<atom:link href="https://blog.gmem.cc/tag/mybatis/feed" rel="self" type="application/rss+xml" />
	<link>https://blog.gmem.cc</link>
	<description></description>
	<lastBuildDate>Tue, 21 Apr 2026 10:40:56 +0000</lastBuildDate>
	<language>en-US</language>
		<sy:updatePeriod>hourly</sy:updatePeriod>
		<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.9.14</generator>
	<item>
		<title>Mybatis学习笔记</title>
		<link>https://blog.gmem.cc/mybatis-study-note</link>
		<comments>https://blog.gmem.cc/mybatis-study-note#comments</comments>
		<pubDate>Thu, 01 Mar 2012 00:50:01 +0000</pubDate>
		<dc:creator><![CDATA[Alex]]></dc:creator>
				<category><![CDATA[Java]]></category>
		<category><![CDATA[Mybatis]]></category>
		<category><![CDATA[学习笔记]]></category>

		<guid isPermaLink="false">https://blog.gmem.cc/?p=13110</guid>
		<description><![CDATA[<p>简介 Mybatis是一个持久化层的Java框架，但是它并不是完整的ORM方案，它是以SQL为中心的，更像JOOQ而不是Hibernate。这意味着，如果使用Mybatis，你在某种程度上需要抛弃OO的领域模型设计（以对象为中心），转而以数据库表为中心进行设计。 Mybatis的特色是可以定制SQL语句（甚至是存储过程），这让你有很好的机会执行SQL优化，但很容易丢失数据库方面的可移植性。能够定制SQL，也使Mybatis能够很好的支持遗留数据库。 HelloWorld 本章结合一个非常简单的例子，介绍Mybatis的基本组件和基础用法。 Maven依赖 [crayon-69e893981cd5c783386398/] SqlSessionFactory  每个Mybatis应用都是以一个SqlSessionFactory实例为中心的，你可以基于XML或者Java的方式，提供SqlSessionFactory的初始化参数。 SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在，没有任何理由对它进行清除或重建。 基于XML初始化 Java代码： [crayon-69e893981cd63205956213/] 对应主配置文件： [crayon-69e893981cd66592331698/] 基于Java代码初始化 [crayon-69e893981cd69651639395/] 映射器 上面两种初始化SqlSessionFactory的方式中，分别引用了UserMapper.xml文件、USerMapper类，这两个文件就是所谓的映射器（Mapper）。 映射器指定了SQL语句和Java类型之间的映射关系。Mybatis支持XML文件、Java注解两种映射器配置方式。 XML映射器 <a class="read-more" href="https://blog.gmem.cc/mybatis-study-note">[...]</a></p>
<p>The post <a rel="nofollow" href="https://blog.gmem.cc/mybatis-study-note">Mybatis学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></description>
				<content:encoded><![CDATA[<div class="wri_content_clear_both"><div class="blog_h1"><span class="graybg">简介</span></div>
<p>Mybatis是一个持久化层的Java框架，但是它并不是完整的ORM方案，它是<span style="background-color: #c0c0c0;">以SQL为中心</span>的，更像JOOQ而不是Hibernate。这意味着，如果使用Mybatis，你在某种程度上需要抛弃OO的领域模型设计（以对象为中心），转而以数据库表为中心进行设计。</p>
<p>Mybatis的特色是可以<span style="background-color: #c0c0c0;">定制SQL语句</span>（甚至是存储过程），这让你有很好的机会执行SQL优化，但很容易丢失数据库方面的可移植性。能够定制SQL，也使Mybatis能够很好的支持<span style="background-color: #c0c0c0;">遗留数据库</span>。</p>
<div class="blog_h1"><span class="graybg">HelloWorld<br /></span></div>
<p>本章结合一个非常简单的例子，介绍Mybatis的基本组件和基础用法。</p>
<div class="blog_h2"><span class="graybg">Maven依赖</span></div>
<pre class="crayon-plain-tag">&lt;!-- Mybatis --&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.mybatis&lt;/groupId&gt;
    &lt;artifactId&gt;mybatis&lt;/artifactId&gt;
    &lt;version&gt;x.x.x&lt;/version&gt;
&lt;/dependency&gt;
&lt;!-- 数据库驱动 --&gt;
&lt;dependency&gt;
    &lt;groupId&gt;mysql&lt;/groupId&gt;
    &lt;artifactId&gt;mysql-connector-java&lt;/artifactId&gt;
    &lt;version&gt;5.1.36&lt;/version&gt;
&lt;/dependency&gt;</pre>
<div class="blog_h2"><span class="graybg">SqlSessionFactory</span> </div>
<p>每个Mybatis应用都是以一个SqlSessionFactory实例为中心的，你可以基于XML或者Java的方式，提供SqlSessionFactory的初始化参数。</p>
<p>SqlSessionFactory 一旦被创建就应该在应用的运行期间一直存在，没有任何理由对它进行清除或重建。</p>
<div class="blog_h3"><span class="graybg">基于XML初始化</span></div>
<p>Java代码：</p>
<pre class="crayon-plain-tag">InputStream is = Resources.getResourceAsStream( "cc/gmem/study/ssm/mybatis/mybatis-config.xml" );
SqlSessionFactory sf = new SqlSessionFactoryBuilder().build( is ); </pre>
<p>对应主配置文件：</p>
<pre class="crayon-plain-tag">&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
&lt;!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"&gt;
&lt;configuration&gt;
    &lt;!-- 事务管理和连接池配置 --&gt;
    &lt;environments default="development"&gt;
        &lt;environment id="development"&gt;
            &lt;transactionManager type="JDBC"/&gt;
            &lt;dataSource type="POOLED"&gt;
                &lt;property name="driver" value="com.mysql.jdbc.Driver"/&gt;
                &lt;property name="url" value="jdbc:mysql://localhost:3306/test"/&gt;
                &lt;property name="username" value="root"/&gt;
                &lt;property name="password" value="root"/&gt;
            &lt;/dataSource&gt;
        &lt;/environment&gt;
    &lt;/environments&gt;
    &lt;!-- UserMapper.xml是一个映射器，包含SQL代码与Java类之间的映射信息 --&gt;
    &lt;mappers&gt;
        &lt;mapper resource="cc/gmem/study/ssm/entity/UserMapper.xml"/&gt;
    &lt;/mappers&gt;
&lt;/configuration&gt;</pre>
<div class="blog_h3"><span class="graybg">基于Java代码初始化</span></div>
<pre class="crayon-plain-tag">DataSource dataSource = new PooledDataSource(
        "com.mysql.jdbc.Driver",
        "jdbc:mysql://localhost:3306/test",
        "root", "root"
);
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment( "development", transactionFactory, dataSource );
Configuration configuration = new Configuration( environment );
// UserMapper也是映射器，它是一个配置了注解的Java类
configuration.addMapper( UserMapper.class );
SqlSessionFactory sf = new SqlSessionFactoryBuilder().build( configuration );</pre>
<div class="blog_h2"><span class="graybg">映射器</span></div>
<p>上面两种初始化SqlSessionFactory的方式中，分别引用了UserMapper.xml文件、USerMapper类，这两个文件就是所谓的映射器（Mapper）。</p>
<p>映射器指定了SQL语句和Java类型之间的映射关系。Mybatis支持XML文件、Java注解两种映射器配置方式。</p>
<div class="blog_h3"><span class="graybg">XML映射器</span></div>
<p>对应映射器文件，被主配置文件引用：</p>
<pre class="crayon-plain-tag">&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
&lt;!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"  "http://mybatis.org/dtd/mybatis-3-mapper.dtd"&gt;
&lt;!-- namespace为映射语句提供统一的命名空间，在项目复杂的时候避免出现重复的映射语句id --&gt;
&lt;mapper namespace="cc.gmem.study.ssm.entity.UserMapper"&gt;
    &lt;select id="selectUser" resultType="cc.gmem.study.ssm.entity.User"&gt;
        select * from User where id = #{id}
    &lt;/select&gt;
&lt;/mapper&gt; </pre>
<div class="blog_h3"><span class="graybg">注解映射器</span></div>
<pre class="crayon-plain-tag">import org.apache.ibatis.annotations.Select;

public interface UserMapper {
    @Select( "SELECT * FROM User WHERE id = #{id}" )
    User selectUser( int id );
} </pre>
<p>尽管基于注解的配置在各大框架的用户中都越来越流行，但是由于注解自身的限制，对于很多Mybatis的高级映射，XML映射方式是必须的。</p>
<p>如果存在和UserMapper.class在<span style="background-color: #c0c0c0;">类路径上一致</span>的UserMapper.xml文件，Mybatis会自动将其加载进来作为映射补充。如果两者存在重复映射语句，Mybatis会报错。</p>
<div class="blog_h2"><span class="graybg">SqlSession</span> </div>
<p>该类包含了面向数据库执行 SQL 命令所需的所有方法，日常工作主要通过它进行。</p>
<p><span style="background-color: #c0c0c0;">每个线程</span>都应该有它自己的 SqlSession 实例。SqlSession 的实例<span style="background-color: #c0c0c0;">不是线程安全</span>的，不能被共享。</p>
<div class="blog_h3"><span class="graybg">基于XML映射器的用法</span></div>
<pre class="crayon-plain-tag">SqlSession session = sf.openSession();
try {
    // 参数由：名字空间 + 映射语句构成
    User user = session.selectOne( "cc.gmem.study.ssm.entity.UserMapper.selectUser", 1 );
} finally {
    session.close();
}</pre>
<div class="blog_h3"><span class="graybg">基于注解映射器的用法</span></div>
<pre class="crayon-plain-tag">SqlSession session = sf.openSession();
try {
    // 映射器类的实例，其生命周期一般限定在一个方法内部
    UserMapper mapper = session.getMapper( UserMapper.class );
    User user = mapper.selectUser( 1 );
} finally {
    session.close();
}</pre>
<p>可以看到，对于简单语句来说，注解使代码显得更加简洁。但是Java注解对于稍微复杂的语句就会力不从心并且会显得更加混乱，因此业务复杂的话，最好使用XML风格的映射器。</p>
<div class="blog_h1"><span class="graybg">主配置文件</span></div>
<p>前面提到的mybatis-config.xml，就是Mybatis主配置文件，定制此文件可以在很大程度上改变Mybatis的行为。该配置文件的根元素是configuration。</p>
<div class="blog_h2"><span class="graybg">properties</span></div>
<p>可以定义一系列属性，让配置文件其它地方基于<pre class="crayon-plain-tag">${prop}</pre> 的语法引用。你可以引用外部的Java属性文件：</p>
<pre class="crayon-plain-tag">&lt;properties resource="cc/gmem/study/ssm/config.properties" /&gt;</pre>
<p>也可以使用子元素直接指定属性：</p>
<pre class="crayon-plain-tag">&lt;properties&gt;
  &lt;property name="username" value="root"/&gt;
&lt;/properties&gt;</pre>
<p>甚至两种方式混合使用。 属性优先级由高到低：</p>
<ol>
<li>SqlSessionFactoryBuilder.build()中传递的属性</li>
<li>通过resource指定的Java属性文件中的属性</li>
<li>property子元素指定的属性</li>
</ol>
<div class="blog_h2"><span class="graybg">settings</span></div>
<p>该元素的setting子元素可以改变Mybatis的行为，在子元素setting中，可以指定以下项：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 30%; text-align: center;">设置项</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>cacheEnabled</td>
<td>Boolean=true，所有映射器中配置的缓存的全局开关</td>
</tr>
<tr>
<td>lazyLoadingEnabled</td>
<td>Boolean=false，如果true，所有关联对象都会延迟加载。 特定关联关系中可通过<span style="background-color: #c0c0c0;">设置fetchType属性</span>来覆盖该项的开关状态</td>
</tr>
<tr>
<td>aggressiveLazyLoading</td>
<td>Boolean=true，如果true，对任意延迟属性的调用会使带有延迟加载属性的对象（该属性引用的对象）完整（激进）加载；反之，每种属性将会按需加载</td>
</tr>
<tr>
<td>multipleResultSetsEnabled</td>
<td>Boolean=true，是否允许单一语句返回多结果集（需要兼容驱动）</td>
</tr>
<tr>
<td>useColumnLabel</td>
<td>Boolean=true，使用列标签代替列名。不同的驱动在这方面会有不同的表现</td>
</tr>
<tr>
<td>useGeneratedKeys</td>
<td>Boolean=false，如果true，强制自动生成主键</td>
</tr>
<tr>
<td>defaultExecutorType</td>
<td>
<p>Enum(SIMPLE|REUSE|BATCH)=SIMPLE。设置默认的SQL执行器：</p>
<ol>
<li>SIMPLE，普通的执行器</li>
<li>REUSE，重用预处理语句</li>
<li>BATCH，重用预处理语句并执行批量更新</li>
</ol>
</td>
</tr>
<tr>
<td>defaultStatementTimeout</td>
<td>SQL执行超时的秒数</td>
</tr>
<tr>
<td>defaultFetchSize</td>
<td>给驱动提示，通知抓取数据的跳数，单个查询可以覆盖之</td>
</tr>
<tr>
<td>safeRowBoundsEnabled</td>
<td>Boolean=false，是否允许在嵌套语句中使用分页（RowBounds），如果允许设置为fasle</td>
</tr>
<tr>
<td>safeResultHandlerEnabled</td>
<td>Boolean=true，是否允许在嵌套语句中使用分页（ResultHandler），如果允许设置为fasle</td>
</tr>
<tr>
<td>mapUnderscoreToCamelCase</td>
<td>
<p>Boolean=false，是否把基于下划线的数据库列映射为驼峰式大小写</p>
<p>该设置项和自动映射机制有关，Mybatis的默认映射方式是：列名、属性名不区分大小写的想等，则匹配</p>
</td>
</tr>
<tr>
<td>localCacheScope</td>
<td>
<p>Enum(SESSION | STATEMENT)=SESSION。Mybatis使用本地缓存机制（Local Cache）来防止循环引用、加速重复嵌套查询：</p>
<ol>
<li>SESSION，会话中所有语句共享缓存，类似于Hibernate一级缓存</li>
<li>STATEMENT，仅单个语句内部使用缓存</li>
</ol>
</td>
</tr>
<tr>
<td>lazyLoadTriggerMethods</td>
<td>Enum(equals|clone|hashCode|toString)，逗号分隔。哪些方法会触发对象的延迟加载</td>
</tr>
<tr>
<td>defaultScriptingLanguage</td>
<td>指定动态SQL生成使用的默认语言</td>
</tr>
<tr>
<td>callSettersOnNulls</td>
<td>Boolean=false，当结果集中值为NULL时，是否调用实体的setter或者Map的Put</td>
</tr>
<tr>
<td>logImpl</td>
<td>Enum(SLF4J | LOG4J|...)指定Mybatis使用的日志实现，默认自动查找</td>
</tr>
<tr>
<td>proxyFactory</td>
<td>Enum(JAVASSIST|CGLIB)=JAVASSIS，创建延迟加载对象时使用的代理库</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">typeAliases</span></div>
<p>该参数仅用于XML配置风格，其子元素typeAlias为某个Java类设置别名：</p>
<pre class="crayon-plain-tag">&lt;typeAliases&gt;
  &lt;typeAlias alias="User" type="cc.gmem.study.entity.User"/&gt;
&lt;/typeAliases&gt;</pre>
<p>或者指定Java类所在的包名，这样，包内所有Java类的Base name自动别设置为别名：</p>
<pre class="crayon-plain-tag">&lt;typeAliases&gt;
    &lt;package name="cc.gmem.study.ssm.entity"/&gt;
&lt;/typeAliases&gt;</pre>
<p>你可以在映射器配置中，使用别名来引用一个Java类，别名更加短小。</p>
<div class="blog_h3"><span class="graybg">内置别名</span></div>
<p>Mybatis为常用的JDK内置类型已经建好了大小写无关的别名，例如：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="text-align: center;">别名</td>
<td style="text-align: center;">Java类型</td>
</tr>
</thead>
<tbody>
<tr>
<td>_byte</td>
<td>byte</td>
</tr>
<tr>
<td>byte</td>
<td>Byte</td>
</tr>
<tr>
<td>decimal</td>
<td>BigDecimal</td>
</tr>
<tr>
<td>object</td>
<td>Object</td>
</tr>
<tr>
<td>collection</td>
<td>Collection</td>
</tr>
<tr>
<td>map</td>
<td>Map</td>
</tr>
<tr>
<td>hashmap</td>
<td>HashMap</td>
</tr>
<tr>
<td>list</td>
<td>List</td>
</tr>
<tr>
<td>arraylist</td>
<td>ArrayList</td>
</tr>
<tr>
<td>iterator</td>
<td>Iterator</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">typeHandlers</span></div>
<p>该元素的typeHandler子元素用于注册自定义的类型处理器。</p>
<p>类型处理器用于执行类型转换。无论在Mybatis在预处理语句中设置一个值时，还是在它从结果集中获取一个值时，都会调用类型处理器在JDBC类型和Java类型之间进行适当的转换。类型处理器的职责类似于Hibernate的类型系统。</p>
<p>Mybatis已经内置了常用的类型处理器，除非你需要定制自己的类型处理器，否则不需要配置该元素。</p>
<div class="blog_h3"><span class="graybg">自定义类型处理器</span></div>
<p>若要定制自己的类型处理器，可以继承BaseTypeHandler类：</p>
<pre class="crayon-plain-tag">// 覆盖Mybatis内置的处理Java的String类型属性和JDBC的VARCHAR参数/结果的类型处理器
@MappedJdbcTypes( JdbcType.VARCHAR )
public class MyTypeHandler extends BaseTypeHandler&lt;String&gt; {

    @Override
    public void setNonNullParameter( PreparedStatement ps, int i, String parameter, JdbcType jdbcType ) throws SQLException {
        ps.setString( i, parameter );
    }

    @Override
    public String getNullableResult( ResultSet rs, String columnName ) throws SQLException {
        return rs.getString( columnName );
    }

    @Override
    public String getNullableResult( ResultSet rs, int columnIndex ) throws SQLException {
        return rs.getString( columnIndex );
    }

    @Override
    public String getNullableResult( CallableStatement cs, int columnIndex ) throws SQLException {
        return cs.getString( columnIndex );
    }
}</pre>
<p>然后在此设置项中注册新的类型处理器：</p>
<pre class="crayon-plain-tag">&lt;typeHandlers&gt;
    &lt;typeHandler handler="cc.gmem.mybatis.ths.MyTypeHandler"/&gt;
&lt;/typeHandlers&gt;</pre>
<p>你也可以指定包，Mybaits在包中查找多个类型处理器：</p>
<pre class="crayon-plain-tag">&lt;typeHandlers&gt;
    &lt;package name="cc.gmem.mybatis.ths"/&gt;
&lt;/typeHandlers&gt;</pre>
<div class="blog_h3"><span class="graybg">处理枚举类型</span></div>
<p>要让Mybatis支持枚举类型，可以注册EnumTypeHandler或者EnumOrdinalTypeHandler，后者默认已经注册。这两者的区别是：</p>
<ol>
<li>EnumTypeHandler，把枚举值对应的名字存入数据库</li>
<li>EnumOrdinalTypeHandler，把枚举值对应的数字存入数据库</li>
</ol>
<p>除了以全局方式来指定枚举处理方式以外，你还可以针对单个枚举设置：</p>
<pre class="crayon-plain-tag">&lt;!-- 此设置仅当Java类型是RoundingMode时生效 --&gt;
&lt;typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="java.math.RoundingMode"/&gt;</pre>
<p>甚至针对单个映射语句设置：</p>
<pre class="crayon-plain-tag">&lt;mapper namespace="org.apache.ibatis.submitted.rounding.Mapper"&gt;
    &lt;resultMap type="org.apache.ibatis.submitted.rounding.User" id="usermap"&gt;
        &lt;id column="id" property="id"/&gt;
        &lt;result column="name" property="name"/&gt;
        &lt;result column="funkyNumber" property="funkyNumber"/&gt;
        &lt;!-- 在查询时为字段指定枚举处理器 --&gt;
        &lt;result column="roundingMode" property="roundingMode" typeHandler="org.apache.ibatis.type.EnumTypeHandler"/&gt;
    &lt;/resultMap&gt;
    &lt;select id="getUser" resultMap="usermap"&gt;
        select * from Users
    &lt;/select&gt;
    &lt;!-- 在插入时为字段指定枚举处理器 --&gt;
    &lt;insert id="insert"&gt;
        insert into Users (id, roundingMode) values (
        #{id}, #{roundingMode, typeHandler=org.apache.ibatis.type.EnumTypeHandler}
        )
    &lt;/insert&gt;

&lt;/mapper&gt;</pre>
<div class="blog_h2"><span class="graybg">objectFactory</span></div>
<p>Mybatis在获取结果集后，需要依据映射器将结果集转换为Java对象，而它创建Java对象就是通过对象工厂来完成的。</p>
<p>默认的对象工厂仅仅是调用Java类的默认构造器，如果你需要修改此默认行为，可以实现自己的对象工厂，并注册：</p>
<pre class="crayon-plain-tag">&lt;objectFactory type="cc.gmem.study.ssm.DebugingObjectFactory"&gt;
    &lt;!-- 这些配置项通过工厂的setProperties传递给工厂 --&gt;
    &lt;property name="debug" value="true"/&gt;
&lt;/objectFactory&gt;</pre>
<div class="blog_h2"><span class="graybg">plugins</span></div>
<p>用于注册Mybatis插件，每个plugin子元素对应一个插件。</p>
<p>Mybatis设计了一个很简单的插件扩展机制——允许你拦截映射语句执行过程的某个点，并加入扩展逻辑。可以拦截的点包括：</p>
<ol>
<li>Executor的update, query, flushStatements, commit, rollback, getTransaction, close, isClosed方法</li>
<li>ParameterHandler的getParameterObject, setParameters方法</li>
<li>ResultSetHandler的handleResultSets, handleOutputParameters方法</li>
<li>StatementHandler的prepare, parameterize, batch, update, query方法 </li>
</ol>
<div class="blog_h3"><span class="graybg">实现拦截器</span></div>
<p>要拦截以上方法，你只需要实现拦截器接口，并添加必要的注解：</p>
<pre class="crayon-plain-tag">package cc.gmem.study.ssm.mybatis.plugins;

import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;

import java.util.Properties;

/* 指定被拦截的方法的签名 */
@Intercepts( { @Signature(
        type = Executor.class,
        method = "update",
        args = { MappedStatement.class, Object.class } )
} )
public class MyInterceptor implements Interceptor {
    private boolean debug;

    public Object intercept( Invocation invocation ) throws Throwable {
        if ( debug ) {
            logDebugInfo( invocation );
        }
        // 下面的调用执行Mybatis的默认行为
        return invocation.proceed();
    }

    public Object plugin( Object target ) {
        return Plugin.wrap( target, this );
    }

    public void setProperties( Properties properties ) {
        if ( properties.containsKey( "debug" ) ) {
            this.debug = true;
        }
    }
}</pre>
<div class="blog_h3"><span class="graybg">注册插件</span></div>
<pre class="crayon-plain-tag">&lt;plugins&gt;
    &lt;plugin interceptor="cc.gmem.study.ssm.mybatis.plugins.MyInterceptor"&gt;
        &lt;property name="debug" value="true"/&gt;
    &lt;/plugin&gt;
&lt;/plugins&gt;</pre>
<div class="blog_h2"><span class="graybg">environments</span></div>
<p>你可以在此元素下定义多个环境，并指定其中一个为默认环境。</p>
<p>所谓环境，是指一个数据源及操控它的事务管理器。通过定义多个环境，Mybatis可以快速的在不同类型的数据库、不同数据实例之间进行切换。</p>
<p>每个SqlSessionFactory只能使用一个环境，你可以使用如下签名的方法为SqlSessionFactory指定（非默认的）环境：</p>
<pre class="crayon-plain-tag">SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment);
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment,properties);</pre>
<p>单个环境的配置，看起来大概是这个样子：</p>
<pre class="crayon-plain-tag">&lt;!-- id为此环境的唯一标识  --&gt;
&lt;environment id="development"&gt;
    &lt;!-- 事务管理器配置 --&gt;
    &lt;transactionManager type="JDBC"/&gt;
    &lt;!-- 数据源配置 --&gt;
    &lt;dataSource type="POOLED" /&gt;
&lt;/environment&gt;</pre>
<div class="blog_h3"><span class="graybg">transactionManager</span></div>
<p>该元素定义某个环境使用的事务管理器，Mybatis支持两种类型的事务管理器：</p>
<ol>
<li>JDBC，直接使用JDBC API进行提交和回滚，依赖于从数据源得到的连接来管理事务</li>
<li>MANAGED，该配置让容器来管理事务的整个生命周期。默认情况下，该事务管理器会负责连接的关闭，如果需要禁止此行为，可以：<br />
<pre class="crayon-plain-tag">&lt;transactionManager type="MANAGED"&gt;
    &lt;property name="closeConnection" value="false"/&gt;
&lt;/transactionManager&gt;</pre>
</li>
</ol>
<p>当你整合Mybatis和Spring时，不需要配置事务管理器。</p>
<div class="blog_h3"><span class="graybg">数据源</span></div>
<p>Mybatis支持三种数据源：</p>
<ol>
<li>UNPOOLED，不启用连接池的简单数据源。配置项：<br />
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="width: 35%; text-align: center;">配置项</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>driver</td>
<td>JDBC 驱动的 Java 类的完全限定名</td>
</tr>
<tr>
<td>url</td>
<td>JDBC URL 地址</td>
</tr>
<tr>
<td>username</td>
<td>登录数据库的用户名</td>
</tr>
<tr>
<td>password</td>
<td>登录数据库的密码</td>
</tr>
<tr>
<td>defaultTransactionIsolationLevel</td>
<td>默认的连接事务隔离级别</td>
</tr>
<tr>
<td>driver.***</td>
<td>将***属性传递给JDBC驱动</td>
</tr>
</tbody>
</table>
</li>
<li>POOLED，使用连接池实现连接复用。除了UNPOOLED提供的配置项外，还支持：<br />
<table class=" fixed-word-wrap full-width">
<thead>
<tr>
<td style="width: 35%; text-align: center;">配置项</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>poolMaximumActiveConnections</td>
<td>任意时间可以存在的活动（也就是正在使用）连接数量，默认值：10</td>
</tr>
<tr>
<td>poolMaximumIdleConnections</td>
<td>任意时间可能存在的空闲连接数</td>
</tr>
<tr>
<td>poolMaximumCheckoutTime</td>
<td>在被强制返回之前，池中连接被拿出来使用的最大时间，默认20000ms</td>
</tr>
</tbody>
</table>
</li>
<li>JNDI，使用JavaEE容器提供的JNDI上下文来查找数据源</li>
</ol>
<div class="blog_h2"><span class="graybg">databaseIdProvider</span></div>
<p>Mybatis可以依据数据库产品的不同，来执行不同的SQL语句。一般你可以使用默认值：</p>
<pre class="crayon-plain-tag">&lt;databaseIdProvider type="DB_VENDOR" /&gt;</pre>
<p>来支持尽可能多的主流数据库。</p>
<div class="blog_h2"><span class="graybg">mappers</span></div>
<p>指定映射器的引用，即SQL映射语句所在的XML文件或者Java类的引用：</p>
<pre class="crayon-plain-tag">&lt;!-- 指定一个类路径下的XML文件作为映射器 --&gt;
&lt;mapper resource="cc/gmem/study/ssm/entity/UserMapper.xml"/&gt;
&lt;!-- 指定文件系统中的XML文件作为映射器 --&gt;
&lt;mapper url="file:///var/mappers/UserMapper.xml"/&gt;
&lt;!-- 指定一个Java接口为映射器 --&gt;
&lt;mapper class="cc.gmem.study.ssm.entity.UserMapper"/&gt;
&lt;!-- 指定一个包，包中所有所有接口被作为映射器 --&gt;
&lt;package name="cc.gmem.study.ssm.mappers"/&gt;</pre>
<div class="blog_h1"><span class="graybg">映射器配置</span></div>
<p>在使用Mybatis进行开发的过程中，映射器配置是一项主要的工作。合理使用映射器，可以减少90%以上的SQL代码。</p>
<div class="blog_h2"><span class="graybg">OGNL</span></div>
<p>原样字符串（${}）、动态SQL元素中，可以使用OGNL表达式。OGNL的基础知识和语法，请参考<a href="/struts2-study-note#ognl">Struts2学习笔记</a>。</p>
<p>在映射器配置中，OGNL上下文对象<pre class="crayon-plain-tag">#this</pre> 是一个类似于Apache Struts值栈的对象。你可以直接访问查询参数的任何属性，以及以下特殊属性：</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">特殊属性</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>_parameter</td>
<td>查询参数对象本身 </td>
</tr>
<tr>
<td>_databaseId</td>
<td>当前数据库类型，用于多数据库支持 </td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">select</span></div>
<p>这是映射器配置最常用的一个元素，用于定义一个将结果集映射到Java对象的查询语句。下面是一个简单的例子：</p>
<pre class="crayon-plain-tag">&lt;select id="selectUser" parameterType="int" resultType="hashmap"&gt;
    SELECT * FROM USER WHERE ID = #{id}
&lt;/select&gt;</pre>
<p>这个配置的含义是，定义一个标识符为selectUser的映射语句，该语句：</p>
<ol>
<li> 接受一个int型的参数</li>
<li>查询结果映射为java.util.HashMap。该Map的键是数据库<span style="background-color: #c0c0c0;">列的别名</span>，值则是结果行中的对应的值</li>
<li><pre class="crayon-plain-tag">#{id}</pre> 告知Mybatis，创建一个预编译SQL语句，并在此处设置一个位置参数，就像这样：<br />
<pre class="crayon-plain-tag">String selectUser = "SELECT * FROM USER WHERE ID=?";
PreparedStatement ps = conn.prepareStatement(selectUser);
ps.setInt(1,id); </pre>
</li>
</ol>
<div class="blog_h3"><span class="graybg">属性列表</span></div>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">属性</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>id</td>
<td>String，在当前命名空间中唯一的标识符，可以被用来引用这条语句</td>
</tr>
<tr>
<td>parameterType</td>
<td>
<p>Type/TypeAlias，传入这条语句的参数类型，可选，因为Mybatis可以基于类型处理器推断参数类型</p>
<p>如果该语句有多个参数，你可以指定参数类型为一个复合的对象类型，并在SQL语句中使用#{propName}引用对象的属性作为参数</p>
</td>
</tr>
<tr>
<td>resultType</td>
<td>
<p>Type/TypeAlias，从该语句结果集构造的Java对象的类型。对于期望返回集合类型的，这里指定的是集合元素的类型</p>
<p>不能和resultMap一起使用</p>
</td>
</tr>
<tr>
<td>resultMap</td>
<td>String，引用外部定义的结果映射规则</td>
</tr>
<tr>
<td>flushCache</td>
<td>Boolean，如果设置为true，那么该语句一旦被执行，本地缓存、二级缓存皆被清空</td>
</tr>
<tr>
<td>useCache</td>
<td>Boolean，对于SELECT语句默认为true，如果为true则该语句的结果被二级缓存</td>
</tr>
<tr>
<td>timeout</td>
<td>Number，语句执行超时的秒数</td>
</tr>
<tr>
<td>fetchSize</td>
<td>Number，提示JDBC，每次抓取数据的行数</td>
</tr>
<tr>
<td>statementType</td>
<td>
<p>Enum(TATEMENT | PREPARED | CALLABLE) = PREPARED</p>
<p>指示Mybatis创建何种类型的JDBC语句</p>
</td>
</tr>
<tr>
<td>resultSetType</td>
<td>
<p>Enum(FORWARD_ONLY|SCROLL_SENSITIVE|SCROLL_INSENSITIVE)</p>
<p>提示Mybatis创建何种类型的结果集，默认取决于驱动</p>
</td>
</tr>
<tr>
<td>resultOrdered</td>
<td>
<p>Boolean=false，仅仅针对嵌套结果SELECT语句使用</p>
</td>
</tr>
<tr>
<td>resultSets</td>
<td>
<p>仅对多结果集的情况，逗号分隔的结果集名称</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">insert|update|delete</span></div>
<p>这三类元素分别表示插入、更新、删除语句，它们的实现方式很接近：</p>
<pre class="crayon-plain-tag">&lt;insert id="insertUser"&gt;
    INSERT INTO USER (ID,NAME AGE,DOB) VALUES (#{id},#{name},#{age},#{dob})
&lt;/insert&gt;
&lt;update id="updateUser"&gt;
    UPDATE USER SET
        NAME = #{name},
        AGE = #{age},
        DOB = #{dob}
    WHERE ID = #{id}
&lt;/update&gt;
&lt;delete id="deleteUser"&gt;
    DELETE FROM USER WHERE ID = #{id}
&lt;/delete&gt;</pre>
<div class="blog_h3"><span class="graybg">属性列表</span></div>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">属性</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>id</td>
<td rowspan="5">参见select元素的属性说明</td>
</tr>
<tr>
<td>parameterType</td>
</tr>
<tr>
<td>flushCache</td>
</tr>
<tr>
<td>timeout</td>
</tr>
<tr>
<td>statementType</td>
</tr>
<tr>
<td>useGeneratedKeys</td>
<td>Boolean=false，仅针对INSERT/UPDATE语句。如果设置为true，则Mybatis会利用JDBC的getGeneratedKeys方法来取回数据库内部自动生成的列值，MySQL、MS SQL Server等数据库支持自增长方式的列值自动生成</td>
</tr>
<tr>
<td>keyProperty</td>
<td>
<p>设置为一个属性名，Mybatis将通过getGeneratedKeys的返回值，或者insert语句的selectKey子元素来设置该属性的值</p>
<p>如果你想得到多个自动生成的列，可以设置逗号分隔的多个属性</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">回填主键</span></div>
<p>插入单个对象时，你可以把数据库自动生成的主键回填给Java对象的某个属性：</p>
<pre class="crayon-plain-tag">&lt;!-- 回填给参数的id属性 --&gt;
&lt;insert id="insertUser" useGeneratedKeys="true" keyProperty="id"&gt;
    INSERT INTO USER (NAME, AGE, DOB) VALUES ( #{name}, #{age}, #{dob} )
&lt;/insert&gt;</pre>
<p>对应Java代码：</p>
<pre class="crayon-plain-tag">session = openSqlSession();
User user = new User( "Alex", 30, "1986-09-12" );
session.insert( "insertUser", user );
Assert.assertTrue( user.getId() != 0 );
session.commit();</pre>
<div class="blog_h3"><span class="graybg">selectKey</span></div>
<p>如果数据库不支持自动生成主键，你可以让Mybatis生成，并插入到数据库：</p>
<pre class="crayon-plain-tag">&lt;insert id="insertUser"&gt;
    &lt;selectKey keyProperty="id" resultType="int" order="BEFORE"&gt;
        SELECT SEQ_MINE.nextval FROM DUAL
    &lt;/selectKey&gt;
    INSERT INTO USER (ID, NAME, AGE, DOB) VALUES ( #{id}, #{name}, #{age}, #{dob} )
&lt;/insert&gt;</pre>
<p>selectKey属性说明如下：</p>
<p>statementTypeÈ</p>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">属性</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>keyProperty</td>
<td>与参数的什么属性匹配，支持逗号分隔多个值</td>
</tr>
<tr>
<td>keyColumn</td>
<td>匹配属性对应的列名称，支持逗号分隔多个值</td>
</tr>
<tr>
<td>resultType</td>
<td>属性值的类型</td>
</tr>
<tr>
<td>order</td>
<td>
<p>Enum(BEFORE|AFTER)：</p>
<ol>
<li>如果设置为BEFORE，先执行此selectKey语句，得到ID，然后执行插入语句</li>
<li>如果设置为AFTER，则先执行插入语句</li>
</ol>
</td>
</tr>
<tr>
<td>statementType</td>
<td>
<p>Enum(TATEMENT | PREPARED | CALLABLE)</p>
</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">多行插入</span></div>
<p>如果数据库支持多行插入，你可以传递一系列对象：</p>
<pre class="crayon-plain-tag">&lt;insert id="insertUsers" useGeneratedKeys="true" keyProperty="id"&gt;
    INSERT INTO USER (NAME, AGE, DOB) VALUES
    &lt;!-- 
        collection，参数对象的什么属性作为别迭代的集合
        item，为迭代元素指定变量名
        separator，分隔每个迭代项的字符串
    --&gt;
    &lt;foreach collection="users" item="item" separator=","&gt;
        ( #{item.name}, #{item.age}, #{item.dob} )
    &lt;/foreach&gt;
&lt;/insert&gt; </pre>
<p>对应Java代码：</p>
<pre class="crayon-plain-tag">List&lt;User&gt; users = new ArrayList&lt;User&gt;();
users.add( new User( "Alex", 30, "1986-09-12" ) );
users.add( new User( "Meng", 26, "1989-11-06" ) );
users.add( new User( "Cai", 1, "2014-11-27" ) );
Map&lt;String, Object&gt; params = new HashMap&lt;String, Object&gt;();
params.put( "users", users );
session.insert( "insertUsers", params );
session.commit();</pre>
<p>注意：Mybatis无法在批量插入时获得数据库自动生成的列。 </p>
<div class="blog_h2"><span class="graybg">sql</span></div>
<p>该元素用来定义可重复使用的SQL片断，它可以被包含在其它语句中。sql支持参数化：</p>
<pre class="crayon-plain-tag">&lt;sql id="userColumns"&gt;${alias}.ID, ${alias}.NAME, ${alias}.AGE, ${alias}.DOB&lt;/sql&gt;</pre>
<p>在其它语句中，你可以引用sql元素：</p>
<pre class="crayon-plain-tag">&lt;select id="selectUser" resultType="User"&gt;
    SELECT
        &lt;include refid="userColumns"&gt;
            &lt;property name="alias" value="u" /&gt;
        &lt;/include&gt;
    FROM USER u WHERE ID = #{id}
&lt;/select&gt;</pre>
<p>sql也可以引用sql元素，甚至引用者的refid都可以参数化：</p>
<pre class="crayon-plain-tag">&lt;sql id="dict"&gt;T_DICT_${basename}&lt;/sql&gt;
&lt;sql id="from"&gt;
    FROM
    &lt;include refid="${tabtype}" /&gt;
&lt;/sql&gt;

&lt;select id="selectDept"&gt;
    SELECT * 
    &lt;include refid="from"&gt;
        &lt;!-- 这个属性传递给直接引用 --&gt;
        &lt;property name="tabtype" value="dict" /&gt;
        &lt;!-- 这个属性传递给简介引用，即dict --&gt;
        &lt;property name="basename" value="DEPT" /&gt;
    &lt;/include&gt;
&lt;/select&gt;</pre>
<div class="blog_h2"><span class="graybg">参数</span></div>
<p>前面很多地方已经用到了参数，你可以为语句明确的指定参数类型（parameterType） ，例如：</p>
<pre class="crayon-plain-tag">&lt;insert id="insertUser" parameterType="User"&gt;
    INSERT INTO USER (NAME, AGE, DOB) VALUES ( #{name}, #{age}, #{dob} )
&lt;/insert&gt;</pre>
<p>这样，当调用此语句时，你传入了一个User类型的对象，则它的name、age、dob属性都会通过反射机制找到，然后传入到预编译语句之中。</p>
<div class="blog_h3"><span class="graybg">属性占位符</span></div>
<p>对于语句中的占位符，不仅可以指定一个名字，还可以指定：</p>
<ol>
<li>javaType，该属性的Java类型，除非该属性的容器对象是一个Map，一般不需要设置</li>
<li>jdbcType，该属性对应的JDBC类型，如果null被当作值传递，那么对于<span style="background-color: #c0c0c0;">所有可能为空的列，该属性都是需要</span>的</li>
<li>numericScale，对于数值类型，你可以用此字段指定小数位数</li>
<li>typeHandler，你可以为属性指定一个类型处理器</li>
<li>mode，可以设置为IN|OUT|INOUT，如果设置为OUT|INOUT，则参数对象的属性值可能会被改变</li>
</ol>
<p>例如：<pre class="crayon-plain-tag">#{property,javaType=int,jdbcType=NUMERIC,typeHandler=MyTypeHandler}</pre>  </p>
<div class="blog_h3"><span class="graybg">参数转换</span></div>
<p>通过SqlSession的API调用映射语句时，如果：</p>
<ol>
<li>传入的是一个<span style="background-color: #c0c0c0;">数组或者列表</span>，则Mybatis会自动将其转换为Map，此Map的键分别为<span style="background-color: #c0c0c0;">array、list</span></li>
<li>传入的是一个简单类型，那么属性占位符的名称可以<span style="background-color: #c0c0c0;">随意取</span></li>
</ol>
<div class="blog_h2"><span class="graybg">原样字符串</span></div>
<p>如果你想往SQL里<span style="background-color: #c0c0c0;">插入一个字面值（而不是预编译语句的位置参数）</span>，可以使用${}，例如：<pre class="crayon-plain-tag">ORDER BY ${columnName}</pre> 。columnName具体是何种，取决于上下文参数。</p>
<div class="blog_h2"><span class="graybg">结果映射</span></div>
<p>当我们指定resultType为Map或者实体类时，已经在间接的使用resultMap，只不过映射方式是Mybatis自动推导出来的：</p>
<ol>
<li>如果结果类型是Map，则把列名映射为Map的键</li>
<li>如果结果类型是实体类，则把列名映射为实体类的属性</li>
</ol>
<p>假设实体类属性和列名不是完全匹配的，你就必须使用SQL的列别名来提示Mybatis进行映射：、</p>
<pre class="crayon-plain-tag">&lt;!-- 列别名指向对象属性名 --&gt;
&lt;select id="selectUser" resultType="User"&gt;
    SELECT
        ID as "userId",
        NAME as "userName",
        AGE as "age",
        DOB as "birthday"
    FROM USER u WHERE ID = #{id}
&lt;/select&gt;</pre>
<div class="blog_h3"><span class="graybg">resultMap简介</span></div>
<p>你可以为语句指定resultMap而不是resultType属性， 前者引用一个resultMap元素。</p>
<p>resultMap元素定义一个数据库列——Java属性的映射规则，例如：</p>
<pre class="crayon-plain-tag">&lt;resultMap id="userResult" type="User"&gt;
    &lt;id property="userId" column="ID"/&gt;
    &lt;result property="userName" column="NAME"/&gt;
    &lt;result property="birthday" column="DOB"/&gt;
&lt;/resultMap&gt;

&lt;!-- 引用一个resultMap --&gt;
&lt;select id="selectUser" resultMap="userResult"&gt;
    SELECT * FROM USER u WHERE ID = #{id}
&lt;/select&gt;</pre>
<div class="blog_h2"><span class="graybg">自动映射</span></div>
<p>在简单的场景下，我们可以让Mybatis自动完成属性——列映射，在复杂的场景下，我们可以使用resultMap。</p>
<p>Mybatis默认映射规则是：如果列名和Java属性名在<span style="background-color: #c0c0c0;">不区分大小写的情况下相等</span>，那么它们匹配成功。但是有些时候，我们数据库列名采用全大写、下划线风格，而Java属性一般都是驼峰式大小写。使用默认的自动映射规则无法满足需求，这时候，我们可以打开设置项mapUnderscoreToCamelCase，改变自动映射的规则。</p>
<p>即使在resultMap中，自动映射也起作用，因此你不必手工编写所有字段的映射规则。</p>
<div class="blog_h2"><span class="graybg">缓存</span></div>
<p>Mybatis3的缓存实现改进了很多。默认情况下，只有局部缓存被启用。要启用SqlSessionFactory级别的二级缓存（跨Session），需要在映射文件中添加一行：</p>
<pre class="crayon-plain-tag">&lt;cache/&gt;</pre>
<p>注意，缓存配置是<span style="background-color: #c0c0c0;">绑定到映射文件的名字空间</span>的。</p>
<div class="blog_h3"><span class="graybg">属性列表</span></div>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">属性</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>eviction</td>
<td>
<p>缓存满了以后，如何进行清理。默认LRU，可选值：</p>
<ol>
<li>LRU，最近最少使用，移除最长时间不被使用的对象</li>
<li>FIFO，先进先出，按照对象进入缓存的顺序来移除</li>
<li>SOFT ，软引用，移除基于垃圾回收器状态和软引用规则的对象</li>
<li>WEAK，弱引用，更积极地移除基于垃圾收集器状态和弱引用规则的对象</li>
</ol>
</td>
</tr>
<tr>
<td>flushInterval</td>
<td>缓存刷新间隔，毫秒数。默认不设置，即仅仅在调用语句时才刷新</td>
</tr>
<tr>
<td>size</td>
<td>缓存对象的最大数量，默认1024</td>
</tr>
<tr>
<td>readOnly</td>
<td>是否只读方式使用缓存，如果true可以提高性能。默认false</td>
</tr>
<tr>
<td>type</td>
<td>可以指定自定义缓存实现</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">自定义缓存</span></div>
<p>你可以实现自己的缓存，只需要实现org.mybatis.cache.Cache接口。要使用自定义缓存机制。</p>
<div class="blog_h3"><span class="graybg">语句级设置</span></div>
<p>你可以设置每个映射语句的缓存规则，默认规则是这样的：</p>
<pre class="crayon-plain-tag">&lt;select  flushCache="false" useCache="true"/&gt;
&lt;insert  flushCache="true"/&gt;
&lt;update  flushCache="true"/&gt;
&lt;delete  flushCache="true"/&gt;</pre>
<div class="blog_h1"><span class="graybg">resultMap元素</span></div>
<p>映射器中的resultMap元素非常强大和重要，因此我们在此详细讨论它。</p>
<p>上面我们尝试了使用resultMap解决数据库字段名、实体类名不对应的问题。实际上resultMap能做的远远不止这点，它可以很好的解决关联映射问题——把结果集映射到一个对象图中。</p>
<div class="blog_h2"><span class="graybg">resultMap属性</span> </div>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">属性</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>id</td>
<td>resultMap在名字空间中的唯一标识</td>
</tr>
<tr>
<td>type</td>
<td>此resultMap将结果集映射为何种对象</td>
</tr>
<tr>
<td>autoMapping</td>
<td>覆盖全局的自动映射设置</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">resultMap子元素概要 </span></div>
<pre class="crayon-plain-tag">&lt;resultMap&gt;
    &lt;!-- 用什么构造器来示例化实体类--&gt;
    &lt;constructor&gt;
        &lt;!-- 构造器参数：注入到实体标识符--&gt;
        &lt;idArg&gt;&lt;/idArg&gt;
        &lt;!-- 构造器参数：注入到普通属性  --&gt;
        &lt;arg&gt;&lt;/arg&gt;
    &lt;/constructor&gt;
    &lt;!-- 注入到实体对象的标识符 --&gt;
    &lt;id&gt;&lt;/id&gt;
    &lt;!-- 注入到实体对象的普通属性 --&gt;
    &lt;result&gt;&lt;/result&gt;
    &lt;!-- 声明一个关联映射 --&gt;
    &lt;association&gt;&lt;/association&gt;
    &lt;!-- 进行一个集合映射 --&gt;
    &lt;collection&gt;&lt;/collection&gt;
    &lt;!-- 根据结果值决定使用哪个结果映射 --&gt;
    &lt;discriminator&gt;&lt;/discriminator&gt;
&lt;/resultMap&gt;</pre>
<p>下面依次介绍这些子元素的功能</p>
<div class="blog_h2"><span class="graybg">id|result</span></div>
<p>这两个子元素都是把单个列的值映射到简单数据类型（数字、字符串、日期、布尔等），只是id提示Mybatis该字段是实体类的标识符，Mybatis会用此信息提升缓存、<span style="background-color: #c0c0c0;">嵌入结果映射（联合映射）</span>的性能。示例：</p>
<pre class="crayon-plain-tag">&lt;id property="id" column="ID"/&gt;
&lt;result property="age" column="AGE"/&gt;</pre>
<div class="blog_h3"><span class="graybg">属性列表</span> </div>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">属性</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>property</td>
<td>Java端属性名，你可以使用点号导航，映射到对象图的深处</td>
</tr>
<tr>
<td>column</td>
<td>数据库列名，或者重命名后的列标签</td>
</tr>
<tr>
<td>javaType</td>
<td>该字段的Java类型，仅在映射到Map的时候需要</td>
</tr>
<tr>
<td>jdbcType</td>
<td>该字段的JDBC类型，仅仅对INSERT/UPDATE/DELETE语句处理可能为空的列时需要</td>
</tr>
<tr>
<td>typeHandler</td>
<td>使用的类型处理器</td>
</tr>
</tbody>
</table>
<div class="blog_h2"><span class="graybg">constructor</span></div>
<p>该子元素用于指定构造实体对象时，使用的构造器。constructor的两个子元素和id、result元素类似。</p>
<div class="blog_h2"><span class="graybg">association</span></div>
<p>该子元素用于处理Has-A关系，例如一个用户有一个部门。Mybatis提供了两种方式来处理这种关联关系：</p>
<ol>
<li>嵌套查询：执行另外一个SQL映射语句，来返回关联对象</li>
<li>嵌套结果：使用嵌套结果映射（nested result mappings）来处理重复的JOIN结果的子（多个用户具有同一部门，那么JOIN的结果中必然有重复的部门数据）集，这要求你手工编写一个JOIN查询，JOIN到关联对象的表</li>
</ol>
<div class="blog_h3"><span class="graybg">属性列表</span></div>
<table class=" full-width fixed-word-wrap">
<thead>
<tr>
<td style="width: 20%; text-align: center;">属性</td>
<td style="text-align: center;">说明</td>
</tr>
</thead>
<tbody>
<tr>
<td>property</td>
<td rowspan="4">参考id|result子元素</td>
</tr>
<tr>
<td>javaType</td>
</tr>
<tr>
<td>jdbcType</td>
</tr>
<tr>
<td>typeHandler</td>
</tr>
<tr>
<td style="text-align: center;" colspan="2"><strong><em>嵌套查询相关属性</em></strong></td>
</tr>
<tr>
<td>column</td>
<td>
<p>当前对象表中，引用关联对象的外键列</p>
<p>如果使用复合主键，需要以<pre class="crayon-plain-tag">{prop1=col1,prop2=col2}</pre> 这样的方式来指定该属性，prop*为关联对象的属性，而col*为当前表的列名</p>
</td>
</tr>
<tr>
<td>select</td>
<td>
<p>指定另外一个映射语句的ID，该映射语句的结果类型必须和关联对象的类型兼容</p>
<p>如果使用复合主键，column配置中的prop*将作为参数传递给该映射语句</p>
</td>
</tr>
<tr>
<td>fetchType</td>
<td>抓取类型，可选值lazy、eager。用于覆盖全局设置</td>
</tr>
<tr>
<td style="text-align: center;" colspan="2"><strong><em>嵌套结果相关属性</em></strong></td>
</tr>
<tr>
<td>resultMap</td>
<td>指定一个结果映射，该结果映射用于把当前结果集中某些列映射到关联对象</td>
</tr>
<tr>
<td>columnPrefix</td>
<td>用于关联对象的列的统一前缀</td>
</tr>
<tr>
<td>notNullColumn</td>
<td>
<p>默认情况下，当前结果集中映射到关联对象属性的那些列，只要有一个为非空值，则关联对象就会被创建</p>
<p>设置此属性，指定逗号分隔的若干列，只有这些列中至少一个不为空才创建关联对象</p>
</td>
</tr>
<tr>
<td>autoMapping</td>
<td>是否启用自动映射，覆盖全局值</td>
</tr>
</tbody>
</table>
<div class="blog_h3"><span class="graybg">嵌套查询示例</span></div>
<pre class="crayon-plain-tag">&lt;resultMap id="userResult" type="User"&gt;
    &lt;association property="dept" column="DEPT_ID" select="selectDept"&gt;&lt;/association&gt;
&lt;/resultMap&gt;

&lt;select id="selectUser" resultMap="userResult"&gt;
    SELECT * FROM USER WHERE ID = #{id}
&lt;/select&gt;
&lt;select id="selectDept" resultType="Dept"&gt;
    SELECT * FROM DEPT WHERE ID = #{id}
&lt;/select&gt;</pre>
<p>实体类和测试代码：</p>
<pre class="crayon-plain-tag">// 实体类
public class Dept {
    private int id;
    private String name;
}
public class User {
    private int id;
    private String name;
    private int age;
    private Date dob;
    private Dept dept;
}

// 测试代码
session = openSqlSession();
User alex = session.selectOne( "selectUser", 1 );
Assert.assertEquals( alex.getDept().getName(), "Development" );</pre>
<p>这种方式获得关联对象，想对简单，但是会导致所谓N + 1问题：</p>
<ol>
<li>执行一个映射语句，获得User的列表，此所谓1</li>
<li>对于每个User，都需要执行一个语句来获得Dept，N个用户需要执行N此SQL</li>
</ol>
<p>如果N数量很大，会导致大量零散的SQL，引起性能问题。延迟加载能够在某些场景下缓解此问题，但是如果你需要对User列表进行迭代，此问题就无法避免。</p>
<div class="blog_h3"><span class="graybg">嵌套结果示例</span></div>
<p>这里仍然以User-Dept为例，最基本的形式：</p>
<pre class="crayon-plain-tag">&lt;select id="selectUser" resultMap="userResult"&gt;
    SELECT
      U.ID,
      U.AGE,
      D.ID   AS DEPT_ID,
      D.NAME AS DEPT_NAME
    FROM USER U LEFT JOIN DEPT D ON D.ID = U.DEPT_ID
    WHERE U.ID = #{id}
&lt;/select&gt;
&lt;resultMap id="userResult" type="User"&gt;
    &lt;id property="id" column="ID"/&gt;
    &lt;result property="age" column="AGE"/&gt;
    &lt;association property="dept" javaType="Dept"&gt;
        &lt;id property="id" column="DEPT_ID"/&gt;
        &lt;result property="name" column="DEPT_NAME"/&gt;
    &lt;/association&gt;
&lt;/resultMap&gt;</pre>
<p>我们可以把association内部的映射规则独立为resultMap，便于重用：</p>
<pre class="crayon-plain-tag">&lt;resultMap id="userResult" type="User"&gt;
    &lt;id property="id" column="ID"/&gt;
    &lt;result property="age" column="AGE"/&gt;
    &lt;association property="dept" javaType="Dept" resultMap="deptResult"&gt;&lt;/association&gt;
&lt;/resultMap&gt;
&lt;resultMap id="deptResult" type="Dept"&gt;
    &lt;id property="id" column="DEPT_ID"/&gt;
    &lt;result property="name" column="DEPT_NAME"/&gt;
&lt;/resultMap&gt; </pre>
<p>最好把DEPT_前缀给移除，这样deptResult被单独使用时可以不必编写列别名，设置association的columnPrefix即可（前提你给那些属于关联对象的列予以规范化的别名）：</p>
<pre class="crayon-plain-tag">&lt;resultMap id="userResult" type="User" autoMapping="true" &gt;
    &lt;!-- 必须指定id，否则会出问题。而且貌似一旦决定手工映射，必须property、column一起提供 --&gt;
    &lt;id property="id" column="ID"/&gt;
    &lt;result property="age" column="AGE"/&gt;
    &lt;association property="dept" javaType="Dept" resultMap="deptResult" columnPrefix="DEPT_"/&gt;
&lt;/resultMap&gt;
&lt;resultMap id="deptResult" type="Dept"&gt;
    &lt;id property="id" column="ID"/&gt;
    &lt;result property="name" column="NAME"/&gt;
&lt;/resultMap&gt;</pre>
<p>columnPrefix还可以用于多次连接到同一类关联对象时，使用同一resultMap时区分各自的列。</p>
<div class="blog_h2"><span class="graybg">collection</span></div>
<p>上面我们学习了如何通过association子元素处理Has-A关系（many-to-one），如果遇到Has-Many情况怎么办呢，比如一个用户具有多个通信地址的情况？</p>
<p>这时可以使用collection子元素。我们以User-Address为例学习collection的用法，首先扩展一下实体类：</p>
<pre class="crayon-plain-tag">public class User {
    private int id;
    private String name;
    private int age;
    private Date dob;
    private Dept dept;
    private List&lt;Address&gt; addresses;
}
public class Address {
    private int id;
    private String province;
    private String city;
    private String street;
    private int zip;
}</pre>
<p>collection和association的属性很类似，我们直接以示例说明它们的差异。</p>
<div class="blog_h3"><span class="graybg">嵌套查询示例</span></div>
<pre class="crayon-plain-tag">&lt;resultMap id="userResultWithAddress" type="User"&gt;
    &lt;!--
        javaType 可选，一般Mybatis可以自动推导
        ofType 集合元素的类型
        column 当前结果集中，哪个字段作为嵌套查询的参数
        select 嵌套查询语句的ID
    --&gt;
    &lt;collection property="addresses" javaType="arraylist" ofType="Address" column="ID" select="selectAddressForUser" /&gt;
&lt;/resultMap&gt;
&lt;select id="selectUser" resultMap="userResultWithAddress"&gt;
    SELECT * FROM USER WHERE ID = #{id}
&lt;/select&gt;
&lt;select id="selectAddressForUser" resultType="Address"&gt;
    SELECT * FROM ADDRESS WHERE USER_ID = #{id}
&lt;/select&gt; </pre>
<div class="blog_h3"><span class="graybg">嵌套结果示例</span></div>
<p>嵌套结果总是基于JOIN来实现的： </p>
<pre class="crayon-plain-tag">&lt;resultMap id="userResultWithAddress" type="User"&gt;
    &lt;id property="id" column="ID" /&gt;
    &lt;collection property="addresses" ofType="Address" resultMap="addressResult" columnPrefix="A_"/&gt;
&lt;/resultMap&gt;
&lt;select id="selectUser" resultMap="userResultWithAddress"&gt;
    SELECT
      U.*,
      A.ID AS A_ID,
      A.CITY AS A_CITY,
      A.PROVINCE AS A_PROVINCE,
      A.STREET AS A_STREET,
      A.ZIP AS A_ZIP
    FROM USER U LEFT JOIN ADDRESS A
      ON U.ID = A.USER_ID
      WHERE U.ID = #{id}
&lt;/select&gt;
&lt;resultMap id="addressResult" type="Address"&gt;
    &lt;id property="id" column="ID" /&gt;
&lt;/resultMap&gt;</pre>
<p>可以看到和association版的非常类似，只是多了一个ofType属性指定集合元素的类型。</p>
<div class="blog_h2"><span class="graybg">discriminator</span></div>
<p>某些情况下，一个查询语句可能返回多行不同类型的数据，这些数据需要采用不同方式来映射。此时需要用到discriminator。</p>
<p>这里举一个通行工具（Vehicle）的例子，通行工具包括小汽车（Car）、卡车（Truck）、货车（Van）、SUV等类别，在这个例子里面Vehicle与后面的几个是父子类关系，但是存放在一张表里，通过一个鉴别字段区分。</p>
<p>我们看看Vehicle的结果映射：</p>
<pre class="crayon-plain-tag">&lt;resultMap id="vehicleResult" type="Vehicle"&gt;
    &lt;!-- 通用映射字段  --&gt;
    &lt;id property="id" column="id"/&gt;
    &lt;result property="vin" column="vin"/&gt;
    &lt;result property="year" column="year"/&gt;
    &lt;result property="make" column="make"/&gt;
    &lt;result property="model" column="model"/&gt;
    &lt;result property="color" column="color"/&gt;
    &lt;!-- vehicle_type用于区分交通工具的具体类型 --&gt;
    &lt;discriminator javaType="int" column="vehicle_type"&gt;
        &lt;!-- 如果vehicle_type为1，那么转而使用 resultMap 映射当前行--&gt;
        &lt;case value="1" resultMap="carResult"/&gt;
        &lt;case value="2" resultMap="truckResult"/&gt;
        &lt;case value="3" resultMap="vanResult"/&gt;
        &lt;case value="4" resultMap="suvResult"/&gt;
    &lt;/discriminator&gt;
&lt;/resultMap&gt;</pre>
<p>再看看Car的结果映射：</p>
<pre class="crayon-plain-tag">&lt;resultMap id="carResult" type="Car" extends="vehicleResult"&gt;
    &lt;result property="doorCount" column="door_count"/&gt;
&lt;/resultMap&gt;</pre>
<p> 首先，当前行会映射为Car类型，另外由于指定了extends自vehicleResult，所以后者定义的映射规则自动被合并进来。 </p>
<div class="blog_h1"><span class="graybg">动态SQL</span></div>
<p>Mybatis的动态SQL功能类似于Struts2的标签库，它利用一些特殊的XML元素，实现流程控制。</p>
<div class="blog_h2"><span class="graybg">if</span></div>
<p>该元素主要用于实现有条件的WHERE子句包含：</p>
<pre class="crayon-plain-tag">&lt;select id="findActiveUserLike" resultType="User"&gt;
    SELECT * FROM USER WHERE state = 'ACTIVE'
    &lt;if test="age != null"&gt;
        AND AGE like #{age}
    &lt;/if&gt;
    &lt;if test="dept != null and dept.name != null"&gt;
        AND DEPT_NAME like #{dept.name}
    &lt;/if&gt;
&lt;/select&gt;</pre>
<div class="blog_h2"><span class="graybg">choose - when - otherwise</span></div>
<p>类似于Java的switch语句：</p>
<pre class="crayon-plain-tag">&lt;select id="findActiveUserLike"  resultType="USer"&gt;
    SELECT * FROM User WHERE STATE = 'ACTIVE'
    &lt;choose&gt;
        &lt;when test="title != null"&gt;
            AND TITLE like #{title}
        &lt;/when&gt;
        &lt;when test="dept != null and dept.name != null"&gt;
            AND DEPT_NAME like #{author.name}
        &lt;/when&gt;
        &lt;otherwise&gt;
            AND AGE = #{age}
        &lt;/otherwise&gt;
    &lt;/choose&gt;
&lt;/select&gt;</pre>
<div class="blog_h2"><span class="graybg">where</span></div>
<p>该元素保证：只有一个以上if条件匹配的情况下，才往SQL中插入WHERE子句，若WHERE子句以OR、AND结尾，该元素也会把它们删除：</p>
<pre class="crayon-plain-tag">&lt;select id="findActiveUserLike" resultType="User"&gt;
    SELECT * FROM USER
    &lt;where&gt;
        state = 'ACTIVE'
        &lt;if test="age != null"&gt;
            AND AGE like #{age}
        &lt;/if&gt;
        &lt;if test="dept != null and dept.name != null"&gt;
            AND DEPT_NAME like #{dept.name}
        &lt;/if&gt;
    &lt;/where&gt;
&lt;/select&gt;</pre>
<div class="blog_h2"><span class="graybg">trim</span></div>
<p>该元素可以为其内部SQL设置前缀、后缀；也可以当内部SQL出现指定前缀、后缀的情况下，将其消除。与上面where等价的trim版本是：</p>
<pre class="crayon-plain-tag">&lt;select id="findActiveUserLike" resultType="User"&gt;
    SELECT * FROM USER
    &lt;trim prefix="WHERE" prefixOverrides="AND |OR "&gt;
        state = 'ACTIVE'
        &lt;if test="age != null"&gt;
            AND AGE like #{age}
        &lt;/if&gt;
        &lt;if test="dept != null and dept.name != null"&gt;
            AND DEPT_NAME like #{dept.name}
        &lt;/if&gt;
    &lt;/trim&gt;
&lt;/select&gt;</pre>
<div class="blog_h2"><span class="graybg">set</span></div>
<p>与where类似，添加必要的SET前缀，而且仅仅添加需要更新的列，处理结尾的逗号：</p>
<pre class="crayon-plain-tag">&lt;update id="updateUserIfNecessary"&gt;
    update USER
    &lt;set&gt;
        &lt;if test="username != null"&gt;NAME=#{name},&lt;/if&gt;
        &lt;if test="password != null"&gt;PASSWD=#{password},&lt;/if&gt;
        &lt;if test="email != null"&gt;EMAIL=#{email},&lt;/if&gt;
        &lt;if test="bio != null"&gt;DOB=#{DOB}&lt;/if&gt;
    &lt;/set&gt;
    where ID=#{id}
&lt;/update&gt;</pre>
<p>与之等价的trim版本是：<pre class="crayon-plain-tag">&lt;trim prefix="SET" suffixOverrides=","&gt;&lt;/trim&gt;</pre>  </p>
<div class="blog_h2"><span class="graybg">foreach</span></div>
<p>当需要对集合进行遍历，例如IN查询、批量插入时，可以使用该元素：</p>
<pre class="crayon-plain-tag">&lt;select id="selectUserIn" resultType="User"&gt;
    SELECT *
    FROM USER U
    WHERE ID in
    &lt;!--
        item 每次迭代的变量，index 当前迭代索引，collection 上下文对象（参数对象）的哪个属性被用来迭代
        open 前缀符号，close 后缀符号，separator 分隔每个迭代的符号
    --&gt;
    &lt;foreach item="item" index="index" collection="list" open="(" separator="," close=")"&gt;
        #{item}
    &lt;/foreach&gt;
&lt;/select&gt;</pre>
<div class="blog_h2"><span class="graybg">bind</span></div>
<p>可以估算一个OGNL表达式，将结果绑定到上下文对象（参数对象）的一个属性：</p>
<pre class="crayon-plain-tag">&lt;select id="selectUsersLike" resultType="User"&gt;
  &lt;bind name="pattern" value="'%' + _parameter.getName() + '%'" /&gt;
  SELECT * FROM USER
  WHERE NAME LIKE #{pattern}
&lt;/select&gt;</pre>
<div class="blog_h2"><span class="graybg">多数据库支持</span></div>
<p>利用Mybatis动态SQL特性，我们可以很容易实现多数据库支持。我们可以随时访问变量_databaseId，来获得数据库厂商类型：</p>
<pre class="crayon-plain-tag">&lt;insert id="insert"&gt;
    &lt;selectKey keyProperty="id" resultType="int" order="BEFORE"&gt;
        &lt;if test="_databaseId == 'oracle'"&gt;
            select seq_users.nextval from dual
        &lt;/if&gt;
        &lt;if test="_databaseId == 'db2'"&gt;
            select nextval for seq_users from sysibm.sysdummy1"
        &lt;/if&gt;
    &lt;/selectKey&gt;
    insert into users values (#{id}, #{name})
&lt;/insert&gt;</pre>
<div class="blog_h1"><span class="graybg">Java API</span></div>
<div class="blog_h2"><span class="graybg">SqlSessionFactoryBuilder</span> </div>
<p>该类提供了多种构件SqlSessionFactory的方法：</p>
<pre class="crayon-plain-tag">// 从配置文件读取信息
SqlSessionFactory build( InputStream inputStream );
// 从配置文件读取信息，设置使用的环境
SqlSessionFactory build( InputStream inputStream, String environment );
// 从配置文件读取信息，同时读取属性文件，后者填充配置文件的占位符
SqlSessionFactory build( InputStream inputStream, Properties properties );
// 从配置文件读取信息，设置使用的环境，使用属性文件填充占位符
SqlSessionFactory build( InputStream inputStream, String env, Properties props );
// 根据一个编程式的配置对象创建工厂 
SqlSessionFactory build( Configuration config );</pre>
<div class="blog_h2"><span class="graybg">Environment</span> </div>
<pre class="crayon-plain-tag">// 数据源与事务管理工厂
DataSource dataSource = BaseDataTest.createBlogDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
// 创建一个环境对象
Environment environment = new Environment("development", transactionFactory, dataSource);</pre>
<div class="blog_h2"><span class="graybg">Configuration</span> </div>
<p>基于XML的主配置的Java编程式等价物：</p>
<pre class="crayon-plain-tag">Configuration configuration = new Configuration(environment);
// 启用延迟加载
configuration.setLazyLoadingEnabled(true);
configuration.setEnhancementEnabled(true);
// 注册别名
configuration.getTypeAliasRegistry().registerAlias(Blog.class);
// 添加映射器
configuration.addMapper(BoundBlogMapper.class);</pre>
<div class="blog_h2"><span class="graybg">SqlSessionFactory</span></div>
<p>主要用来获取SqlSession，有很多方法变体：</p>
<pre class="crayon-plain-tag">// 开启事务（不自动提交）、从连接池获取连接对象、事务隔离级别使用驱动默认、不复用预处理语句、不批量更新
SqlSession openSession();
// 指定是否自动提交事务
SqlSession openSession(boolean autoCommit);
// 给出底层连接对象
SqlSession openSession(Connection connection);
// 设置事务隔离级别
SqlSession openSession(TransactionIsolationLevel level);
// 设置执行器类型：SIMPLE、REUSE、BATCH
SqlSession openSession(ExecutorType execType,TransactionIsolationLevel level);
SqlSession openSession(ExecutorType execType);
SqlSession openSession(ExecutorType execType, boolean autoCommit);
SqlSession openSession(ExecutorType execType, Connection connection);</pre>
<div class="blog_h2"><span class="graybg">SqlSession</span></div>
<p>包含日常工作需要使用的大部分方法： </p>
<pre class="crayon-plain-tag">// 根据参数对象（上下文）填充语句，执行查询，返回单个对象
&lt;T&gt; T selectOne(String statement, Object parameter);
// 类似上面，返回列表
&lt;E&gt; List&lt;E&gt; selectList(String statement, Object parameter);
// 类似上面，返回映射，mapKey是作为映射Key的对象属性
&lt;K,V&gt; Map&lt;K,V&gt; selectMap(String statement, Object parameter, String mapKey);
// DML操作
int insert(String statement, Object parameter);
int update(String statement, Object parameter);
int delete(String statement, Object parameter);

// 限定其实位置、返回结果数量，用于分页查询
int offset = 100;
int limit = 25;
RowBounds rowBounds = new RowBounds(offset, limit);
&lt;E&gt; List&lt;E&gt; selectList (String statement, Object parameter, RowBounds rowBounds);
&lt;K,V&gt; Map&lt;K,V&gt; selectMap(String statement, Object parameter, String mapKey, RowBounds rowbounds);

// 结果处理器用于定制行处理方式
void select (String statement, Object parameter, ResultHandler&lt;T&gt; handler);
void select (String statement, Object parameter, RowBounds rowBounds, ResultHandler&lt;T&gt; handler);

// 事务控制方法
void commit();
void commit(boolean force);
void rollback();
void rollback(boolean force);

// 清理会话级缓存
void clearCache();

// 确保 SqlSession 被关闭
void close();</pre>
<div class="blog_h2"><span class="graybg">SQL语句构建器</span></div>
<p>有时候你不得不在Java代码中编写SQL语句，为了减轻拼写SQL的痛苦，你可以使用MyBatis提供的SQL语句构建器：</p>
<pre class="crayon-plain-tag">SQL sql = new SQL() {{
    SELECT( "P.ID, P.USERNAME, P.PASSWORD, P.FULL_NAME" );
    SELECT( "P.LAST_NAME, P.CREATED_ON, P.UPDATED_ON" );
    FROM( "PERSON P" );
    FROM( "ACCOUNT A" );
    INNER_JOIN( "DEPARTMENT D on D.ID = P.DEPARTMENT_ID" );
    INNER_JOIN( "COMPANY C on D.COMPANY_ID = C.ID" );
    WHERE( "P.ID = A.ID" );
    WHERE( "P.FIRST_NAME like ?" );
    OR();
    WHERE( "P.LAST_NAME like ?" );
    GROUP_BY( "P.ID" );
    HAVING( "P.LAST_NAME like ?" );
    OR();
    HAVING( "P.FIRST_NAME like ?" );
    ORDER_BY( "P.ID" );
    ORDER_BY( "P.FULL_NAME" );
}}.toString();</pre>
<div class="blog_h1"><span class="graybg">与Spring集成</span></div>
<div class="blog_h2"><span class="graybg">Maven依赖</span></div>
<pre class="crayon-plain-tag">&lt;!-- 与Spring集成模块 --&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.mybatis&lt;/groupId&gt;
    &lt;artifactId&gt;mybatis-spring&lt;/artifactId&gt;
    &lt;version&gt;1.2.3&lt;/version&gt;
&lt;/dependency&gt;
&lt;!-- 连接池 --&gt;
&lt;dependency&gt;
    &lt;groupId&gt;proxool&lt;/groupId&gt;
    &lt;artifactId&gt;proxool&lt;/artifactId&gt;
    &lt;version&gt;0.9.1&lt;/version&gt;
&lt;/dependency&gt;

&lt;!-- Spring JDBC--&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.springframework&lt;/groupId&gt;
    &lt;artifactId&gt;spring-jdbc&lt;/artifactId&gt;
    &lt;version&gt;3.1.2.RELEASE&lt;/version&gt;
&lt;/dependency&gt;
&lt;!-- Spring事务管理 --&gt;
&lt;dependency&gt;
    &lt;groupId&gt;org.springframework&lt;/groupId&gt;
    &lt;artifactId&gt;spring-tx&lt;/artifactId&gt;
    &lt;version&gt;3.1.2.RELEASE&lt;/version&gt;
&lt;/dependency&gt;</pre>
<div class="blog_h2"><span class="graybg">基本配置</span></div>
<pre class="crayon-plain-tag">&lt;?xml version="1.0" encoding="UTF-8"?&gt;
&lt;beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xsi:schemaLocation="
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
            http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
        "&gt;
    &lt;context:annotation-config/&gt;
    &lt;context:component-scan base-package="cc.gmem.study.ssm"/&gt;
    &lt;tx:annotation-driven transaction-manager="txManager" mode="proxy"/&gt;

    &lt;bean id="dataSource" class="org.logicalcobwebs.proxool.ProxoolDataSource"&gt;
        &lt;property name="alias" value="connectionPool"/&gt;
        &lt;property name="driver" value="com.mysql.jdbc.Driver"/&gt;
        &lt;property name="driverUrl" value="jdbc:mysql://localhost:3306/test"/&gt;
        &lt;property name="user" value="root"/&gt;
        &lt;property name="password" value="root"/&gt;
        &lt;property name="statistics" value="10s"/&gt;
        &lt;property name="minimumConnectionCount" value="50"/&gt;
        &lt;property name="maximumConnectionCount" value="300"/&gt;
        &lt;property name="simultaneousBuildThrottle" value="50"/&gt;
        &lt;property name="maximumActiveTime" value="3600000"/&gt;
    &lt;/bean&gt;
    &lt;bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"&gt;
        &lt;property name="dataSource" ref="dataSource" /&gt;
    &lt;/bean&gt;
    &lt;!-- 用于创建SqlSessionFactory --&gt;
    &lt;bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"&gt;
        &lt;!-- SqlSessionFactor使用的数据源必须和事务管理器的一致 --&gt;
        &lt;property name="dataSource" ref="dataSource"/&gt;
        &lt;!-- 可以指定映射器XML文件的位置 --&gt;
        &lt;property name="mapperLocations" value="classpath*:cc/gmem/study/ssm/mapper/*.xml" /&gt;
        &lt;!-- 从1.3.0版本开始，可以直接传入Mybatis基本配置 --&gt;
        &lt;property name="configuration"&gt;
            &lt;bean class="org.apache.ibatis.session.Configuration"&gt;
                &lt;property name="mapUnderscoreToCamelCase" value="true"/&gt;
            &lt;/bean&gt;
        &lt;/property&gt;
    &lt;/bean&gt;
    &lt;!-- 这是一个线程安全的SqlSession实现，你可以在任何需要的地方注入它 --&gt;
    &lt;!-- 当调用selectOne/select/insert/update...等方法时、或者调用来自Mapper的方法时，SqlSessionTemplate保证关联了Spring的事务管理器--&gt;
    &lt;bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"&gt;
        &lt;constructor-arg index="0" ref="sqlSessionFactory" /&gt;
        &lt;!-- 你可以指定ExecutorType --&gt;
        &lt;constructor-arg index="1" value="BATCH" /&gt;
    &lt;/bean&gt;

&lt;/beans&gt;</pre>
<div class="blog_h3"><span class="graybg">SqlSessionTemplate</span></div>
<p>与Mybatis默认的SqlSession实现DefaultSqlSession不同，SqlSessionTemplate是线程安全的，你可以放心的传递它。</p>
<div class="blog_h2"><span class="graybg">管理映射器接口</span></div>
<p>你可以把映射器接口配置为Bean，Spring会自动生成其代理：</p>
<pre class="crayon-plain-tag">&lt;!-- 你可以把映射器接口配置为Bean --&gt;
&lt;bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"&gt;
    &lt;!-- 映射器接口，对应的XML版本的配置文件会自动加载 --&gt;
    &lt;property name="mapperInterface" value="cc.gmem.study.ssm.mapper.UserMapper"/&gt;
    &lt;property name="sqlSessionFactory" ref="sqlSessionFactory"/&gt;
&lt;/bean&gt;</pre>
<p>逐个配置映射器接口太麻烦，你可以配置自动扫描：</p>
<pre class="crayon-plain-tag">&lt;bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"&gt;
    &lt;!-- 扫描所有子包，发现映射器接口 --&gt;
    &lt;property name="basePackage" value="cc.gmem.study.ssm.mapper" /&gt;
    &lt;!-- 仅仅扫描具有如下注解的接口  --&gt;
    &lt;property name="annotationClass" value="cc.gmem.mybatis.MapperInterface" /&gt;
    &lt;!-- 仅仅扫描如下接口的子接口 --&gt;
    &lt;property name="markerInterface" value="cc.gmem.mybatis.Mapper" /&gt;
&lt;/bean&gt;</pre>
<p>你可以在自己的DAO或者Service中注入这些映射器接口，并直接调用其中的SQL方法。</p>
<div class="blog_h1"><span class="graybg">常见问题</span></div>
<div class="blog_h2"><span class="graybg">重复映射导致异常</span></div>
<p>启动报错：java.lang.IllegalArgumentException: Mapped Statements collection already contains value for ...</p>
<p>原因：可能是因为你在XML映射器和注解映射器中指定了重复的映射语句，即XML的id与Java方法名相同。</p>
<div class="blog_h2"><span class="graybg">找不到映射语句导致异常</span></div>
<p>执行语句时报错：Mapped Statements collection does not contain value for &lt;statementName&gt; </p>
<p>可能原因：</p>
<ol>
<li>Java API中写错了语句ID</li>
<li>映射器没有被扫描到，注意mappers/package仅仅能识别基于注解的映射器</li>
</ol>
<div class="blog_h2"><span class="graybg">association不指定javaType导致异常</span></div>
<p>报错信息：</p>
<p>at org.apache.ibatis.exceptions.ExceptionFactory.wrapException(ExceptionFactory.java:30)<br /> at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:122)<br /> at org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(DefaultSqlSession.java:113)<br /> at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:73)</p>
<p>Caused by: java.lang.NullPointerException</p>
<p>at org.apache.ibatis.reflection.DefaultReflectorFactory.findForClass(DefaultReflectorFactory.java:42)<br /> at org.apache.ibatis.reflection.MetaClass.&lt;init&gt;(MetaClass.java:39)<br /> at org.apache.ibatis.reflection.MetaClass.forClass(MetaClass.java:43)<br /> at org.apache.ibatis.executor.resultset.DefaultResultSetHandler.createResultObject(DefaultResultSetHandler.java:524) </p>
<p>原因：使用嵌套结果映射时association上没有标注关联对象的类型时，出现此错误。</p>
<div class="blog_h2"><span class="graybg">collection不指定必要的id子元素导致异常</span></div>
<p>报错信息：Expected one result (or null) to be returned by selectOne(), but found: N</p>
<p>原因：User与Address是1:N关系，通过User左连接到Address，当userResult、addressResult两个结果映射都不设置id子元素时，会出现此异常</p>
<p>总结：id子元素最好都写上</p>
<div class="blog_h2"><span class="graybg">无法显示Mybatis日志</span></div>
<p>首先配置好Log4j的logger：<pre class="crayon-plain-tag">org.apache.ibatis</pre> 。</p>
<p>然后，对于3.2以前的版本，调用下面的方法之一：</p>
<pre class="crayon-plain-tag">org.apache.ibatis.logging.LogFactory.useSlf4jLogging();
org.apache.ibatis.logging.LogFactory.useLog4JLogging();
org.apache.ibatis.logging.LogFactory.useLog4J2Logging();
org.apache.ibatis.logging.LogFactory.useJdkLogging();
org.apache.ibatis.logging.LogFactory.useCommonsLogging();
org.apache.ibatis.logging.LogFactory.useStdOutLogging();</pre>
<p>对于3.2以后的版本，添加类似下面的配置项即可：</p>
<pre class="crayon-plain-tag">&lt;setting name="logImpl" value="LOG4J"/&gt;</pre>
<div class="blog_h2"><span class="graybg">如何编写模糊查询</span></div>
<p>可以有不同的写法：</p>
<pre class="crayon-plain-tag">&lt;!-- 方法一：注意SQL注入的可能 --&gt;
${'%' + name + '%'}
'%${name}%'
&lt;!-- 方法二：位置参数风格 --&gt;
"%"#{name}"%"</pre>
</div><p>The post <a rel="nofollow" href="https://blog.gmem.cc/mybatis-study-note">Mybatis学习笔记</a> appeared first on <a rel="nofollow" href="https://blog.gmem.cc">绿色记忆</a>.</p>
]]></content:encoded>
			<wfw:commentRss>https://blog.gmem.cc/mybatis-study-note/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
