Skip to content

Commit 00c3999

Browse files
author
graeme
committed
fix for GRAILS-398/GRAILS-674
git-svn-id: https://svn.codehaus.org/grails/trunk@2969 1cfb16fd-6d17-0410-8ff1-b7e8e1e2867d
1 parent d314f92 commit 00c3999

File tree

4 files changed

+150
-32
lines changed

4 files changed

+150
-32
lines changed

src/persistence/org/codehaus/groovy/grails/orm/hibernate/metaclass/AbstractSavePersistentMethod.java

Lines changed: 39 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import java.io.Serializable;
2121
import java.util.regex.Pattern;
22+
import java.sql.SQLException;
2223

2324
import org.codehaus.groovy.grails.commons.GrailsApplication;
2425
import org.codehaus.groovy.grails.commons.GrailsDomainClass;
@@ -27,9 +28,13 @@
2728
import org.codehaus.groovy.grails.metaclass.DomainClassMethods;
2829
import org.codehaus.groovy.runtime.InvokerHelper;
2930
import org.hibernate.SessionFactory;
31+
import org.hibernate.Session;
32+
import org.hibernate.HibernateException;
33+
import org.hibernate.FlushMode;
3034
import org.springframework.beans.BeanWrapper;
3135
import org.springframework.beans.BeanWrapperImpl;
3236
import org.springframework.orm.hibernate3.HibernateTemplate;
37+
import org.springframework.orm.hibernate3.HibernateCallback;
3338
import org.springframework.validation.BindException;
3439
import org.springframework.validation.Errors;
3540
import org.springframework.validation.Validator;
@@ -124,31 +129,49 @@ private void autoRetrieveAssocations(GrailsDomainClass domainClass, Object targe
124129
}
125130
}
126131

127-
/**
128-
* Handles a validation error
129-
* @return Returns null, as this represents a validation error
130-
* @param target The target object being validated
132+
/**
133+
* This method willl set the save() method will set the flush mode to manual. What this does
134+
* is ensure that the database changes are not persisted to the database if a validation error occurs.
135+
* If save() is called again and validation passes the code will check if there is a manual flush mode and
136+
* flush manually if necessary
137+
*
138+
* @param target The target object that failed validation
131139
* @param errors The Errors instance
140+
* @return This method will return null signaling a validation failure
132141
*/
133142
protected Object handleValidationError(Object target, Errors errors) {
134143
HibernateTemplate t = getHibernateTemplate();
135144
// if the target is within the session evict it
136145
// this is so that if validation fails hibernate doesn't save
137146
// the object automatically when the session is flushed
138-
if(t.contains(target)) {
139-
t.evict(target);
140-
}
141-
if(target instanceof GroovyObject) {
142-
((GroovyObject)target).setProperty(DomainClassMethods.ERRORS_PROPERTY,errors);
143-
}
144-
else {
145-
DynamicMethodsMetaClass metaClass = (DynamicMethodsMetaClass)InvokerHelper.getInstance().getMetaRegistry().getMetaClass(target.getClass());
146-
metaClass.setProperty(target.getClass() ,target,DomainClassMethods.ERRORS_PROPERTY,errors, false, false);
147-
}
148-
return null;
147+
t.execute(new HibernateCallback() {
148+
149+
public Object doInHibernate(Session session) throws HibernateException, SQLException {
150+
session.setFlushMode(FlushMode.MANUAL);
151+
return null;
152+
}
153+
});
154+
setErrorsOnInstance(target, errors);
155+
return null;
149156
}
150157

151-
/**
158+
/**
159+
* Associates the Errors object on the instance
160+
*
161+
* @param target The target instance
162+
* @param errors The Errors object
163+
*/
164+
protected void setErrorsOnInstance(Object target, Errors errors) {
165+
if(target instanceof GroovyObject) {
166+
((GroovyObject)target).setProperty(DomainClassMethods.ERRORS_PROPERTY,errors);
167+
}
168+
else {
169+
DynamicMethodsMetaClass metaClass = (DynamicMethodsMetaClass) InvokerHelper.getInstance().getMetaRegistry().getMetaClass(target.getClass());
170+
metaClass.setProperty(target.getClass() ,target,DomainClassMethods.ERRORS_PROPERTY,errors, false, false);
171+
}
172+
}
173+
174+
/**
152175
* Checks whether validation should be performed
153176
* @return True if the domain class should be validated
154177
* @param arguments The arguments to the validate method

src/persistence/org/codehaus/groovy/grails/orm/hibernate/metaclass/MergePersistentMethod.java

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,10 +16,12 @@
1616
package org.codehaus.groovy.grails.orm.hibernate.metaclass;
1717

1818
import java.util.regex.Pattern;
19+
import java.sql.SQLException;
1920

2021
import org.codehaus.groovy.grails.commons.GrailsApplication;
21-
import org.hibernate.LockMode;
22-
import org.hibernate.SessionFactory;
22+
import org.hibernate.*;
23+
import org.springframework.orm.hibernate3.HibernateCallback;
24+
import org.springframework.orm.hibernate3.HibernateTemplate;
2325

2426
/**
2527
* The merge() method follows the semantics of merge which attempts to "merge" an object
@@ -42,11 +44,19 @@ public MergePersistentMethod(SessionFactory sessionFactory, ClassLoader classLoa
4244
/* (non-Javadoc)
4345
* @see org.codehaus.groovy.grails.orm.hibernate.metaclass.AbstractSavePersistentMethod#performSave(java.lang.Object)
4446
*/
45-
protected void performSave(Object target, boolean flush) {
46-
getHibernateTemplate().merge(target);
47-
getHibernateTemplate().lock(target, LockMode.NONE);
48-
if(flush)
49-
getHibernateTemplate().flush();
50-
}
47+
protected void performSave(final Object target, final boolean flush) {
48+
HibernateTemplate ht = getHibernateTemplate();
49+
50+
ht.execute(new HibernateCallback() {
51+
public Object doInHibernate(Session session) throws HibernateException, SQLException {
52+
session.merge(target);
53+
session.lock(target, LockMode.NONE);
54+
55+
if(flush && FlushMode.isManualFlushMode(session.getFlushMode()))
56+
getHibernateTemplate().flush();
57+
return target;
58+
}
59+
});
60+
}
5161

