MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

1. MyBatis 工作原理

每个基于 MyBatis 的应用程序都以一个 SqlSessionFactory 实例为核心。它是由 SqlSessionFactoryBuilder 通过 MyBatis 配置文件(XML 配置文件或 Configuration 实例)构建出来的,所以 SqlSessionFactory 保存着我们对 MyBatis 的所有配置信息。

构建 SqlSessionFactory

我们知道 SqlSessionFactory 是通过 MyBatis 配置文件构建出来的。而配置文件包含了对 MyBatis 系统的核心配置,包含了获取数据库连接实例的数据源(DataSource)、决定事务作用域和控制方式的事务管理器(TransactionManager)。并且配置文件有两种形式,下面分别使用两种方式构建 SqlSessionFactory

使用 XML 配置文件构建 SqlSessionFactory

XML 配置文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<environments default="development">
<environment id="development">
<!-- 事务管理器 -->
<transactionManager type="JDBC"/>
<!-- 数据源 -->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!-- 映射器 -->
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
// 获取配置文件
String resource = "org/mybatis/example/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
// 通过配置文件构建 SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

使用 Java 代码构建 SqlSessionFactory

// 数据源、事务管理器
DataSource dataSource = BlogDataSourceFactory.getBlogDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
// 使用 数据源、事务管理器 创建配置类
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
// 添加映射类
configuration.addMapper(BlogMapper.class);

// 使用 Java 配置类构建 SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(configuration);

使用 SqlSessionFactory

我们有了 SqlSessionFactory 实例就可以从中获取 SqlSession 实例了。SqlSessionFactory 会在应用程序运行期间一直存在,在我们需要的时候为我们创建 SqlSession

SqlSession

SqlSession 为我们提供了在数据库运行 SQL 命令的方法。可以使用 SqlSession 实例来直接执行已映射的 SQL 语句,也可以使用和指定语句的参数和返回值相匹配的接口(映射类)。

使用 SqlSession 访问数据库

try (SqlSession session = sqlSessionFactory.openSession()) {
// 对应 XML 映射文件的 SQL 语句
Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
}
try (SqlSession session = sqlSessionFactory.openSession()) {
// 获取映射类
BlogMapper mapper = session.getMapper(BlogMapper.class);
// 每个映射类方法对应一条 SQL 语句(来自 XML 映射文件或者映射注解)
Blog blog = mapper.selectBlog(101);
}

映射 SQL 语句

通过映射文件 SqlSession 可以根据映射的 SQL 语句访问数据库。从上面的示例知道,一个 SQL 语句可以通过 XML 定义也可以通过注解定义。我们继续通过示例来了解 MyBatis 是如何映射 SQL 语句的。

XML 映射文件:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<!-- 在 mapper 中定义 SQL 语句 -->
<mapper namespace="org.mybatis.example.BlogMapper">
<select id="selectBlog" resultType="Blog">
select * from Blog where id = #{id}
</select>
</mapper>

每个映射都包含一个 mapper,并且声明了它的命名空间 org.mybatis.example.BlogMapper。在命名空间中又定义了一个名为 “selectBlog” 的 SQL 语句。在上面的示例就是使用了该语句的全限定名来映射出 SQL 语句的。

// 使用全限定名映射 SQL 语句
Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);

同样的,可以映射到与命名空间同名的 Java 映射类(BlogMapper.class),并且将 “selectBlog” SQL 语句映射到具有同名、同参数类型、同返回类型的方法上。

BlogMapper mapper = session.getMapper(BlogMapper.class);
// 将 SQL 语句映射到与全限定名相同的方法上。
Blog blog = mapper.selectBlog(101);

总结

MyBatis工作流程图解

2. MyBatis 配置文件

我们已经从宏观的角度了解到 MyBatis 是如何进行工作的,但是一些细节还是没有接触到。接下来将学习 MyBatis 配置文件,MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。

