MyBatis:如何映射“逆”关系?

时间:2013-06-07 18:52:43

标签: mybatis

我的问题是坚持两个具有1:n关系的类:

public class DayRecord {
    private Long id;
    private List<TimeRecord> timeRecordsToday = new ArrayList<TimeRecord>(4);
    ...
}

public class TimeRecord {
    private Long id;
    ...
}

因此,在代码中,DayRecord知道TimeRecord。

create table DAY_RECORDS (
    id int primary key,
);

create table TIME_RECORDS (
    id int primary key,
    day_record_id int not null,
    foreign key (day_record_id) references DAY_RECORDS (id)
);

在数据库中,TimeRecord知道DayRecord。

我可以一步保存DayRecord及其所有TimeRecords吗?

在Hibernate中,我可以设置逆映射,只保存DayRecord,它的所有TimeRecords也会被保存。使用MyBatis,我试图将这些类彼此独立保存:

<mapper
    namespace="de.stevenschwenke.java.javafx.xyz.DayRecordMapper">
    <insert id="insertDayRecord" 
        parameterType="de.stevenschwenke.java.javafx.xyz.DayRecord">
        insert into DAY_RECORDS (id) values (NEXT VALUE FOR DAY_RECORDS_SEQ);
    </insert>
</mapper>

<mapper
    namespace="de.stevenschwenke.java.javafx.xyz.TimeRecordMapper">
    <insert id="insertTimeRecord"
        parameterType="de.stevenschwenke.java.javafx.xyz.TimeRecord">
        insert into TIME_RECORDS (id) values (NEXT VALUE FOR TIME_RECORDS_SEQ);
    </insert>
</mapper>

但是如何在TimeRecord中保存DayRecord-ID?

想法:

  1. 为TimeRecord提供属性dayRecordId。这样,将创建循环依赖。但是,映射会在保存时处理延迟。
  2. 在一个事务中,首先保存DayRecord,获取其ID,在TimeRecords中设置并保存此对象。
  3. 在插入中使用嵌套的select语句,如文档
  4. 中所示

    保存这两个对象的最佳方法是什么?谢谢你的帮助!

2 个答案:

答案 0 :(得分:2)

正如jdevelop已经提到的,MyBatis只是一个SQL包装器。因为SQL不提供插入两个具有关系的对象的方法,所以MyBatis也不能这样做。

所以这是我的解决方法:正如我所提到的,我不想通过让TimeRecord了解DayRecord来添加循环依赖。所以我创建了一个仅用于插入TimeRecords的包装类:

public class TimeRecordInsertWrapper {
    public Long id;
    public int hours;
    public long dayRecordId;
    [constructor/getter/setter omited but there with public access modifier]
}

首先,我存储DayRecord并获取它的ID。然后我创建包装器对象并存储TimeRecords:

public long insertDayRecord(DayRecord newRecord) {
    SqlSession session = sqlSessionFactory.openSession();
    try {

        session.insert(
                "de.stevenschwenke.java.javafx.xyz.DayRecordMapper.insertDayRecord",
                newRecord);

        for (TimeRecord tr : newRecord.getTimeRecordsToday()) {
            TimeRecordInsertWrapper wrapper = new TimeRecordInsertWrapper(tr.getHours(), newRecord.getId());
            session.insert("de.stevenschwenke.java.javafx.xyz.TimeRecordMapper.insertTimeRecord",
                    wrapper);
        }
        return newRecord.getId();
    } finally {
        session.commit();
        session.close();
    }
}

这样,我可以使用我的单向对象模型并在数据库中使用“正确”的映射。

答案 1 :(得分:1)

Mybatis只是SQL映射框架,它允许您从Java代码中抽象出SQL代码,或多或少。他们假装看起来像最新版本的Hibernate,但这导致了XML中奇怪的结构。

我建议存储DayRecord并从selectKey获取它,然后在后续的mapper调用中使用该ID。这是映射器内部实际发生的情况,但复杂的XML意味着内置的复杂FSM。所以保持简单,使用myBatis安全,或者使用Hibernate。

更好的是,您可以为任务定义自定义DAO接口,然后您可以设置某种具有@Transactional属性的服务层。这需要mybatis-guice,但它确实很好,您不需要处理代码中的事务(它们是声明性的)。