我有一个像这样的JPA实体:
@Entity
@Table(name = "category")
public class Category implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Basic(optional = false)
@Column(name = "id")
private Integer id;
@Basic(optional = false)
@Column(name = "name")
private String name;
@OneToMany(cascade = CascadeType.ALL,mappedBy = "category")
private Collection
使用MysqL作为底层数据库. “name”被设计为唯一键.使用Hibernate作为JPA提供程序.
使用merge方法的问题是因为pk是由db生成的,所以如果记录已经存在(名称已经存在),那么Hibernate会尝试将其插入到db中,我将获得一个唯一的键约束违例异常而不是更新.有没有人有一个很好的做法来处理?谢谢!
P.S:我的解决方法是这样的:
public void save(Category entity) {
Category existingEntity = this.find(entity.getName());
if (existingEntity == null) {
em.persist(entity);
//code to commit ...
} else {
entity.setId(existingEntity.getId());
em.merge(entity);
//code to commit ...
}
}
public Category find(String categoryName) {
try {
return (Category) getEm().createNamedQuery("Category.findByName").
setParameter("name",categoryName).getSingleResult();
} catch (NoResultException e) {
return null;
}
}
最佳答案
How to use em.merge() to insert OR update for jpa entities if primary key is generated by database?
无论您是否使用生成的标识符,IMO都无关紧要.这里的问题是你想在一些唯一的密钥上实现“upsert”而不是PK和JPA并没有真正提供对它的支持(merge依赖于数据库身份).
所以你有AFAIK 2选项.
首先执行INSERT并在失败的情况下实施一些重试机制,因为存在唯一的约束违规,然后查找并更新现有记录(使用新的实体管理器).
或者,首先执行SELECT,然后根据SELECT的结果插入或更新(这就是你所做的).这可以工作,但不是100%保证,因为你可以在两个并发线程之间有一个竞争条件(他们可能找不到给定categoryName的记录并尝试并行插入;最慢的线程将失败).如果不太可能,这可能是一个可接受的解决方案.
更新:如果您不介意使用MysqL专有功能,可能会有第三个奖励选项,请参阅12.2.5.3. INSERT … ON DUPLICATE KEY UPDATE Syntax.但从未使用JPA进行过测试.