我如何确定对象是否已经存在而不是再次创建。
@Component
public class ProductServiceImpl implements ProductService
{
@PersistenceContext
private EntityManager em;
public Product getOrCreateProduct(String productName, String peoductDescr)
{
Product product =(new Product (productName, peoductDescr));
em.merge(product);
return product;
}
}
我这样做了,但因为它仍然继续创建新的数据库条目而不是返回新的。
最佳答案
虽然 John 的回答在大多数情况下都有效,但存在一个多线程问题,如果两个线程同时调用 getOrCreateProduct
,这可能会导致一次调用失败。如果没有,两个线程都可能会尝试查找现有产品并进入 NoResultException
block 。然后两者都会创建一个新产品并尝试合并它。在 transaction.commit()
上,只有一个线程会成功,另一个线程将进入 PersistenceException
block 。
这可以通过可选地使用双重检查锁定来同步您的方法(对性能有影响)来处理,或者因为您已经在使用 spring,您可以使用 spring 的 @Retryable
功能。
以下是不同方式的示例。所有的方法都是线程安全的并且可以工作。但在性能方面,getOrCreateProductWithSynchronization
将是最差的,因为它会同步每个调用。 getOrCreateProductWithDoubleCheckedLocking
和 getOrCreateProductWithRetryable
从性能角度来看应该几乎相同。也就是说,您必须决定是使用双重检查锁定引入的额外代码复杂性,还是使用仅限 spring 的 @Retryable
功能。
@Transactional(propagation = Propagation.REQUIRES_NEW)
public synchronized Product getOrCreateProductWithSynchronization(final String productName, final String productDescr) {
Product product = findProduct(productName);
if (product != null) {
return product;
}
product = new Product(productName, productDescr);
em.persist(product);
return product;
}
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Product getOrCreateProductWithDoubleCheckedLocking(final String productName, final String productDescr) {
Product product = findProduct(productName);
if (product != null) {
return product;
}
synchronized (this) {
product = findProduct(productName);
if (product != null) {
return product;
}
product = new Product(productName, productDescr);
em.persist(product);
}
return product;
}
@Retryable(include = DataIntegrityViolationException.class, maxAttempts = 2)
@Transactional(propagation = Propagation.REQUIRES_NEW)
public Product getOrCreateProductWithRetryable(final String productName, final String productDescr) {
Product product = findProduct(productName);
if (product != null) {
return product;
}
product = new Product(productName, productDescr);
em.persist(product);
return product;
}
private Product findProduct(final String productName) {
// try to find an existing product by name or return null
}
更新:
还有一件事要注意。如果您只有一个服务实例,则使用 synchronized
的实现只会正常工作。也就是说,在分布式设置中,如果在您的服务的两个或多个实例上并行调用,这些方法仍然可能会失败。 @Retryable
解决方案也能正确处理这个问题,因此应该是首选解决方案。
关于java - 实体管理器获取或创建,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47274119/