在开始学习 MyBatis 配置文件前,可以通过下图快速的了解 MyBatis 配置文件的信息。在上面的学习中,为了方便理解,我只提出了数据源(DataSource)、事务管理器(transactionManager)、映射器(mapper)的配置信息,其实 MyBatis 中还有很多可选的配置信息。

mybatis配置文件结构

接下来也会对每项配置进行说明,但是进行更完整更准确的学习查看相关的官方文档更有帮助。

属性(properties)

属性相当于变量,可以在配置文件的其他地方引用属性。我们可以在 <properties> 子元素中设置,也可以在典型的 Java 属性文件中配置,还可以在 SqlSessionFactoryBuilder.build() 方法中传入属性值。

<properties resource="org/mybatis/example/config.properties">
<property name="username" value="dev_user"/>
<property name="password" value="F2Fa3!33TYyg"/>
</properties>
<!-- 使用属性 -->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>

加载顺序

如果同名属性在多个地方进行了设置,会根据以下的顺序进行加载(越往下优先度越高):

  • 首先读取在 properties 元素体内指定的属性。
  • 然后根据 properties 元素中的 resource 属性读取类路径下属性文件,或根据 url 属性指定的路径读取属性文件,并覆盖之前读取过的同名属性。
  • 最后读取作为方法参数传递的属性,并覆盖之前读取过的同名属性。

默认值

在 MyBatis 3.4.2 之后,还可以为占位符提供一个默认值(这个特性默认是关闭的,需要手动开启)。

<properties resource="org/mybatis/example/config.properties">
<!-- 启用默认值特性 -->
<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/> </properties>
<dataSource type="POOLED">
<!-- 如果属性 'username' 没有被配置,'username' 属性的值将为 'ut_user' -->
<property name="username" value="${username:ut_user}"/>
</dataSource>

默认值与属性名使用 “:”冒号分隔的,如果属性名也存在冒号就会发生错误。这种情况可以通过修改分隔符解决。

<properties resource="org/mybatis/example/config.properties">
<!-- 修改默认值的分隔符 -->
<property name="org.apache.ibatis.parsing.PropertyParser.default-value-separator" value="?:"/>
</properties>
<dataSource type="POOLED">
<!-- 使用 ?: 分隔符 -->
<property name="username" value="${db:username?:ut_user}"/>
</dataSource>

设置(settings)

在设置中有许多重要的调整设置,可以改变 MyBatis 的运行时行为。在 MyBtis 文档 中对各项设置进行了详细的说明。

settings 配置示例:

<settings>
<setting name="cacheEnabled" value="true"/>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="multipleResultSetsEnabled" value="true"/>
<setting name="useColumnLabel" value="true"/>
<setting name="useGeneratedKeys" value="false"/>
<setting name="autoMappingBehavior" value="PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" value="100"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

类型别名(typeAliases)

给 Java 类型设置一个别名,用来代替冗长的全限定类名(在 XML 配置中需要使用到 Java 类型的地方可以使用别名代替)。

<!-- 给 Java 类型取别名 -->
<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
<typeAlias alias="Blog" type="domain.blog.Blog"/>
<typeAlias alias="Comment" type="domain.blog.Comment"/>
<typeAlias alias="Post" type="domain.blog.Post"/>
<typeAlias alias="Section" type="domain.blog.Section"/>
<typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>
<!-- 指定一个包名,MyBatis 会搜索需要的 JavaBean -->
<typeAliases>
<package name="domain.blog"/>
</typeAliases>
// 使用注解给 Java 类型取别名
@Alias("author")
public class Author {
...
}

类型处理器(typeHandlers)

在 MyBatis 设置预处理语句的参数或者从结果集中取值时,会使用类型处理器将值转换成适合的 Java 类型。同样的 MyBatis 文档 对 MyBatis 默认类型处理器进行了说明。

创建类型处理器

