接口 io.mybatis.mapper.fn.FnMapper<T>
# 2.5.1 基本用法
这个接口主要目的是为了演示如何通过传入字段改变查询的逻辑。接口包含下面几个方法:
public interface FnMapper<T> {
/**
* 根据主键更新实体中不为空的字段,强制字段不区分是否 null,都更新
*
* @param entity 实体类
* @param forceUpdateFields 强制更新的字段,不区分字段是否为 null,通过 {@link Fn#of(Fn...)} 创建 {@link Fn.Fns}
* @return 1成功,0失败
*/
@Lang(Caching.class)
@UpdateProvider(type = FnProvider.class, method = "updateByPrimaryKeySelectiveWithForceFields")
int updateByPrimaryKeySelectiveWithForceFields(@Param("entity") T entity, @Param("fns") Fn.Fns<T> forceUpdateFields);
/**
* 根据实体字段条件查询唯一的实体({@link io.mybatis.mapper.example.ExampleMapper} 可以实现一样的功能,当前方法只是示例)
*
* @param entity 实体类
* @param selectFileds 查询的字段,不区分字段是否为 null,通过 {@link Fn#of(Fn...)} 创建 {@link Fn.Fns}
* @return 单个实体,查询结果由多条时报错
*/
@Lang(Caching.class)
@SelectProvider(type = FnProvider.class, method = "selectColumns")
Optional<T> selectColumnsOne(@Param("entity") T entity, @Param("fns") Fn.Fns<T> selectFileds);
/**
* 根据实体字段条件批量查询({@link io.mybatis.mapper.example.ExampleMapper} 可以实现一样的功能,当前方法只是示例)
*
* @param entity 实体类
* @param selectFileds 查询的字段,不区分字段是否为 null,通过 {@link Fn#of(Fn...)} 创建 {@link Fn.Fns}
* @return 实体列表
*/
@Lang(Caching.class)
@SelectProvider(type = FnProvider.class, method = "selectColumns")
List<T> selectColumns(@Param("entity") T entity, @Param("fns") Fn.Fns<T> selectFileds);
}
第一个方法名有点长,用列表展示不太合适了。
这几个方法,都可以通过传入字段改变行为,并且第一个名字最长的方法,直接被复制到了 Mapper
接口的定义中,这就是第 3 点要说明的内容,
实际上你可以复制粘贴的方式构造一个自己的基类 Mapper。
名字最长这个方法是许多人想要的一个方法,在选择更新的情况下还能指定必须更新的字段,这个方法的示例如下:
User user = mapper.selectByPrimaryKey(1L).get();
user.setUserName(null);
int count = mapper.updateByPrimaryKeySelectiveWithForceFields(user, Fn.of(User::getUserName));
单纯从 Mapper 接口调用来看,这个方法名字太长,写法也有点别扭,等看到后续 Service 层封装时, 会掩盖这些不舒服的地方,会变得更简洁,例如 Service 中的接口定义:
/**
* 更新(非空字段),指定的强制更新字段不区分是否为空
*
* @param entity 实体类
* @param forceUpdateFields 强制更新的字段,不区分字段是否为 null
* @return 返回更新成功后的实体,远程服务调用时,由于序列化和反序列化,入参和返回值不是同一个对象
*/
T updateSelective(T entity, Fn<T, Object>... forceUpdateFields);
调用的示例:
userService.updateSelective(user, User::getName, User::getRoleId);
# 2.5.2 字符串用法
2.1.0 版本之后,Fn支持直接输入字段名或列名,支持使用字符串形式的参数。
由于字段只能通过方法引用方式,这样会导致部分场景无法使用字符串传递字段或列名。为了解决这个问题,Fn 增加了几个方法,可以直接传递字段名或列名,支持使用字符串形式的参数。
Assert.assertEquals("is_admin", Fn.field(UserIs.class, UserIs::isAdmin).toColumn());
Assert.assertEquals("is_admin", Fn.field(UserIs.class, "admin").toColumn());
Assert.assertEquals("is_admin", Fn.column(UserIs.class, "is_admin").toColumn());
第一个方法时之前默认提供的方法,第二个方法允许指定Java字段名,第三个方法允许指定数据库列名。
下面是示例:
users = mapper.selectColumns(user, Fn.of(UserIds.class, "id1", "name"));
users.forEach(u -> {
Assert.assertNotNull(u.getId1());
Assert.assertNull(u.getId2());
Assert.assertNotNull(u.getName());
});
//使用字段名
example.createCriteria().andEqualTo(Fn.field(User.class, "roleId"), user.getRoleId());
//使用列名
example.createCriteria().andEqualTo(Fn.column(User.class, "role_id"), user.getRoleId());