как сделать составной первичный ключ (аннотация постоянства Java) - PullRequest
18 голосов
/ 31 июля 2009

Как сделать так, чтобы таблица 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 ]

22 голосов
/ 07 августа 2009

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

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

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

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

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

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

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

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

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

12 голосов
/ 10 августа 2009

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

С уважением,

2 голосов
/ 07 августа 2009

Составные ключи создаются с помощью @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 будут вашими идентификатором пользователя и roleid

1 голос
/ 14 июня 2011

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

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

Обратите внимание, что используется разница Установить или Список

1 голос
/ 31 июля 2009

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

(Извините, немного странно, что вы добавляете свои сущности к Даосу, но это не главное.)

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

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