JPA:PersistenceException,无法更新

时间:2013-07-31 10:44:42

标签: java mysql jpa playframework

我正在使用Play Framework 1.2.5和Java,但这更像是一个JPA问题。

在我的项目中,我经常通过电子邮件搜索用户,因此我创建了以下方法:

public static User getUserByEmail(String email) {
    User user = User.find("email = ?", email).first();
    return user;
}

我用各种方法称之为:

User user = User.getUserByEmail("test@email.com");

当我尝试像这样修改用户字段时:

User user = User.getUserByEmail("kospol@test.com");
user.name = "kospol";
user.save();

我经常遇到以下异常导致总冻结:

Execution exception (In /app/controllers/*******.java around line 46)
PersistenceException occured : update User set activated=?, ... registered=?,      registeredFrom=?, version=? where id=? and version=?


play.exceptions.JavaExecutionException: update User set activated=?, ... registered=?,     registeredFrom=?, version=? where id=? and version=?
    at play.mvc.ActionInvoker.invoke(ActionInvoker.java:237)
    at Invocation.HTTP Request(Play!)
Caused by: javax.persistence.PersistenceException: update User set activated=?, ... registered=?, registeredFrom=?, version=? where id=? and version=?
    at play.db.jpa.JPABase._save(JPABase.java:44)
    at play.db.jpa.GenericModel.save(GenericModel.java:204)
    at controllers.PushService.register(PushService.java:46)
    at play.mvc.ActionInvoker.invokeWithContinuation(ActionInvoker.java:557)
    at play.mvc.ActionInvoker.invoke(ActionInvoker.java:508)
    at play.mvc.ActionInvoker.invokeControllerMethod(ActionInvoker.java:484)
    at play.mvc.ActionInvoker.invokeControllerMethod(ActionInvoker.java:479)
    at play.mvc.ActionInvoker.invoke(ActionInvoker.java:161)
    ... 1 more
Caused by: javax.persistence.PersistenceException:  org.hibernate.exception.GenericJDBCException: could not update: [models.User#3606]
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1389)
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1317)
    at org.hibernate.ejb.AbstractEntityManagerImpl.convert(AbstractEntityManagerImpl.java:1323)
    at org.hibernate.ejb.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:965)
    at play.db.jpa.JPABase._save(JPABase.java:41)
    ... 8 more
Caused by: org.hibernate.exception.GenericJDBCException: could not update: [models.User#3606]
    at org.hibernate.exception.SQLStateConverter.handledNonSpecificException(SQLStateConverter.java:140)
    at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:128)
    at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:66)
    at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2613)
    at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2495)
    at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2822)
    at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:113)
    at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:273)
    at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:265)
    at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:185)
    at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:345)
    at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:51)
    at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1216)
    at org.hibernate.ejb.AbstractEntityManagerImpl.flush(AbstractEntityManagerImpl.java:962)
    ... 9 more
Caused by: java.sql.SQLException: Lock wait timeout exceeded; try restarting transaction
    at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1073)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4096)
    at com.mysql.jdbc.MysqlIO.checkErrorPacket(MysqlIO.java:4028)
    at com.mysql.jdbc.MysqlIO.sendCommand(MysqlIO.java:2490)
    at com.mysql.jdbc.MysqlIO.sqlQueryDirect(MysqlIO.java:2651)
    at com.mysql.jdbc.ConnectionImpl.execSQL(ConnectionImpl.java:2683)
    at com.mysql.jdbc.PreparedStatement.executeInternal(PreparedStatement.java:2144)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2444)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2362)
    at com.mysql.jdbc.PreparedStatement.executeUpdate(PreparedStatement.java:2347)
    at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2595)
    ... 19 more

我已将@Version注释添加到模型中。似乎问题是超过锁定等待超时。我怎么能避免这个?

我试过使用.merge()方法没有结果,我想删除getUserByEmail方法并直接得到模型。

3 个答案:

答案 0 :(得分:1)

免责声明:在没有整个项目的情况下回答您的问题并不容易,但这里有两个可能需要调查的问题。

我很确定问题不是由方法getUserByEmail本身引起的。 (特别是因为有时它可以工作,有时候不工作。)

问题是由另一个非常繁重的查询导致很长一段时间(超过等待超时)锁定User表(或者可能只是您搜索的行)引起的)。

因此,调查繁重的查询(一种方法是记录所有SQL查询并查看在获得异常时运行的查询)。

另一个选项是死锁(如果您没有看到任何长查询):这是一个典型的场景。

线程1需要锁定表用户和表B.线程2需要锁定表B和表用户。

时间0:

  • 主题1在表User
  • 上获得锁定
  • 主题2在表B上获得锁定

在时间1:

  • 线程1阻止并等待表B上的锁定
  • 线程2阻塞并等待表用户锁定

在时间2:

  • 一个线程发生等待锁定超时,释放锁定并抛出异常。

在时间3:

  • 另一个线程最后接收所需的锁并执行它的查询。

答案 1 :(得分:0)

您是否尝试使用显式保存?

根据Play framework persistence docs,您可以这样做:

public static void save(Long id) {
    User user = User.findById(id);
    user.edit(params);
    validation.valid(user);

    if(validation.hasErrors()) {
        // Here we have to explicitly discard the user modifications...
        user.refresh();
        edit(id);
    }
    show(id);
}

也许你必须使用edit()方法(我很新玩框架,不知道是否有效)。

希望这有帮助。

答案 2 :(得分:0)

如果我走在正确的轨道上,这就是你所面临的问题.. !!

问题在于您在JPA中添加了getUserByEmail功能。

  

实体类总是全部检查,所以如果你想创建一个   JPA中的一些变量或函数。你必须告诉我们是什么   程序必须使用函数或变量。为此你可以使用   注释

在您的情况下,您必须告诉程序忽略该功能。为此你可以使用

@Transient

此批注指定属性或字段不是持久的。它用于注释实体类,映射的超类或可嵌入类的属性或字段。

在您的情况下,您可以在JPA中使用它

@Transient
public static User getUserByEmail(String email) {
    User user = User.find("email = ?", email).first();
    return user;
}