Skip to content

MyBatis Constructor Injection should support <collection> as well? #101

Closed
@djvdorp

Description

@djvdorp

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

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions