как сделать составной первичный ключ (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 или список