JPA和Hibernate支持一组查询提示(hits),允许你提供有关查询及其执行方式的其他信息。查询提示QueryHints.HINT_READONLY告诉Hibernate以只读模式查询实体。因此,Hibernate不需要对它们执行任何脏检查,也可以应用其他优化。
你可以通过在Query接口上调用setHint方法来设置此提示。
long timeTx = 0; long timeQuery = 0; long iterations = 1000; // Perform 1000 iterations for (int i = 0; i < iterations; i++) { EntityManager em = emf.createEntityManager(); long startTx = System.currentTimeMillis(); em.getTransaction().begin(); // Execute Query long startQuery = System.currentTimeMillis(); Query query = em.createQuery("SELECT b FROM Book b"); query.setHint(QueryHints.HINT_READONLY, true); query.getResultList(); long endQuery = System.currentTimeMillis(); timeQuery += endQuery - startQuery; em.getTransaction().commit(); long endTx = System.currentTimeMillis(); em.close(); timeTx += endTx - startTx; } System.out.println("Transaction: total " + timeTx + " per iteration " + timeTx / (double)iterations); System.out.println("Query: total " + timeQuery + " per iteration " + timeQuery / (double)iterations);你可能希望将查询设置为只读来让性能显著的提升——Hibernate执行了更少的工作,因此应该更快。
但正如你在下面看到的,执行时间几乎与之前的测试相同。至少在此测试场景中,将QueryHints.HINT_READONLY设置为true不会提高性能。
Transaction: total 2842 per iteration 2.842 Query: total 2006 per iteration 2.006 3.6.查询DTO加载100 本书实体大约需要2ms。让我们看看在JPQL查询中使用构造函数表达式获取相同的数据是否表现更好。
当然,你也可以在Criteria查询中使用构造函数表达式。
long timeTx = 0; long timeQuery = 0; long iterations = 1000; // Perform 1000 iterations for (int i = 0; i < iterations; i++) { EntityManager em = emf.createEntityManager(); long startTx = System.currentTimeMillis(); em.getTransaction().begin(); // Execute the query long startQuery = System.currentTimeMillis(); List<BookValue> books = em.createQuery("SELECT new org.thoughts.on.java.model.BookValue(b.id, b.title) FROM Book b").getResultList(); long endQuery = System.currentTimeMillis(); timeQuery += endQuery - startQuery; em.getTransaction().commit(); long endTx = System.currentTimeMillis(); em.close(); timeTx += endTx - startTx; } System.out.println("Transaction: total " + timeTx + " per iteration " + timeTx / (double)iterations); System.out.println("Query: total " + timeQuery + " per iteration " + timeQuery / (double)iterations);正如所料,DTO投影比实体(Entity)投影表现更好。
Transaction: total 1678 per iteration 1.678 Query: total 1143 per iteration 1.143平均而言,执行查询需要1.143ms,执行事务需要1.678ms。查询的性能提升43%,事务的性能提高约42%。
对于一个花费一分钟实现的小改动而言,这已经很不错了。
在大多数项目中,DTO投影的性能提升将更高。它允许你选择用例所需的数据,而不仅仅是实体映射的所有属性。选择较少的数据几乎总能带来更好的性能。
4.摘要为你的用例选择正确的投影比你想象的更容易也更重要。
如果要实现写入操作,则应使用实体(Entity)作为投影。Hibernate将管理其状态,你只需在业务逻辑中更新其属性。然后Hibernate会处理剩下的事情。
你已经看到了我的小型性能测试的结果。我的笔记本电脑可能不是运行这些测试的最佳环境,它肯定比生产环境慢。但是性能的提升是如此之大,很明显你应该使用哪种投影。
使用DTO投影的查询比选择实体的查询快约40%。因此,最好花费额外的精力为你的只读操作创建DTO并将其用作投影。