我们也可以重写或创建自己的类型处理器。只需要实现 org.apache.ibatis.type.TypeHandler 接口或者继承 org.apache.ibatis.type.BaseTypeHandler ,如何将它映射到一个 JDBC 类型上。

// 映射到 VARCHAR 类型
@MappedJdbcTypes(JdbcType.VARCHAR)
public class ExampleTypeHandler extends BaseTypeHandler<String> {

@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);
}
}
<!-- mybatis-config.xml -->
<typeHandlers>
<typeHandler handler="org.mybatis.example.ExampleTypeHandler"/>
</typeHandlers>

对象工厂(objectFactory)

每次 MyBatis 创建结果对象实例时,都会使用一个对象工厂实例来完成实例化工作。MyBatis 提供的默认对象工厂仅提供实例化目标类,如果想覆盖对象工厂的默认行为,可以通过创建自己的对象工厂来实现。

// ExampleObjectFactory.java
public class ExampleObjectFactory extends DefaultObjectFactory {

// 处理无参构造方法
public Object create(Class type) {
return super.create(type);
}

// 处理带参构造方法
public Object create(Class type, List<Class> constructorArgTypes, List<Object> constructorArgs) {
return super.create(type, constructorArgTypes, constructorArgs);
}

// 配置对象工厂,在配置文件定义的属性会被传到这里
public void setProperties(Properties properties) {
super.setProperties(properties);
}

public <T> boolean isCollection(Class<T> type) {
return Collection.class.isAssignableFrom(type);
}
}
<!-- mybatis-config.xml -->
<objectFactory type="org.mybatis.example.ExampleObjectFactory">
<!-- 属性会被传到 setProperties 方法 -->
<property name="someProperty" value="100"/>
</objectFactory>

插件(plugins)

MyBatis 允许你在映射语句执行过程中在某一个点进行拦截。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

这些类的方法细节可以通过方法签名或查看源代码来了解。

使用插件

在使用插件时,只需要实现 Interceptor 接口,并指定想要拦截的方法签名即可。

