Description
I currently have the problem that it seems like MyBatis constructor injection does not support the <collection>
in resultMaps and I don't seem to understand why. I have already read this previous issues related:
mybatis/old-google-code-issues#15
mybatis/old-google-code-issues#480
There also is no example in the docs using <collection>
in constructor injection:
for example I should expect it here:
http://mybatis.github.io/mybatis-3/sqlmap-xml.html#Result_Maps --> Advanced Result Maps
Reasoning behind this issue:
We want Immutable objects without no-args constructors and setters, only final fields which are set in a constructor with all info needed to set those fields. Some of the fields are a Set so I expect the constructor injection to support as this currently works without constructor injection (but we need a private constructor for this, and the fields cannot be made final now).
Let say we have the following table structure:
OBJA_ID | OBJA_NAME | OBJB_RELATED_NAME
---------------------------------------------------------------------------
1 | NAME1 | A
1 | NAME1 | B
1 | NAME1 | C
2 | NAME2 | D
2 | NAME2 | E
We have the following object structure and hierarchy:
public class ObjA {
private String name;
private Set<ObjB> objectBs;
private ObjA() {
// MyBatis only constructor
}
public ObjA(String name, Set<ObjB> objectBs) {
this.name = name;
this.objectBs = objectBs;
}
}
public class ObjB {
private String relatedName;
private ObjB() {
// MyBatis only constuctor
}
public ObjB(String relatedName) {
this.relatedName = relatedName;
}
}
I expect ObjA with ID 1 to have a Set { 1,2,3 } and ObjA with ID 2 to have a Set { 4,5 }.
We currently map it this way, but we need that private constructor and can't make the fields final:
<resultMap id="ObjAResult" type="ObjA" >
<result property="name" column="OBJA_NAME"/>
<collection property="objectBs" resultMap="ObjBResult"/>
</resultMap>
<resultMap id="ObjBResult" type="ObjB">
<result property="relatedName" column="OBJB_RELATED_NAME"/>
</resultMap>
We would prefer the following objects, by using constructor injection:
public class ObjA {
private final String name;
private final Set<ObjB> objectBs;
public ObjA(String name, Set<ObjB> objectBs) {
this.name = name;
this.objectBs = objectBs;
}
}
public class ObjB {
private final String relatedName;
public ObjB(String relatedName) {
this.relatedName = relatedName;
}
}
For this we would use something like the following resultmap:
<resultMap id="ObjAResult" type="ObjA" >
<constructor>
<arg column="OBJA_NAME" javaType="String">
<arg resultMap="ObjBResult" javaType="java.util.Set">
</constructor>
</resultMap>
<resultMap id="ObjBResult" type="ObjB">
<constructor>
<arg column="OBJB_RELATED_NAME" javaType="String">
</constructor>
</resultMap>
However, it seems impossible to use the right javaType here (java.util.Set<ObjB>
) in the constructor, and the biggest issue is that there is no <collection>
behaviour here, meaning that ObjA with ID 1 will never have a Set { 1,2,3 } and ObjA with ID 2 will never have a Set { 4,5 }, because it's the <collection>
behaviour that does this, and this <collection>
can not be used when using constructor injection.
Referring to the 2 other related issues in the past (Google Code links), it seems that I am not the only one with this problem, but it bothers me a lot, as I think the cleaner solution here (constructor injection everywhere) should just be possible, instead of private constructors for MyBatis only and Reflection.
I hope all is clear and look forward to any response,
Daniel