Closed shaoguanlee closed 1 year ago
你用的不是最新版本吧?测试也没有问题。
注意看 Reflections
,增加了下面的代码,会找到基类的子类:
//主要是这里 serializedLambda.getInstantiatedMethodType()
Matcher matcher = INSTANTIATED_CLASS_PATTERN.matcher(serializedLambda.getInstantiatedMethodType());
String implClass;
if (matcher.find()) {
implClass = matcher.group("cls").replaceAll("/", "\\.");
} else {
implClass = serializedLambda.getImplClass().replaceAll("/", "\\.");
}
2021年11月3号发布的1.0.3版本就解决了。
非常感谢您的回复
我使用的版本是
<!--通用Mapper-->
<dependency>
<groupId>io.mybatis</groupId>
<artifactId>mybatis-service</artifactId>
<version>1.2.2</version>
</dependency>
<!--通用Mapper的SpringBootStarter-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
我仔细跟踪了代码的执行过程,发现问题并不是在Reflections
类上,Reflections
确实能找到类
能否提交个单测复现这个问题?
一样的问题,Fn<T, Object> fn 用范型T::getXxx()报错,而且没有直接传字段名的方法,从tk转过来很难受,打算转回去了
public class BaseService<D extends IBaseMapper<T>, T extends BaseDO> {
@Autowired
protected D dao;
public List<T> findIds(List<String> idList) {
if (CollUtil.isEmpty(idList)) {
return Collections.emptyList();
}
Example<T> example = dao.wrapper()
.in(t -> "uuid", idList)
.example();
return dao.selectByExample(example);
}
}
可以试试这样获取范型
ParameterizedType parameterizedType = (ParameterizedType) this.getClass().getGenericSuperclass();
Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
return (Class<T>) actualTypeArguments[0]; // 下标是范型顺序
这里是针对当前问题的单测: Assert.assertEquals(2, userService.deleteByIdList(ids));
可以看看和自己用法有什么区别,可以提交一个能复现问题的示例。
我对于该问题提交了一个测试案例 https://github.com/shaoguanlee/mybatis-mapper-issues-50 可以复现这个问题 再次感谢
这里确实存在问题,而且根据class记录的信息来说,T::getId
没有包含子类的信息,只能找到 BaseId
,当前项目的测试中,之所以没有出错,是因为 BaseId
也有注解:
@Entity.Table
public class BaseId<T extends BaseId> {
@Entity.Column(id = true, insertable = false)
private Integer id;
如果去掉这里的 @Entity.Table
就能复现该问题。
上面的例子也可以作为一个解决办法,在你的 BaseId
添加注解也能解决问题。
目前没有办法从BaseId获取到子类。
问题已解决,Fn增加下面方法,在调用过程中提前记录下实体类:
/**
* 当前字段所属的实体类,当实体存在继承关系时
* 父类的方法引用无法获取字段所属的实体类,需要通过该方法指定
*
* @param entityClass 指定实体类
* @return 带有指定实体类的 Fn
*/
default Fn<T, R> in(Class<?> entityClass) {
return new FnImpl<>(this, entityClass);
}
对应的 FnImpl:
/**
* 带有指定类型的方法引用
*/
class FnImpl<T, R> implements Fn<T, R> {
final Fn<T, R> fn;
final Class<?> entityClass;
public FnImpl(Fn<T, R> fn, Class<?> entityClass) {
this.fn = fn;
this.entityClass = entityClass;
}
@Override
public R apply(T t) {
return fn.apply(t);
}
}
在 Reflections 中处理时会判断:
public static ClassField fnToFieldName(Fn fn) {
try {
Class<?> clazz = null;
if (fn instanceof Fn.FnImpl) {
clazz = ((Fn.FnImpl<?, ?>) fn).entityClass;
fn = ((Fn.FnImpl<?, ?>) fn).fn;
//避免嵌套多次的情况
while (fn instanceof Fn.FnImpl) {
fn = ((Fn.FnImpl<?, ?>) fn).fn;
}
}
//其他...
目前内置的方法中,有两个地方已经加上,自己实现抽象类时不用在指定:
/**
* 根据指定字段集合查询:field in (fieldValueList)
* <p>
* 这个方法是个示例,你也可以使用 Java8 的默认方法实现一些通用方法
*
* @param field 字段
* @param fieldValueList 字段值集合
* @param <F> 字段类型
* @return 实体列表
*/
default <F> List<T> selectByFieldList(Fn<T, F> field, Collection<F> fieldValueList) {
Example<T> example = new Example<>();
example.createCriteria().andIn((Fn<T, Object>) field.in(entityClass()), fieldValueList);
return selectByExample(example);
}
/**
* 根据指定字段集合删除:field in (fieldValueList)
* <p>
* 这个方法是个示例,你也可以使用 Java8 的默认方法实现一些通用方法
*
* @param field 字段
* @param fieldValueList 字段值集合
* @param <F> 字段类型
* @return 实体列表
*/
default <F> int deleteByFieldList(Fn<T, F> field, Collection<F> fieldValueList) {
Example<T> example = new Example<>();
example.createCriteria().andIn((Fn<T, Object>) field.in(entityClass()), fieldValueList);
return deleteByExample(example);
}
BaseMapper 中已经加上,所以你的用法中不用特殊处理。
以后遇到类似问题时,比如基类不支持,你的方法可以修改:
/**
* 根据ID列表进行删除
*/
public int deleteByIdList(List<Long> ids) {
return deleteByFieldList(((Fn<T,Long>)T::getId).in(baseMapper.entityClass()), ids);
}
问题描述
我有一个基类,里面有一个deleteByIdList方法,希望提供通用的删除操作。
调用这个方法的时候发生了NPE如下 io.mybatis.provider.EntityFactory
debug,发现entityClass为BaseId。
但是当这么使用时,就一切正常
请教
我不确定这是bug还是我使用不当,所以想问一下
deleteByFieldList(T::getId, ids);
这种使用方法是否正确。