// 使用注解指定拦截方法签名
// 将会拦截在 Executor 实例中所有的 “update” 方法调用, 这里的 Executor 是负责执行底层映射语句的内部对象。
@Intercepts({@Signature(
type= Executor.class,
method = "update",
args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {

// 属性
private Properties properties = new Properties();

// 拦截方法
public Object intercept(Invocation invocation) throws Throwable {
// 在执行前的处理...
Object returnObject = invocation.proceed();
// 在执行后的处理...
return returnObject;
}

// 在配置文件定义的属性会被传到这里
public void setProperties(Properties properties) {
this.properties = properties;
}
}
<!-- mybatis-config.xml -->
<plugins>
<plugin interceptor="org.mybatis.example.ExamplePlugin">
<!-- 属性会被传到 setProperties 方法 -->
<property name="someProperty" value="100"/>
</plugin>
</plugins>

环境配置(environments)

在介绍 MyBatis 工作原理时就已经提到,在环境配置中包含了获取数据库连接实例的数据源(DataSource)、决定事务作用域和控制方式的事务管理器(TransactionManager)。并且 MyBatis 支持配置多种环境来适应不同的环境(比如开发、测试、生产环境都会有不同的配置)。

<!-- default 指定默认使用的环境 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="..." value="..."/>
</transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${dev.driver}"/>
<property name="url" value="${dev.url}"/>
<property name="username" value="${dev.username}"/>
<property name="password" value="${dev.password}"/>
</dataSource>
</environment>

<environment id="test">
<transactionManager type="JDBC">
<property name="..." value="..."/>
</transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${test.driver}"/>
<property name="url" value="${test.url}"/>
<property name="username" value="${test.username}"/>
<property name="password" value="${test.password}"/>
</dataSource>
</environment>

</environments>

尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。

事务管理器

MyBatis 提供了两种类型的事务管理器(也就是 type=”[JDBC|MANAGED]”)

  • JDBC :这个配置直接使用了 JDBC 的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。
  • MANAGED: 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。

创建事务管理器

我们也可以自定义事务管理器,我们只需要实现 TransactionFactory 接口,然后使用全限定类名或者类型别名在配置文件设置事务管理器。

public interface TransactionFactory {
// 在事务管理器实例化后,所有在 XML 中配置的属性将会被传递给 setProperties() 方法
// 从 3.5.2 开始,该方法为默认方法(Java 8 的接口默认方法)
default void setProperties(Properties props) {
// 空实现
}
Transaction newTransaction(Connection conn);
Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}

还需要实现 Transaction 接口。

public interface Transaction {
Connection getConnection() throws SQLException;
void commit() throws SQLException;
void rollback() throws SQLException;
void close() throws SQLException;
Integer getTimeout() throws SQLException;
}

数据源

MyBatis 提供了三种内置的数据源类型(也就是 type=”[UNPOOLED|POOLED|JNDI]”)。

UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。UNPOOLED 类型的数据源仅仅需要配置以下 5 种属性:

  • driver – 这是 JDBC 驱动的 Java 类全限定名(并不是 JDBC 驱动中可能包含的数据源类)。
  • url – 这是数据库的 JDBC URL 地址。
  • username – 登录数据库的用户名。
  • password – 登录数据库的密码。
  • defaultTransactionIsolationLevel – 默认的连接事务隔离级别。
  • defaultNetworkTimeout – 等待数据库操作完成的默认网络超时时间(单位:毫秒)。

作为可选项,你也可以传递属性给数据库驱动。只需在属性名加上“driver.”前缀即可,例如:

  • driver.encoding=UTF8 - 这将通过 DriverManager.getConnection(url, driverProperties) 方法传递值为 UTF8encoding 属性给数据库驱动。

POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。

除了上述提到 UNPOOLED 下的属性外,还有更多属性用来配置 POOLED 的数据源:

  • poolMaximumActiveConnections – 在任意时间可存在的活动(正在使用)连接数量,默认值:10
  • poolMaximumIdleConnections – 任意时间可能存在的空闲连接数。
  • poolMaximumCheckoutTime – 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒)
  • poolTimeToWait – 这是一个底层设置,如果获取连接花费了相当长的时间,连接池会打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直失败且不打印日志),默认值:20000 毫秒(即 20 秒)。
  • poolMaximumLocalBadConnectionTolerance – 这是一个关于坏连接容忍度的底层设置, 作用于每一个尝试从缓存池获取连接的线程。 如果这个线程获取到的是一个坏的连接,那么这个数据源允许这个线程尝试重新获取一个新的连接,但是这个重新尝试的次数不应该超过 poolMaximumIdleConnectionspoolMaximumLocalBadConnectionTolerance 之和。 默认值:3(新增于 3.4.5)
  • poolPingQuery – 发送到数据库的侦测查询,用来检验连接是否正常工作并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动出错时返回恰当的错误消息。
  • poolPingEnabled – 是否启用侦测查询。若开启,需要设置 poolPingQuery 属性为一个可执行的 SQL 语句(最好是一个速度非常快的 SQL 语句),默认值:false。
  • poolPingConnectionsNotUsedFor – 配置 poolPingQuery 的频率。可以被设置为和数据库连接超时时间一样,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用)。

JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。这种数据源配置只需要两个属性:

  • initial_context – 这个属性用来在 InitialContext 中寻找上下文(即,initialContext.lookup(initial_context))。这是个可选属性,如果忽略,那么将会直接从 InitialContext 中寻找 data_source 属性。
  • data_source – 这是引用数据源实例位置的上下文路径。提供了 initial_context 配置时会在其返回的上下文中进行查找,没有提供时则直接在 InitialContext 中查找。

和其他数据源配置类似,可以通过添加前缀“env.”直接把属性传递给 InitialContext。比如:

  • env.encoding=UTF8 - 这就会在 InitialContext 实例化时往它的构造方法传递值为 UTF8encoding 属性。

