关注公众号: 锅外的大佬
每日推送国外优秀的技术翻译文章,励志帮助国内的开发者更好地成长!
JPA和Hibernate允许你在JPQL和Criteria查询中使用DTO和Entity作为映射。当我在我的在线培训或研讨会上讨论Hibernate性能时,我经常被问到,选择使用适当的映射是否是重要的? 答案是:是的!为你的用例选择正确的映射会对性能产生巨大影响。我只选择你需要的数据。很明显,选择不必要的信息不会为你带来任何性能优势。
1.DTO与Entity之间的主要区别Entity和DTO之间常被忽略的区别是——Entity被持久上下文(persistence context)所管理。当你想要更新Entity时,只需要调用setter方法设置新值。Hibernate将处理所需的SQL语句并将更改写入数据库。
天下没有免费的午餐。Hibernate必须对所有托管实体(managed entities)执行脏检查(dirty checks),以确定是否需要在数据库中保存变更。这很耗时,当你只想向客户端发送少量信息时,这完全没有必要。
你还需要记住,Hibernate和任何其他JPA实现都将所有托管实体存储在一级缓存中。这似乎是一件好事。它可以防止执行重复查询,这是Hibernate写入优化所必需的。但是,需要时间来管理一级缓存,如果查询数百或数千个实体,甚至可能发生问题。
使用Entity会产生开销,而你可以在使用DTO时避免这种开销。但这是否意味着不应该使用Entity?显然不是。
2.写操作投影实体投影(Entity Projections)适用于所有写操作。Hibernate以及其他JPA实现管理实体的状态,并创建所需的SQL语句以在数据库中保存更改。这使得大多数创建,更新和删除操作的实现变得非常简单和有效。
EntityManager em = emf.createEntityManager(); em.getTransaction().begin(); Author a = em.find(Author.class, 1L); a.setFirstName("Thorben"); em.getTransaction().commit(); em.close(); 3.读操作投影但是只读(read-only)操作要用不同方式处理。如果想从数据库中读取数据,那么Hibernate就不会管理状态或执行脏检查。 因此,从理论上说,对于读取数据,DTO投影是更好的选择。但真的有什么不同吗?我做了一个小的性能测试来回答这个问题。
3.1.测试设置我使用以下领域模型进行测试。它由Author和Book实体组成,使用多对一关联(many-to-one)。所以,每本书都是由一位作者撰写。
@Entity public class Author { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", updatable = false, nullable = false) private Long id; @Version private int version; private String firstName; private String lastName; @OneToMany(mappedBy = "author") private List bookList = new ArrayList(); ... }要确保Hibernate不获取任何额外的数据,我设置了@ManyToOne的FetchType为LAZH。你可以阅读 Introduction to JPA FetchTypes获取不同FetchType及其效果的更多信息。
@Entity public class Book { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id", updatable = false, nullable = false) private Long id; @Version private int version; private String title; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "fk_author") private Author author; ... }我用10个作者创建了一个测试数据库,他们每人写了10 本书,所以数据库总共包含100 本书。在每个测试中,我将使用不同的投影来查询100 本书并测量执行查询和事务所需的时间。为了减少任何副作用的影响,我这样做1000次并测量平均时间。 OK,让我们开始吧。
3.2.查询实体在大多数应用程序中,实体投影(Entity Projection)是最受欢迎的。有了Entity,JPA可以很容易地将它们用作投影。 运行这个小测试用例并测量检索100个Book实体所需的时间。
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(); List<Book> books = em.createQuery("SELECT b 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);