我想要做的主要事情是,如果发生错误或域类无效,则返回编辑页面。
我有一个User
对象,其中包含Person
对象作为属性。
我的控制器看起来像这样:
Object update(){
User.withTransaction{status ->
try {
personDaoService?.bindObject(person, params)
person.validate()
} catch(Exception e){
log.error(e.printStackTrace())
status.setRollbackOnly()
}
if(person.hasErrors()){
status.setRollbackOnly()
}
if(status.isRollbackOnly()){
person.discard()
person.refresh()
return render(view: 'edit', model:[user:user, person:person], params: params)
}
try {
userDaoService?.bindObject(person, params)
user.validate()
} catch(Exception e){
log.error(e.printStackTrace())
status.setRollbackOnly()
}
if(status.isRollbackOnly()){
user.discard()
user.refresh()
return render(view: 'edit, model:[user:user, person:person], params: params)
}
}
}
返回编辑页面时,我总是得到一个异常,告诉我没有打开会话来加载人物对象。
为了快速解决我在用户对象的映射中添加了lazy:false
但是可疑的结果是整个项目运行得非常慢,所以我不得不删除lazy:false
映射选项。
现在我正试图找到让它重新运转的方法。
为什么hibernate会在回滚时关闭会话?
如何重新开启会话?
修改
我尝试在设置回滚之前呈现视图:
if(person.hasErrors()){
person.errors.allErrors.each{user.errors.rejectValue("person", "", "${validationTagLib.message(error: it)}")}
render(view: 'edit', model: [user: user], params: params)
status.setRollbackOnly()
return
}
这会产生以下异常:
org.hibernate.LazyInitializationException: could not initialize proxy - no Session
答案 0 :(得分:2)
我在DomainClasses中创建了一个方法来加载所需的所有对象,并在返回编辑页面时调用此方法:
Object update(){
User.withTransaction{status ->
try {
personDaoService?.bindObject(person, params)
person.validate()
} catch(Exception e){
log.error(e.printStackTrace())
status.setRollbackOnly()
}
if(person.hasErrors()){
status.setRollbackOnly()
}
if(status.isRollbackOnly()){
person.discard()
person.refresh()
loadObject(person)
return render(view: 'edit', model:[user:user, person:person], params: params)
}
try {
userDaoService?.bindObject(person, params)
user.validate()
} catch(Exception e){
log.error(e.printStackTrace())
status.setRollbackOnly()
}
if(status.isRollbackOnly()){
user.discard()
user.refresh()
loadObject(user)
return render(view: 'edit', model:[user:user, person:person], params: params)
}
}
}
加载所有类型属于domainClass的属性的方法如下所示:
void loadObject(Object object, List<Object> loadedObjects = []){
loadedObjects.add(object)
for(MapEntry propertyEntryMap in object.properties){
Object property = propertyEntryMap?.value
if(grailsApplication.isDomainClass(property.getClass())){
if(!loadedObjects.contains(property))
loadObject(property, loadedObject)
}
}
if(object.hasProperty("hasMany")){
for(def manyProperty in object.hasMany){
for(Object property in object."${manyProperty.key}"){
if(grailsApplication.isDomainClass(property.getClass())){
if(!loadedObjects.contains(property))
loadObject(property, loadedObject)
}
}
}
}
}
唯一可以解决的问题是,hasMany关系可能会很大并且会减慢它的速度。
为了解决这个问题,我在DomainClass中添加了一个属性,其中包含应该加载的hasMany关系:
static lazyProperties = ['addresses']
此属性会影响将遍历该列表中属性的loadObject
方法:
void loadObject(Object object, List<Object> loadedObjects = []){
loadedObjects.add(object)
for(MapEntry propertyEntryMap in object.properties){
Object property = propertyEntryMap?.value
if(object.hasProperty('lazyProperties')){ // check if object contains the property 'lazyProperties'
if(object?.lazyProperties?.contains(propertyEntryMap.key))
continue
}
if(grailsApplication.isDomainClass(property.getClass())){
if(!loadedObjects.contains(property))
loadObject(property, loadedObject)
}
}
if(object.hasProperty("hasMany")){
for(def manyProperty in object.hasMany){
if(object.hasProperty('lazyProperties')){ // check if object contains the property 'lazyProperties'
if(object?.lazyProperties?.contains(manyProperty.key))
continue
}
for(Object property in object."${manyProperty.key}"){
if(grailsApplication.isDomainClass(property.getClass())){
if(!loadedObjects.contains(property))
loadObject(property, loadedObject)
}
}
}
}
}
答案 1 :(得分:0)
尝试扩展上述评论。
错误表明某些事情严重错误,因此休眠必须关闭会话。
考虑这个控制器:
def doSomething() {
SomeBean bean = new SomeBean(params.long('id'))
bean.updateUser=userService.currentUser
try {
serice.foSomething(bean)
} catch (ValidationException ve) {
flash.message=message(error:ve.errors.getGlobalError())?:ve.errors
} catch (DataIntegrityViolationException e) {
} catch (HibernateObjectRetrievalFailureException e) {
} catch (Throwable t) {
flash.message= t.toString()
}
redirect(controller:'someController', action:'list')
}
它有如此之多的尝试捕获的原因是因为我找到了例如我正在获得HibernateObjectRetrievalFailureException
的一些争吵的代码,但实际的工作已经完成 - 这使得你概述的那种情况发生了 - 或者可以发生
我的意思是尝试使用try catch来解决服务方法并找出最初的实际情况。
最重要的是,前3次尝试捕获实际上告诉控制器继续 - 只是忽略那些问题(我认为它们都在这种情况下)但如果它不是重定向你可以在throwable中添加一些子句或重定向因此你可以去其他地方或者简短地忽略它
要完成上述所有操作,this may help you可以解决您的问题
import grails.validation.ValidationException
class AuthorService {
void updateAge(id, int age) {
def author = Author.get(id)
author.age = age
if (!author.validate()) {
throw new ValidationException("Author is not valid", author.errors)
}
}
}
基本上,当您最终捕获到问题并找出导致问题的原因时,您可以在问题发生之前将其捕获并返回自己的自定义验证异常。
然后,您的标准故障就会被接收,其他一切都会顺利进行。
你有你的图层验证并确保一切正常 - 你显示的当前验证图层 - 问题实际上并不在这里 - 当你问Hibernate并做一些无效的事情时 - 删除可能的事情'被删除 - 做一些它不能做的事情 - 然后打破它并引起你当前的头痛 - 所以通过在它发生之前捕获它们并做上述将是你如何解决这些问题