JPA 更新查询——为什么我的交易不起作用?
我在 JavaDB (Derby) 写入事务时遇到性能问题。每笔交易需要超过 500 毫秒,而我一天可能有数十万笔交易。我期望在 MySql 上部署,我不知道是否会遇到同样的问题,但在尝试之前我正在尝试提高 Derby 的性能。
一个事务涉及更新一串记录——多达 6 条记录,如下所示:
public void update(List<Tally> list) {
try {
utx.begin();
for (Tally tally : list) em.merge(tally);
utx.commit();
// etc
我想看看 UPDATE 查询是否会工作得更好,因为 JPA 不必跟踪更新的列并组成 SQL 查询(侧面)问题:这是一个好的理论吗?)
所以我编写了一个如下所示的命名查询:
@NamedQuery(name="TallyUpdate",
query="UPDATE Tally t SET t.vote = t.vote + 1 WHERE t.id IN :idSet"
操作如下:
@PersistenceContext protected EntityManager em;
@Resource protected UserTransaction utx;
public void incrementVote(List<Long> idList) {
Query q = em.createNamedQuery("TallyUpdate");
q.setParameter("idSet", idList);
try {
utx.begin();
q.executeUpdate();
utx.commit();
//etc
executeUpdate() 调用总是抛出异常:
SEVERE: javax.persistence.TransactionRequiredException: executeUpdate is not supported for a Query object obtained through non-transactional access of a container-managed transactional EntityManager
at com.sun.enterprise.container.common.impl.QueryWrapper.executeUpdate(QueryWrapper.java:225)
JPA 实现是 EclipseLink 2.3。 0
那是什么word-salad 异常的含义以及我应该如何运行更新?
结果发布:
I did move the createnamedQuery() call to after the utx.begin() call and the update started working. I am not sure why but I am going to run with it for now.还没有关于相对表现的结果,但我上面的帖子具有误导性。计票时,会采用 24-36 种不同的方式进行计票,而不是 6 种。因此,对原始 update() 方法(使用 merge())进行了六次调用)每个都有大约 6 条记录的列表。正是这个调用集合花费了超过 500 毫秒。如果 Tally 表为空,则该基准仅约为 27 毫秒,这大约是我希望看到的速度。当表填充到 50,000 行或更多时,我会看到 500ms+ 的数字。
如果有人对我发布的结果感兴趣,请评论说出来。
PS这是一个在GlassFish 下运行的JSF 应用程序,而不是EJB。我不确定这是否与性能有关。
I am having a performance problem with JavaDB (Derby) writing transactions. Each transaction takes more than 500ms and I might have hundreds of thousands a day. I am expecting to deploy on MySql and I don't know if I am going to have the same problem there, but I am trying to improve the performance on Derby before trying it.
One transaction involves updating a string of records -- as many as 6 like so:
public void update(List<Tally> list) {
try {
utx.begin();
for (Tally tally : list) em.merge(tally);
utx.commit();
// etc
I wanted to see if an UPDATE query would work better, since JPA wouldn't have to keep track of the columns that updated and compose a SQL query (side question: is this a good theory?)
So I coded a named query that looks like this:
@NamedQuery(name="TallyUpdate",
query="UPDATE Tally t SET t.vote = t.vote + 1 WHERE t.id IN :idSet"
That is operated like this:
@PersistenceContext protected EntityManager em;
@Resource protected UserTransaction utx;
public void incrementVote(List<Long> idList) {
Query q = em.createNamedQuery("TallyUpdate");
q.setParameter("idSet", idList);
try {
utx.begin();
q.executeUpdate();
utx.commit();
//etc
THE executeUpdate() call always throws an exception:
SEVERE: javax.persistence.TransactionRequiredException: executeUpdate is not supported for a Query object obtained through non-transactional access of a container-managed transactional EntityManager
at com.sun.enterprise.container.common.impl.QueryWrapper.executeUpdate(QueryWrapper.java:225)
The JPA implementation is EclipseLink 2.3.0
What does that word-salad exception mean and how am I supposed to run the update?
RESULT POSTING:
I did move the createnamedQuery() call to after the utx.begin() call and the update started working. I am not sure why but I am going to run with it for now.
No result yet regarding the relative performance, but my posting above is misleading. When a vote is tallied it is tallied about 24-36 different ways, not 6. So there are six calls to the original update() method (which uses merge()) each with a list of about 6 records. It is this collection of calls that take more than 500ms. If the Tally table is empty then that benchmark is only about 27ms, which is about the speed I would like to see. The 500ms+ number is what I see when the table fills to 50,000 rows or more.
If anyone is interested in me posting the results of where I end up on this, please comment saying so.
P.S. this is a JSF application running under GlassFish, not EJB. I am not sure if there is any implication of that related to performance.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
由于您有一个容器管理的实体管理器,我猜您已将其注入到 EJB 中。因为这种情况意味着事务由容器管理,即您不需要在代码中显式启动它们。默认情况下,事务在 EJB 方法启动时开始,在该方法完成时结束。这是针对无状态 EJB 的。对于有状态 EJB,事务可以跨越多个方法调用,并在调用 EJB“销毁”方法时结束。所以你应该决定:你要么使用容器管理的实体管理器(一个注入到 EJB 中,就像你在这里一样),然后让容器管理事务(通过 JTA),或者创建一个应用程序管理的实体管理器,然后处理自己在代码中进行交易。
关于性能,Apache 人员表示 Derby 非常适合生产,在某些情况下可以匹配甚至有时甚至超越 MySQL 和 PostgreSQL,正如您可以在 Apache 文档中看到的那样:
http://home.online.no/~olmsan/publications/pres/apachecon05us/apachecon05.pdf
持保留态度,Derby 并不因其性能而闻名于世。甚至这个文档也表明,在某些情况下,Derby 比 MySQL 慢很多。而且 Derby 的 CPU 使用率更高。
我的建议:再次在 MySQL 中进行概念验证,并在该数据库上进行基准测试。如果这就是您要在生产中使用的内容,那么在不同的数据库上对应用程序进行性能调整没有多大意义。
Since you have a container managed entity manager I'm guessing you have the thing injected into an EJB. Since that's the case that means that the transactions are managed by the container, i.e. you do not need to start them explicitlly in your code. By default a transaction start when an EJB method starts and ends when that method finishes. That's for stateless EJBs. For statefull EJBs the transaction can span multiple method calls, and end when the EJB "destroy" methods is called. So you should decide: you either use a container-managed entity manager (one injected into an EJB, as you have here) and you let the container manage the trasactions (via JTA) or you make an application-managed entity manager and you handle the transactions yourself in the code.
Regarding performance the Apache guys say Derby is perfectlly good for production, matching and sometimes even surpassing MySQL and PostgreSQL in certain scenarios as you can see in this Apache document:
http://home.online.no/~olmsan/publications/pres/apachecon05us/apachecon05.pdf
Take that with a grain of salt, Derby is not world renouned for its performance. And even this document shows that in certain situations Derby is a lot slower than MySQL. Also the CPU usage is bigger on Derby.
My advice: make a proof of concept aganins MySQL and do your benchmarks on that database. If that's what you're gonna use in production, it doesn't make much sense to do performance tune-ups of your app on a different database.