尝试使用Hibernate使乐观锁定在Grails中工作
我发现在我的带有hibernate域类的Grails小演示应用程序中,乐观锁定不适用于我。域类很简单:尝试使用Hibernate使乐观锁定在Grails中工作,hibernate,grails,jpa,Hibernate,Grails,Jpa,我发现在我的带有hibernate域类的Grails小演示应用程序中,乐观锁定不适用于我。域类很简单: @Entity @Table(name = "DELME", uniqueConstraints = @UniqueConstraint(columnNames = "ID")) public class DelmeV implements java.io.Serializable { private int id; private Long version; pri
@Entity
@Table(name = "DELME", uniqueConstraints = @UniqueConstraint(columnNames = "ID"))
public class DelmeV implements java.io.Serializable {
private int id;
private Long version;
private String d2;
private String dsc;
// ... constructors goes here
@Id
@Column(name = "ID", nullable = false)
public int getId() {...}
public void setId(int id) {...}
@Version
@Column(name = "VERSION")
public Long getVersion() { ... }
public void setVersion(Long version) { ... }
@Column(name = "D2")
// appropriate getters and setters goes here ...
@Column(name = "DSC")
// appropriate getters and setters goes here ...
}
控制器代码为脚手架:
@Transactional(readOnly = true)
class DelmeVController {
// methods which are out of interest skipped...
@Transactional
def update(DelmeV delmeVInstance) {
if (delmeVInstance == null) {
notFound()
return
}
if (delmeVInstance.hasErrors()) {
respond delmeVInstance.errors, view:'edit'
return
}
delmeVInstance.save flush:true
request.withFormat {
form multipartForm {
flash.message = message(code: 'default.updated.message', args: [message(code: 'DelmeV.label', default: 'DelmeV'), delmeVInstance.id])
redirect controller:"delmeV", id:delmeVInstance.id
}
'*'{ respond delmeVInstance, [status: OK] }
}
}
}
测试程序如下:
@Entity
@Table(...)
public class DelmeV implements org.hibernate.classic.Lifecycle {
// unisteresting stuff...
@Transient
private String version;
// getters, setters ...
public void onLoad (Session s, java.io.Serializable id) {
if (this != null) {
try {
com.fasterxml.jackson.databind.ObjectMapper om = new com.fasterxml.jackson.databind.ObjectMapper();
om.configure (com.fasterxml.jackson.databind.SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
this.version = om.writeValueAsString (this);
} catch (com.fasterxml.jackson.core.JsonProcessingException e) {
System.out.println ("Jackson exception caught on #" + this.id);
e.printStackTrace();
this.version = "/x/";
}
}
@javax.persistence.PreUpdate
public void actualOnUpdate (Session s) throws StaleObjectStateException {
SessionFactory sessionFactory = s.getSessionFactory();
Session ns = null;
Object newInstance = null;
String dbVersion = null;
try {
ns = sessionFactory.openSession();
Transaction tx = ns.beginTransaction ();
newInstance = ns.get (this.getClass(), this.id);
dbVersion = ((DelmeV) newInstance).getVersion();
tx.commit();
} catch (Throwable ex) {
System.out.println ("Session creation exception caught on " + this.getClass().getName() + ", id=" + this.id);
ex.printStackTrace();
} finally {
ns.close();
}
boolean eq = dbVersion.equals (this.version);
if (!eq) {
throw new StaleObjectStateException (this.getClass().getName(), this.id);
}
}
}
我注册了事件监听器,它扫描域类中的@preUpdate方法并调用它,只要Grails不自动调用它。到目前为止,这个解决方案还不错,但它恰好与其他框架(特别是SpringBatch)不兼容,因为JPA要求@preUpdate方法具有空参数列表,我不知道如何从preUpdate方法中找出当前会话或SessionFactory(或EntityManagerFactory,只要涉及JPA)(在Grails中,我将会话作为参数传递,从事件侦听器调用preUpdate方法)因此问题2:如何以一致的、独立于框架的方式从域方法中找出Session/SessionFactory/EntityManagerFactory?直接通过数据库更新数据是一个问题。Hibernate使用自己的缓存将以前的缓存版本与新更新的版本进行比较,并看到一切正常是的。如果您要直接对数据库进行更新,而不仅仅是通过Hibernate进行更新,请关闭Hibernate缓存。当您绕过系统时,系统无法工作。这是您的问题。谢谢,但如何实现呢?我试着将cache.use_second_level_cache=false,cache.region.factory_class='org.Hibernate.cache.internDataSource.groovy中的al.NoCachingRegionFactory',cache.provider='org.hibernate.cache.NoCacheProvider',域类中的@cache(usage=cacheconcurrentystrategy.NONE)。两者都没有帮助。我认为无状态会话在这里可能有帮助,但我不知道如何配置Grails来使用它。挖掘工作正在继续。。。