mybatis常用注解

1 介绍
mybatis 最初配置信息是基于 XML ,映射语句(SQL)也是定义在 XML 中的。而到了 MyBatis 3提供了新的基于注解的配置。mybatis提供的注解有很多,大致可以分为以下几类:
-
增删改查:@Insert、@Update、@Delete、@Select、@MapKey、@Options、@SelelctKey、@Param、@InsertProvider、@UpdateProvider、@DeleteProvider、@SelectProvider
-
结果集映射:@Results、@Result、@ResultMap、@ResultType、@ConstructorArgs、@Arg、@One、@Many、@TypeDiscriminator、@Case
-
缓存:@CacheNamespace、@Property、@CacheNamespaceRef、@Flush
绝大部分注解,在xml映射文件中都有元素与之对应,但是不是所有。此外在mybatis-spring中提供了@Mapper注解和@MapperScan注解,用于和spring进行整合。
2 增删改查相关注解
| 注解 | 使用对象 | 相对应的 XML | 描述 |
|---|---|---|---|
| @Insert @Update @Delete @Select | 方法 | 这四个注解分别代表将会被执行的 SQL 语句。它们用字符串数组(或单个字符串)作为参数。如果传递的是字符串数组,字符串之间先会被填充一个空格再连接成单个完整的字符串。这有效避免了以 Java 代码构建 SQL 语句时的“丢失空格”的问题。然而,你也可以提前手动连接好字符串。属性有:value,填入的值是用来组成单个 SQL 语句的字符串数组。 | |
| @Options | 方法 | 映射语句的属性 | 这个注解提供访问大范围的交换和配置选项的入口,它们通常在映射语句上作为属性出现。Options 注解提供了通俗易懂的方式来访问它们,而不是让每条语句注解变复杂。属性有:useCache=true, flushCache=FlushCachePolicy.DEFAULT, resultSetType=FORWARD_ONLY, statementType=PREPARED, fetchSize=-1, timeout=-1, useGeneratedKeys=false, keyProperty=“id”, keyColumn=“”, resultSets=“”。值得一提的是, Java 注解无法指定 null 值。因此,一旦你使用了 Options 注解,你的语句就会被上述属性的默认值所影响。要注意避免默认值带来的预期以外的行为。 注意: keyColumn 属性只在某些数据库中有效(如 Oracle、PostgreSQL等)。请在插入语句一节查看更多关于 keyColumn 和 keyProperty 两者的有效值详情。 |
| @MapKey | 方法 | 这是一个用在返回值为 Map 的方法上的注解。它能够将存放对象的 List 转化为 key 值为对象的某一属性的 Map。属性有: value,填入的是对象的属性名,作为 Map 的 key 值。 | |
| @SelectKey | 方法 | 这个注解的功能与 |
|
| @Param | 参数 | N/A | 如果你的映射方法的形参有多个,这个注解使用在映射方法的参数上就能为它们取自定义名字。若不给出自定义名字,多参数(不包括 RowBounds 参数)则先以 “param” 作前缀,再加上它们的参数位置作为参数别名。例如 #{param1}, #{param2},这个是默认值。如果注解是 @Param(“person”),那么参数就会被命名为 #{person}。 |
| @InsertProvider @UpdateProvider @DeleteProvider @SelectProvider | 方法 | 允许构建动态 SQL。这些备选的 SQL 注解允许你指定类名和返回在运行时执行的 SQL 语句的方法。(自从MyBatis 3.4.6开始,你可以用 CharSequence 代替 String 来返回类型返回值了。)当执行映射语句的时候,MyBatis 会实例化类并执行方法,类和方法就是填入了注解的值。你可以把已经传递给映射方法了的对象作为参数,“Mapper interface type” 和 “Mapper method” 会经过 ProviderContext (仅在MyBatis 3.4.5及以上支持)作为参数值。(MyBatis 3.4及以上的版本,支持多参数传入)属性有: type, method。type 属性需填入类。method 需填入该类定义了的方法名。注意 接下来的小节将会讨论类,能帮助你更轻松地构建动态 SQL。 |
映射器接口示例,假设有以下UserMapper接口:
1 | public interface UserMapper { |
说明:
2.1 @Insert、@Update、@Delete、@Options、@SelectKey注解
mybatis会根据接口方法上的@Insert、@Update、@Delete注解,分别去调用SqlSession的insert、update、delete方法。这个几个方法返回的都是一个int,表示影响的记录行数。
特别的,在UserMapper接口的insert方法上,除了添加了@Insert注解,还添加了@Options注解。在上面的案例中,@Options注解用于获取自动生成主键,并设置到User实体中。此外,@SelectKey注解也可以用于获取自动生成的主键,使用方式如下:
1 | @Insert("INSERT INTO user(id,name) VALUES (#{id},#{name})") |
2.2 @Select注解
在上面的案例中,UserMapper的selectById、selectAll、selectMap、selectByIds、selectPage方法都添加了@Select注解。mybatis会根据方法的返回值类型User、List
2.3 @MapKey注解
特别的,对于返回值是Map的情况,UserMapper的selectMap方法上额外添加了一个@MapKey(“id”)注解,表示将User实例的id属性当做Map的key。
2.4 动态sql与,这是因为SQL中使用了动态sql标签。不管是@Insert、@Update、@Delete、@Select注解,只要SQL里使用了mybatis的动态sql标签(包括:if、choose …when …otherwise、trim 、where、 set、foreach、bind)等,都建议在sql前后分别加上,否则可能会出现一些参数找不到的情况。
2.5 @Param注解
@Param注解用于给方法参数起一个名字。以下是笔者总结的使用原则:
-
在方法只接受一个参数的情况下,可以不使用@Param。
-
在方法接受多个参数的情况下,建议一定要使用@Param注解给参数命名。
例如上述案例的selectPage方法接受2个参数,所以其两个参数都使用了@Param注解。@Param注解看起来配置最简单,实际上理解确实最复杂,下面进行详细的介绍。
前面已经提到,当映射器接口定义的方法被调用时,mybatis内部根据方法上注解:@Insert、@Update、@Delete、@Select来选择调用SqlSession的insert、update、delete、selectXXX方法。以下是SqlSession接口的相关方法定义(部分省略):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public interface SqlSession extends Closeable {
...
<T> T selectOne(String statement);
<T> T selectOne(String statement, Object parameter);
<E> List<E> selectList(String statement);
<E> List<E> selectList(String statement, Object parameter);
<K, V> Map<K, V> selectMap(String statement, String mapKey);
<K, V> Map<K, V> selectMap(String statement, Object parameter, String mapKey);
...
int insert(String statement);
int insert(String statement, Object parameter);
int update(String statement);
int update(String statement, Object parameter);
int delete(String statement);
int delete(String statement, Object parameter);
...
}
可以看到部分方法接受一个Object类型的parameter参数,另外一些方法则没有此参数。mybatis在执行前,除了会根据方法上注解:@Insert、@Update、@Delete、@Select来选择调用SqlSession的insert、update、delete、selectXXX方法;还会提前对传入映射器接口方法中的参数进行一些处理,然后再调用SqlSession的相应方法。逻辑如下:
1、如果映射器接口方法不接受参数,mybatis在执行时会调用相应无parameter参数的方法重载形式。例如,如UserMapper接口的selectAll方法,其不接受参数,返回值类型为List,因此调用SqlSession不接受paramter参数的selectList方法:
1
<E> List<E> selectList(String statement);
2、如果映射器方法只有一个参数,并且这个参数没有使用@Param注解,则直接用这个参数来调用SqlSession相应接受Object类型parameter方法参数的重载形式。例如,如UserMapper接口的selectByIds方法,其接受1个int[]数组类型参数作为查询条件,返回值类型为List,因此调用SqlSession接受paramter参数的selectList方法:
1
<E> List<E> selectList(String statement, Object parameter);
3、如果映射器方法只接受一个参数,但是使用了@Param注解,也会先封装到Map中;活着映射器方法总是接受多个参数,不管有没有使用@Param注解指定参数,总是会先封装到一个Map中。之后,调用SqlSession的相应方法把这个Map当做parameter参数传入。
例如:UserMapper接口的selectPage方法, 通过@Param("offset”)和@Param("limit”)为2个int参数指定了名字。
1
2
@Select("SELECT id,name FROM user LIMIT #{offset},#{limit}")
public List<User> selectPage(@Param("offset") int offset, @Param("limit") int limit);
假设我们传入0和10,那么参数封装后的Map结构如下:
key value
--------------------
offset 0 //1
limit 10 //2
param1 0 //3
param2 10 //4
关于Map中1、2两个key-value,比较好理解,是我们通过@Param注解指定的映射关系。而3、4两个key-value,实际上是参数位置(param1、param2…,下标从1开始)和参数值的映射关系,不管我们有没有使用@Param注解,存在多个参数的情况下,我们总是可以按照位置进行引用。
因此,将UserMapper的selectPage方法定义改成以下形式也是正确的:
1
2
3
4
5
6
7
//使用了@Param注解的情况下,依然根据参数位置进行引用(param1,param2…,下标从1开始)
@Select("SELECT id,name FROM user LIMIT #{param1},#{param2}")
public List<User> selectPage(@Param("offset") int offset, @Param("limit") int limit);
//不使用@Param注解,直接根据参数位置进行引用
@Select("SELECT id,name FROM user LIMIT #{param1},#{param2}")
public List<User> selectPage(int offset, int limit);
显然,根据参数位置进行引用不太直观,因此建议在存在多个参数的情况,总是通过@Param注解显式的指定参数名。
2.6 @InsertProvider、@UpdateProvider、@DeleteProvider、@SelectProvider注解
这几个注解主要用于动态sql构建。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
public interface UserBuilderMapper {
@SelectProvider(type = UserSqlBuilder.class, method = "buildSelectByIdSql")
public User selectById(@Param("id") int id);
@InsertProvider(type = UserSqlBuilder.class, method = "buildInsertSql")
@Options(useGeneratedKeys = true, keyColumn = "id", keyProperty = "id")
public int insert(User user);
@UpdateProvider(type = UserSqlBuilder.class, method = "buildUpdateSql")
public int update(User user);
@DeleteProvider(type = UserSqlBuilder.class, method = "buildDeleteSql")
public int delete(@Param("id") int id);
//建议将sql builder以映射器接口内部类的形式进行定义
public static class UserSqlBuilder {
public static String buildSelectByIdSql(@Param("id") int id) {
return new SQL() {
{
SELECT("id, name");
FROM("user");
WHERE("id=#{id}");
}
}.toString();
}
public static String buildInsertSql(User user) {
return new SQL() {
{
INSERT_INTO("user");
VALUES("name", "#{name}");
}
}.toString();
}
public static String buildUpdateSql(User user) {
return new SQL() {
{
UPDATE("user");
SET("name=#{name}");
WHERE("id=#{id}");
}
}.toString();
}
public static String buildDeleteSql(@Param("id") int id) {
return new SQL() {
{
DELETE_FROM("user");
WHERE("id=#{id}");
}
}.toString();
}
}
}
}
3 结果集映射相关注解
注解
使用对象
相对应的 XML
描述
@Results
方法
结果映射的列表,包含了一个特别结果列如何被映射到属性或字段的详情。属性有:value, id。value 属性是 Result 注解的数组。这个 id 的属性是结果映射的名称。
@Result
方法
在列和属性或字段之间的单独结果映射。属性有:id, column, javaType, jdbcType, typeHandler, one, many。id 属性是一个布尔值,来标识应该被用于比较(和在 XML 映射中的相似)的属性。one 属性是单独的联系,和 相似,而 many 属性是对集合而言的,和相似。它们这样命名是为了避免名称冲突。
@ResultMap
方法
N/A
这个注解给 @Select 或者 @SelectProvider 提供在 XML 映射中的 的id。这使得注解的 select 可以复用那些定义在 XML 中的 ResultMap。如果同一 select 注解中还存在 @Results 或者 @ConstructorArgs,那么这两个注解将被此注解覆盖。
@ResultType
方法
N/A
此注解在使用了结果处理器的情况下使用。在这种情况下,返回类型为 void,所以 Mybatis 必须有一种方式决定对象的类型,用于构造每行数据。如果有 XML 的结果映射,请使用 @ResultMap 注解。如果结果类型在 XML 的
@ConstructorArgs
方法
收集一组结果传递给一个结果对象的构造方法。属性有:value,它是形式参数数组。
@Arg
N/A
单参数构造方法,是 ConstructorArgs 集合的一部分。属性有:id, column, javaType, jdbcType, typeHandler, select和 resultMap。id 属性是布尔值,来标识用于比较的属性,和 XML 元素相似。
@One
N/A
复杂类型的单独属性值映射。属性有:select,已映射语句(也就是映射器方法)的全限定名,它可以加载合适类型的实例。fetchType会覆盖全局的配置参数 lazyLoadingEnabled。注意 联合映射在注解 API中是不支持的。这是因为 Java 注解的限制,不允许循环引用。
@Many
N/A
映射到复杂类型的集合属性。属性有:select,已映射语句(也就是映射器方法)的全限定名,它可以加载合适类型的实例的集合,fetchType 会覆盖全局的配置参数 lazyLoadingEnabled。注意 联合映射在注解 API中是不支持的。这是因为 Java 注解的限制,不允许循环引用
@TypeDiscriminator
方法
一组实例值被用来决定结果映射的表现。属性有:column, javaType, jdbcType, typeHandler 和 cases。cases 属性是实例数组。
@Case
N/A
单独实例的值和它对应的映射。属性有:value, type, results。results 属性是结果数组,因此这个注解和实际的 ResultMap 很相似,由下面的 Results 注解指定。
3.1 @ConstructorArgs、@Arg注解
如果实体类没有无参的构造方法,那么我们就必须通过@ConstructorArgs与@Arg注解来提供实体类的构造方法参数信息。例如上述UserMapper的selectById方法,其返回一个User对象。假设User对象只有以下构造方法:
1
2
3
4
public User(Integer id, String name) {
this.id = id;
this.name = name;
}
那么需要在selectById方法上,添加@ConstructorArgs注解,提供构造方法信息,如下:
1
2
3
4
5
@Select("SELECT id,name FROM user where id= #{id}")
@ConstructorArgs({
@Arg(column = "id",javaType = Integer.class,id = true),
@Arg(column = "name",javaType = String.class)})
public User selectById(int id);
注意,通常情况下,我们都建议为实体类提供无参的构造方法的,这是最佳实践的总结,因此@ConstructorArgs注解和@Arg注解基本使用不到。
3.2 @Results、@Result注解
如果实体字段的名称与数据库表字段名称不一致时,我们就需要显式的指定映射关系。这是通过@Results、@Result注解来指定的,例如为UserMapper的selectById指定映射关系:
1
2
3
4
5
@Select("SELECT id,name FROM user where id= #{id}")
@Results(id = "userMap", value = {
@Result(property = "id", column = "id", javaType = Integer.class,jdbcType = JdbcType.INTEGER,id = true),
@Result(property = "name", column = "name",javaType = String.class,jdbcType = JdbcType.VARCHAR)})
public User selectById(int id);
其中:
@Results注解:id属性用于给这个映射关系起一个名字(这里指定的为userMap),其内部还包含了一个@Result[]来表示实体属性和数据库表字段的映射关系
@Result注解:property属性是java实体属性的名称,column表示对应的数据库字段的名称。javaType和JdbcType属性可以不指定。
3.3 @ResultMap注解
上述selectById方法已经通过@Results注解指定了结果映射关系,通过@ConstructorArgs指定了构造方法(必要的情况下才使用),那么在其他的查询方法中,我们不需要重复定义,可以通过@ResultMap来引用@Results的id属性值进行复用。如我们在UserMapper的selectAll方法进行复用:
1
2
3
@Select("SELECT id,name FROM user")
@ResultMap("userMap")
public List<User> selectAll();
3.4 @TypeDiscriminator、@Case注解
鉴别器注解基本没用,有空的时候再补充
3.5 @Many、@One注解
主要用于关联关系映射
假设有作者(author)和文章(article)两张数据库表,一个author可以有多个article,一个article只能属于一个author。相关表结构以及初始数据如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
--author表
CREATE TABLE `author` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `author` (`id`, `name`)
VALUES
(1, 'tianshouzhi');
--article表
CREATE TABLE `article` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`title` varchar(255) NOT NULL,
`content` longtext NOT NULL,
`author_id` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `article` (`id`, `title`, `content`, `author_id`)
VALUES
(1, 'title1', 'content1', 1),
(2, 'title2', 'content2', 1);
相应的Java实体类如下所示:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class Article {
private Integer id;
private String title;
private String content;
private Author author;
//...setters getters and toString...
}
public class Author {
private Integer id;
private String name;
private List<Article> articles;
//...setters getters and toString...
}
映射器接口定义分别如下:
ArticleMapper
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public interface ArticleMapper {
//1、根据文章id查询文章Article对象,同时通过One注解关联查询出作者Author信息
@Select("SELECT id,title,content,author_id FROM article where id= #{articleId}")
@Results(id = "articleWithAuthor", value = {
@Result(property = "id", column = "id"),
@Result(property = "title", column = "title"),
@Result(property = "content", column = "content"),
//property属性:指定将关联查询的结果封装到Article对象的author属性上
//column属性指定:指定在执行@One注解中定义的select语句时,把article表的author_id字段当做参数传入
//one属性:通过@One注解定义关联查询的语句是AuthorMapper中的findAuthorByAuthorId方法
@Result(property = "author",column = "author_id”,
one = @One(select = "com.tianshouzhi.mapper.AuthorMapper.findAuthorByAuthorId"))})
public Article findArticleWithAuthorByArticleId(@Param("articleId") int articleId);
//2、根据作者(Author)的id查询其所有的文章(Article)
@Select("SELECT id,title,content,author_id FROM article WHERE author_id=#{authorId}")
@Results(id = "articlesWithoutAuthor", value = {
@Result(property = "id", column = "id"),
@Result(property = "title", column = "title"),
@Result(property = "content", column = "content")})
List<Article> findArticlesByAuthorId(@Param("authorId") int authorId);
}
AuthorMapper
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public interface AuthorMapper {
//根据作者id查询Author信息
@Select("SELECT id,name FROM author WHERE id=#{authorId}")
Author findAuthorByAuthorId(int authorId);
//根据作者id查询Author信息,通过@Many注解关联查询出所有的文章信息
@Select("SELECT id,name FROM author WHERE id=#{authorId}")
@Results(id = "authorWithArticles", value = {
@Result(property = "id", column = "id"),
@Result(property = "name", column = "name”),
//property属性:指定将关联查询的结果封装到Author对象的articles属性上
//column属性指定:指定在执行@Many注解中定义的select语句时,把author表的id字段当做参数传入
//many属性:指定通过@Many注解定义关联查询的语句是ArticleMapper中的findArticlesByAuthorId方法
@Result(property = "articles",column = "id”,
many = @Many(select = "com.tianshouzhi.mapper.ArticleMapper.findArticlesByAuthorId"))})
Author findAuthorWithArticlesByAuthorId(int authorId);
}
测试如下:
1
2
3
4
5
6
7
8
9
10
@Test
public void testOneAndMany(){
System.out.println("===========通过@One注解查询出Article关联的Auhtor===========");
Article article = articleMapper.findArticleWithAuthorByArticleId(1);
System.out.println(article);
System.out.println("===========通过@Many注解查询出Auhtor关联的Article==========");
Author author = authorMapper.findAuthorWithArticlesByAuthorId(1);
System.out.println(author);
}
控制台输出结果为:
===========通过@One注解查询出Article关联的Auhtor===========
Article{id=1, title=‘title1’, content=‘content1’, author=Author{id=1, name=‘tianshouzhi’, articles=null}}
===========通过@Many注解查询出Auhtor关联的Article==========
Author{id=1, name=‘tianshouzhi’, articles=[Article{id=1, title=‘title1’, content=‘content1’, author=null}, Article{id=2, title=‘title2’, content=‘content2’, author=null}]}
4 缓存相关注解
注解
使用对象
相对应的 XML
描述
@CacheNamespace
类
为给定的命名空间(比如类)配置缓存。属性有:implemetation, eviction, flushInterval, size, readWrite, blocking 和properties。
@Property
N/A
指定参数值或占位值(placeholder)(能被 mybatis-config.xml内的配置属性覆盖)。属性有:name, value。(仅在MyBatis 3.4.2以上版本生效)
@CacheNamespaceRef
类
参照另外一个命名空间的缓存来使用。属性有:value, name。如果你使用了这个注解,你应设置 value 或者 name 属性的其中一个。value 属性用于指定 Java 类型而指定命名空间(命名空间名就是指定的 Java 类型的全限定名),name 属性(这个属性仅在MyBatis 3.4.2以上版本生效)直接指定了命名空间的名字。
@Flush
方法
N/A
如果使用了这个注解,定义在 Mapper 接口中的方法能够调用 SqlSession#flushStatements() 方法。(Mybatis 3.3及以上)
2.5 @Param注解
@Param注解用于给方法参数起一个名字。以下是笔者总结的使用原则:
-
在方法只接受一个参数的情况下,可以不使用@Param。
-
在方法接受多个参数的情况下,建议一定要使用@Param注解给参数命名。
例如上述案例的selectPage方法接受2个参数,所以其两个参数都使用了@Param注解。@Param注解看起来配置最简单,实际上理解确实最复杂,下面进行详细的介绍。
前面已经提到,当映射器接口定义的方法被调用时,mybatis内部根据方法上注解:@Insert、@Update、@Delete、@Select来选择调用SqlSession的insert、update、delete、selectXXX方法。以下是SqlSession接口的相关方法定义(部分省略):
1 | public interface SqlSession extends Closeable { |
可以看到部分方法接受一个Object类型的parameter参数,另外一些方法则没有此参数。mybatis在执行前,除了会根据方法上注解:@Insert、@Update、@Delete、@Select来选择调用SqlSession的insert、update、delete、selectXXX方法;还会提前对传入映射器接口方法中的参数进行一些处理,然后再调用SqlSession的相应方法。逻辑如下:
1、如果映射器接口方法不接受参数,mybatis在执行时会调用相应无parameter参数的方法重载形式。例如,如UserMapper接口的selectAll方法,其不接受参数,返回值类型为List,因此调用SqlSession不接受paramter参数的selectList方法:
1 | <E> List<E> selectList(String statement); |
2、如果映射器方法只有一个参数,并且这个参数没有使用@Param注解,则直接用这个参数来调用SqlSession相应接受Object类型parameter方法参数的重载形式。例如,如UserMapper接口的selectByIds方法,其接受1个int[]数组类型参数作为查询条件,返回值类型为List
1 | <E> List<E> selectList(String statement, Object parameter); |
3、如果映射器方法只接受一个参数,但是使用了@Param注解,也会先封装到Map中;活着映射器方法总是接受多个参数,不管有没有使用@Param注解指定参数,总是会先封装到一个Map中。之后,调用SqlSession的相应方法把这个Map当做parameter参数传入。
例如:UserMapper接口的selectPage方法, 通过@Param("offset”)和@Param("limit”)为2个int参数指定了名字。
1 | @Select("SELECT id,name FROM user LIMIT #{offset},#{limit}") |
假设我们传入0和10,那么参数封装后的Map结构如下:
key value
--------------------
offset 0 //1
limit 10 //2
param1 0 //3
param2 10 //4
关于Map中1、2两个key-value,比较好理解,是我们通过@Param注解指定的映射关系。而3、4两个key-value,实际上是参数位置(param1、param2…,下标从1开始)和参数值的映射关系,不管我们有没有使用@Param注解,存在多个参数的情况下,我们总是可以按照位置进行引用。
因此,将UserMapper的selectPage方法定义改成以下形式也是正确的:
1 | //使用了@Param注解的情况下,依然根据参数位置进行引用(param1,param2…,下标从1开始) |
显然,根据参数位置进行引用不太直观,因此建议在存在多个参数的情况,总是通过@Param注解显式的指定参数名。
2.6 @InsertProvider、@UpdateProvider、@DeleteProvider、@SelectProvider注解
这几个注解主要用于动态sql构建。
1 | public interface UserBuilderMapper { |
3 结果集映射相关注解
| 注解 | 使用对象 | 相对应的 XML | 描述 |
|---|---|---|---|
| @Results | 方法 | 结果映射的列表,包含了一个特别结果列如何被映射到属性或字段的详情。属性有:value, id。value 属性是 Result 注解的数组。这个 id 的属性是结果映射的名称。 | |
| @Result | 方法 | 在列和属性或字段之间的单独结果映射。属性有:id, column, javaType, jdbcType, typeHandler, one, many。id 属性是一个布尔值,来标识应该被用于比较(和在 XML 映射中的 |
|
| @ResultMap | 方法 | N/A | 这个注解给 @Select 或者 @SelectProvider 提供在 XML 映射中的 |
| @ResultType | 方法 | N/A | 此注解在使用了结果处理器的情况下使用。在这种情况下,返回类型为 void,所以 Mybatis 必须有一种方式决定对象的类型,用于构造每行数据。如果有 XML 的结果映射,请使用 @ResultMap 注解。如果结果类型在 XML 的 |
| @ConstructorArgs | 方法 | 收集一组结果传递给一个结果对象的构造方法。属性有:value,它是形式参数数组。 | |
| @Arg | N/A | 单参数构造方法,是 ConstructorArgs 集合的一部分。属性有:id, column, javaType, jdbcType, typeHandler, select和 resultMap。id 属性是布尔值,来标识用于比较的属性,和 |
|
| @One | N/A | 复杂类型的单独属性值映射。属性有:select,已映射语句(也就是映射器方法)的全限定名,它可以加载合适类型的实例。fetchType会覆盖全局的配置参数 lazyLoadingEnabled。注意 联合映射在注解 API中是不支持的。这是因为 Java 注解的限制,不允许循环引用。 | |
| @Many | N/A | 映射到复杂类型的集合属性。属性有:select,已映射语句(也就是映射器方法)的全限定名,它可以加载合适类型的实例的集合,fetchType 会覆盖全局的配置参数 lazyLoadingEnabled。注意 联合映射在注解 API中是不支持的。这是因为 Java 注解的限制,不允许循环引用 | |
| @TypeDiscriminator | 方法 | 一组实例值被用来决定结果映射的表现。属性有:column, javaType, jdbcType, typeHandler 和 cases。cases 属性是实例数组。 | |
| @Case | N/A | 单独实例的值和它对应的映射。属性有:value, type, results。results 属性是结果数组,因此这个注解和实际的 ResultMap 很相似,由下面的 Results 注解指定。 |
3.1 @ConstructorArgs、@Arg注解
如果实体类没有无参的构造方法,那么我们就必须通过@ConstructorArgs与@Arg注解来提供实体类的构造方法参数信息。例如上述UserMapper的selectById方法,其返回一个User对象。假设User对象只有以下构造方法:
1 | public User(Integer id, String name) { |
那么需要在selectById方法上,添加@ConstructorArgs注解,提供构造方法信息,如下:
1 | @Select("SELECT id,name FROM user where id= #{id}") |
注意,通常情况下,我们都建议为实体类提供无参的构造方法的,这是最佳实践的总结,因此@ConstructorArgs注解和@Arg注解基本使用不到。
3.2 @Results、@Result注解
如果实体字段的名称与数据库表字段名称不一致时,我们就需要显式的指定映射关系。这是通过@Results、@Result注解来指定的,例如为UserMapper的selectById指定映射关系:
1 | @Select("SELECT id,name FROM user where id= #{id}") |
其中:
@Results注解:id属性用于给这个映射关系起一个名字(这里指定的为userMap),其内部还包含了一个@Result[]来表示实体属性和数据库表字段的映射关系
@Result注解:property属性是java实体属性的名称,column表示对应的数据库字段的名称。javaType和JdbcType属性可以不指定。
3.3 @ResultMap注解
上述selectById方法已经通过@Results注解指定了结果映射关系,通过@ConstructorArgs指定了构造方法(必要的情况下才使用),那么在其他的查询方法中,我们不需要重复定义,可以通过@ResultMap来引用@Results的id属性值进行复用。如我们在UserMapper的selectAll方法进行复用:
1 | @Select("SELECT id,name FROM user") |
3.4 @TypeDiscriminator、@Case注解
鉴别器注解基本没用,有空的时候再补充
3.5 @Many、@One注解
主要用于关联关系映射
假设有作者(author)和文章(article)两张数据库表,一个author可以有多个article,一个article只能属于一个author。相关表结构以及初始数据如下所示:
1 | --author表 |
相应的Java实体类如下所示:
1 | public class Article { |
映射器接口定义分别如下:
ArticleMapper
1 | public interface ArticleMapper { |
AuthorMapper
1 | public interface AuthorMapper { |
测试如下:
1 | @Test |
控制台输出结果为:
===========通过@One注解查询出Article关联的Auhtor===========
Article{id=1, title=‘title1’, content=‘content1’, author=Author{id=1, name=‘tianshouzhi’, articles=null}}
===========通过@Many注解查询出Auhtor关联的Article==========
Author{id=1, name=‘tianshouzhi’, articles=[Article{id=1, title=‘title1’, content=‘content1’, author=null}, Article{id=2, title=‘title2’, content=‘content2’, author=null}]}
4 缓存相关注解
| 注解 | 使用对象 | 相对应的 XML | 描述 |
|---|---|---|---|
| @CacheNamespace | 类 | 为给定的命名空间(比如类)配置缓存。属性有:implemetation, eviction, flushInterval, size, readWrite, blocking 和properties。 | |
| @Property | N/A | 指定参数值或占位值(placeholder)(能被 mybatis-config.xml内的配置属性覆盖)。属性有:name, value。(仅在MyBatis 3.4.2以上版本生效) | |
| @CacheNamespaceRef | 类 | 参照另外一个命名空间的缓存来使用。属性有:value, name。如果你使用了这个注解,你应设置 value 或者 name 属性的其中一个。value 属性用于指定 Java 类型而指定命名空间(命名空间名就是指定的 Java 类型的全限定名),name 属性(这个属性仅在MyBatis 3.4.2以上版本生效)直接指定了命名空间的名字。 | |
| @Flush | 方法 | N/A | 如果使用了这个注解,定义在 Mapper 接口中的方法能够调用 SqlSession#flushStatements() 方法。(Mybatis 3.3及以上) |
参考资料
mybatis与spring整合详解
mybatis可以单独使用,也可以与spring进行整合。我们在自己的项目中,需要引入相关依赖,如你的项目管理工具使用的是maven,可以在pom.xml中添加以下配置
1 | <!--spring依赖--> |
在整合之后,框架的之间的依赖关系如下图所示:
整合一:SqlSessionFactoryBean
在单独使用mybatis时,我们通过SqlSessionFactoryBuilder 来创建SqlSessionFactory。当mybatis与spring进行整合时,我们使用mybatis-spring提供的SqlSessionFactoryBean 来替代,SqlSessionFactoryBean实现了 Spring 的 FactoryBean 接口,用于创建 SqlSessionFactory 对象实例。
SqlSessionFactoryBean的配置有2种风格:
- 保留mybatis的核心配置文件
- 不保留mybatis的核心配置文件
保留mybatis的核心配置文件
我们将绝大部分关于mybatis的配置依然保留在mybatis的核心配置文件mybatis-config文件中,以下是一个示例:
mybatis-config.xml
1 | <?xml version="1.0" encoding="UTF-8" ?> |
细心的读者注意到,在mybatis-config.xml文件中我们并没有通过
此时SqlSessionFactoryBean配置方式如下:
1 | <!—-SqlSessionFactoryBean-—> |
不保留mybatis的核心配置文件
从mybatis-spring 1.3.0之后,我们可以移除mybatis-config.xml文件,将所有关于myabtis的配置都通过SqlSessionFactoryBean来指定。
以下配置案例演示了与上述等价的配置:
1 | <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> |
在mybatis与spring整合后, 通常我们不会再直接使用SqlSessionFactory。mybatis-spring提供了其他更加易于操作的工具类,如SqlSessionTemplate、SqlSessionDaoSupport,当然还有其他更加高级的使用方式,如:MapperFactoryBean,MapperScannerConfigurer。
整合二:使用SqlSessionTemplate
SqlSessionTemplate 是 mybatis-spring 的核心,其实现了SqlSession接口。在使用了SqlSessionTemplate之后,我们不再需要通过SqlSessionFactory.openSession()方法来创建SqlSession实例;使用完成之后,也不要调用SqlSession.close()方法进行关闭。另外,对于事务,SqlSessionTemplate 将会保证使用的 SqlSession 是和当前 Spring 的事务相关的。
SqlSessionTemplate依赖于SqlSessionFactory,其配置方式如下所示:
1 | <bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate"> |
之后我们可以在UserD类中直接进行注入。SqlSessionTemplate 是线程安全的, 可以被多个 DAO 所共享使用,以下是一个示例:
1 | public class UserDao { |
SqlSessionTemplate本质上是一个代理,关于SqlSessionTemplate的源码分析,后期会抽空编写。
整合三:继承SqlSessionDaoSupport
mybatis提供了抽象类SqlSessionDaoSupport,调用其getSqlSession()方法你会得到一个 SqlSessionTemplate,之后可以用于执行 SQL 方法, 就像下面这样:
1 | public class UserDao extends SqlSessionDaoSupport{ |
SqlSessionDaoSupport 需要一个 sqlSessionFactory 或 sqlSessionTemplate 属性来设置 。如果两者都被设置了 , 那么SqlSessionFactory是被忽略的。由于我们的UserDao类继承了SqlSessionDaoSupport,所以你可以在UserDao类中进行设置:
1 | <bean id="userDao" class="com.tianshouzhi.zebracost.dao.UserDao"> |
或:
1 | <bean id="userDao" class="com.tianshouzhi.zebracost.dao.UserDao"> |
事实上,如果你提供的是一个SqlSessionFactory,SqlSessionDaoSupport内部也会使用其来构造一个SqlSessionTemplate实例。
整合四:MapperFactoryBean
无论是使用SqlSessionTemplate,还是继承SqlSessionDaoSupport,我们都需要手工编写DAO类的代码。熟悉mybatis同学知道,SqlSession有一个getMapper()方法,可以让我们通过映射器接口的方式来使用mybatis。
例如针对有以下UserMapper.xml
1 | <?xml version="1.0" encoding="UTF-8" ?> |
我们可以定义以下UserMapper接口:
1 | package com.tianshouzhi.mybatis.mapper; |
细心的读者注意到:
1、映射文件的namespace属性值”com.tianshouzhi.mybatis.mapper.UserMapper"就是UserMapper接口的全路径
2、映射文件中的
之后,我们调用UserMapper接口中的方法,就会执行UserMapper.xml中对应的sql。
你甚至可以通过注解的方式直接将sql写到UserMapper接口的方法上,此时你就不再需要UserMapper.xml,如:
1 | package com.tianshouzhi.mybatis.mapper; |
此时我们可以修改mybatis-config.xml中
1 | <mappers> |
之后,我们可以通过以下方式来使用UserMapper接口,更加直观,不容易出错。
1 | SqlSession session = sqlSessionFactory.openSession(); |
但是在与spring进行整合时,是否有更加简单的使用方法呢?能够在一个业务Bean中注入UserMapper接口,不需要通过SqlSession的getMapper来创建。我们期望的使用方式如下:
1 | public class UserService { |
直接这样操作显然是会报错的,因为UserMapper是一个接口,且不是spring管理的bean,因此无法直接注入。
这个时候,本节的主角MapperFactoryBean登场了,通过如下配置,MapperFactoryBean会针对UserMapper接口创建一个代理,并将其变成spring的一个bean。MapperFactoryBean继承了SqlSessionDaoSupport类,这也是为什么我们先介绍SqlSessionDaoSupport,再介绍MapperFactoryBean的原因。显然的,你可以想到,我们可以为MapperFactoryBean指定一个SqlSessionTemplate或者SqlSessionFactory,如果两个属性都设置了,那么 SqlSessionFactory 就会被忽略。
通过以下配置,我们就可以在一个业务bean中直接注入UserMapper接口了:
1 | <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> |
你可能想知道MapperFactoryBean为什么具有这样的魔力,但是这不是本文的重点,本文讲解的是mybatis是如何spring进行整合的。笔者将其会在其他章节分析MapperFactoryBean的源码。
上述方式,已经是mybatis与spring进行时理想的方式了。但是如果你的业务很复杂,有许多的XxxMapper接口要配置,针对每个接口都配置一个MapperFactoryBean,会使得我们的配置文件很臃肿。关于这一点,mybatis团队提供了MapperScannerConfigurer来帮助你解决这个问题。
整合五:MapperScannerConfigurer
通常我们在开发时,会将同一类型的类放于同一个包下,例如现在我们com.tianshouzhi.mybatis.mappers包下,有两个接口:UserMapper、UserAccountMapper。
MapperScannerConfigurer可以指定扫描某个包,将这个包下的所有接口,自动的为每个映射器接口都注册一个MapperFactoryBean。配置如下:
1 | <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> |
其中:basePackage 属性是让你为映射器接口的包路径。如果的映射器接口位于不同的包下,可以使用分号”;”或者逗号”,”进行分割。如:
1 | <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> |
如果你指定的还包含子包,子包中的映射器接口递归地被搜索到。因此对于上述配置,我们可以通过公共的包名"org.tianshouzhi.mybatis”进行简化。如:
1 | <property name="basePackage" value="org.tianshouzhi.mybatis" /> |
你可能想到了,如果指定的公共的包名下面还包含了一些其他的接口,这些接口是你作为其他用途使用到的,并不能作为mybatis的映射器接口来使用。此时,你可以通过markerInterface属性或者annotationClass属性来进行过滤。
对于markerInterface,首先,你需要定义一个标记接口,接口名随意,这个接口中不需要定义任何方法,如:
1 | public interface MybatisMapperInterface{} |
接着,你需要将你的映射器接口继承MybatisMapperInterface,如:
1 | public interface UserMapper implements MybatisMapperInterface{ |
此时你可以指定MapperScannerConfigurer中指定,只有继承了MybatisMapperInterface接口的子接口,才为其自动注册MapperFactoryBean,如:
1 | <property name="markerInterface" value="com.tianshouzhi.mybatis.MybatisMapperInterface"/> |
对于annotationClass属性,作用是类似的,根据注解进行过滤。一般我们是映射器接口上添加mybatis提供的@Mapper注解进行过滤,你也可以自定义一个注解。配置如下:
1 | <property name="annotationClass" value="org.apache.ibatis.annotations.Mapper"/> |
如果同时指定了markerInterface和annotationClass属性,那么只有同时满足这两个条件的接口才会被注册为MapperFactoryBean。
细心的读者可能意识到了,到目前,我们还没有为MapperScannerConfigurer指定一个SqlSessionFactory,或者SqlSessionTemplate。前面配置MapperFactoryBean的时候,我们已经看到,我们至少需要为其提供一项。之所以不指定,是因为MapperScannerConfigurer将会spring上下文中自动进行寻找类型为SqlSessionFactory,或者SqlSessionTemplate的bean,然后利用其来创建MapperFactoryBean实例。
当然你也可以手工的进行指定任意一个,如:
1 | <property name="sqlSessionFactory" ref=“sqlSessionFactory"/> |
然而,sqlSessionFactory和sqlSessionTemplate属性已经不建议使用了。原因在于,这两个属性不支持你使用spring提供的PropertyPlaceholderConfigurer的属性替换。例如你配置了SqlSessionFactoryBean来创建SqlSessionFactory实例,前面已经看到必须为其指定一个dataSource属性。很多用户习惯将数据源的配置放到一个独立的配置文件,如jdbc.properties文件中,之后在spring配置中,通过占位符来替换,如:
1 | <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> |
对于这样的配置,spring在初始化时会报错,因为MapperScannerConfigurer会在PropertyPlaceholderConfigurer初始化之前,就加载dataSource的配置,而此时PropertyPlaceholderConfigurer还未准备好替换的占位符的内容,所以抛出异常。
当然,这个问题并不是无解,我们可以使用sqlSessionFactoryBeanName、sqlSessionTemplateBeanName属性来替代sqlSessionFactory和sqlSessionTemplate属性。如下:
1 | <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/> |
此时,你依然可以放心大胆的在你的数据源配置中,使用占位符了。
事实上,笔者总是建议你,在MapperScannerConfigurer的配置中,显示的指定sqlSessionFactoryBeanName或sqlSessionTemplateBeanName。如果你不指定,MapperScannerConfigurer就会在spring上下文中自动的寻找类型为SqlSessionFactory或者SqlSessionTemplate的bean。如果你的数据源配置中使用了占位符,还是会报错。
最后,你可能想知道,为什么MapperScannerConfigurer指定一个basePackage属性,就可以为包下的每个接口都注册一个MapperFactoryBean?其内部是如何自动在spring上下文中寻找类型为SqlSessionFactory或者SqlSessionTemplate的bean实例的?以及为什么又做了如此多限制,只有指定sqlSessionFactoryBeanName或sqlSessionTemplateBeanName才能在数据源的配置中使用占位符?
关于这些问题,笔者将在MapperScannerConfigurer的源码分析中进行解答。
整合六:@MapperScan
如果读者习惯使用注解,而不是xml文件的方式进行配置,mybatis-spring提供了@MapperScan注解,其用于取代MapperScannerConfigurer。以下演示了如何通过注解的方式来配置mybatis。
1 | @Configuration |
整合七:事务
上述所有的配置,还没有涉及到mybatis与spring进行整合另一个核心要点,即事务。整合后,我们需要将事务委派给spring来管理。
spring使用PlatformTransactionManager接口来表示一个事务管理器。其有2个重要的实现类:
DataSourceTransactionManager:用于支持本地事务,简单理解,你可以认为就是操作单个数据库的事务,其内部也是通过操作java.sql.Connection来开启、提交(commit)和回滚(rollback)事务。
JtaTransactionManager:用于支持分布式事务,其实现了JTA规范,使用XA协议进行两阶段提交。
在本文中,我们主要介绍的是DataSourceTransactionManager,绝大部分情况下, 我们使用的都是这个事务管理器。其配置方式如下:
1 | <!--spring 事务管理器--> |
关于事务管理器使用方式有2种:声明式事务管理、编程式事务管理
声明式事务管理
1、基于注解
首先,你需要配置事务管理是基于注解驱动的,如下:
1 | <tx:annotation-driven transaction-manager="transactionManager"/> |
之后,在业务bean的方法上添加@Transactional注解,此时这个方法就自动具备了事务的功能,如果出现异常,会自动回滚,没有出现异常则自动交。
1 | @Transactional |
基于注解的形式的声明式事务管理器,是最为简单的,也是建议使用的方式。
2、基于切面
如果你有很多方法,都需要有事务管理,你觉得每个方法都添加@Transactional注解比较麻烦,此时你可以使用以下配置取代tx:annotation-driven元素。
1 | <!-- 1.配置事务通知:(事务的增强) --> |
事实上,基于切面的配置,太麻烦了,即使对于一些老鸟,长时间没有编写过类似的配置,可能也无法立即正确的进行配置,没有@Transactional注解来的直观。
编程式事务管理
如果上述两种声明式事务的配置都不是你要想要,那么你可以采取编程式事务。你可以在业务bean中注入事务管理器,然后进行编程式事务的管理,如:
1 | public class AccountService{ |
另外,Spring还提供了一个TransactionTemplate,用于简化编程式事务代码的编写,配置方式如下:
1 | <bean id="transactionTemplate" class="org.springframework.transaction.support.TransactionTemplate"> |
使用方式如下:
1 | public void transfer(final String out,final String in,final Double money) { |
总的来说,编程式事务管理还是显得略微麻烦。
整合八:springboot
mybatis开发团队为Spring Boot 提供了 mybatis-spring-boot-starter。你需要引入如下依赖:
1 | <dependency> |
使用了该starter之后,只需要定义一个DataSource即可,它会自动利用该DataSource创建需要使用到的SqlSessionFactoryBean、SqlSessionTemplate、以及ClassPathMapperScanner来自动扫描你的映射器接口,并针对每个接口都创建一个MapperFactoryBean,注册到Spring上下文中。
关于mybatis-spring-boot-starter如何实现自动配置的相关源码,参见:org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration类。
默认情况下,扫描的basePackage是spring boot的根目录(这里指的是应用启动类Application.java类所在的目录),且只会对添加了@Mapper注解的映射器接口进行注册。
因此,最简单的情况下,你只需要在application.yml中进行数据源的相关配置即可,以下配置依赖于spring-boot-starter-jdbc:
1 | spring: |
之后,你就可以直接在你的业务bean中注入映射器接口来使用了。
需要注意的是,一旦你自己提供了MapperScannerConfigurer,或者配置了MapperFactoryBean,那么mybatis-spring-boot-starter的自动配置功能将会失效。此时所有关于mybatis与spring进行整合的配置,都需要由你自行控制。
整合九:多数据源
有的时候,应用需要访问多个数据库,假设现在有两个mysql库db_user、db_acount。这个时候我们就需要配置2个数据源,来连接不同的库,如下:
1 | <bean id="ds_user" class="com.alibaba.druid.pool.DruidDataSource"> |
针对上述的数据源ds_user,ds_account,我们需要为每个都配置一个SqlSessionFactoryBean,如下:
1 | <bean id="ssf_user" class="org.mybatis.spring.SqlSessionFactoryBean"> |
细心的读者可能已经注意到,这两个SqlSessionFactoryBean分别操作数据源ds_user、ds_account,且mapperLocations属性值指向的是不同的目录:
-
在ssf_user中,指定关于ds_user的映射器xml文件都位于类路径的mybatis/mappers/db_user/目录下
-
在ssf_account中,指定关于ds_account的映射器xml文件都位于类路径的mybatis/mappers/db_account/目录下
如果你是使用映射接口的方式来操作mybatis,那么还应该针对ssf_user和ssf_account两个SqlSessionFactoryBean,各配置一个MapperScannerConfigurer。如下:
1 | <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> |
可以看到,两个MapperScannerConfigurer的basePackage属性是不同的,我们将操作db_user库下的映射器XxxMapper接口都放在com.tianshouzhi.mybatis.mappers.user包中,将我们将操作db_account库下的映射器XxxMapper接口都放在com.tianshouzhi.mybatis.mappers.account包中。
再次提醒,在使用多数据源时,将操作不同库的xml映射文件、以及对应的映射器接口放到不同的目录下很重要,如果不这样,在使用时你几乎100%会遇到问题。
最后,由于你现在有2个数据源,因此你应该配置两个事务管理器,如:
1 | <bean id="userTxManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> |
如果你是使用@Transactional注解来进行声明式事务管理,应该要指定你使用的是哪一个事务管理器,如:
1 | @Transactional("accountTxManager") |
一个事务管理器,只能保证操作单个库的事务,例如在这里,accountTxManager只能保证db_account库的事务,证userTxManager只能保证db_user库的事务。
如果你在一个方法内,想同时操作两个库,并保证事务,使用普通的DataSourceTransactionManager已经无法满足这种需求,这属于分布式事务的范畴。在笔者的另一篇文章atomikos JTA/XA全局事务演示了如何使用atomikos与mybatis、spring进行整合,来进行分布式事务的管理。
整合十:zebra
你应该使用:
1、使用zebra-api中提供了GroupDataSource(读写分离)或者ShardDataSource(分库分表),而不是直接使用第三方的数据库连接池。
2、使用zebra-dao提供了ZebraMapperScannerConfigurer替代mybatis-spring原生的MapperScannerConfigurer
3、此外,你应该使用zebra-ds-monitor-client,只需引入依赖即可,会自动将连接池的监控信息上报到cat中
mybatis技巧
Mybatis中判断集合的 size 不为空
1 | <if test="null != staffCodeList and staffCodeList.size > 0"> |
判断参数为空
1.判断Double类型:
2.判断Integer类型:
3.判断String类型:
4.判断Date类型:
因为String类型是符合的,但是如果是Integer类型的话,如果变量的值是0,即 faceValue = 0, mybatis在进行 faceValue != ‘’ 的时候会认为 faceValue 的值是空字符串, 即 faceValue == ‘’ 为true;
同理,Double,Date也是如此。所以如果是Integer类型,Double类型,Date类型只需要判断 != null 即可。如果String类型需要判断不等于0,则需要写name != ‘0’.toString(),否则会报错。
mybatis 中 foreach collection的三种用法
mybatis 中 foreach collection的三种用法
Mybatis使用IN语句查询
返回数据
注意事项
写入数据库的po,不要给字段设置默认值,除非能保证在update时,默认值不会覆盖错误。
mybatis用法总结
selectById
1 | @Select({ |
selectByCondition
1 | @Select({ |
分页查询的两个参数,分别是偏移量和size。偏移量需要通过页数和size计算出来。
insert
1 | @Insert({ |
batchInsert
1 | @Insert({ |
update
1 | @Update({ |
用法对比
jdbcType=“DATE” 和 jdbcType=“TIMESTAMP” 两种类型的区别
- DATE只有年月日,没有时分秒
NoSuchMethod错误排查
- 如果包含了不同版本的包,它的加载顺序是如何的?
- NoSuchMethod的报错是error级别的,exception是catch不住的。
工程问题
Untitled
1 | 21/09/08 03:17:10 WARN AbstractCoordinator [kafka-coordinator-heartbeat-thread | spark-kafka-source-babd462a-415a-413a-aeea-e55435fff762-448121830-driver-0]: [Consumer clientId=consumer-1, groupId=spark-kafka-source-babd462a-415a-413a-aeea-e55435fff762-448121830-driver-0] This member will leave the group because consumer poll timeout has expired. This means the time between subsequent calls to poll() was longer than the configured max.poll.interval.ms, which typically implies that the poll loop is spending too much time processing messages. You can address this either by increasing max.poll.interval.ms or by reducing the maximum size of batches returned in poll() with max.poll.records. |
1 | 21/09/06 07:15:58 WARN TaskSetManager [task-result-getter-3]: Lost task 78.0 in stage 11.0 (TID 3591, wn29-msnbi.awfbdxsze1iudhhki0l2sbzfaf.bx.internal.cloudapp.net, executor 22): org.apache.kafka.common.errors.TimeoutException: Failed to allocate memory within the configured max blocking time 60000 ms. |
1 | Cannot fetch record for offset xxx in 120000 milliseconds |