Releases: mybatis-mapper/mapper
2.2.3
What's Changed
insert
和update
泛型增加<S extends T>
Example
支持TypeHandler
配置
insert
和update
泛型增加<S extends T>
/**
* 保存实体
*
* @param entity 实体类
* @return 1成功,0失败
*/
@Lang(Caching.class)
@InsertProvider(type = EntityProvider.class, method = "insert")
<S extends T> int insert(S entity);
insert和update时可以使用实体类的子类进行操作。
Example
支持TypeHandler
配置
原来在实体类配置的 TypeHandler
对Example
操作无效,这次更新后可以自动生效。
单元测试方法如下:
@Test
public void testUpdateByExampleSetValues() {
SqlSession sqlSession = getSqlSession();
try {
ExampleMapper<UserAuto, Example<UserAuto>> exampleMapper = sqlSession.getMapper(UserAutoBaseMapper.class);
Example<UserAuto> example = new Example();
example.createCriteria().andEqualTo(UserAuto::getId, 1L);
example.set(UserAuto::getAddress, new UserAuto.Address("河北省", "秦皇岛市"));
Assert.assertEquals(1, exampleMapper.updateByExampleSetValues(example));
sqlSession.rollback();
} finally {
//不要忘记关闭sqlSession
sqlSession.close();
}
}
这里的 UserAuto.Address
是一个对象,需要类型处理为单个字段。
这个改动后,Example
中的 andLike(Fn<T, Object> fn, String value)
改为了 andLike(Fn<T, Object> fn, Object value)
,因为字段是对象的情况下可以通过类型处理为 String
,这里只接收 String
就无法支持 UserAuto.Address
模糊查询。该字段的模糊查询也发生了变化,这会导致相关代码的不兼容:
Example<UserAuto> example = userAutoMapper.example();
example.createCriteria().andLike(UserAuto::getAddress, "河北省%");
改为:
Example<UserAuto> example = userAutoMapper.example();
example.createCriteria().andLike(UserAuto::getAddress, new UserAuto.Address("河北省", "%"));
Full Changelog: 2.2.2...2.2.3
2.2.2
官方文档 https://mapper.mybatis.io/ 新增加了 2.x 版本的新文档,快速上手、接口介绍、注解配置更新了大量最新内容,欢迎查阅。
功能增强
- Provider 升级: 将 provider 升级至 2.2.4 版本。
- 项目结构调整:
Fn
,Reflections
和测试FnTest
移动到了 mybatis-provider 项目,包名保持不变。 - 注解增强:
- JPA 新增支持
@Entity
注解,配置该注解相当于开启默认注解中的autoResultMap=true
,配合@Convert
注解使用时可以对查询结果进行转换。 - JPA 新增支持
@Convert
注解,用于类型转换,可以将查询结果转换为指定类型,需要指定 MyBatis TypeHandler 子类。默认可以用于参数中,想让查询结果列生效时需要配合@Entity
注解开启autoResultMap=true
。 - JPA 支持: 支持 JPA
@Table
注解的catalog
和schema
属性。
- JPA 新增支持
- SQL 生成: 通过 LogicalMapper 生成的 SQL 现在包含 schema 信息。
- JPA 注解兼容性: jakarta-jpa 复制了 jpa 的改动,功能相同,但注解包名不同。
性能优化
- 内存管理: 解决了 Fn 缓存内存溢出的问题,修复了 issue #108。
- 依赖管理: 移除了无用的
javax.persistence-api
依赖。
兼容性改进
测试与质量保证
- 测试用例修复: 修复了测试用例
testExampleUseCondition
。 - 测试代码完善: 增加了
createCriteriaSelective
方式创建 Criteria,GeneratedCriteria
类增加了useCondition
方法。
贡献者
- liuzh
- aunix*
- 曹赛军
Example 新增变化介绍
所有方法增加了一个带 boolean useCondition
参数的方法,示例如下:
@Test
public void testExampleUseCondition() {
try (SqlSession sqlSession = getSqlSession()) {
ExampleMapper<User, Example<User>> exampleMapper = sqlSession.getMapper(UserMapper.class);
Example<User> example = new Example<>();
User user = new User();
user.setUserName("殷%");
example.createCriteria()
.andNotEqualTo(Objects.nonNull(user.getId()), User::getId, user.getId())
.andLike(Utils.isNotBlank(user.getUserName()), User::getUserName, user.getUserName())
.andEqualTo(Utils.isNotBlank(user.getSex()), User::getSex, user.getSex());
Assert.assertEquals(5, exampleMapper.countByExample(example));
}
}
还有一个 createCriteriaSelective
方法,默认会校验值是否为空,如果空就不使用条件:
@Test
public void testExampleUseSelective() {
try (SqlSession sqlSession = getSqlSession()) {
ExampleMapper<User, Example<User>> exampleMapper = sqlSession.getMapper(UserMapper.class);
Example<User> example = new Example<>();
User user = new User();
user.setUserName("殷%");
user.setSex("女");
example.createCriteriaSelective()
.andLike(User::getUserName, user.getUserName())
.andEqualTo(User::getId, user.getId())
.andNotEqualTo(User::getSex, user.getSex());
Assert.assertEquals(3, exampleMapper.countByExample(example));
}
}
Full Changelog: 2.1.1...2.2.2
2.2.1 - 2023-12-16
- 复杂example查询示例
- 测试批量插入ID
- 完善测试代码
- JPA不加注解时排除接口、数组、枚举、基本类型、注解类型,fixed #93
- 依赖的 mybatis-provider 升级为 2.2.3
Example 示例
Example<User> example = new Example<>();
example.createCriteria().andEqualTo(User::getSex, "男").andOr(
example.orPart().andLike(User::getUserName, "杨%"),
example.orPart().le(User::getId, 10));
List<User> users1 = mapper.selectByExample(example);
//wrapper写法,结果一样
List<User> list = mapper.wrapper().eq(User::getSex, "男")
.or(
c -> c.startsWith(User::getUserName, "杨"),
c -> c.le(User::getId, 10)).list();
生成的 SQL:
SELECT id,name AS userName,sex,status FROM user WHERE ( sex = ? AND ( ( name LIKE ? ) OR ( id <= ? ) ) )
简化括号后:
SELECT id,name AS userName,sex,status FROM user WHERE sex = ? AND ( name LIKE ? OR id <= ? )
2.2.0 - 2023-12-05
- 升级mybatis-provider2.2.2版本,添加insert测试jpa,closed #92
- 增加mybatis-jakarta-jpa支持jakarta.persistence-api by 曹赛军
- jakarta需要jdk11,修改运行环境依赖的jdk版本,其他模块仍然以jdk8编译
2.1.1 - 2023-6-4
- 解决接口方法冲突
- 优化Fn中的字段处理
- LogicalMapper实现
2.1.0 - 2022-12-26
- Fn支持直接输入字段名或列名,支持使用字符串形式的参数。
- Fn增加方法,当通过基类或基类泛型的字段引用时,可以指定所属的实体类 fixed#50
- 兼容 spring boot 3.0
- 增加h2驱动和相关测试,去掉mysql依赖,完善批量查询功能,支持复合主键的批量更新。
- 批量更新支持联合主键
- 新增批量更新 updateList、updateListSelective
- Example 增加 excludeColumns 排除查询列的方法,ExampleWrapper 增加 exclude 排除查询列的方法。由于重用了之前的 selectColumns 方法,这会导致之前可以多次调用 selectColumns追加查询列变成了每次调用都会覆盖查询列,不想指定时,可以 selectColumns() 空参数设置为空。fixed #63
- ExampleWrapper 增加 clear 方法,方便重用 wrapper, fixed #70
- 解决 mapper-bom 依赖 mapper-parent 导致的依赖管理冲突问题, fixed #67
- 修改字段批量操作方法为 Collection 类型后,这是一个不兼容的测试接口方法
- 通过 .editorconfig 统一代码样式,格式化代码
2.0.0 发布 - 2022-07-31
- mybatis-provider 升级为 2.1.1,同时引入了 mybatis-config 依赖,增加 mybatis-mapper.proerteis 用户自定义配置
- ExampleWrapper condition 系列方法新增 Supplier 重载 #47 by darren
- 更新代码生成器,解决
NAME=>nAME
错误,现在可以转换为name
- example / wrapper 新增字符串形式的排序方法重载 #34 by darren
- 修复 jpa 模块 获取 Entity 类型错误的问题(排除了基本类型) #41 by darren
- 完善和Spring的集成,完善Bean的初始化和加载,fixed #51
- 针对 mybatis-provider 中 @Entity.Prop 注解的兼容性改动
- 添加 bom 子模块,管理 mybatis-mapper 中所有子模块的依赖 #45(** by zhousiwei**),可以使用下面的方式管理依赖:
<dependencyManagement> <dependencies> <dependency> <groupId>io.mybatis</groupId> <artifactId>mapper-bom</artifactId> <version>2.0.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
mybatis-provider 升级到 2.1.1 版本
mapper 依赖 provider,provider 2.1.1 版本有大量改动对 mapper 也有影响,具体改动参考对应的版本日志:
mybatis-mapper.proerteis 用户自定义配置
关于配置的详细设计和实现,参考: https://blog.csdn.net/isea533/category_11904924.html
目前可配置的参数如下:
mybatis.provider.style
,默认值为lower_underscore
,可选值如下:- normal 不做转换
- lower_underscore 驼峰转下划线
- upper_underscore 驼峰转大写下划线
- lower 转小写
- upper 转大写
支持扩展,详情看 mybatis-provider 项目
支持下面 3 个参数:
@Entity.Table(value = "user",
props = {
@Entity.Prop(name = "deleteByExample.allowEmpty", value = "false"),
@Entity.Prop(name = "updateByExample.allowEmpty", value = "false"),
@Entity.Prop(name = "updateByExampleSelective.allowEmpty", value = "false")
}
)
上面代码可以针对单个实体进行配置,也可以在配置文件中配置全局的默认配置。
除了直接在用户自定义配置文件中配置外,也支持系统变量和环境变量,使用 Spring Boot 时,也支持 Spring Boot 的所有外部化配置方式,支持在 application.[yaml|properties] 中配置,例如 yaml 方式:
mybatis:
provider:
style: lower_underscore
deleteByExample:
allowEmpty: false
updateByExample:
allowEmpty: false
updateByExampleSelective:
allowEmpty: false
# 还可以使用 spring boot 推荐的形式
update-by-example-selective:
allow-empty: false
1.2.2 发布
1.2.1 发布
2022年4月17日 - 1.2.1 发布
mybatis-mapper 项目升级到 1.2.1
ExampleWrapper
方法增加 boolean useCondition
参数,当值为 true
时,条件生效,反之无效
用法:
mapper.wrapper()
.eq(StrUtil.isNotEmpty(sex), User::getSex, "女")
.or(c -> c.gt(User::getId, 40), c -> c.lt(false, User::getId, 10))
.or()
.startsWith(User::getUserName, "张")
.orderByAsc(User::getId).list();
当 StrUtil.isNotEmpty(sex)
结果为 true
时,该条件有效,为 false
时无效。
有效时输出的 SQL 如下:
SELECT id,name AS userName,sex FROM user WHERE ( sex = ? AND ( ( id > ? ) ) ) OR ( name LIKE ? ) ORDER BY id ASC
-- Parameters: 女(String), 40(Integer), 张%(String)
无效时输出的 SQL 如下:
SELECT id,name AS userName,sex FROM user WHERE ( ( ( id > ? ) ) ) OR ( name LIKE ? ) ORDER BY id ASC
-- Parameters: 40(Integer), 张%(String)
通过这种用法可以保证当需要判断来设置条件时,可以仍然在链式调用中使用。
注意
or()
和or(参数)
方法的区别,详细用法和区别请看 1.2.0 更新日志。
1.2.0 发布
2022年4月9日 - 1.2.0 发布
mybatis-provider 项目升级到 1.1.1
- 升级依赖版本
- 替换log4j为logback(测试用)
- 去掉了一个
System.out.println
输出
mybatis-mapper 项目升级到 1.2.0
- 升级依赖版本
- 替换log4j为logback(测试用)
Example
增加set
方法设置要更新的字段,ExampleMapper
增加updateByExampleSetValues
方法使用设置的 set 更新数据。- 新增加
ExampleWrapper
类,封装了Example
方法,该类可以通过BaseMapper.wrapper()
直接获取使用,通过链式调用方便使用。代码示例:
BaseMapper.wrapper()
示例
//获取 Mapper,在 Spring 中可以直接注入使用
UserMapper2 mapper = sqlSession.getMapper(UserMapper2.class);
//查询 "sex=男 or name like '杨%' 的数量
long count = mapper.wrapper().eq(User::getSex, "男")
.or(
c -> c.startsWith(User::getUserName, "杨")
).count();
Assert.assertEquals(1, count);
//查询 "sex=男 or (name like '杨%' or (name like '俞%' and name like '%舟')) 的数量
count = mapper.wrapper().eq(User::getSex, "男")
.or(
c -> c.startsWith(User::getUserName, "杨"),
c -> c.startsWith(User::getUserName, "俞").endsWith(User::getUserName, "舟")
).count();
Assert.assertEquals(2, count);
//查询 name 和 sex 列,条件为 (name like '杨%' or sex = '男') or id > 1 and id <= 16 and sex = '女' 的数据
List<User> users = mapper.wrapper()
.select(User::getUserName, User::getSex)
.or(c -> c.startsWith(User::getUserName, "杨"),
c -> c.eq(User::getSex, "男"))
.or()
.gt(User::getId, 1L)
.le(User::getId, 16L)
.eq(User::getSex, "女").list();
//构建的wrapper可以多次使用
//查询条件为 id > 50 or id <= 5 or sex = '女'
ExampleWrapper<User, Long> wrapper = mapper.wrapper()
.gt(User::getId, 50L)
.or()
.le(User::getId, 5L)
.or()
.eq(User::getSex, "女");
//使用当前条件获取前5条数据
users = wrapper.top(5);
Assert.assertEquals(5, users.size());
//追加条件后查询数量
count = wrapper.select(User::getSex).distinct().count();
Assert.assertEquals(2, count);
//根据条件"name=张无忌",更新名字和性别
Assert.assertEquals(1, mapper.wrapper()
.set(User::getUserName, "弓长无忌")
.set(User::getSex, "M")
.eq(User::getUserName, "张无忌").update());
//根据条件"sex=M"查询数量
Assert.assertEquals(1, mapper.wrapper().eq(User::getSex, "M").count());
warpper
封装的方法中,相比 Example
增加了 contains, startsWith, endsWith
3个模糊查询方法,原有的 like 和 notLike 仍然需要手动添加 '%'。
contains
在两侧加上%
,如%内容%
startsWith
在右侧加上%
,如内容%
endsWith
在左侧加上%
,如%内容
上面代码中所有方法对应的数据库日志如下:
countByExample - ==> Preparing: SELECT COUNT( * ) FROM user WHERE ( sex = ? AND ( ( name LIKE ? ) ) )
countByExample - ==> Parameters: 男(String), 杨%(String)
countByExample - <== Total: 1
countByExample - ==> Preparing: SELECT COUNT( * ) FROM user WHERE ( sex = ? AND ( ( name LIKE ? ) OR ( name LIKE ? AND name LIKE ? ) ) )
countByExample - ==> Parameters: 男(String), 杨%(String), 俞%(String), %舟(String)
countByExample - <== Total: 1
selectByExample - ==> Preparing: SELECT name AS userName,sex FROM user WHERE ( ( ( name LIKE ? ) OR ( sex = ? ) ) ) OR ( id > ? AND id <= ? AND sex = ? )
selectByExample - ==> Parameters: 杨%(String), 男(String), 1(Long), 16(Long), 女(String)
selectByExample - <== Total: 44
selectByExample - ==> Preparing: SELECT id,name AS userName,sex FROM user WHERE ( id > ? ) OR ( id <= ? ) OR ( sex = ? )
selectByExample - ==> Parameters: 50(Long), 5(Long), 女(String)
countByExample - ==> Preparing: SELECT COUNT( distinct sex ) FROM user WHERE ( id > ? ) OR ( id <= ? ) OR ( sex = ? )
countByExample - ==> Parameters: 50(Long), 5(Long), 女(String)
countByExample - <== Total: 1
updateByExampleSetValues - ==> Preparing: UPDATE user SET name = ?, sex = ? WHERE ( name = ? )
updateByExampleSetValues - ==> Parameters: 弓长无忌(String), M(String), 张无忌(String)
updateByExampleSetValues - <== Updates: 1
countByExample - ==> Preparing: SELECT COUNT( * ) FROM user WHERE ( sex = ? )
countByExample - ==> Parameters: M(String)
countByExample - <== Total: 1
特别注意 or()
和 or(Function<Example.OrCriteria<T>, Example.OrCriteria<T>>... orParts)
方法。
or()
是后面的条件和前面用 or 连接。
带参数的 or(...)
是一个嵌套的块,块中的每个部分使用 or 连接,块内使用 and,当前 or(...)
这部分和前面是 and 连接。
如下面代码:
mapper.wrapper()
.eq(User::getSex, "女")
.or(c -> c.gt(User::getId, 40), c -> c.lt(User::getId, 10)).list();
对应的查询条件为:sex = '女' and (id > 40 or id < 10)
,运行时输出的 SQL 如下:
selectByExample - ==> Preparing: SELECT id,name AS userName,sex FROM user WHERE ( sex = ? AND ( ( id > ? ) OR ( id < ? ) ) )
selectByExample - ==> Parameters: 女(String), 40(Integer), 10(Integer)
selectByExample - <== Columns: ID, USERNAME, SEX
selectByExample - <== Row: 41, 武青婴, 女
selectByExample - <== Row: 46, 郭襄, 女
selectByExample - <== Row: 50, 韩姬, 女
selectByExample - <== Row: 51, 黄衫女子, 女
selectByExample - <== Row: 2, 赵敏, 女
selectByExample - <== Row: 3, 周芷若, 女
selectByExample - <== Row: 4, 小昭, 女
selectByExample - <== Row: 5, 殷离, 女
selectByExample - <== Row: 7, 殷素素, 女
selectByExample - <== Total: 9
在上面基础上增加一个 or()
如下:
mapper.wrapper()
.eq(User::getSex, "女")
.or(c -> c.gt(User::getId, 40), c -> c.lt(User::getId, 10))
.or()
.startsWith(User::getUserName, "张").list();
此时的日志如下:
selectByExample - ==> Preparing: SELECT id,name AS userName,sex FROM user WHERE ( sex = ? AND ( ( id > ? ) OR ( id < ? ) ) ) OR ( name LIKE ? )
selectByExample - ==> Parameters: 女(String), 40(Integer), 10(Integer), 张%(String)
selectByExample - <== Columns: ID, USERNAME, SEX
selectByExample - <== Row: 2, 赵敏, 女
selectByExample - <== Row: 3, 周芷若, 女
selectByExample - <== Row: 4, 小昭, 女
selectByExample - <== Row: 5, 殷离, 女
selectByExample - <== Row: 6, 张翠山, 男
selectByExample - <== Row: 7, 殷素素, 女
selectByExample - <== Row: 9, 张三丰, 男
selectByExample - <== Row: 13, 张松溪, 男
selectByExample - <== Row: 41, 武青婴, 女
selectByExample - <== Row: 46, 郭襄, 女
selectByExample - <== Row: 47, 张君宝, 男
selectByExample - <== Row: 50, 韩姬, 女
selectByExample - <== Row: 51, 黄衫女子, 女
selectByExample - <== Total: 13
这里 or()
是前后两大块之间的 or
,SQL 格式化一下:
SELECT id,name AS userName,sex FROM user
WHERE
( sex = ? AND ( ( id > ? ) OR ( id < ? ) ) )
OR
( name LIKE ? )
or(...)
对应的 AND ( ( id > ? ) OR ( id < ? ) )
,是 AND
连接,块内通过 OR
连接。
最后执行的结果是在前面基础上,把所有姓张的也都查询出来了。