【问题标题】:How to check if a record exists using JPA如何使用 JPA 检查记录是否存在
【发布时间】:2010-12-07 08:35:55
【问题描述】:

我想知道给定记录是否存在于数据库中。到目前为止,我已经通过编写 JPA 查询并通过 getSingleResult() 方法运行它来实现这一点。如果具有给定参数的记录不存在,这将抛出NoResultException。当然,记录不是必须存在的,所以有时这是正常行为,这就是为什么我问自己,是否有必要抛出一个我必须通过catch块处理的异常?据我所知,异常处理的成本是相当大的,所以我对这个解决方案不是很满意,而且我什至不需要对象,我只需要知道它在数据库中的存在。

有没有更好的方法来检查一个对象是否存在?例如。使用getResultList() 并检查它的大小可能吗?

【问题讨论】:

  • 好问题,但您应该将标题更改为“如何使用 JPA 检查记录是否存在”或类似内容。
  • 感谢您的评论,完成:)
  • 更新:存储库存在(id)方法。如果记录存在,repo.exist(id) 返回 true,否则返回 false

标签: java jpa


【解决方案1】:

如果您只想知道对象是否存在,请将SELECT COUNT 发送到您的数据库。这将返回 0 或 1。

异常处理的成本并没有那么大(除非您在正常操作期间这样做数百万次),所以我不会打扰。

但代码并不能真正反映您的意图。由于getSingleResult() 在内部调用getResultList(),所以更清楚如下:

public boolean objExists(...) {
    return getResultList(...).size() == 1;
}

如果您通过对象 id 查询并且启用了缓存,那么如果对象已经加载,那么这将成为在缓存中的简单查找。

【讨论】:

  • 哦,您对 SELECT COUNT 解决方案的看法是完全正确的,我出于某种原因忽略了它。但您回答的第二部分包含我真正需要的信息,非常感谢。
  • 你是否同意如果你想得到查询的第一个结果,并且查询通常不会返回任何内容,最好先执行'query.setMaxResults(1)'然后'getResultList()'?这样我们就可以避免处理异常。 (注意:我在一个被多次调用的工作中运行它)。
  • @GuilhermeA:有太多变数无法确定。影响性能的因素:JDBC 驱动程序、OR 映射器中对 DB 的支持(它们如何在 SQL 级别实现 setMaxResults()?)、表大小、查询本身等。运行几个性能测试以找出答案.
【解决方案2】:

尽量避免将实体加载到会话中 (getSingleResult()) 只是为了检查它是否存在。计数在这里更好。使用 Criteria Query API,它看起来像这样:

public <E extends AbstractEntity> boolean exists(final Class<E> entityClass, final int id) {
    final EntityManager em = getEntityManager();
    final CriteriaBuilder cb = em.getCriteriaBuilder();

    final CriteriaQuery<Long> cq = cb.createQuery(Long.class);
    final Root<E> from = cq.from(entityClass);

    cq.select(cb.count(from));
    cq.where(cb.equal(from.get(AbstractEntity_.id), id));

    final TypedQuery<Long> tq = em.createQuery(cq);
    return tq.getSingleResult() > 0;
}

【讨论】:

  • 什么是AbstractEntity_
  • JPA 元模型。检查this answser
【解决方案3】:

只需在查询中使用count(e),因此不会抛出NoResultException,您将避免加载实体对象
所以Java代码可以如下:

public boolean isRecordExist() {
    String query = "select count(e) from YOUR_ENTITY e where ....";
    // you will always get a single result
    Long count = (Long) entityManager.createQuery( query ).getSingleResult();
    return ( ( count.equals( 0L ) ) ? false : true );
}

希望对某人有所帮助:)

【讨论】:

    【解决方案4】:

    如果您按主键搜索,您还可以使用Entitymanger.find(entityClass, primaryKey),当实体不存在时返回null

    【讨论】:

    • 如果实体存在,它将被加载到会话中,只是留在那里。
    • @Radu 我已经有一段时间没有使用 jpa 了,但是同样的问题也适用于使用 getResultList 的解决方案,对吧?
    • 是的,考虑到getSingleResult() 也适用于内部的结果列表。但是我们在这里需要的是只检查实体的存在,而不需要将其加载到会话中的可能开销。计数操作总是会返回一个数字,因此我们甚至可以避免 NoResultException。
    • 我认为更好的是使用“select item.id where..”并将MaxResults设置为1而不是count,因为通过大表进行计数可能效率不高(有时查询必须遍历所有记录 - 取决于查询)
    【解决方案5】:

    这是使用类型 T 和任意 ID 值的通用方法

    public boolean exists(Object key) { 
        EntityManager entityManager = getEntityManager();
        Metamodel metamodel = entityManager.getMetamodel();
        EntityType<T> entity = metamodel.entity(entityClass);
        SingularAttribute<T, ? extends Object> declaredId = entity.getDeclaredId(key.getClass());
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        javax.persistence.criteria.CriteriaQuery<T> cq = cb.createQuery(entityClass);
        Root<T> from = cq.from(entityClass);
        Predicate condition = cb.equal(from.get(declaredId), key);
        cq.where(condition);
        TypedQuery<T> q = entityManager.createQuery(cq);
        return q.getResultList().size() > 0; 
    }
    

    【讨论】:

      【解决方案6】:

      试试这个

      public boolean exists(Object id){
          return getEntityManager().find(entityClass, id)!=null;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-11-03
        • 2012-11-02
        • 2021-06-01
        • 2019-04-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多