如何在MyBatis插入语句中读取嵌套Optional对象?
相关代码定义
MyObj类
public class MyObj { private String myField; private MyChildObj myChild; public MyObj(String myField, MyChildObj myChild) { this.myField = myField; this.myChild = myChild; } public String getMyField() { return this.myField; } public Optional<MyChildObj> getMyChildObj() { return Optional.ofNullable(this.myChild); } }
MyChildObj类
public class MyChildObj { private String myField2; public MyChildObj(String myField2) { this.myField2 = myField2; } public String getMyField2() { return this.myField2; } }
MyBatis Mapper接口
public interface MyMapper { @Insert("INSERT INTO `Entity` VALUES(" + "#{entity.myField}," + "#{entity.myChild.myField2})") public int insertEntity(@Param("entity") MyObj entity); }
运行报错
There is no getter for property named 'myField2' in 'class java.util.Optional'
原因
MyBatis会按照JavaBean命名规范解析属性表达式:当你写entity.myChild时,它会尝试调用getMyChild()方法,但MyObj中并没有这个方法,反而存在返回Optional<MyChildObj>的getMyChildObj()。由于属性名与getter不匹配,MyBatis错误地将myChild解析为调用getMyChildObj(),拿到了Optional实例,之后试图在Optional上访问myField2,自然会报错。
解决方案(无需编写TypeHandler)
方案1:修改Mapper表达式,直接从Optional中取值
将Mapper中的#{entity.myChild.myField2}替换为#{entity.myChildObj.orElse(null).myField2},让MyBatis先获取Optional实例,再通过orElse(null)取出内部的MyChildObj对象后访问属性。如果确定myChild不会为null,也可以用orElseThrow()避免空指针风险。
修改后的Mapper:
public interface MyMapper { @Insert("INSERT INTO `Entity` VALUES(" + "#{entity.myField}," + "#{entity.myChildObj.orElse(null).myField2})") public int insertEntity(@Param("entity") MyObj entity); }
方案2:为MyObj添加符合规范的getter方法
在MyObj中新增一个返回MyChildObj的getter,匹配属性名myChild:
public MyChildObj getMyChild() { return this.myChild; }
这样原Mapper中的#{entity.myChild.myField2}就能正常解析,同时保留getMyChildObj()方法供其他场景使用。
方案3:调整getter名称与属性一致
如果不需要保留getMyChildObj()方法,可以直接修改getter名称为getMyChild()并返回Optional:
public Optional<MyChildObj> getMyChild() { return Optional.ofNullable(this.myChild); }
然后将Mapper表达式改为#{entity.myChild.orElse(null).myField2}即可。
内容的提问来源于stack exchange,提问作者Holger Veltrup