数据源的配置繁多,通常我们只会按照下面示例来配置数据源。但有特殊需求时在查询相关配置进行设置。

<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>

数据库厂商标识(databaseIdProvider)

基于映射语句中的 databaseId 属性,MyBatis 可以根据不同的数据库厂商执行不同的语句。

<databaseIdProvider type="DB_VENDOR">
<property name="SQL Server" value="sqlserver"/>
<property name="DB2" value="db2"/>
<property name="Oracle" value="oracle" />
</databaseIdProvider>

映射器(mappers)

除了进行对 MyBatis 的相关配置,在配置文件中还需要告诉 MyBatis 到哪里去找 SQL 映射语句。

<!-- 使用相对于类路径的资源引用 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/BlogMapper.xml"/>
<mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
<mapper class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>

3. MyBatis 映射文件

同样我们通过一张图来快速了解一下 XML 映射器的组成。

mybatis_mapper

查询语句(select)

<select id="selectPerson" parameterType="int" resultType="hashmap">
SELECT * FROM person WHERE id = #{id}
</select>

这是一个简单的 select 查询语句。这个语句的名称叫 selectPerson(id=”selectPerson”),接受一个 int 类型参数(parameterType=”int”),返回一个 HashMap 类型的结果(resultType=”hashmap”)。标签内的就是 SQL 语句,在 SQL 语句中有一个参数符号 “#{id}”。说明这是一条预处理语句,参数会被传到语句处理语句中成为一条可执行的 SQL 语句。

除了上面示例出现的属性之外,select 元素还有许多其他的属性。

Select 元素的属性:

属性 描述
id 在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType 将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)
resultType 期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。
resultMap 对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。
flushCache 将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false。
useCache 将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true。
timeout 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。
fetchSize 这是一个给驱动的建议值,尝试让驱动程序每次批量返回的结果行数等于这个设置值。 默认值为未设置(unset)(依赖驱动)。
statementType 可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
resultSetType FORWARD_ONLY,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或 DEFAULT(等价于 unset) 中的一个,默认值为 unset (依赖数据库驱动)。
databaseId 如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。
resultOrdered 这个设置仅针对嵌套结果 select 语句:如果为 true,将会假设包含了嵌套结果集或是分组,当返回一个主结果行时,就不会产生对前面结果集的引用。 这就使得在获取嵌套结果集的时候不至于内存不够用。默认值:false。
resultSets 这个设置仅适用于多结果集的情况。它将列出语句执行后返回的结果集并赋予每个结果集一个名称,多个名称之间以逗号分隔。

插入,更新,删除语句(insert, update, delete)

insert, update, delete 语句使用方法十分相似。

