【问题标题】:JPA Query Returning Same Result for different ParameterJPA查询为不同的参数返回相同的结果
【发布时间】:2015-09-11 16:23:23
【问题描述】:

我遇到了一个问题,我的查询方法位于 foreach 循环中,并且每次我传入不同的参数以检索不同的信息。但是,在循环的第一次迭代之后,查询数据被缓存(我认为)并为后续循环返回相同的数据。

这是我的代码:

@Transactional(readOnly = true)
public List<InitiativeReport> getInitiativeReports() throws Exception {
try {
    List<InitiativeReport> ir = new ArrayList<InitiativeReport>();
    List<Initiative> in = initiativeRepository.findAll();
    for(Initiative i : in) {
        i.getTheme().getId(); // lazy initialize

        InitiativeReport report = new InitiativeReport();
        report.setId(i.getId());
        report.setInitiativeId(i.getInitiativeId());
        report.setName(i.getName());
        report.setTheme(i.getTheme());

        // this is the call to the query, which is cached after the first iteration
        List<InitiativeProfileQuestion> q = initiativeProfileQuestionRepository.getQuestionsAndAnswerLogs(i.getInitiativeId());
        report.setQuestions(q);
        ir.add(report);
    }

    return ir;
}
catch (Exception e) {
    throw new Exception(e);
}

这是我的仓库界面:

public interface InitiativeProfileQuestionRepository extends JpaRepository<InitiativeProfileQuestion, Long> {
    @Query("select distinct q from InitiativeProfileQuestion q "
         + "left join fetch q.answers "
         + "left join fetch q.answerLogs al "
         + "where al.initiative.initiativeId = ?1 "
         + "and al.revision = al.initiative.revision 
         + "order by q.question asc")
    public List<InitiativeProfileQuestion> getQuestionsAndAnswerLogs(String initiativeId);
}

这是我的 application.yml 文件:

spring:
    datasource:
        dataSourceClassName: com.mysql.jdbc.jdbc2.optional.MysqlDataSource
        url: jdbc:mysql://localhost/testdb
        username: root
        password: XXXXXXXXX
        driverClassName: com.mysql.jdbc.Driver
        testOnBorrow: true
        validationQuery: SELECT 1
    jpa:
        database-platform: org.hibernate.dialect.MySQLInnoDBDialect
        database: MYSQL
        openInView: false
        show_sql: true
        generate-ddl: false
        hibernate:
            ddl-auto: none
            naming-strategy: org.hibernate.cfg.EJB3NamingStrategy

这个问题与我在这里找到的帖子非常相似:Native Query (JPA ) not reset and return the same old result

但是,该用户正在使用 EntityManager,而我的应用程序中没有 EntityManager 的实现——我让 JPA 完成所有工作,并且只有查询注释。

任何帮助将不胜感激!

【问题讨论】:

  • 向我们展示课程 InitiativeReport
  • 我在上面添加了 InitiativeReport 和 InitiativeProfileQuestion
  • 而调用的 SQL 是?在日志中,这可能会告诉您是否将正确的参数传递给它
  • SQL 正确。我知道这是因为如果我执行单独的 GET 请求,每个请求都运行一次查询方法,它们会返回正确的值。但是,在单个 GET 请求中完成所有操作(使用 for 循环)往往会缓存结果。我还用不同的参数测试了日志中显示的查询,它可以工作。所以某处发生了某种奇怪的缓存——我只是不知道在哪里可以找到它,或者我可以做些什么来禁用它。
  • 我认为应用程序可能正在为我的 JPA 查询使用 EntityManager,它正在实现一级缓存。我不确定如何清除它,因为我没有在我的 spring 应用程序的任何地方显式调用 EntityManager。

标签: java spring hibernate jpa spring-data


【解决方案1】:

晚会有点晚了,但对于那些现在发现这个问题的人来说,你需要做的是解决这个问题:

当您在已经启动事务的循环中查询时,您需要分离从该循环内的查询返回的实体,这些实体共享相同的 id 但可能具有不同的数据。

这是一个例子:

@Service
public class ProductService {

    @Autowired
    private ProductRepository productRepository;

    @Autowired
    private ProductWarehouseRepository productWarehouseRepository;

    @Autowired
    private EntityManager entityManager;

    @Transactional
    public List<Product> getProducts(){
        List<Product> products = this.productRepository.findAll();
        products.forEach(product -> {
            List<ProductWarehouse> warehouses = this.productWarehouseRepository.findAllByProductId(product.getId());
            warehouses.forEach(warehouse -> {
                //THIS IS THE IMPORTANT PART
                //You have to detach the entity from the session
                this.entityManager.detach(warehouse);
            });
            product.setWarehouses(warehouses);
        });
        return products;
    }
}

在此示例中,产品 A 可以在仓库 id 1 中,产品 B 也可以,但它们在仓库中的可用数量可能不同。

当返回的结果可能在@Id 列上发生冲突时,您必须从会话中分离实体。这与 Hibernate 中 1 级缓存的工作方式有关。您可以查看此链接以获取更多信息http://docs.jboss.org/hibernate/entitymanager/3.6/reference/en/html/objectstate.html

【讨论】:

  • 只需将我的两分钱加到这个答案中:这在 EclipseLink 上也有效。干杯。
  • 太棒了,像魔术一样工作。
【解决方案2】:

我遇到了一个非常相似的问题。我通过更改我的 Entity 类以添加一个额外的 @Id 注释来准确反映数据库表上的复合主键来解决它。您可能想尝试一下。

或者,看看我最近对你上面提到的问题的回答 (Native Query (JPA ) not reset and return the same old result)。正如您所注意到的,它与这个问题有一些相似之处。

此外,您的 InitiativeProfileQuestion 在变量 id 上显示 @Id。看起来您正在通过 InitiativeId 进行查询。也许您应该按 id 查询,因为它被注释为主键。

注意:我不明白为什么有人最初删除了这个作为没有回答问题。我尝试将其作为评论发布,但我没有足够的声望点来这样做。尽管如此,我确实相信我的建议可以解决提问者的问题,所以我认为这是一个答案。我承认它可以更多地被解释为建议而不是答案。但事实上,我也不会严格称其为评论。提问者不应该是权衡是否是答案的人吗?我的回答真的符合这些标准吗?:https://stackoverflow.com/help/deleted-answers

【讨论】:

  • 对此也很晚,但在我的情况下,我有一个 JpaRepository 扩展接口,所以我也无法轻松访问 EntityManager。该查询返回一个 Pojos 列表。我正在使用带有多个连接的本机查询,所以我的 Pojo 没有真正的 \@Id 或者我想。我只指定了一个 \@Id 就获得了缓存结果。我需要使用composit-id。一旦我用 \@Embedded 添加了 IdClass,我的问题就解决了。希望这会有所帮助
【解决方案3】:

我遇到了同样的问题,我搜索了很多网页,都没有找到确切的解决方案。 然后我发现下面的方法可以解决问题。

在查询前调用entityManager.clear()可以解决这个问题。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-09-24
    • 1970-01-01
    • 2011-06-24
    • 1970-01-01
    • 2011-11-08
    • 2013-08-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多