java - 实体管理器获取或创建

标签 java spring hibernate-entitymanager

我如何确定对象是否已经存在而不是再次创建。

@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 将是最差的,因为它会同步每个调用。 getOrCreateProductWithDoubleCheckedLockinggetOrCreateProductWithRetryable 从性能角度来看应该几乎相同。也就是说,您必须决定是使用双重检查锁定引入的额外代码复杂性,还是使用仅限 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/

相关文章:

java - Java 中的 KeyBinds 在按住 Shift 时会中断

java - 如何从搜索中获取 boolean 结果 - SQLite 数据库

java - 获取 UnmarshallingFailureException 预期的元素是(无)

java - 过滤 JPA 实体而不从数据库中删除它们

java - MySQL EntityManager 范围查询

java.lang.NoSuchMethodError : main error in simple java program

java - 构建器模式与依赖注入(inject)(例如通过 Guice)

java - 使用 java sdk 为 amazon s3 对象设置对象到期日期

java - 防止选项卡或相同浏览器窗口之间的共享 session

spring - 如何使用Hibernate JPA和Spring Framework启用加载时间/运行时编织