2013-10-16 56 views
2

我建立一個樣品之間的多對多關係:(1)用戶 - ()ACCESSLEVEL() - (1)角色Hibernate的春天:@ManyToMany DataIntegrityViolationException ConstraintViolationException

我已經實現3班在Java中使用Hibernate實現如下:

類用戶

@Entity 
@Table(name="user") 
public class User { 
    @Id 
    @GeneratedValue 
    @Column(name="USER_ID") 

    @ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL) 
    @JoinTable(name = "access_level", joinColumns = { 
      @JoinColumn(name = "user_id", nullable = false, updatable = false) }, 
      inverseJoinColumns = { @JoinColumn(name = "role_id", nullable = false, updatable = false) }) 
    private Set<Role> roles = new HashSet<Role>(0); 

級角色

@Entity 
@Table(name="role") 
public class Role { 

    @Id 
    @GeneratedValue 
    @Column(name="role_id") 
    private Integer id; 

    @Column(name="role_name") 
    private String roleName; 

類ACCESSLEVEL

@Entity 
@Table(name="access_level") 
public class AccessLevel { 

    @Id 
    @GeneratedValue 
    private Integer id; 
    @Column(name="role_id") 
    private Integer roleId; 
    @Column(name="user_id") 
    private Integer userId; 

問題:

當我持續使用合併方法的用戶豆然後異常出現:

@Service 
public class UserServiceImpl implements UserService { 

    @PersistenceContext 
    private EntityManager em; 

    @Override 
    @Transactional 
    public void save(User user) { 
     em.merge(user); 
    } 

異常

org.springframework.web.util.NestedServletException:傳進程

ing failed; nested exception is org.springframework.dao.DataIntegrityViolationException: Could not execute JDBC batch update; SQL [insert into access_level (user_id, role_id) values (?, ?)]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update 
    org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:894) 
    org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:789) 
    javax.servlet.http.HttpServlet.service(HttpServlet.java:641) 
    javax.servlet.http.HttpServlet.service(HttpServlet.java:722) 

正如你可以看到Hibernate是試圖運行此查詢:

insert into access_level (user_id, role_id) values (?, ?) 

從我的觀點。看起來好像hibernate沒有爲AccessLevel生成主鍵,即使我已經將@GeneratedValue添加到了id屬性。

注: 我在生產環境中使用PostgreSQL和環境,纔有發展與基於實體的描述開始時創建的所有模式HSQL數據庫工作。兩種環境都會產生同樣的問

問候, 克里斯蒂安科羅拉多

回答

3

原因:

這似乎是多對多的關係,你不需要定義一個類爲「加入表」。因此,如果我消除AccessLevel實體,邏輯將工作得很好。我再解釋一下:

說明:

當我定義了用戶級的我也描述了與角色的關係:

@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL) 
    @JoinTable(name = "access_level", joinColumns = { 
      @JoinColumn(name = "user_id", nullable = false, updatable = false) }, 
      inverseJoinColumns = { @JoinColumn(name = "role_id", nullable = false, updatable = false) }) 
    private Set<Role> roles = new HashSet<Role>(0); 
這裏

重要的是我已經告訴了Hibernate用戶實體將涉及通過一個名爲「access_level」的表對角色實體,這樣的表將具有user_id和role_id列以便加入先前的實體。

到目前爲止,這是爲了使許多工作,一對多的關係,因此,所有的Hibernate需要mergin它使用該信息來創建和囤此腳本時:

insert into access_level (user_id, role_id) values (?, ?) 

現在,這個問題的時候,我定義卡梅斯對於ACCESSLEVEL一個新的實體:

@Entity 
    @Table(name="access_level") 
    public class AccessLevel { 

     @Id 
     @GeneratedValue 
     private Integer id; 
     @Column(name="role_id") 
     private Integer roleId; 
     @Column(name="user_id") 
     private Integer userId; 

現在我告訴冬眠,有一個表「ACCESS_LEVEL」有關ACCESSLEVEL實體,它有3列,最重要的是編號是主鍵。

所以我定義了「access_level」兩次!

解決方案:

  • 我消滅了實體ACCESS_LEVEL表。
  • 我重寫了我的生產腳本,以便僅具有 user_id/role_id列的「access_level」。

注:這將是很好的瞭解如何將主鍵添加到表連接,而不會產生問題。另一種方法是在數據庫(user_id/role_id)中添加一個獨立於休眠狀態的組合主鍵。

0

爲什麼你需要連接表中的PK列?將會有一個由user_id和role_id組成的組合PK。現在,正如您發現@ManyToMany的JoinTable將只有兩列,並且在某些時候您可能需要關於此關係的其他數據。

例如

USER_ID ROLE_ID date_granted

然後,您可能需要使用您的ACCESSLEVEL實體但是你從用戶更換@OneToMany的@ManyToMany到ACCESSLEVEL以及任選角色> ACCESSLEVEL。

Hibernate的文檔本身建議對@ManyToMany:

不要使用複雜的關聯映射:

實際測試的情況下真正的許多-to-many關聯是罕見的。大多數 的時候你需要額外的信息存儲在「鏈接 表」中。在這種情況下,使用兩個一對多關聯到中間鏈接類會更好。實際上,大多數協會 是一對多和多對一的。出於這個原因,在使用任何其他關聯風格時,您應謹慎行事 。