神奇好望角 The Magical Cape of Good Hope

庸人不必自扰,智者何需千虑?
posts - 26, comments - 50, trackbacks - 0, articles - 11
  BlogJava :: 首页 ::  :: 联系 :: 聚合  :: 管理

JPA 应用技巧 2:主键外键合体映射

Posted on 2011-09-13 11:27 蜀山兆孨龘 阅读(3869) 评论(0)  编辑  收藏 所属分类: Java EE

考虑两个具有一对一关联的实体类,受控方除了有一个自己的主键,还有一个引用主控方的外键。如果主控方和受控方是同生同灭的关系,换句话说,双方的一对一关联一旦确立就不可更改,就可以考虑让双方共享相同的主键,简化受控方的表结构。下面就让楼主通过实例来说明如何用 JPA 2.0 来实现这种映射。

盯着眼前的电脑,楼主想到了一个也许不太贴切的例子:员工和公司配的电脑的关系。员工的主键就是工号。虽然电脑可能被换掉,但电脑实体完全可以用工号做主键,只是把电脑配置的详细信息改掉而已。此处的难点就在与如何将电脑的主键字段同时映射一个员工,请看楼主是怎么一步步推导出来的。

一开始是最想当然的写法:

        public class Computer implements Serializable {
            @Id
            @OneToOne
            private Employee employee;
            // 此处省略若干行
        }
    

然而根据规范,只有这些类型可以作为主键:Java 原始类型(例如 int)、原始包装类型(例如 Integer)、Stringjava.util.Datejava.sql.Datejava.math.BigDecimaljava.math.BigInteger。所以直接拿 Employee 做主键是不行的。顺便提一下,也许某些 JPA 实现自己做了扩展,使得可以直接拿实体类做主键,这已经超出了 JPA 规范的范畴,此处不讨论。

直接映射是行不通了,那有什么间接的方式吗?这时楼主想起了一个特殊的注解:EmbeddedId。该注解的本意是用于联合主键,不过变通一下,是否可以将 Employee 包装进一个嵌入式主键,然后再将这个嵌入式主键作为 Computer 的主键以达到目的?带着这种想法,楼主有了下面的代码:

        @Embeddable
        public class ComputerId implements Serializable {
            @OneToOne
            private Employee employee;
            // 此处省略若干行
        }
    
        public class Computer implements Serializable {
            @EmbeddedId
            private ComputerId id;
            // 此处省略若干行
        }
    

现在又出现了新的问题:JPA 不支持定义在嵌入式主键类中的关联映射。好在天无绝人之路,EmbeddedId 的文档中直接指出,可以使用 MapsId 注解来间接指定嵌入式主键类中的关联映射,而且还附带了一个例子!于是最终的成品就出炉了:

        @Embeddable
        public class ComputerId implements Serializable {
            private String employeeId;
            // 此处省略若干行
        }
    
        public class Computer implements Serializable {
            @EmbeddedId
            private Employee employee;
            @MapsId("employeeId")
            @OneToOne
            private Employee employee;
            // 此处省略若干行
        }
    

唯一的遗憾是,逻辑上的主控方 Employee 现在不得不成为受控方了,好在可以定义级联操作来达到差不多的效果:

        public class Employee implements Serializable {
            @Id
            private String id;
            @OneToOne(mappedBy = "employee", cascade = CascadeType.ALL)
            private Computer computer;
            // 此处省略若干行
        }
    

虽然做到了,但确实挺绕的。希望未来版本的 JPA 能直接支持将实体类作为主键,楼主个人觉得不是一个技术问题。


只有注册用户登录后才能发表评论。


网站导航: