MyBatis自诞生以来一直是一个以XML驱动的框架。配置是基于XML的,映射语句也是在XML中定义的。随着MyBatis 3的推出,有了新的选择。MyBatis 3建立在一个全面而强大的基于Java的配置API之上。该配置API是基于XML的MyBatis配置以及新的基于注解的配置的基础。注解提供了一种简单的方式来实现简单的映射语句,而不引入过多的开销。
注意:不幸的是,Java注解在表达能力和灵活性方面存在一定的限制。尽管我们花费了大量时间进行调查、设计和试验,但最强大的MyBatis映射无法只使用注解构建,除非变得荒谬可笑。C#的属性(例如)没有这些限制,因此MyBatis.NET将享受到一个更丰富的替代XML的选择。尽管如此,基于Java注解的配置也有其优点。
以下是一些注解:
Annotation(注解) | Target(目标) | XML equivalent(等价于) | Description(描述) |
---|---|---|---|
@CacheNamespace | Class | <cache> | 为给定的命名空间(即类)配置缓存。属性包括:implementation(实现类),eviction(淘汰策略),flushInterval(刷新间隔时间),size(缓存大小),readWrite(读写模式),blocking(是否阻塞),properties(其他属性)。 |
@Property | N/A | <property> | 指定属性的值或占位符(可以由在mybatis-config.xml中定义的配置属性替换)。属性包括:name(属性名),value(属性值)。(仅适用于MyBatis 3.4.2及以上版本) |
@CacheNamespaceRef | Class | <cacheRef> | 引用另一个命名空间的缓存以供使用。注意,即使XML映射文件中声明的缓存使用相同的全限定类名,它们仍被视为不同的命名空间。属性包括:value(命名空间对应的Java类型)和name(命名空间名称)。如果您使用这个注解,应该指定value或name属性之一。对于value属性,指定一个Java类型来表示命名空间(命名空间名称将成为指定的Java类型的全限定类名),而对于name属性(从3.4.2版本开始可用),指定一个表示命名空间的名称。 |
@ConstructorArgs | Method | <constructor> | 收集一组结果并传递给结果对象的构造函数。属性包括:value,是一个Args数组。 |
@Arg | N/A |
| 一个构造函数参数,作为ConstructorArgs集合的一部分。属性包括:id(标识符),column(列名),javaType(Java类型),jdbcType(JDBC类型),typeHandler(类型处理器),select(查询语句),resultMap(结果映射)。id属性是一个布尔值,用于标识用于比较的属性,类似于?<idArg> XML元素。从3.5.4版本开始,它可以作为可重复注解使用。 |
@TypeDiscriminator | Method | <discriminator> | 一组值情况,可用于确定要执行的结果映射。属性包括:column(列名),javaType(Java类型),jdbcType(JDBC类型),typeHandler(类型处理器),cases(值情况)。cases属性是一个Cases数组。 |
@Case | N/A | <case> | 一个值及其对应映射的单个情况。属性包括:value(值),type(类型),results(结果)。results属性是一个Results数组,因此这个Case注解类似于下面的Results注解,指定了一个实际的ResultMap。 |
@Results | Method | <resultMap> | 一个Result映射列表,包含了特定结果列如何映射到属性或字段的详细信息。属性包括:value(值),id(标识符)。value属性是一个Result注解的数组。id属性是结果映射的名称。 |
@Result | N/A |
| 一列与属性或字段之间的单个结果映射。属性包括:id(标识符),column(列名),property(属性名),javaType(Java类型),jdbcType(JDBC类型),typeHandler(类型处理器),one(单个关联),many(集合)。id属性是一个布尔值,表示该属性应该用于比较(类似于XML映射中的?<id> )。one属性用于单个关联,类似于?<association> ,而many属性用于集合,类似于?<collection> 。它们被命名为此,以避免类命名冲突。从3.5.4版本开始,它可以作为可重复注解使用。 |
@One | N/A | <association> | 一个复杂类型的单个属性值映射。属性包括:select(完全限定名称的映射语句,即映射器方法),fetchType(覆盖该映射的全局配置参数lazyLoadingEnabled),resultMap(自3.5.5版本可用,用于映射到从select结果中的单个容器对象的完全限定名称的结果映射),columnPrefix(自3.5.5版本可用,用于对嵌套结果映射中的select列进行分组的列前缀)。注意,您将注意到,通过注解API不支持连接映射。这是由于Java注解的限制,它不允许存在循环引用。 |
@Many | N/A | <collection> | 一个复杂类型的集合属性映射。属性包括:select(完全限定名称的映射语句,即映射器方法),fetchType(覆盖该映射的全局配置参数lazyLoadingEnabled),resultMap(自3.5.5版本可用,用于映射到从select结果中的集合对象的完全限定名称的结果映射),columnPrefix(自3.5.5版本可用,用于对嵌套结果映射中的select列进行分组的列前缀)。注意,您将注意到,通过注解API不支持连接映射。这是由于Java注解的限制,不允许存在循环引用。 |
@MapKey | Method | 该注解用于返回类型为Map的方法上。它用于根据结果对象的某个属性,将结果对象的列表转换为Map。属性包括:value,该属性作为Map的键值。 | |
@Options | Method | Attributes of mapped statements. | 该注解提供了访问映射语句上通常存在的各种开关和配置选项的途径。通过使用Options注解,可以以一致清晰的方式访问这些选项,而不会使每个语句注解变得复杂。属性包括:useCache=true、flushCache=FlushCachePolicy.DEFAULT、resultSetType=DEFAULT、statementType=PREPARED、fetchSize=-1、timeout=-1、useGeneratedKeys=false、keyProperty=""、keyColumn=""、resultSets="" 和 databaseId=""。 重要的是要了解,在Java注解中,无法指定null作为值。因此,一旦启用Options注解,您的语句将受到所有默认值的影响。请注意默认值,以避免意外行为发生。数据库标识(自3.5.5版本可用):如果存在已配置的DatabaseIdProvider,MyBatis将使用不带databaseId属性的Options注解,或者databaseId与当前databaseId匹配的Options注解。在找到带有和不带databaseId的情况下,后者将被丢弃。 ? |
| Method |
| 每个注解表示要执行的实际SQL语句。它们都接受一个字符串数组(或者只有一个字符串也可以)。如果传递了一个字符串数组,则会使用单个空格将它们连接起来,以分隔它们。这有助于在Java代码中构建SQL时避免“缺少空格”的问题。但是,如果您愿意,您也可以将单个字符串连接在一起。 属性包括:value,该属性是用于形成单个SQL语句的字符串数组。数据库标识(自3.5.5版本可用):如果存在已配置的DatabaseIdProvider,MyBatis将使用没有databaseId属性的语句,或者databaseId与当前databaseId匹配的语句。在找到带有和不带databaseId的情况下,后者将被丢弃。 |
| Method |
| 允许创建动态SQL。这些替代的SQL注解允许您指定一个类和一个方法名,在执行时返回要运行的SQL(自3.4.6版本开始,您可以将CharSequence指定为方法的返回类型而不是String)。在执行映射语句时,MyBatis将实例化该类,并按照提供程序指定的方式执行该方法。您可以通过ProviderContext(自MyBatis 3.4.5或更高版本可用)作为方法参数传递传递给映射器方法的对象,“Mapper接口类型”,“Mapper方法”和“Database ID”(在MyBatis 3.4或更高版本中,它允许多个参数)。 属性包括:value、type、method和databaseId。value和type属性都是类(type属性是value的别名,必须指定其中一个。但是,在指定defaultSqlProviderType为全局配置时,这两个属性都可以省略)。method是该类上的方法名称(自3.5.1版本以后,您可以省略method属性,MyBatis将通过ProviderMethodResolver接口解析目标方法。如果无法通过该接口解析,MyBatis将使用名为"provideSql"的保留回退方法)。数据库标识(自3.5.5版本可用):如果存在已配置的DatabaseIdProvider,MyBatis将使用没有databaseId属性的提供程序方法,或者databaseId与当前databaseId匹配的提供程序方法。在找到带有和不带databaseId的情况下,后者将被丢弃。 注意:在此部分之后,我们将讨论有关类的内容,它可以以更干净、更易读的方式构建动态SQL。 |
@Param | Parameter | N/A | 如果您的映射器方法接受多个参数,可以将此注解应用于映射器方法的参数,以为每个参数指定一个名称。否则,默认情况下,多个参数将按其位置以“param”为前缀进行命名(不包括任何RowBounds参数)。例如,#{param1}、#{param2}等是默认值。使用@Param("person")注解,则参数将被命名为#{person}。 |
@SelectKey | Method | <selectKey> | 该注解可以在使用@Insert、@InsertProvider、@Update或@UpdateProvider注解的方法中复制 属性包括:statement(字符串数组),表示要执行的SQL语句;keyProperty,表示将使用新值更新的参数对象的属性;before,必须为true或false,表示SQL语句在插入之前或之后执行;resultType,表示keyProperty的Java类型;statementType,表示statement的类型,可以是STATEMENT、PREPARED或CALLABLE,分别对应Statement、PreparedStatement和CallableStatement,默认为PREPARED。 数据库标识(自3.5.5版本可用):如果存在已配置的DatabaseIdProvider,MyBatis将使用没有databaseId属性的语句,或者databaseId与当前databaseId匹配的语句。在找到带有和不带databaseId的情况下,后者将被丢弃。 |
@ResultMap | Method | N/A | 此注解用于将XML映射器中的<resultMap>元素的ID提供给@Select或@SelectProvider注解。这允许使用注释的查询重用在XML中定义的resultmap。如果在注解的查询上同时指定了@Results或@ConstructorArgs注解,此注解将覆盖它们。 |
@ResultType | Method | N/A | 该注解用于使用结果处理器时。在这种情况下,返回类型是void,所以MyBatis必须有一种方法来确定每一行的对象类型。如果存在XML的resultmap,则使用@ResultMap注解。如果在<select>元素中指定了结果类型,则不需要其他注解。在其他情况下,请使用此注解。例如,如果@Select注解的方法将使用结果处理器,则返回类型必须是void,并且需要使用此注解(或@ResultMap)。除非方法的返回类型是void,否则将忽略此注解的使用。 |
@Flush | Method | N/A | 如果使用了此注解,可以通过在映射器接口中定义的方法调用SqlSession#flushStatements()。(适用于MyBatis 3.3或更高版本) |
以下示例展示了使用@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();
}
}
以下示例展示了使用@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提供器类共享给所有映射器方法的用法(自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);
}// 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();
}
}
该示例展示了在`@Statement`注解上使用`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();