spring data jpa复合密钥重复密钥记录插入导致更新

时间:2014-11-26 14:03:15

标签: java spring jpa spring-data spring-data-jpa

我有一个具有复合键的实体,我试图通过将spring数据jpa存储库用于mysql数据库来保持它,如下所示:

@Embeddable
public class MobileVerificationKey implements Serializable{
private static final long serialVersionUID = 1L;

@Column(name="CUSTOMERID")
private Long customerId;

@Column(name="CUSTOMERTYPE")
private Integer customerType;

@Column(name="MOBILE")
private Long mobile;
@Embeddable
public class MobileVerificationKey implements Serializable{

    private static final long serialVersionUID = 1L;

    @Column(name="CUSTOMERID")
    private Long customerId;

    @Column(name="CUSTOMERTYPE")
    private Integer customerType;

    @Column(name="MOBILE")
    private Long mobile;
//getter and setters
}

实体为

@Entity
@Table(name="mobileverificationdetails")
public class MobileVerificationDetails {

    @EmbeddedId
    private MobileVerificationKey key;

    @Column(name="MOBILETYPE")
    private String mobileType;

    @Column(name="MOBILEPIN")
    private Integer mobilePin;
//getters and setters

}

我的spring数据jpa存储库看起来像这样:

public interface MobileVerificationDetailsRepository extends
        CrudRepository<MobileVerificationDetails, MobileVerificationKey> {

    @Override
    MobileVerificationDetails save(MobileVerificationDetails mobileVerificationDetails);

    @Override
    MobileVerificationDetails  findOne(MobileVerificationKey id);
}

现在,如果我尝试为原始记录添加重复记录,为其他字段添加不同的值。当我尝试插入第二条记录时,会导致使用新值更新现有记录,而不是因为违反主键而抛出异常约束......任何人都可以解释一下这种行为。

2 个答案:

答案 0 :(得分:3)

解决这个问题的最简单(也是最少侵入性)的方法可能是确保id只在persist之前设置。这可以通过@PrePersist回调来实现:

abstract class MobileVerificationDetails {

  @EmbeddedId
  private MobileVerificationKey id;

  @PrePersist
  void initIdentifier() {

    if (id == null) {
      this.id = … // Create ID instance here.
    }
  }
}

除此之外,您可以通过实施persist(…)并相应地实施Persistable来强制执行isNew()。确保此方法在第一次插入时返回true。我们通常会看到人们持有一个瞬态布尔标志,该标志在@PostPersist / @PostLoad带注释的方法中更新。

abstract class AbstractEntity<ID extends Serializable> implements Persistable<ID> {

  private @Transient boolean isNew = true;

  @Override
  public boolean isNew() {
    return isNew;
  }

  @PostPersist
  @PostLoad
  void markNotNew() {
    this.isNew = false;
  }
}

答案 1 :(得分:1)

Spring Data Jpa Repository功能是通过包含以下save(..)方法的SimpleJpaRepository类实现的:

@Transactional
public <S extends T> S save(S entity) {

    if (entityInformation.isNew(entity)) {
        em.persist(entity);
        return entity;
    } else {
        return em.merge(entity);
    }
}

因此,Spring Jpa Data Repository save(...)方法合并了一个已存在的实体。

与此相反,如果使用已存在的实体调用,则裸EntityManager#persist()会抛出异常。

可以通过向Spring Data Repository / ies添加custom behavior来解决问题。可以使用1.3.1 Adding custom behavior to single repositoriesexample here1.3.2 Adding custom behavior to all repositoriesexample here中所述的方法之一添加自定义行为。在这两种情况下,自定义行为都包括委托给EntityManager#persist()的新persist()方法。请注意,在方法1.3.2中。你已经拥有一个EntityManager实例,在方法1.3.1中你可以使用@PersistenceContext注入EntityManager实例。

反对我的评论,我建议在存储库中添加新方法,而不是覆盖现有的保存(...)。

相关问题