5262
}

src/persistence/org/codehaus/groovy/grails/orm/hibernate/metaclass/SavePersistentMethod.java

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,16 @@
1515
*/
1616
package org.codehaus.groovy.grails.orm.hibernate.metaclass;
1717

18-
import java.util.regex.Pattern;
19-
2018
import org.codehaus.groovy.grails.commons.GrailsApplication;
19+
import org.hibernate.FlushMode;
20+
import org.hibernate.HibernateException;
21+
import org.hibernate.Session;
2122
import org.hibernate.SessionFactory;
23+
import org.springframework.orm.hibernate3.HibernateCallback;
24+
import org.springframework.orm.hibernate3.HibernateTemplate;
25+
26+
import java.sql.SQLException;
27+
import java.util.regex.Pattern;
2228

2329
/**
2430
* This method follows the semantics of saveOrUpdate of scheduling the object
@@ -27,7 +33,9 @@
2733
* @author Steven Devijver
2834
* @author Graeme Rocher
2935
*
30-
* @since Aug 7, 2005
36+
* @since 0.1
37+
*
38+
* Created: Aug 7, 2005
3139
*/
3240
public class SavePersistentMethod extends AbstractSavePersistentMethod {
3341

@@ -40,10 +48,16 @@ public SavePersistentMethod(SessionFactory sessionFactory, ClassLoader classLoad
4048
super(METHOD_PATTERN,sessionFactory, classLoader, application);
4149
}
4250

43-
protected void performSave(Object target, boolean flush) {
44-
getHibernateTemplate().saveOrUpdate(target);
45-
if(flush)
46-
getHibernateTemplate().flush();
51+
protected void performSave(final Object target, final boolean flush) {
52+
HibernateTemplate ht = getHibernateTemplate();
53+
ht.execute(new HibernateCallback() {
54+
public Object doInHibernate(Session session) throws HibernateException, SQLException {
55+
session.saveOrUpdate(target);
56+
if(flush && FlushMode.isManualFlushMode(session.getFlushMode()))
57+
getHibernateTemplate().flush();
58+
return target;
59+
}
60+
});
4761
}
48-
62+
4963
}
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package org.codehaus.groovy.grails.orm.hibernate;
2+
3+
4+
import org.hibernate.FlushMode;
5+
6+
class ValidationFailureTests extends AbstractGrailsHibernateTests {
7+
8+
void onSetUp() {
9+
gcl.parseClass(
10+
"""
11+
class Book {
12+
Long id
13+
Long version
14+
String title
15+
}
16+
class Author {
17+
Long id
18+
Long version
19+
String name
20+
Set books
21+
static hasMany = [books: Book]
22+
static constraints = {
23+
name(size:8..16)
24+
}
25+
}
26+
"""
27+
)
28+
}
29+
30+
31+
void testValidationFailure() {
32+
def authorClass = ga.getDomainClass("Author")
33+
def bookClass = ga.getDomainClass("Book")
34+
35+
def a = authorClass.newInstance()
36+
a.name = "123456789"
37+
38+
def b1 = bookClass.newInstance()
39+
b1.title = "foo"
40+
a.addBook(b1)
41+
def b2 = bookClass.newInstance()
42+
b2.title = "bar"
43+
a.addBook(b2)
44+
45+
a.save(true)
46+
47+
assert session.contains(a)
48+
49+
session.evict(a)
50+
session.evict(b1)
51+
session.evict(b2)
52+
a = null
53+
b1 = null
54+
b2 = null
55+
56+
a = authorClass.clazz.get(1)
57+
58+
// now invalidate a
59+
a.name = "bad"
60+
a.save()
61+
62+
assertTrue FlushMode.isManualFlushMode(session.getFlushMode())
63+
64+
assertEquals 2, a.books.size()
65+
66+
}
67+
68+
void onTearDown() {
69+
70+
}
71+
}

0 commit comments

Comments
 (0)