我有一个实体" Job"使用布尔标志"暂停":
@Entity
@XmlRootElement(name = "Job")
@Where(clause = "deleted=0")
public class Job {
...
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private boolean suspended;
...
}
Spring CrudRepository(JPA Hibernate)用于持久化:
@Repository
public interface JobRepository extends CrudRepository<Job, Integer>, JobStatusSupport {}
我需要更新&#34;暂停&#34;单独标记,不覆盖对并发线程中其他字段的更新。因此,自然而然的事情似乎是编写一种方法,只更新&#34;暂停&#34;字段:
public class JobRepositoryImpl implements JobStatusSupport {
private final static String SET_SUSPENDED = "UPDATE Job SET suspended = :suspended, modificationDate = :modificationDate WHERE id = :id";
@Override
public int setSuspended(int id, boolean suspended, Instant modificationDate) {
int updateCount = em.createQuery(SET_SUSPENDED)
.setParameter("suspended", suspended)
.setParameter("modificationDate", modificationDate)
.setParameter("id", id)
.executeUpdate();
return updateCount;
}
}
现在我在我的代码中有以下场景(显然缩短了,实际上这是分散在几个方法上,但这个例子确实重现了这个问题):
@Transactional
public void resumeJob(int id) {
Job jobA = jobRepository.findOne(Integer.valueOf(id));
// jobA.suspended == true
// let's set "suspended" to "false"
int updateCount = jobRepository.setSuspended(id, false, Instant.now());
// OK: updateCount is 1
Job jobB = jobRepository.findOne(Integer.valueOf(id));
// jobB.suspended == true ??? that was just set to "false, wasn't it?
}
可能我错过了一些关于JPA / Hibernate的基础知识。但是,这仍然非常违反直觉:为什么jobB.suspended仍然是&#34; true&#34;虽然更新成功,但数据再次从数据库中读取&#34;?为什么在交易中看不到单个字段的更新?
(正如人们所料,在事务完成后,Job.suspended在数据库中为&#34; false&#34;以及后续读取。)
如何正确地解决这个问题?我应该如何编写更新单个字段的代码,以便JPA知道做了什么?我是否需要研究&#34;合并&#34;对于像这样简单的事情?
能够编写我们自己的SQL语句对我们的项目至关重要。我正在尝试Spring Data JPA,主要是为了避免编写大量CRUD操作的繁琐工作。但是,如果我在这个简单的场景中遇到过这样的问题,我想知道我们是否会更好地使用JdbcTemplate。
更新:了解有关ORM的内容
男人,我一无所知!我曾在之前使用过JPA的项目中工作过。但我从来没有详细处理过它(我想知道其他人是否做过)。
编写&#34;更新方法的全部工作&#34;是徒劳的!我把它减少到以下几点:
@Transactional
public void resumeJob(int id) {
Job job = jobRepository.findOne(Integer.valueOf(id));
job.setSuspended(false);
job.setName("And Now for Something Completely Different.");
}
这就是全部!这会更新数据库和缓存,天知道什么。仅@Transactional注释就足以持久化更改。如果删除注释,则DB保持不变。因此,ORM基本上是针对每个人都看到的缓存(通过&#34;附加对象&#34;)。然后人们希望人们将@Transactional放在正确的位置(而不是私有方法,例如......),并且ORM机器知道它在做什么(例如,在开放事务之外不显示缓存更新)。
老实说,这对我来说似乎有点太神奇了。但既然我知道它的全部意义,我会试一试。编写广泛的CRUD方法也不是很有吸引力。如果我弄错了或者您有最佳做法的链接,请发表评论。 (我开始怀疑是否最好不要立即分离我从数据库获得的每个对象,从而击败ORM的整个目的: - )
更新:EntityManager#clear()足以进行快速修复
这绝对不是使用ORM的聪明方法,但目前我只需在我编写的一些更新方法中调用clear()。这使整个缓存无效,并且事务中其他位置的下一个读取将接收更新的数据。当然,正确的方法是简单地修改附加的实体,即&#34; job.setSuspended(false);&#34;。
不需要调用flush(),当您想要在系统崩溃时最大限度地降低丢失数据的风险时,它可能会引起您的兴趣。我想Hibernate不会立即将完成的事务写入磁盘?
答案 0 :(得分:3)
这是违反直觉的,但如果你考虑一下,这很正常。
如果您想要更新的实体,您有两个解决方案: