Search code examples
javarecursionmappingmybatisibatis

MyBatis: recursion while mapping more than one association/collection of the same type


I've faced with one interesting problem in MyBatis, which leads to recursion while trying to map two collections of objects that are the same type.

Introduction.

Here is my db schema: simple_db_schema

I have a simple class User:

public class User {
    private Long id;
    private String login;
    private String password;
    private Employee employee;
    // Constructors, setters and getters go here...
}

Then I have class Employee:

public class Employee {
    private Long id;
    private String lastName;
    private User user;
    // Constructors, setters and getters go here...
}

And Project:

public class Project {
    private Long id;
    private String name;
    private List<Employee> observers;
    private List<Employee> executors;
    // Constructors, setters and getters go here...
}

Mappings

Here I show my mappings for this schema.

UserMapping

<resultMap type="User" id="userMap">
    <id property="id" column="user_id"/>
    <result property="name" column="user_login" />
    <result property="password" column="user_password" />
    <association property="employee" javaType="org.project.entity.Employee" resultMap="Employee.employeeMap"/>
</resultMap>

EmployeeMapping

<resultMap id="employeeMap" type="Employee">
    <id property="id" column="emp_id" />
    <result property="lastName" column="emp_last_name" />
    <association property="user" javaType="org.project.entity.User" resultMap="User.userMap"/>
</resultMap>

ProjectMapping

<resultMap type="Project" id="projectMap">
    <id property="id" column="project_id" />
    <result property="name" column="project_name" />
    <collection property="managers" columnPrefix="obsrv" ofType="Employee" 
                javaType="java.util.ArrayList" resultMap="employeeMap" />
    <collection property="executors" columnPrefix="exec" ofType="Employee" 
                javaType="java.util.ArrayList" resultMap="employeeMap" />
</resultMap>

Note that I use "columnPrefix" in ProjectMapping. One is for observers and the other for executors. These collections use the same "resultMap" - "employeeMap".

Problem.

With this approach my mapping does not work. There's no problem with mapping User in Employee and vice versa - that works fine.

The query to DB is correct.

I've tried different ways to sort this out (I put all mappings in one file, I removed Employee association in UserMapping) but nevertheless nothing helped.

I found one solution, but it's not exactly what I was looking for.

One way is to duplicate UserMapping and EmployeeMapping and change its IDs, and than to use it like that:

<resultMap type="Project" id="projectMap">
    <id property="id" column="project_id" />
    <result property="name" column="project_name" />
    <collection property="managers" columnPrefix="obsrv" ofType="Employee" 
                javaType="java.util.ArrayList" resultMap="employeeMap" />
    <collection property="executors" columnPrefix="exec" ofType="Employee"
                javaType="java.util.ArrayList" resultMap="employeeMap1" />
</resultMap>

It means that I have 2 copies of UserMapping and EmployeeMapping with following IDs: userMap, userMap1 and employeeMap, employeeMap1.

Question

Please, could anybody explain me this situation? Why must I duplicate mappings? Or, probably, I've missed some settings.

I don't want to duplicate my mappings, because I want to reuse them.

Is there a solutions without duplicating these mappings?


Solution

  • So, some days ago I faced the same problem again and I've solved it.

    The answer is so simple: do not forget to write correct equals & hashCode for your domains.

    The reason of my problem was absence of equals & hashCode methods (or they were incorrect), that's why mybatis couldn't resolve which object should go to collection #1 or #2.

    So, if anyone is interested in some details, let me know and I'll provide some samples.