<insert id="insertAuthor">
INSERT INTO Author (id,username,password,email,bio)
VALUES (#{id},#{username},#{password},#{email},#{bio})
</insert>
<update id="updateAuthor">
UPDATE Author SET
username = #{username},
password = #{password},
email = #{email},
bio = #{bio}
WHERE id = #{id}
</update>
<delete id="deleteAuthor">
DELETE FROM Author WHERE id = #{id}
</delete>

Insert, Update, Delete 元素的属性:

属性 描述
id 在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType 将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。
flushCache 将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:(对 insert、update 和 delete 语句)true。
timeout 这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。
statementType 可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
useGeneratedKeys (仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。
keyProperty (仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset)。如果生成列不止一个,可以用逗号分隔多个属性名称。
keyColumn (仅适用于 insert 和 update)设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。
databaseId 如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。

insert 自动生成主键

如果数据库支持自动生成主键,设置 useGeneratedKeys=”true”,然后再把 keyProperty 设置为目标属性.

<insert id="insertAuthor" useGeneratedKeys="true"
keyProperty="id">
INSERT INTO Author (username,password,email,bio)
VALUES (#{username},#{password},#{email},#{bio})
</insert>

insert 多行插入

如果数据库支持多行插入,可以传入一个数组或集合,并返回自动生成的主键。

<insert id="insertAuthor" useGeneratedKeys="true"
keyProperty="id">
insert into Author (username, password, email, bio) values
<foreach item="item" collection="list" separator=",">
(#{item.username}, #{item.password}, #{item.email}, #{item.bio})
</foreach>
</insert>

SQL 片段(sql)

sql 用来定义可以重用的 SQL 语句片段,以便在其他语句中使用。

<sql id="userColumns"> 
${alias}.id,${alias}.username,${alias}.password
</sql>
<select id="selectUsers" resultType="map">
select
<include refid="userColumns"><property name="alias" value="t1"/></include>,
<include refid="userColumns"><property name="alias" value="t2"/></include>
from some_table t1
cross join some_table t2
</select>

使用 <sql> 标签定义可重用 SQL 片段,在 <include> 标签的 refid 属性指定 SQL 片段 id,在 <property> 标签中传入属性。

参数

<select id="selectPerson" parameterType="int" resultType="hashmap">
SELECT * FROM person WHERE id = #{id}
</select>

这是我们使用参数的最简单的情况,也足够满足大多数的应用场景。对于基本数据类型或简单数据类型,会直接用它们的值作为参数。

传入复杂对象作为对象

有时我们也希望传入 Java 对象作为参数。

<insert id="insertUser" parameterType="User">
INSERT INTO users (id, username, password)
VALUES (#{id}, #{username}, #{password})
</insert>

在 MyBatis 传入对象作为参数非常简单。上面示例传入了 User 对象作为参数,MyBatis 会在 User 对象内查找 id,username,password属性,然后将它们传入预处理语句中。

替换字符串

通常来说,我们使用 #{} 参数语法来创建参数占位符。如果你希望直接在 SQL 语句中插入字符串可以使用 ${}

我们来考虑一种情况,当我们想查询一个表的某一列数据时,我们会创建多条相似的 SQL 语句。

@Select("select * from user where id = #{id}")
User findById(@Param("id") long id);

@Select("select * from user where name = #{name}")
User findByName(@Param("name") String name);

@Select("select * from user where email = #{email}")
User findByEmail(@Param("email") String email);

// 其它的 "findByXxx" 方法

使用 ${} 动态生成列名。

@Select("select * from user where ${column} = #{value}")
User findByColumn(@Param("column") String column, @Param("value") String value);

${column} 会被作为字符串直接替换,#{value} 会进行预处理操作。

结果映射(resultMap)

MyBatis 会将结果集映射到 Java 对象上。比如使用 resultType 属性将查询结果映射到 User 对象上。

<select id="selectUsers" resultType="com.someapp.model.User">
select id, username, hashedPassword
from some_table
where id = #{id}
</select>

如果在 MyBatis 配置文件中为 Java 对象创建了别名,那么就不需要使用全限定类名了。

<!-- mybatis-config.xml 中 -->
<typeAlias type="com.someapp.model.User" alias="User"/>

<!-- SQL 映射 XML 中 -->
<select id="selectUsers" resultType="User">
select id, username, hashedPassword
from some_table
where id = #{id}
</select>

在上面的示例中,我们都没有显式地配置 resultMap 。它会根据表中的列名和 Java 对象的属性名进行映射。如果列名与 Java 对象属性名不匹配,可以在 SQL 语句中给列起别名。也可以显示配置 resultMap

<!-- 将列名与 Java 对象属性名一一对应 -->
<resultMap id="userResultMap" type="User">
<id property="id" column="user_id" />
<result property="username" column="user_name"/>
<result property="password" column="hashed_password"/>
</resultMap>
<select id="selectUsers" resultMap="userResultMap">
select user_id, user_name, hashed_password
from some_table
where id = #{id}
</select>

显式配置结果映射后在语句设置 resultMap 属性(而不是 resultType 属性)。


参考文档:MyBatis 文档

评论