【问题标题】:Play Model save function isn't actually writing to the database播放模型保存功能实际上并未写入数据库
【发布时间】:2012-03-06 00:31:33
【问题描述】:

我有一个名为“JobStatus”的游戏模型,它只有一个属性,一个带有 JobState 的枚举 (Running/notRunning)。

类扩展模型并作为单例实现。您调用它的 getInstance() 方法来获取基础表中的唯一记录。

我有一个每月运行的工作,在工作中我将在不同时间来回切换 JobStatus 对象的状态并调用 .save()。

我注意到它实际上并没有保存。

当作业开始时,它的第一行代码是

JobStatus thisJobStatus = jobStatus.getInstance();
...// exit if already running

thisJobStatus.JobState = JobState.Running;
thisJobStatus.save()

然后当作业完成时,它会将状态更改回 NotRunning 并再次保存。

问题是当我查看 MySql 数据库时,实际记录值从未改变。

这会导致灾难性故障,因为当其他节点尝试运行作业时,它们会检查状态并且由于它们将其视为 NotRunning,因此它们都尝试运行作业。

所以我管理作业状态的聪明方案失败了,因为实际值没有提交给数据库。

当我在模型上调用 .save() 时,如何强制 Play 立即写入数据库?

谢谢 乔什

【问题讨论】:

  • 顺便说一句,我尝试根据 Play 的文档将该函数声明为非事务性的,但这不起作用。
  • ...如果是因为交易...
  • 我只是认为这可能是因为切换作业状态并将其写入数据库是在长时间运行的 Play Job 中发生的操作。
  • 我尝试使用 .flush() 并没有将其提交给数据库。如果我使用刷新并提交它确实会写入数据库,但是围绕它的所有其他语句都会抛出一个异常,说明没有可用的事务。
  • Play 必须支持以 ad-hoc 方式将对象写入 DB,这真的很令人沮丧。

标签: jpa playframework


【解决方案1】:

尝试将此添加到您的 JobStatus 并在保存后调用它。

public static void commit(){
    JobStatus.em().getTransaction().commit();
    JobStatus.em().getTransaction().begin();
    JobStatus.em().flush();
    JobStatus.em().clear();
}

【讨论】:

  • 这部分工作,状态设置为“正在运行”,然后作业运行,但在作业结束时,当我将状态更改回“NotRunning”时,然后我打电话.save() 然后 .commit(),在我现有的 JobStatus 实例上,我收到以下错误。 PersistenceException occured : org.hibernate.PersistentObjectException: detached entity passed to persist: models.JobStatus。我的实例在初始保存后是否不再有效保存?我是否需要再次调用 getInstance() 从数据库重新查询它?
  • 我很聪明的SMRT。我不得不从数据库中重新填充对象。所以现在我有一个辅助功能。 // Helpers for managing job state private void MarkJobRunning() { BillingJob thisJob = BillingJob.getInstance(); thisJob.state = JobState.Running; thisJob.save(); thisJob.commit(); } private void MarkJobNotRunning() { BillingJob thisJob = BillingJob.getInstance(); thisJob.state = JobState.NotRunning; thisJob.save(); thisJob.commit(); } 非常感谢 @emt14 !!一般来说,这是一个好习惯吗?我现在很想为我的所有模型添加一个提交方法。
  • 我不确定这是好还是坏的做法。但是,您的作业将在事务中运行,因此这是在作业仍在运行时将数据持久保存到数据库的唯一方法。其实还有一个。可以从您的初始工作中调用另一个工作。第二个作业将启动一个单独的事务,并且在该作业中修改的数据将在第一个作业仍在运行时保留。
【解决方案2】:

我想您想将您的工作标记为“正在运行”,就像工作开始时的第一件事一样?在这种情况下,您不应该有任何其他正在进行的数据库语句......

要立即在数据库中提交您的更改(而不是在作业结束后),请在 thisJobStatus.save(); 方法调用之后添加以下命令:

JPA.em().flush();
JPA.em().getTransaction().commit();

此外,由于您使用的是 MySQL,您可能希望在检索时使用 SELECT ... FOR UPDATE 子句立即锁定该行。 (有关更多信息,请参阅 MySQL Reference Manual。)当然,您不希望在 getInstance() 方法中包含它,否则每个提取操作都会锁定记录。

【讨论】:

  • 我昨天试了一堆Tom,但是当作业的DB语句开始时它总是导致错误。 @emt14 的解决方案有效。通过将事务提交放入实际模型本身的函数中,提交似乎不会破坏其余的工作。
猜你喜欢
  • 2017-08-03
  • 2012-03-14
  • 2016-07-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-19
  • 1970-01-01
相关资源
最近更新 更多