【问题标题】:how to make a composite primary key (java persistence annotation)如何制作复合主键(java持久化注解)
【发布时间】:2010-11-15 18:31:44
【问题描述】:

如何使表 user_roles 将两列(userID、roleID)定义为复合主键。应该很容易,只是不记得/找不到。

user 实体中:

@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "user_roles")
public List<RoleDAO> getRoles() {
    return roles;
}

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Integer getUserID() {
    return userID;
}

roles 实体中:

@ManyToMany(fetch = FetchType.LAZY)
@JoinTable(name = "user_roles")
public List<UserDAO> getUsers() {
    return users;
}

@Id
@GeneratedValue(strategy = GenerationType.AUTO)
public Integer getRoleID() {
    return roleID;
}

谢谢。

** 更多信息

所以有第三个表user_roles(由上面自动生成)从user 实体获取userID,从roles 实体获取roleID。现在我需要生成表 (user_roles) 中的这两列作为复合主键。

【问题讨论】:

    标签: java persistence annotations primary-key many-to-many


    【解决方案1】:

    感谢您改进您的问题...并考虑建议。

    (对不起,你用 Daos 后缀你的实体有点奇怪,但这不是重点。)

    我不确定还有什么问题:

    • 您描述的两个实体各有一个 PK,而不是一对。
    • 链接表没有对应的实体,它由两个实体及其多对多关系隐含定义。如果您需要第三个实体,请将您的 ManyToMany 更改为一对 OneToMany 和 ManyToOne 关系。

    【讨论】:

    • 但这不是我想要的。在这里,它似乎在非自动生成的类上创建了复合键。我需要在表 user_roles 中没有任何类的复合键。该表是从用户和角色 dao 类自动生成的。因此,如果我想要一个复合键,例如 userdao,那么这将起作用。但我不需要/想要那个。
    • dao 后缀是别人遗留下来的。
    • "也可以在两个类之间映射多对多关系。"是的,我已经这样做了(有问题)。现在我需要那个 GENERATED 表不能没有主键。而且我需要该主键是 user_roles 表中所有(两个)列的复合主键。
    • 另外,感谢您的帮助。我改进了一个问题。不太确定还要写什么:(
    • 好吧,如果你感谢我的帮助...你能不能删除你在我的回答上点击的“没有帮助”?
    【解决方案2】:

    复合键是使用@IdClass 完成的(另一种方法是使用@EmbeddedId 和@Embeddable 不知道你在找哪个)@IdClass 如下

    @Entity
    @IdClass(CategoryPK.class)
    public class Category {
        @Id
        protected String name;
    
        @Id
        protected Date createDate;
    
    }
    
    public class CategoryPK implements Serializable {
    
        String name;
    
        Date createDate;
    
        public boolean equals(object other) {
            //implement a equals that the PP can use to determine 
            //how the CategoryPK object can be identified.
        }
    
        public int hashCode(){
            return Super.hashCode();
        }
    }
    

    我的 Category 将是您的 user_roles,name 和 createDate 将是您的 userid 和 roleid

    【讨论】:

    • 是的,重要的是你需要一个额外的组合键类。我建议使用映射自动生成工具为您制作 ORM 层。我个人使用 Netbeans。
    【解决方案3】:

    你已经在这里得到了一些关于如何完全按照你的要求去做的好答案..

    作为参考,我只提一下在 Hibernate 中执行此操作的推荐方法,即使用代理键作为主键,并将业务键标记为 NaturalId:

    虽然我们建议使用 代理键作为主键,你 应该尝试识别自然键 对于所有实体。自然键是 属性或属性组合 那是唯一且非空的。它是 也是不可变的。映射的属性 里面的自然键 元素。冬眠会 生成必要的唯一密钥和 可空性约束,并且作为 结果,您的映射将更多 自我记录。

    建议您实施 equals() 和 hashCode() 比较 实体的自然键属性。

    在代码中,使用注释,这看起来像这样:

    @Entity
    public class UserRole {
      @Id
      @GeneratedValue
      private long id;
    
      @NaturalId
      private User user;
      @NaturalId
      private Role role;
    }
    

    使用它可以为您省去很多麻烦,因为当您经常需要引用/映射组合主键时,您会发现。

    我发现这一点很艰难,最后只是放弃了与 Hibernate 的斗争,而是决定顺其自然。我完全理解在您的情况下这可能是不可能的,因为您可能正在处理遗留软件或依赖项,但我只是想提一下以供将来参考。 (如果你不能使用它,也许其他人可以!)

    【讨论】:

    • 我不知道 Hibernate 是否会给你任何超越使用 JPA 的东西。
    • @JamesMcMahon 你能解释一下你对此的看法吗?
    【解决方案4】:

    为了满足您的要求,您可以将@ManyToMany 映射为@OneToMany 映射。 这样,USER_ROLE 将同时包含 USER_ID 和 ROLE_ID 作为复合主键

    我会告诉你如何:

    @Entity
    @Table(name="USER")
    public class User {
    
        @Id
        @GeneratedValue
        private Integer id;
    
        @OneToMany(cascade=CascadeType.ALL, mappedBy="joinedUserRoleId.user")
        private List<JoinedUserRole> joinedUserRoleList = new ArrayList<JoinedUserRole>();
    
        // no-arg required constructor
        public User() {}
    
        public User(Integer id) {
            this.id = id;
        }
    
        // addRole sets up bidirectional relationship
        public void addRole(Role role) {
            // Notice a JoinedUserRole object
            JoinedUserRole joinedUserRole = new JoinedUserRole(new JoinedUserRole.JoinedUserRoleId(this, role));
    
            joinedUserRole.setUser(this);
            joinedUserRole.setRole(role);
    
            joinedUserRoleList.add(joinedUserRole);
        }
    
    }
    
    @Entity
    @Table(name="USER_ROLE")
    public class JoinedUserRole {
    
        public JoinedUserRole() {}
    
        public JoinedUserRole(JoinedUserRoleId joinedUserRoleId) {
            this.joinedUserRoleId = joinedUserRoleId;
        }
    
        @ManyToOne
        @JoinColumn(name="USER_ID", insertable=false, updatable=false)
        private User user;
    
        @ManyToOne
        @JoinColumn(name="ROLE_ID", insertable=false, updatable=false)
        private Role role;
    
        @EmbeddedId
        // Implemented as static class - see bellow
        private JoinedUserRoleId joinedUserRoleId;
    
        // required because JoinedUserRole contains composite id
        @Embeddable
        public static class JoinedUserRoleId implements Serializable {
    
            @ManyToOne
            @JoinColumn(name="USER_ID")
            private User user;
    
            @ManyToOne
            @JoinColumn(name="ROLE_ID")
            private Role role;
    
            // required no arg constructor
            public JoinedUserRoleId() {}
    
            public JoinedUserRoleId(User user, Role role) {
                this.user = user;
                this.role = role;
            }
    
            public JoinedUserRoleId(Integer userId, Integer roleId) {
                this(new User(userId), new Role(roleId));
            }
    
            @Override
            public boolean equals(Object instance) {
                if (instance == null)
                    return false;
    
                if (!(instance instanceof JoinedUserRoleId))
                    return false;
    
                final JoinedUserRoleId other = (JoinedUserRoleId) instance;
                if (!(user.getId().equals(other.getUser().getId())))
                    return false;
    
                if (!(role.getId().equals(other.getRole().getId())))
                    return false;
    
                return true;
            }
    
            @Override
            public int hashCode() {
                int hash = 7;
                hash = 47 * hash + (this.user != null ? this.user.hashCode() : 0);
                hash = 47 * hash + (this.role != null ? this.role.hashCode() : 0);
                return hash;
            }
    
        }
    
    }
    

    记住

    如果一个对象有一个赋值 标识符或复合键, 标识符应该分配给 调用 save() 之前的对象实例。

    所以我们创建了一个像这样的 JoinedUserRoleId 构造函数来处理它

    public JoinedUserRoleId(User user, Role role) {
        this.user = user;
        this.role = role;
    }
    

    最后是角色类

    @Entity
    @Table(name="ROLE")
    public class Role {
    
        @Id
        @GeneratedValue
        private Integer id;
    
        @OneToMany(cascade=CascadeType.ALL, mappedBy="JoinedUserRoleId.role")
        private List<JoinedUserRole> joinedUserRoleList = new ArrayList<JoinedUserRole>();
    
        // no-arg required constructor
        public Role() {}
    
        public Role(Integer id) {
            this.id = id;
        }
    
        // addUser sets up bidirectional relationship
        public void addUser(User user) {
            // Notice a JoinedUserRole object
            JoinedUserRole joinedUserRole = new JoinedUserRole(new JoinedUserRole.JoinedUserRoleId(user, this));
    
            joinedUserRole.setUser(user);
            joinedUserRole.setRole(this);
    
            joinedUserRoleList.add(joinedUserRole);
        }
    
    }
    

    根据测试,我们写如下

    User user = new User();
    Role role = new Role();
    
    // code in order to save a User and a Role
    Session session = HibernateUtil.getSessionFactory().openSession();
    session.beginTransaction();
    
    Serializable userId  = session.save(user);
    Serializable roleId = session.save(role);
    
    session.getTransaction().commit();
    session.clear();
    session.close();
    
    // code in order to set up bidirectional relationship
    Session anotherSession = HibernateUtil.getSessionFactory().openSession();
    anotherSession.beginTransaction();
    
    User savedUser = (User) anotherSession.load(User.class, userId);
    Role savedRole = (Role) anotherSession.load(Role.class, roleId);
    
    // Automatic dirty checking
    // It will set up bidirectional relationship
    savedUser.addRole(savedRole);
    
    anotherSession.getTransaction().commit();
    anotherSession.clear();
    anotherSession.close();
    

    注意根据上面的代码NO REFERENCE to JoinedUserRole 类。

    如果您想检索 JoinedUserRole,请尝试以下操作

    Session session = HibernateUtil.getSessionFactory().openSession();
    session.beginTransaction();
    
    Integer userId;
    Integer roleId;
    
    // Lets say you have set up both userId and roleId
    JoinedUserRole joinedUserRole = (JoinedUserRole) session.get(JoinedUserRole.class, new JoinedUserRole.JoinedUserRoleId(userId, roleId));
    
    // some operations
    
    session.getTransaction().commit();
    session.clear();
    session.close();
    

    问候,

    【讨论】:

      【解决方案5】:

      我在主键上遇到了同样的问题。 我也知道@Embeddable 和@EmbeddedId 类的解决方案。但我只想要带有注释的简单解决方案。

      嗯,我通过这篇文章找到了启示: http://www.vaannila.com/hibernate/hibernate-example/hibernate-mapping-many-to-many-using-annotations-1.html

      这就是魔法:

      这会在连接表上生成一个主键:

      @ManyToMany(cascade = CascadeType.ALL)
      @JoinTable(name="classA_classB")
      private Set<ClassA> classesA;
      

      这不会在连接表上生成主键:

      @ManyToMany(cascade = CascadeType.ALL)
      @JoinTable(name="classA_classB")
      private List<ClassA> classesA;
      

      至少在我的环境中

      请注意,区别在于使用 SetList

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-01-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2013-08-26
        • 2015-07-22
        • 1970-01-01
        相关资源
        最近更新 更多