Closed babyfish-ct closed 1 year ago
MappingTarget目的是为了不生成新的对象,而直接给现有的对象属性赋值,不可变对象会有影响吗。
方便举个例子看一下么
是的,@MappingTarget的目的是为了修改已有对象。
但问题是,对象可能是不可变的,比如Java语言的record类型。
也就是说,@MappingTarget本身,就是在假设用户的的Target类型一定是传统get/set风格的POJO,再假设数据是可以直接修改的。
建议在@AutoMapper注解中加一个选项,用来控制是否是否生成@MappingTarget的版本,这个问题是我在整合jimmer和mapstruct-plus的时候发现的
我考虑下实现方案。
如果这个对象不可变,第一个方法不会报错么
上面说错了,是@AutoMapping注解,是的第一个方法是生成一个新的对象,不会报错
不可变对象,比如String,一旦被“修改”就会产生新的对象
就是可以忽略生成带MappingTarget的方法对吧。我考虑下如何做
第一个方法不报错的原因这样的。MapStruct本身靠build模式支持不可变对象,并且它通过此方法内置了5种Java领域的不可变对象方案。Jimmer的不可变对象远比它内置的这些强大,自然不可能用MapStruct内置的这些简单不可变对象框架,所以定义了一套全新的不可变对象技术体系,并且利用MapStruct留下的annotation processor扩展点教会MapStruct如何处理这种全新的对象风格。
即,无论非传统non-pojo-style对象如何设计,都可以扩展mapstruct让第一个方法能正确工作。但是,不可变对象无法用@MappingTarget模式是谁也无法改变的。
是的,不生成MappingTarget版本即可。
比如,给@AutoMapping
一个boolean immutable() default false
方便提供一个测试的类么
虽然此问题是因不可变对象而起,但不需要用不可变对象来测试和验证这个新加的功能。
就mapstructplus首页那个入门介绍
@AutoMapper(target = UserDto.class)
public class User {
// ...
}
@AutoMapper
加一个boolean targetImmutable() default false
上述代码即可改成
@AutoMapper(target = UserDto.class, targetImmutable = true)
public class User {
// ...
}
保证生成的MapStruct Mapper没有@MappingTarget UserDto existingUserDto
即可。
如果UserDto真的是不可变类型,无论是mapstruct内置的行为,还是用户对mapstruct的扩展,都能正确生成正常转化逻辑, 那是mapstruct内置行为或用户扩展行为的责任。mapstructplus本身只需要保证targetImmutable = true
时不生成含MappingTarget参数的方法即可
最终生成的方法,直接返回 target,这样子没问题吧,就不再尝试修改了
你是说类似这样吗?
default void assignTo(S source, @MappingTarget TImmutable target) {
// Donithing
}
给了默认实现,所以MapStruct不会去研究怎么生成代码,自然不会报错。但总感觉怪怪的,没用的东西,就不要放到这里了。
仅留下
TImmutable convert(S source)
让mapstruct或基于它的扩展去生成代码即可
default Timutable assignTo(S source, @MappingTarget Timutable target) {
return target;
}
应该是这个样子的,父接口不能动,所以这个方法删不掉
这样子是不行的,因为这个问题会在编译是时候报错,编译根本通不过,必须有不带@MappingTarget的版本
这个项目访问不了。 另外,你发的这个截图,与不可变对象没啥关系吧,只是属性没有对应上
把这几个类的代码发一下吧,我看一下
我代码仓库上面已经贴了:https://gitee.com/mayihua/msp-jimmer
gitee权限已修改
你上面的做法好像是可以的,给了默认实现,ms编译就不报错了
看了下这个仓库的代码,Book 类是接口么,如果是接口,BookInput怎样转成Book的呢
这个项目访问不了。 另外,你发的这个截图,与不可变对象没啥关系吧,只是属性没有对应上
你先看下我的代码,你现在对jimmer的不可变对象还没什么概念
关于应该是这个样子的,父接口不能动,所以这个方法删不掉
提供两个父接口即可
interace ImmutableBaseMapper<S, T> {
T convert(S source);
}
interface BaseMapper<S, T> extends ImmutableBaseMapper<S, T> {
void assignTo(S source, @MappingTarget target);
}
根据@AutoMapper
上targetImmutable是否为true,决定选用哪个基接口即可。
我看了下你的用法, 其实没有那么复杂, 直接判断target是接口的话,直接不处理,你目前的用法,都不用修改配置项
看了下这个仓库的代码,Book 类是接口么,如果是接口,BookInput怎样转成Book的呢
是jimmer用ms的扩展点以builder方式实现了bookInput->book,book接口可以像普通类一样用ms
没有判断是否接口的API,我考虑下实现
我看了下你的用法, 其实没有那么复杂, 直接判断target是接口的话,直接不处理,你目前的用法,都不用修改配置项
babyfish的意思是支持“不可变对象”,而不是仅仅解决jimmer这一个问题(java领域还有其他不可变对象),所以他想预留针对不可变对象的接口方法
这些是MapStruct内置的不可变对象支持(Lombok可以生成不可变对象),当然,MapStruct文档列举的只是纯Java的,如果算上Kotlin的,至少十几个不可变对象相关的框架(但,无论多少种,都可以扩展mapstruct予以支持)。
因Jimmer的需求非常强大,整个行业找不到任何不可变框架能满足,所以Jimmer选择创建一套全新的不可变对方方案:为JVM移植immerjs。在真个过程中,Jimmer应自身特点,不可变对象让用户声明为接口(Java下annotaton processor, Kotlin下KSP去实现这些接口,但用户感知不到)
但,这不代表,Java下所有不可变框架的都是定义接口的,很多框架还是定义class而非interface。至于不可变遍对象框架有多少种、用户会选用什么,你完全无法预料。
所以,你需要支持一个普适性的特征:只要用户表明target type是immutable的,就不得能生成带@MappingTarget参数的方法。至于用户采用何种不可变对象技术方案,不重要,而且太多了,你相关管也管不过来。
懂你的意思,我测试了下你提供的代码。 并没有因为不可变对象而报错,例如BookInputToBook,编译生成的转换为:
@Override
public Book convert(BookInput arg0) {
if ( arg0 == null ) {
return null;
}
BookDraft.MapStruct book = new BookDraft.MapStruct();
book.id( arg0.getId() );
return book.build();
}
@Override
public Book convert(BookInput arg0, Book arg1) {
if ( arg0 == null ) {
return arg1;
}
return arg1;
}
没有找到赋值的方法,看起来并没有啥问题
就是这段
BookDraft.MapStruct book = new BookDraft.MapStruct();
book.id( arg0.getId() );
return book.build();
很明显,一个笨重的builder模式,mapstruct只支持builder拓展,jimmer只能选择如此用如此不雅的方式拓展它。
我们的问题不是第二个方法么,我现在都不清楚为啥编译会有问题了 😶🌫️
我看你说没找赋值语句,理解错了
我们的问题不是第二个方法么,我现在都不清楚为啥编译会有问题了 😶🌫️
编译问题是ms报的,因为第二个方法没有提供默认实现的情况下,ms就会自己实现,但ms自己确实就找不到store.id这个属性所以编译报错
为什么jimmer不提供第二个方法的实现?因为不可变对象无法提供,即便提供了,也不符合@MappingTarget的语义
懂你的意思,我测试了下你提供的代码。 并没有因为不可变对象而报错,例如BookInputToBook,编译生成的转换为:
@Override public Book convert(BookInput arg0) { if ( arg0 == null ) { return null; } BookDraft.MapStruct book = new BookDraft.MapStruct(); book.id( arg0.getId() ); return book.build(); } @Override public Book convert(BookInput arg0, Book arg1) { if ( arg0 == null ) { return arg1; } return arg1; }
没有找到赋值的方法,看起来并没有啥问题
没报错?你可能要拉一下我gitee最新的代码,主要是加了
在
AutoMappergenerator
类,存在如下两个行为第一个行为生成无@MappingTarget的版本,没有任何问题 第二个行为生成带@MappingTarget的版本,如果相关类采用不可变对象设计,会导致问题问题。
不可变对象有多种,比如
建议支持一个开关,给用户一个关闭第二个行为的机会。