Java API
现在您已了解如何配置 MyBatis 和创建映射,您已准备好进行更深入的学习。MyBatis Java API 是您获得努力成果的地方。您将看到,与 JDBC 相比,MyBatis 极大地简化了您的代码,并使其保持简洁、易于理解和维护。MyBatis 3 引入了许多重大改进,使使用 SQL 映射变得更好。
目录结构
在我们深入了解 Java API 之前,了解有关目录结构的最佳实践非常重要。MyBatis 非常灵活,您可以使用您的文件执行几乎任何操作。但与任何框架一样,都有一个首选方式。
让我们看看一个典型的应用程序目录结构
/my_application
/bin
/devlib
/lib <-- MyBatis *.jar files go here.
/src
/org/myapp/
/action
/data <-- MyBatis artifacts go here, including, Mapper Classes, XML Configuration, XML Mapping Files.
/mybatis-config.xml
/BlogMapper.java
/BlogMapper.xml
/model
/service
/view
/properties <-- Properties included in your XML Configuration go here.
/test
/org/myapp/
/action
/data
/model
/service
/view
/properties
/web
/WEB-INF
/web.xml
请记住,这些是首选项,而不是要求,但其他人会感谢您使用一个通用的目录结构。
本节中的其余示例将假定您遵循此目录结构。
SqlSessions
用于处理 MyBatis 的主要 Java 接口是 SqlSession。通过此接口,您可以执行命令、获取映射器和管理事务。我们很快会详细讨论 SqlSession 本身,但首先我们必须学习如何获取 SqlSession 的实例。SqlSession 由 SqlSessionFactory 实例创建。SqlSessionFactory 包含用于创建 SqlSessions 实例的所有不同方法。SqlSessionFactory 本身由 SqlSessionFactoryBuilder 创建,后者可以从 XML、注释或手工编码的 Java 配置创建 SqlSessionFactory。
注意在将 MyBatis 与 Spring 或 Guice 等依赖注入框架一起使用时,SqlSession 由 DI 框架创建和注入,因此您无需使用 SqlSessionFactoryBuilder 或 SqlSessionFactory,可以直接转到 SqlSession 部分。有关详细信息,请参阅 MyBatis-Spring 或 MyBatis-Guice 手册。
SqlSessionFactoryBuilder
SqlSessionFactoryBuilder 有五个 build() 方法,每个方法都允许您从不同的源构建 SqlSessionFactory。
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)
前四种方法是最常见的,因为它们采用引用 XML 文档的 InputStream 实例,或者更具体地说,采用上面讨论的 mybatis-config.xml 文件。可选参数是环境和属性。环境确定要加载哪个环境,包括数据源和事务管理器。例如
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
...
<dataSource type="POOLED">
...
</environment>
<environment id="production">
<transactionManager type="MANAGED">
...
<dataSource type="JNDI">
...
</environment>
</environments>
如果您调用采用环境参数的构建方法,则 MyBatis 将使用该环境的配置。当然,如果您指定无效的环境,您将收到错误。如果您调用不采用环境参数的其中一种构建方法,则使用默认环境(在上面的示例中指定为 default=“development”)。
如果您调用采用属性实例的方法,则 MyBatis 将加载那些属性并使其可用于您的配置。那些属性可以使用语法 ${propName} 代替配置中的大多数值。
回想一下,属性还可以从 mybatis-config.xml 文件中引用,或直接在其中指定。因此,了解优先级非常重要。我们之前在本文档中提到过,但这里再次提到以便于参考
如果某个属性存在于这些位置中的多个位置,则 MyBatis 将按以下顺序加载它们。
- 首先读取属性元素主体中指定的属性,
- 其次读取从属性元素的 classpath 资源或 url 属性加载的属性,并覆盖已指定的任何重复属性,
- 最后读取作为方法参数传递的属性,并覆盖可能已从属性主体和资源/url 属性加载的任何重复属性。
因此,优先级最高的属性是作为方法参数传递的属性,其次是资源/url 属性,最后是属性元素主体中指定的属性。
因此,总结一下,前四种方法基本上是相同的,但可以通过覆盖来允许您选择性地指定环境和/或属性。以下是如何从 mybatis-config.xml 文件构建 SqlSessionFactory 的示例。
String resource = "org/mybatis/builder/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(inputStream);
请注意,我们正在使用 Resources 实用程序类,该类位于 org.apache.ibatis.io 包中。顾名思义,Resources 类可帮助您从 classpath、文件系统甚至 Web URL 加载资源。快速查看类源代码或通过您的 IDE 进行检查将揭示其相当明显的实用方法集。这里有一个快速列表
URL getResourceURL(String resource)
URL getResourceURL(ClassLoader loader, String resource)
InputStream getResourceAsStream(String resource)
InputStream getResourceAsStream(ClassLoader loader, String resource)
Properties getResourceAsProperties(String resource)
Properties getResourceAsProperties(ClassLoader loader, String resource)
Reader getResourceAsReader(String resource)
Reader getResourceAsReader(ClassLoader loader, String resource)
File getResourceAsFile(String resource)
File getResourceAsFile(ClassLoader loader, String resource)
InputStream getUrlAsStream(String urlString)
Reader getUrlAsReader(String urlString)
Properties getUrlAsProperties(String urlString)
Class classForName(String className)
最终构建方法采用 Configuration 的一个实例。Configuration 类包含你可能需要了解的有关 SqlSessionFactory 实例的所有内容。Configuration 类可用于内省配置,包括查找和操作 SQL 映射(不建议在应用程序接受请求后使用)。configuration 类具有你已经了解的每个配置开关,只是作为 Java API 暴露出来。下面是手动创建一个 Configuration 实例并将其传递给 build() 方法以创建 SqlSessionFactory 的一个简单示例。
DataSource dataSource = BaseDataTest.createBlogDataSource();
TransactionFactory transactionFactory = new JdbcTransactionFactory();
Environment environment = new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.setLazyLoadingEnabled(true);
configuration.setEnhancementEnabled(true);
configuration.getTypeAliasRegistry().registerAlias(Blog.class);
configuration.getTypeAliasRegistry().registerAlias(Post.class);
configuration.getTypeAliasRegistry().registerAlias(Author.class);
configuration.addMapper(BoundBlogMapper.class);
configuration.addMapper(BoundAuthorMapper.class);
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(configuration);
现在,你拥有一个可用于创建 SqlSession 实例的 SqlSessionFactory。
SqlSessionFactory
SqlSessionFactory 有六个用于创建 SqlSession 实例的方法。通常,在选择其中一个方法时你将做出的决策是
- 事务:你希望为会话使用事务范围,还是使用自动提交(通常表示大多数数据库和/或 JDBC 驱动程序没有事务)?
- 连接:你希望 MyBatis 为你从已配置的 DataSource 获取一个 Connection,还是希望自己提供一个?
- 执行:你希望 MyBatis 重用 PreparedStatements 和/或批处理更新(包括插入和删除)吗?
重载的 openSession() 方法签名集合允许你选择这些选项的任何有意义的组合。
SqlSession openSession()
SqlSession openSession(boolean autoCommit)
SqlSession openSession(Connection connection)
SqlSession openSession(TransactionIsolationLevel level)
SqlSession openSession(ExecutorType execType, TransactionIsolationLevel level)
SqlSession openSession(ExecutorType execType)
SqlSession openSession(ExecutorType execType, boolean autoCommit)
SqlSession openSession(ExecutorType execType, Connection connection)
Configuration getConfiguration();
不带任何参数的默认 openSession() 方法将创建一个具有以下特征的 SqlSession
- 将启动一个事务范围(即,不自动提交)。
- 将从活动环境配置的 DataSource 实例获取一个
Connection
对象。 - 事务隔离级别将是驱动程序或数据源使用的默认级别。
- 不会重用任何 PreparedStatements,也不会批处理任何更新。
大多数方法都具有很强的自解释性。要启用自动提交,请将 true
值传递给可选的 autoCommit
参数。要提供你自己的连接,请将 Connection
实例传递给 connection
参数。请注意,没有覆盖来同时设置 Connection
和 autoCommit
,因为 MyBatis 将使用提供的连接对象当前正在使用的任何设置。MyBatis 为事务隔离级别使用一个名为 TransactionIsolationLevel
的 Java 枚举包装器,但除此之外,它们按预期工作,并具有 JDBC 支持的 5 个级别(NONE
、READ_UNCOMMITTED
、READ_COMMITTED
、REPEATABLE_READ
、SERIALIZABLE
)。
你可能不熟悉的一个参数是 ExecutorType
。此枚举定义了 3 个值
ExecutorType.SIMPLE
:此类型的执行器不执行任何特殊操作。它为每个语句执行创建一个新的 PreparedStatement。ExecutorType.REUSE
:此类型的执行器将重用 PreparedStatements。ExecutorType.BATCH
:此执行器将批处理所有更新语句,并在它们之间执行 SELECT 时根据需要对它们进行分隔,以确保易于理解的行为。
注意SqlSessionFactory 上还有一个我们没有提到的方法,即 getConfiguration()。此方法将返回 Configuration 实例,你可以使用它在运行时内省 MyBatis 配置。
注意如果你使用过 MyBatis 的早期版本,你就会记得会话、事务和批处理都是独立的。现在不再是这样了。所有这三个都整齐地包含在会话的范围内。你无需单独处理事务或批处理即可充分利用它们。
SqlSession
如上所述,SqlSession 实例是 MyBatis 中最强大的类。你可以在其中找到执行语句、提交或回滚事务以及获取映射器实例的所有方法。
SqlSession 类上有二十多种方法,因此让我们将它们分解为更易于理解的分组。
语句执行方法
这些方法用于执行在 SQL 映射 XML 文件中定义的 SELECT、INSERT、UPDATE 和 DELETE 语句。它们非常自解释,每个都采用语句的 ID 和参数对象,该对象可以是基本类型(自动装箱或包装)、JavaBean、POJO 或 Map。
<T> T selectOne(String statement, Object parameter)
<E> List<E> selectList(String statement, Object parameter)
<T> Cursor<T> selectCursor(String statement, Object parameter)
<K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey)
int insert(String statement, Object parameter)
int update(String statement, Object parameter)
int delete(String statement, Object parameter)
selectOne
和 selectList
之间的区别仅在于 selectOne
必须返回一个对象或 null
(无)。如果多于一个,将抛出异常。如果你不知道预期有多少个对象,请使用 selectList
。如果你想检查对象是否存在,最好返回一个计数(0 或 1)。selectMap
是一个特例,因为它旨在将结果列表转换为基于结果对象中某个属性的 Map
。由于并非所有语句都需要参数,因此这些方法被重载为不需要参数对象的版本。
insert
、update
和 delete
方法返回的值表示语句影响的行数。
<T> T selectOne(String statement)
<E> List<E> selectList(String statement)
<T> Cursor<T> selectCursor(String statement)
<K,V> Map<K,V> selectMap(String statement, String mapKey)
int insert(String statement)
int update(String statement)
int delete(String statement)
Cursor
提供与列表相同的结果,但它使用 Iterator
延迟获取数据。
try (Cursor<MyEntity> entities = session.selectCursor(statement, param)) {
for (MyEntity entity : entities) {
// process one entity
}
}
最后,有三个高级版本的 select
方法,允许您限制要返回的行范围,或提供自定义结果处理逻辑,通常用于非常大的数据集。
<E> List<E> selectList (String statement, Object parameter, RowBounds rowBounds)
<T> Cursor<T> selectCursor(String statement, Object parameter, RowBounds rowBounds)
<K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey, RowBounds rowbounds)
void select (String statement, Object parameter, ResultHandler<T> handler)
void select (String statement, Object parameter, RowBounds rowBounds, ResultHandler<T> handler)
RowBounds
参数导致 MyBatis 跳过指定数量的记录,以及将返回的结果数量限制为某个数字。RowBounds
类有一个构造函数来获取 offset
和 limit
,并且是不可变的。
int offset = 100;
int limit = 25;
RowBounds rowBounds = new RowBounds(offset, limit);
不同的驱动程序能够在这一点上实现不同的效率级别。为了获得最佳性能,请使用 SCROLL_SENSITIVE
或 SCROLL_INSENSITIVE
的结果集类型(换句话说:不是 FORWARD_ONLY
)。
ResultHandler
参数允许您按您喜欢的方式处理每一行。您可以将其添加到 List
中,创建一个 Map
、Set
,或者丢弃每个结果,而只保留计算的汇总总计。您可以使用 ResultHandler
做很多事情,而 MyBatis 本身在内部使用它来构建结果集列表。
从 3.4.6 开始,传递给 CALLABLE
语句的 ResultHandler
用于存储过程的每个 REFCURSOR
输出参数(如果有)。
该界面非常简单。
package org.apache.ibatis.session;
public interface ResultHandler<T> {
void handleResult(ResultContext<? extends T> context);
}
ResultContext
参数使您可以访问结果对象本身、创建的结果对象的数量以及 Boolean stop()
方法,您可以使用该方法阻止 MyBatis 加载更多结果。
使用 ResultHandler
有两个您应该注意的限制
- 使用
ResultHandler
调用的方法获取的数据不会被缓存。 - 在使用高级
resultMap
时,MyBatis 可能需要几行来构建一个对象。如果使用ResultHandler
,您可能会得到一个尚未填充其关联或集合的对象。
批量更新语句刷新方法
有一个方法用于刷新(执行)存储在 JDBC 驱动程序类中的批量更新语句。当 ExecutorType
为 ExecutorType.BATCH
时可以使用此方法。
List<BatchResult> flushStatements()
事务控制方法
控制事务范围有四种方法。当然,如果您选择使用自动提交或使用外部事务管理器,这些方法不起作用。但是,如果您使用由 Connection
实例管理的 JDBC 事务管理器,那么以下四种方法将派上用场
void commit()
void commit(boolean force)
void rollback()
void rollback(boolean force)
默认情况下,MyBatis 实际上不会提交,除非它检测到数据库已因调用 insert
、update
、delete
或启用了 affectData
的 select
而发生更改。如果您在不调用这些方法的情况下进行更改,则可以将 true
传递到 commit
和 rollback
方法中,以确保它们将被提交(请注意,您仍然无法强制处于自动提交模式的会话,或使用外部事务管理器的会话)。大多数情况下,您不必调用 rollback()
,因为如果您不调用提交,MyBatis 会为您执行此操作。但是,如果您需要对可能进行多次提交和回滚的会话进行更细粒度的控制,则可以使用回滚选项来实现此目的。
注意 MyBatis-Spring 和 MyBatis-Guice 提供声明式事务处理。因此,如果您将 MyBatis 与 Spring 或 Guice 一起使用,请参阅其特定手册。
本地缓存
MyBatis 使用两个缓存:本地缓存和二级缓存。
每次创建新会话时,MyBatis 都会创建一个本地缓存并将其附加到会话。在会话中执行的任何查询都将存储在本地缓存中,因此使用相同输入参数对同一查询的进一步执行不会命中数据库。本地缓存会在 update
、commit
、rollback
和 close
时被清除。
默认情况下,本地缓存数据用于整个会话持续时间。此缓存对于解析循环引用和加速重复嵌套查询是必需的,因此它永远不能完全禁用,但您可以将本地缓存配置为仅在语句执行期间使用,方法是设置 localCacheScope=STATEMENT
。
请注意,当 localCacheScope
设置为 SESSION
时,MyBatis 会返回对存储在本地缓存中的相同对象的引用。对返回的对象(列表等)的任何修改都会影响本地缓存内容,进而影响会话生命周期中从缓存返回的值。因此,作为最佳实践,不要修改 MyBatis 返回的对象。
您可以随时通过调用清除本地缓存
void clearCache()
确保关闭 SqlSession
void close()
您必须确保关闭打开的任何会话。确保这一点的最佳方法是使用以下工作单元模式
try (SqlSession session = sqlSessionFactory.openSession()) {
// following 3 lines are pseudocode for "doing some work"
session.insert(...);
session.update(...);
session.delete(...);
session.commit();
}
注意就像 SqlSessionFactory
一样,您可以通过调用 getConfiguration()
方法获取 SqlSession
使用的 Configuration
实例。
Configuration getConfiguration()
使用映射器
<T> T getMapper(Class<T> type)
虽然上述各种 insert
、update
、delete
和 select
方法很强大,但它们也很冗长,不是类型安全的,并且对您的 IDE 或单元测试的帮助也不如它们可能的那样大。我们已经在上面的“入门”部分中看到一个使用 Mapper
的示例。
因此,执行映射语句的更常见方法是使用 Mapper
类。Mapper
类只是一个接口,其中包含与 SqlSession
方法相匹配的方法定义。以下示例类演示了一些方法签名以及它们如何映射到 SqlSession
。
public interface AuthorMapper {
// (Author) selectOne("selectAuthor", 5);
Author selectAuthor(int id);
// (List<Author>) selectList("selectAuthors")
List<Author> selectAuthors();
// (Map<Integer,Author>) selectMap("selectAuthors", "id")
@MapKey("id")
Map<Integer, Author> selectAuthors();
// insert("insertAuthor", author)
int insertAuthor(Author author);
// updateAuthor("updateAuthor", author)
int updateAuthor(Author author);
// delete("deleteAuthor", 5)
int deleteAuthor(int id);
}
简而言之,每个 Mapper
方法签名都应与其关联的 SqlSession
方法的签名相匹配,但没有 String
参数 ID。相反,方法名称必须与映射语句 ID 相匹配。
此外,返回类型必须与单个结果的预期结果类型或多个结果或 Cursor
的数组或集合相匹配。支持所有通常的类型,包括:基元、Maps
、POJO 和 JavaBeans
。
注意映射器接口不需要实现任何接口或扩展任何类。只要方法签名可用于唯一标识相应的映射语句即可。
注意映射器接口可以扩展其他接口。在使用 XML 绑定到 Mapper
接口时,请确保在适当的命名空间中包含语句。此外,唯一的限制是您不能在层次结构中的两个接口中具有相同的方法签名(无论如何,这是一个坏主意)。
你可以将多个参数传递给映射器方法。如果你这样做,它们将被命名为文字“param”,后跟它们在参数列表中的位置,例如:#{param1}
、#{param2}
等。如果你希望更改参数的名称(仅限多个),则可以在参数上使用 @Param("paramName")
注解。
你还可以将 RowBounds
实例传递给该方法以限制查询结果。
映射器注解
从一开始,MyBatis 就一直是一个 XML 驱动的框架。配置基于 XML,映射语句在 XML 中定义。使用 MyBatis 3,有新的选项可用。MyBatis 3 建立在全面且强大的基于 Java 的配置 API 之上。此配置 API 是基于 XML 的 MyBatis 配置以及新的基于注解的配置的基础。注解提供了一种简单的方法来实现简单的映射语句,而不会引入大量开销。
注意不幸的是,Java 注解在其表达性和灵活性方面受到限制。尽管花费大量时间进行调查、设计和试验,但最强大的 MyBatis 映射根本无法使用注解构建——除非变得荒谬。C# 特性(例如)不受这些限制,因此 MyBatis.NET 将享受比 XML 更丰富的替代方案。也就是说,基于 Java 注解的配置并非没有好处。
注解如下
注解 | 目标 | XML 等效项 | 描述 |
---|---|---|---|
@CacheNamespace |
类 |
<cache> |
配置给定命名空间(即类)的缓存。属性:implementation 、eviction 、flushInterval 、size 、readWrite 、blocking 、properties 。 |
@Property |
不适用 | <property> |
指定属性值或占位符(可以通过在 mybatis-config.xml 中定义的配置属性替换)。属性:name 、value 。(适用于 MyBatis 3.4.2+) |
@CacheNamespaceRef |
类 |
<cacheRef> |
引用要使用的另一个命名空间的缓存。请注意,即使 XML 映射器文件中声明的缓存共享相同的 FQCN,它们也被视为一个单独的命名空间。属性:value 和 name 。如果你使用此注解,则应指定 value 或 name 属性。对于 value 属性,指定一个表示命名空间的 Java 类型(命名空间名称变为指定 Java 类型的 FQCN),对于 name 属性(此属性自 3.4.2 起可用),指定一个表示命名空间的名称。 |
@ConstructorArgs |
方法 |
<constructor> |
收集一组结果,传递给结果对象构造函数。属性:value ,它是 Arg 的数组。 |
@Arg |
不适用 |
|
单个构造函数参数,是 ConstructorArgs 集合的一部分。属性:id 、column 、javaType 、jdbcType 、typeHandler 、select 、resultMap 。id 属性是布尔值,用于标识要用于比较的属性,类似于 <idArg> XML 元素。从 3.5.4 开始,它可用作可重复注释。 |
@TypeDiscriminator |
方法 |
<discriminator> |
一组值案例,可用于确定要执行的结果映射。属性:column 、javaType 、jdbcType 、typeHandler 、cases 。cases 属性是 Case 的数组。 |
@Case |
不适用 | <case> |
单个值案例及其对应的映射。属性:value 、type 、results 。results 属性是 Results 的数组,因此此 Case 注释类似于实际的 ResultMap ,由下面的 Results 注释指定。 |
@Results |
方法 |
<resultMap> |
Result 映射列表,其中包含有关特定结果列如何映射到属性或字段的详细信息。属性:value 、id 。value 属性是 Result 注释的数组。id 属性是结果映射的名称。 |
@Result |
不适用 |
|
列与属性或字段之间的单个结果映射。属性:id 、column 、property 、javaType 、jdbcType 、typeHandler 、one 、many 。id 属性是布尔值,表示该属性应用于比较(类似于 XML 映射中的 <id> )。one 属性用于单个关联,类似于 <association> ,many 属性用于集合,类似于 <collection> 。它们被命名为它们,以避免类命名冲突。从 3.5.4 开始,它可用作可重复注释。 |
@One |
不适用 | <association> |
映射到复杂类型的单个属性值。属性:select ,它是映射语句(即映射器方法)的完全限定名称,该语句可以加载适当类型的实例。fetchType ,它取代了此映射的全局配置参数 lazyLoadingEnabled 。resultMap (从 3.5.5 开始可用),它是从选择结果映射到单个容器对象的 ResultMap 的完全限定名称。columnPrefix (从 3.5.5 开始可用),它是用于在嵌套结果映射中对选择列进行分组的列前缀。注意您会注意到,通过注释 API 不支持连接映射。这是由于 Java 注释的限制,它不允许循环引用。 |
@Many |
不适用 | <collection> |
对复杂类型的集合属性的映射。属性:select ,即映射语句(即映射器方法)的完全限定名称,该语句可以加载适当类型的实例集合。fetchType ,它取代了此映射的全局配置参数 lazyLoadingEnabled 。resultMap (自 3.5.5 起可用),即从选择结果映射到集合对象的完全限定名称。columnPrefix (自 3.5.5 起可用),即用于在嵌套结果映射中对选择列进行分组的列前缀。注意您会注意到,联接映射不受注释 API 支持。这是由于 Java 注释中的限制,它不允许循环引用。 |
@MapKey |
方法 |
用于返回类型为 Map 的方法。它用于基于这些对象的属性将结果对象列表转换为 Map。属性:value ,用作映射键的属性。 | |
@Options |
方法 |
映射语句的属性。 | 此注释提供了对映射语句上通常作为属性存在的各种开关和配置选项的访问。Options 注释提供了一种一致且清晰的方式来访问这些选项,而不是使每个语句注释复杂化。属性:useCache=true 、flushCache=FlushCachePolicy.DEFAULT 、resultSetType=DEFAULT 、statementType=PREPARED 、fetchSize=-1 、timeout=-1 、useGeneratedKeys=false 、keyProperty="" 、keyColumn="" 、resultSets="" 和 databaseId="" 。了解一点很重要,使用 Java 注释,无法将 null 指定为值。因此,一旦使用 Options 注释,您的语句将受所有默认值约束。注意默认值是什么,以避免意外行为。databaseId (自 3.5.5 起可用),如果配置了 DatabaseIdProvider ,MyBatis 将使用没有 databaseId 属性或具有与当前 databaseId 匹配的 Options 。如果使用和不使用 databaseId 找到,后者将被丢弃。请注意, keyColumn 仅在某些数据库(如 Oracle 和 PostgreSQL)中需要。有关这些属性中允许值的更多信息,请参阅上面插入语句讨论中的有关 keyColumn 和 keyProperty 的讨论。 |
|
方法 |
|
这些注释中的每一个都表示要执行的实际 SQL。它们各自接受一个字符串数组(或一个字符串)。如果传递了一个字符串数组,则它们之间用一个空格连接以分隔它们。这有助于避免在 Java 代码中构建 SQL 时出现的“缺少空格”问题。但是,如果您愿意,也可以将一个字符串连接在一起。属性:value ,它是要形成单个 SQL 语句的字符串数组。databaseId (自 3.5.5 起可用),如果配置了 DatabaseIdProvider ,MyBatis 将使用没有 databaseId 属性或与当前属性匹配的 databaseId 的语句。如果同时找到带和不带 databaseId 的语句,则后者将被丢弃。 |
|
方法 |
|
允许创建动态 SQL。这些备用 SQL 注释允许您指定一个类和一个方法名称,该方法将在执行时返回要运行的 SQL(自 3.4.6 起,您可以指定 CharSequence 而不是 String 作为方法返回类型)。在执行映射语句时,MyBatis 将实例化该类,并按提供程序指定的执行该方法。您可以通过 ProviderContext (自 MyBatis 3.4.5 或更高版本可用)作为方法参数传递传递给映射器方法、"映射器接口类型"、"映射器方法" 和 "数据库 ID" 的对象。(在 MyBatis 3.4 或更高版本中,它允许多个参数)属性:value 、type 、method 和 databaseId 。value 和 type 属性是一个类(type 属性是 value 的别名,您必须指定其中一个。但在指定 defaultSqlProviderType 作为全局配置时,可以省略这两个属性)。method 是该类上的方法名称(自 3.5.1 起,您可以省略 method 属性,MyBatis 将通过 ProviderMethodResolver 接口解析目标方法。如果未通过它解析,MyBatis 将使用名为 provideSql 的保留后备方法)。databaseId (自 3.5.5 起可用),如果配置了 DatabaseIdProvider ,MyBatis 将使用没有 databaseId 属性或与当前属性匹配的 databaseId 的提供程序方法。如果同时找到带和不带 databaseId 的语句,则后者将被丢弃。注意本节后面是对该类的讨论,该类可以帮助以更简洁、更易于阅读的方式构建动态 SQL。 |
@Param |
参数 |
不适用 | 如果你的映射器方法采用多个参数,则可以将此注释应用到映射器方法参数,以便为每个参数指定一个名称。否则,多个参数将按其位置命名,并以“param”为前缀(不包括任何 RowBounds 参数)。例如,#{param1} 、#{param2} 等是默认值。使用 @Param("person") ,参数将被命名为 #{person} 。 |
@SelectKey |
方法 |
<selectKey> |
此注释为使用 @Insert 、@InsertProvider 、@Update 或 @UpdateProvider 注释的方法复制了 <selectKey> 功能。它被其他方法忽略。如果你指定了 @SelectKey 注释,则 MyBatis 将忽略通过 @Options 注释或配置属性设置的任何生成键属性。属性:statement 是要执行的 SQL 语句的字符串数组,keyProperty 是将使用新值更新的参数对象的属性,before 必须为 true 或 false ,表示 SQL 语句应在插入之前还是之后执行,resultType 是 keyProperty 的 Java 类型,statementType 是语句的类型,可以是 STATEMENT 、PREPARED 或 CALLABLE ,分别映射到 Statement 、PreparedStatement 和 CallableStatement 。默认值为 PREPARED 。databaseId (自 3.5.5 起可用),如果配置了 DatabaseIdProvider ,MyBatis 将使用没有 databaseId 属性或 databaseId 与当前 databaseId 匹配的语句。如果同时找到了有和没有 databaseId 的语句,则将丢弃后者。 |
@ResultMap |
方法 |
不适用 | 此注释用于将 XML 映射器中 <resultMap> 元素的 ID 提供给 @Select 或 @SelectProvider 注释。这允许带注释的选择重用在 XML 中定义的结果集。如果在带注释的选择上同时指定了 @Results 或 @ConstructorArgs 注释,此注释将覆盖它们。 |
@ResultType |
方法 |
不适用 | 使用结果处理程序时使用此注释。在这种情况下,返回类型为 void,因此 MyBatis 必须有一种方法来确定为每行构造的对象类型。如果有 XML 结果集,请使用 @ResultMap 注释。如果结果类型在 <select> 元素上的 XML 中指定,则不需要其他注释。在其他情况下,请使用此注释。例如,如果带 @Select 注释的方法将使用结果处理程序,则返回类型必须为 void,并且需要此注释(或 @ResultMap)。除非方法返回类型为 void,否则将忽略此注释。 |
@Flush |
方法 |
不适用 | 如果使用了此注释,则可以通过映射器接口中定义的方法调用 SqlSession#flushStatements() 。(MyBatis 3.3 或更高版本) |
Mapper 注解示例
此示例展示了在插入之前使用 @SelectKey 注解从序列中检索值
@Insert("insert into table3 (id, name) values(#{nameId}, #{name})")
@SelectKey(statement="call next value for TestSequence", keyProperty="nameId", before=true, resultType=int.class)
int insertTable3(Name name);
此示例展示了在插入之后使用 @SelectKey 注解检索标识值
@Insert("insert into table2 (name) values(#{name})")
@SelectKey(statement="call identity()", keyProperty="nameId", before=false, resultType=int.class)
int insertTable2(Name name);
此示例展示了使用 @Flush
注解来调用 SqlSession#flushStatements()
@Flush
List<BatchResult> flush();
这些示例展示了如何通过指定 @Results 注解的 id 属性来命名 ResultMap。
@Results(id = "userResult", value = {
@Result(property = "id", column = "uid", id = true),
@Result(property = "firstName", column = "first_name"),
@Result(property = "lastName", column = "last_name")
})
@Select("select * from users where id = #{id}")
User getUserById(Integer id);
@Results(id = "companyResults")
@ConstructorArgs({
@Arg(column = "cid", javaType = Integer.class, id = true),
@Arg(column = "name", javaType = String.class)
})
@Select("select * from company where id = #{id}")
Company getCompanyById(Integer id);
此示例展示了使用 SelectProvider 注解的单一参数
@SelectProvider(type = UserSqlBuilder.class, method = "buildGetUsersByName")
List<User> getUsersByName(String name);
class UserSqlBuilder {
public static String buildGetUsersByName(final String name) {
return new SQL(){{
SELECT("*");
FROM("users");
if (name != null) {
WHERE("name like #{value} || '%'");
}
ORDER_BY("id");
}}.toString();
}
}
此示例展示了使用 Sql Provider 注解的多重参数
@SelectProvider(type = UserSqlBuilder.class, method = "buildGetUsersByName")
List<User> getUsersByName(
@Param("name") String name, @Param("orderByColumn") String orderByColumn);
class UserSqlBuilder {
// If not use @Param, you should be define same arguments with mapper method
public static String buildGetUsersByName(
final String name, final String orderByColumn) {
return new SQL(){{
SELECT("*");
FROM("users");
WHERE("name like #{name} || '%'");
ORDER_BY(orderByColumn);
}}.toString();
}
// If use @Param, you can define only arguments to be used
public static String buildGetUsersByName(@Param("orderByColumn") final String orderByColumn) {
return new SQL(){{
SELECT("*");
FROM("users");
WHERE("name like #{name} || '%'");
ORDER_BY(orderByColumn);
}}.toString();
}
}
此示例展示了使用全局配置将一个 sql provider 类共享给所有 mapper 方法(自 3.5.6 起可用)
Configuration configuration = new Configuration();
configuration.setDefaultSqlProviderType(TemplateFilePathProvider.class); // Specify an sql provider class for sharing on all mapper methods
// ...
// Can omit the type/value attribute on sql provider annotation
// If omit it, the MyBatis apply the class that specified on defaultSqlProviderType.
public interface UserMapper {
@SelectProvider // Same with @SelectProvider(TemplateFilePathProvider.class)
User findUser(int id);
@InsertProvider // Same with @InsertProvider(TemplateFilePathProvider.class)
void createUser(User user);
@UpdateProvider // Same with @UpdateProvider(TemplateFilePathProvider.class)
void updateUser(User user);
@DeleteProvider // Same with @DeleteProvider(TemplateFilePathProvider.class)
void deleteUser(int id);
}
此示例展示了使用 ProviderMethodResolver
的默认实现(自 MyBatis 3.5.1 或更高版本可用)
@SelectProvider(UserSqlProvider.class)
List<User> getUsersByName(String name);
// Implements the ProviderMethodResolver on your provider class
class UserSqlProvider implements ProviderMethodResolver {
// In default implementation, it will resolve a method that method name is matched with mapper method
public static String getUsersByName(final String name) {
return new SQL(){{
SELECT("*");
FROM("users");
if (name != null) {
WHERE("name like #{value} || '%'");
}
ORDER_BY("id");
}}.toString();
}
}
此示例展示了在语句注解上使用 databaseId
属性(自 3.5.5 起可用)
@Select(value = "SELECT SYS_GUID() FROM dual", databaseId = "oracle") // Use this statement if DatabaseIdProvider provide "oracle"
@Select(value = "SELECT uuid_generate_v4()", databaseId = "postgres") // Use this statement if DatabaseIdProvider provide "postgres"
@Select("SELECT RANDOM_UUID()") // Use this statement if the DatabaseIdProvider not configured or not matches databaseId
String generateId();