как сделать составной первичный ключ (Java persistence annotation)

Как сделать так, чтобы таблица 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 (автоматически генерируется выше), который принимает userID С user сущность и roleID С roles сущности. Теперь мне нужно эти два столбца в сгенерированной таблице (user_roles) быть составным первичным ключом.

5 ответов


вы уже имели несколько хороших ответов здесь о том, как сделать именно так, как вы просите..

для справки позвольте мне просто упомянуть рекомендуемый способ сделать это в спящем режиме, а именно использовать суррогатный ключ в качестве первичного ключа и пометить бизнес-ключи как NaturalId:

хотя мы рекомендуем использовать суррогатные ключи как первичные ключи, вы следует попытаться определить естественные ключи для всех сущностей. Естественный ключ-это свойство или сочетание свойств это уникальный и ненулевой. Это тоже неизменный. Карта свойств естественный ключ внутри элемент. Hibernate будет создайте необходимый уникальный ключ и ограничения недействительности и, как a результат, ваше отображение будет больше самодокументируемыми.

рекомендуется осуществлять equals() и hashCode () для сравнения естественные ключевые свойства сущности.

в коде, используя аннотации, это будет выглядеть это:

@Entity
public class UserRole {
  @Id
  @GeneratedValue
  private long id;

  @NaturalId
  private User user;
  @NaturalId
  private Role role;
}

использование этого сэкономит вам много головных болей по дороге, как вы узнаете, когда вам часто приходится ссылаться / сопоставлять составленный первичный ключ.

я узнал это на своем горьком опыте, и в конце концов просто отказался от борьбы с Hibernate и вместо этого решил пойти с потоком. Я полностью понимаю, что это может быть невозможно в вашем случае, поскольку вы можете иметь дело с устаревшим программным обеспечением или зависимостями, но я просто хотел упомянуть об этом в будущем ссылка. (если вы не можете использовать его, может быть, кто-то может!)


чтобы выполнить ваше требование, вы можете отобразить @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();

уведомление в соответствии с кодом выше нет ссылки на класс 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();

С уважением,


составные ключи выполняются с помощью @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();
    }
}

моя категория здесь будет вашим user_roles и имя и createDate будет вашим userid и roleid


Спасибо за улучшение вашего вопроса ... и с учетом предложений.

(извините, немного странно, что вы исправляете свои сущности с помощью Daos, но дело не в этом.)

Я не уверен, что осталась какая-либо проблема:

  • две сущности, которые вы описываете, имеют один ПК, а не пару.
  • таблица ссылок не имеет соответствующей сущности, она определяется имплицитно двумя сущностями и их отношениями ManyToMany. Если вам нужна третья сущность, измените свою ManyToMany на пару отношений OneToMany и ManyToOne.

у меня была такая же проблема с первичными ключами. Я также знал решение с классом @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;

этот dosn't генерирует первичный ключ в таблице соединений:

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

по крайней мере в моей среде

обратите внимание, что разница в использовании Set или список