JPA阻止实体被保存

时间:2016-06-30 16:41:12

标签: spring hibernate spring-boot jpa spring-data-jpa

我有以下数据库架构:

account -< account_role >- role

总结:account可以绑定到多个rolesaccount_role是一个连接表。预定义了roles,并且在迁移中将角色插入到DB中。

不幸的是,在新account创建hibernate尝试将role插入到表中时会导致以下异常:

org.postgresql.util.PSQLException: ERROR: duplicate key value violates unique constraint "role_role_name_key"
Detail: Key (role_name)=(CUSTOMER) already exists.

如何配置关系以防止插入?

下面提到的课程:

角色

import javax.persistence.*;

import static javax.persistence.EnumType.STRING;
@Entity
@Table(name = "ROLE")
class Role {

  @Id
  @Column(name = "ROLE_NAME")
  @Enumerated(STRING)
  private RoleName role;
  //getters, setters, constructors
}

帐户

import javax.persistence.*;
import java.util.HashSet;
import java.util.Set;

import static javax.persistence.FetchType.EAGER;
import static javax.persistence.GenerationType.SEQUENCE;

@Entity
@Table(name = "ACCOUNT")
public class Account {

  @Id
  @SequenceGenerator(allocationSize = 1, sequenceName = "ACCOUNT_PK_SEQ", name = "ACCOUNT_PK_SEQ")
  @GeneratedValue(generator = "ACCOUNT_PK_SEQ", strategy = SEQUENCE)
  @Column(name = "ID")
  private Long id;

  @Column(name = "USERNAME")
  private String username;

  @ManyToMany(fetch = EAGER)
  @JoinTable(
      name = "ACCOUNT_ROLE",
      joinColumns = @JoinColumn(name = "USERNAME", referencedColumnName = "USERNAME"),
      inverseJoinColumns = @JoinColumn(name = "ROLE_NAME", referencedColumnName = "ROLE_NAME")
  )
  private Set<Role> roles = new HashSet<>();
  //getters, setters, constructor
}

以下是负责保存新帐户的代码:

@Transactional
public Account createNewAccount(Account account, String password) {
  validator.validateNewAccount(account, password);

  String email = account.getUsername().toLowerCase();

  checkIfEmailAlreadyTaken(email);

  LOG.info("Creating new account for username: '{}'.", account.getUsername());

  account.setPassword(encodePassword(password));

  account = repository.save(account);

  return account;
}

Account类的对象是从请求中自动创建的。

可以找到完整的异常堆栈跟踪here

1 个答案:

答案 0 :(得分:1)

问题是由

引起的
@Id
@Column(name = "ROLE_NAME")
@Enumerated(STRING)

JPA规范说:

  

2.1.4主键和实体标识

     

主键(或复合主键的字段或属性)应为以下类型之一:任何Java基本类型;任何原始包装类型; java.lang.String中; java.util.Date; java.sql.Date。然而,通常,不应在主键中使用近似数字类型(例如,浮点类型)。主键使用其他类型的实体将不可移植。

因此,当您使用枚举作为id时,似乎不允许@Id和@Enumeration的组合。

使用当前代码,您只能编写与枚举RoleName具有元素一样多的角色。这些是您系统的预定义角色。 你应该在启动时做一次(或以其他方式确保它们存在)

添加注释

@Column(insertable=false, updatable=false)

到Account.roles,这会告诉JPA提供者在插入或更新帐户时不插入或更新角色。

这应该有用。