休眠 Spring:@ManyToMany DataIntegrityViolationException ConstraintViolationException
Posted
技术标签:
【中文标题】休眠 Spring:@ManyToMany DataIntegrityViolationException ConstraintViolationException【英文标题】:Hibernate Spring: @ManyToMany DataIntegrityViolationException ConstraintViolationException 【发布时间】:2013-10-24 22:53:39 【问题描述】:我正在为以下之间的多对多关系构建一个示例:User(1) - ()AccessLevel() - (1)Role
我已经在 Java 中使用 hibernate 实现了 3 个类,如下所示:
班级用户
@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;
类访问级别
@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;
问题:
当我使用合并方法持久化用户 bean 时,会出现异常:
@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 (?, ?)
从我的角度来看,即使我已将@GeneratedValue 添加到 id 属性,hibernate 似乎也没有为 AccessLevel 生成主键。
注意: 我正在使用 Postgresql 开发生产环境,并使用 HSQL 数据库开发环境,该数据库从一开始就根据实体描述创建所有模式。两种环境都会产生相同的问题。
问候, 克里斯蒂安·科罗拉多
【问题讨论】:
【参考方案1】:原因:
对于多对多关系,您似乎不需要为“联接表”定义类。因此,如果我消除 AccessLevel 实体,逻辑将完全正常。我进一步解释:
说明:
当我定义 User 类时,我还描述了与 Role 的关系:
@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 处理多对多关系所需的全部,因此在合并时它使用该信息来创建和调整此脚本:
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;
现在我告诉 hibernate 有一个与 AccessLevel 实体相关的表“access_level”,它有 3 列,最重要的是 Id,它是主键。
所以我定义了两次“access_level”!
解决方法:
我删除了 access_level 表的实体。 我重写了我的生产脚本,以使“access_level”具有 仅限 user_id/role_id 列。注意:最好知道如何在不产生问题的情况下向联接表添加主键。另一种方法是在数据库中添加一个组合主键(user_id/role_id),它独立于休眠。
【讨论】:
【参考方案2】:为什么在连接表中需要一个 PK 列?会有一个由 user_id 和 role_id 组成的复合 PK。现在,您已经发现 @ManyToMany 的 JoinTable 永远只有两列,在某些时候您可能需要有关此关系的其他数据。
例如
user_id 角色 ID date_granted
然后您可能想要使用您的 AccessLevel 实体,但是您将 @ManyToMany 替换为从 User 到 AccessLevel 的 @OneToMany,也可以从 Role > AccessLevel 替换。
Hibernate 文档自己建议不要使用 @ManyToMany:
不要使用奇异的关联映射:
真正多对多关联的实际测试用例很少见。最多 您需要存储在“链接”中的其他信息的时间 表”。在这种情况下,最好使用两个一对多 与中间链接类的关联。事实上,大多数协会 是一对多和多对一。因此,您应该继续 使用任何其他关联样式时要谨慎。
【讨论】:
以上是关于休眠 Spring:@ManyToMany DataIntegrityViolationException ConstraintViolationException的主要内容,如果未能解决你的问题,请参考以下文章
注释 ConcurrentHashMap 时,在休眠中“非法尝试将非集合映射为 @OneToMany、@ManyToMany 或 @CollectionOfElements”