2009-07-31 19 views
18

Come fare in modo che la tabella user_roles definisca le due colonne (userID, roleID) come una chiave primaria composta. dovrebbe essere facile, non riesco a ricordare/trovare.come creare una chiave primaria composta (annotazione di persistenza java)

In user entità:

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

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

In roles entità:

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

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

Grazie.

** INFO

cui v'è una terza tabella user_roles (auto generato da sopra) che prende userID da user entità e roleID da roles entità. Ora ho bisogno che quelle due colonne nella tabella generata (user_roles) siano una chiave primaria composta.

risposta

22

hai già avuto un paio di buone risposte qui su come fare esattamente come si chiede ..

per riferimento vorrei solo citare il metodo consigliato per fare questo in Hibernate invece, che è quello di utilizzare una chiave surrogata come chiave primaria, e per segnare chiavi business come NaturalId di:

Anche se si consiglia l'uso di chiavi surrogate come chiavi primarie, si dovrebbe prova a identificare le chiavi naturali per tutte le entità. Una chiave naturale è una proprietà o una combinazione di proprietà che è univoca e non nulla. È anche immutabile. Mappare le proprietà di la chiave naturale all'interno dell'elemento . Hibernate sarà generare la chiave univoca necessaria e i vincoli di nullability e, come risultato , la mappatura sarà più autodocumentazione.

Si consiglia di implementare equals() e hashCode() per confrontare le proprietà della chiave naturale dell'entità.

Nel codice, utilizzando le annotazioni, questo sarebbe simile a questa:

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

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

Usando questo vi farà risparmiare un sacco di mal di testa in fondo alla strada, come scoprirete quando si ha spesso a riferimento/mappa la chiave primaria composta.

Ho trovato questo fuori strada, e alla fine ho rinunciato a combattere contro Hibernate e invece ho deciso di seguire il flusso. Comprendo perfettamente che questo potrebbe non essere possibile nel tuo caso, in quanto potresti avere a che fare con software o dipendenze legacy, ma volevo solo menzionarlo per riferimenti futuri. (se non puoi usarlo forse qualcun altro può!)

+0

Non so se Hibernate è goi ng per darti qualcosa rispetto all'utilizzo di JPA. –

1

Grazie per aver migliorato la tua domanda ... e tenendo conto dei suggerimenti.

(Siamo spiacenti, si è un po 'strano che si Postfix tuoi entità con Tao, ma non è questo il punto.)

Non sono sicuro che c'è qualche problema sinistra:

  • Il due entità che descrivi hanno ciascuna un PK, non un paio di.
  • La tabella di collegamento non ha un'entità corrispondente, è definita implicitamente dalle due entità e dalla loro relazione ManyToMany. Se hai bisogno di una terza entità, cambia il tuo ManyToMany per un paio di relazioni OneToMany e ManyToOne.
+0

ma che non è quello che voglio. qui sembra rendere la chiave composita sulla classe che non viene generata automaticamente. Ho bisogno di una chiave composta nella tabella user_roles per la quale non ho QUALUNQUE classe. la tabella viene generata automaticamente dalle classi utente e ruolo dao. quindi se volessi una chiave composta, ad esempio, userdao, allora funzionerebbe. ma non ho bisogno/voglio quello – b0x0rz

+0

dao postfix è legacy da qualcun altro. – b0x0rz

+0

"È anche possibile mappare una relazione molti-a-molti tra le due classi." Sì, l'ho già fatto (in questione). Ora ho bisogno che quella tabella GENERATA non sia senza una chiave primaria. E ho bisogno che quella chiave primaria sia una chiave primaria composta di tutte (entrambe) le colonne nella tabella user_roles. – b0x0rz

2

chiavi composite sono fatte usando @IdClass (l'altro modo è utilizzando EmbeddedId e @Embeddable non è sicuro che quello che si sta cercando) la @IdClass è la seguente

@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(); 
    } 
} 

mia categoria qui sarà essere il vostro user_roles e il nome e l'CreateDate sarà il vostro ID utente e ID ruolo

+0

Sì, la cosa importante è che è necessario un classe addizionale per la chiave combinata Suggerisco di utilizzare uno strumento di generazione automatica della mappatura per creare lo strato ORM per te. Io uso personalmente Netbeans. –

12

Per soddisfare la tua richiesta, puoi mappare il tuo @ManyToMany come una mappatura @OneToMany. In questo modo, USER_ROLE conterrà sia USER_ID e ROLE_ID come composto primario chiave

vi mostrerò come:

@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; 
     } 

    } 

} 

ricordare

Se un oggetto ha un assegnato identificatore, o una chiave composta, l'identificatore DEVE ESSERE ASSEGNATO all'istanza dell'oggetto PRIMA di chiamare save().

per questo abbiamo creato un costruttore JoinedUserRoleId come questo, al fine di prendersi cura di esso

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

E infine classe Ruolo

@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); 
    } 

} 

Secondo testarlo, scriviamo la seguente

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(); 

Accordo di notifica per codificare sopra NO REFERENCE alla classe JoinedUserRole.

Se si desidera recuperare un JoinedUserRole, provare le seguenti

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(); 

saluti,

+2

Wow, ci hai messo un sacco di lavoro! +1 – Tim

1

ho avuto lo stesso problema con le chiavi primarie. Conoscevo anche la soluzione con la classe @Embeddable e @EmbeddedId. Ma volevo solo la soluzione semplice con le annotazioni.

Ebbene ho trovato l'illuminazione attraverso questo articolo: http://www.vaannila.com/hibernate/hibernate-example/hibernate-mapping-many-to-many-using-annotations-1.html

e qui è la magia:

questo genera una chiave primaria nella tabella join:

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

questo non è possibile generare una chiave primaria nella tabella di join:

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

almeno nel mio ambiente

nota che la differenza sta usando Impostare o Lista

Problemi correlati