Description
Mybatis has immutable object support but not ideal. Suppose we have class and mapper
public enum TestEnum {
ONE,
TWO;
public static final Map<Integer, TestEnum> BY_CODE = ...
}
public class TestRecord {
private final TestEnum field;
public TestRecord(final Integer field) {
super();
this.field = TestEnum.BY_CODE.get(field);
}
public TestEnum getField() {
return field;
}
}
public interface TestMapper {
@Select("SELECT 1 AS field")
TestRecord get();
}
If we set AutoMappingBehavior to NONE, then MyBatis does not use constructor with parameters and throws exception ExecutionException: Do not know how to create an instance of class.
If we set AutoMappingBehavior to PARTIAL/FULL, then MyBatis finds constructor, calls it BUT also sets final fields with the same name because JDK allows that via reflection. I can't see any reason why would MyBatis ignores all conventions, the fact that there is no setter, ignores the fact that field is final (i.e. set once in constructor) and still tries to set final field. In my example another exception is thrown, I don't remember exact wording, but the problem was trying to set value 1 to field of type TestEnum.
I used very awkward workaround changing field names in select so that they won't match java bean field names. It makes my code stink. I need to write comment on every select/java bean to explain why I do this. New developer implementing new functionality might forget/never know this and receive this exception again in new code and I (as a lead dev) has no way to prevent this except reviewing every single line of new code and remembering this problem by heart.
- This is a very bad idea to set final fields via reflection. If they are final, then constructor must initialize them, MyBatis may only call correct constructor in this case. Again I can think of no single reason why MyBatis may need to set final fields.
- Even if constructor arguments has the same type as fields then no exception is thrown, but under the hood MyBatis initializes all fields TWICE. First time passing value to constructor which sets those fields using developer logic (which is not always set as is), second time setting value directly to each field overriding any custom constructor initialization logic.
- If you want to keep this strange behavior for some reason at least give us opportunity to tell MyBatis: Do constructor auto mapping, don't do field auto mapping. In other case pls give me correct configuration/annotation settings so that my example above works. Yes I can use mapping with explicit list of arguments, but the whole idea of auto mapping is get rid of explicit mappings, otherwise what's the advantage of MyBatis against good old JDBC queries?