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?
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.