开发者

How can I create bi-directional mappings with MyBatis 3.0.5?

开发者 https://www.devze.com 2023-04-13 03:38 出处:网络
Say I have two domain objects and a mapper interface. class Person { int id; List<Problem> problems = new ArrayList<Problem>();

Say I have two domain objects and a mapper interface.

class Person {
    int id;
    List<Problem> problems = new ArrayList<Problem>();
}

class Problem {
    int id;
    Person person;
}

interface PersonMapper {
    public List<Person> selectAllPersons();
}

And two database tables.

create table person (
    id integer not null generated always as identity constraint person_pk primary key,
)

create table problem (
    id integer not null generated always as identity constraint problem_pk primary key,
    person_id integer not null constraint problem_person_fk references person
)

I can create a mapping file that gets the data I want.

<resultMap id="personMap" type="Person">
    <id column="person_id" property="id" />
    <collection column="problem_person_id" property="problems"
                javaType="ArrayList" ofType="Problem" resultMap="problemMap" />
</resultMap>

<resultMap id="problemMap" type="Problem">
    <id column="problem_id" property="id" />
    <!-- Adding an association here will cause a circular dependency -->
    <!-- The circular dependency results in a StackOverflowException -->
</resultMap>

<select id="selectAllPersons" resultMap="personMap">
    select
        person.id as person_id,
        problem.id as problem_id
    from person left outer join problem on person.id = problem.person_id
</开发者_如何学Cselect>

However, since MyBatis doesn't do bi-directional mapping, none of the Problem objects in the returned collections will have their Person reference set correctly.

According to this issue, it sounds like I should be able to update my mapper interface and add a custom result handler that can be supplied by the calling class.

interface PersonMapper {
    public List<Person> selectAllPersons(ResultHandler handler);
}

class PersonResultHandler implements ResultHandler {
    @Override
    public void handleResult(ResultContext context) {
        System.out.println(context.getResultObject());
    }
}

class PersonDAO {
    // Get SqlSession sqlSession
    sqlSession.getMapper(PersonMapper.class).selectAllPersons(new PersonResultHandler());
}

However, the handleResult method of my ResultHandler never gets called. I've seen this example, but the extra fluff class in there makes it very difficult to understand. Can anyone give me a simple example of using a custom ResultHandler with mapper interfaces? I'm using MyBatis 3.0.5+.

I've also read through the MyBatis mailing list and there are several suggestions of using caching and lazy loading to solve circular dependencies, but I can't find any examples of how to do it.


You should replace your method declaration to:

interface PersonMapper {
    public void selectAllPersons(ResultHandler handler);
}

And populate List<Person> inside your PersonResultHandler

class PersonResultHandler implements ResultHandler {

    List<Person> persons = new ArrayList<Person>();

    @Override
    public void handleResult(ResultContext context) {
        Object result = context.getResultObject();
        if (result instanceof Person) {
            Person person = (Person) result;
            for (Problem problem : person.getProblems()) {
                problem.setPerson(person);
            }
            persons.add(person);
        }
    }

    public List<Person> getPersons() {
        return persons;
    }
}
0

精彩评论

暂无评论...
验证码 换一张
取 消