Skip to content

Commit 6e32945

Browse files
committed
GRAILS-6250 created HibernateTransactionManager subclass to set the session's flush mode to manual for read-only transactions
1 parent acf1b10 commit 6e32945

File tree

4 files changed

+83
-4
lines changed

4 files changed

+83
-4
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2004-2010 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.codehaus.groovy.grails.orm.hibernate
17+
18+
import org.hibernate.FlushMode
19+
import org.hibernate.Session
20+
import org.springframework.orm.hibernate3.HibernateTransactionManager
21+
import org.springframework.transaction.TransactionDefinition
22+
23+
/**
24+
* Extends the standard class to always set the flush mode to manual when in a read-only transaction.
25+
*
26+
* @author Burt Beckwith
27+
*/
28+
class GrailsHibernateTransactionManager extends HibernateTransactionManager {
29+
30+
@Override
31+
protected void doBegin(Object transaction, TransactionDefinition definition) {
32+
super.doBegin transaction, definition
33+
34+
if (definition.isReadOnly()) {
35+
// always set to manual; the base class doesn't because the OSIVI has already registered a session
36+
transaction.sessionHolder.session.flushMode = FlushMode.MANUAL
37+
}
38+
}
39+
}

src/java/org/codehaus/groovy/grails/orm/hibernate/cfg/GORMSessionFactoryDefinitionParser.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import org.codehaus.groovy.grails.commons.DomainClassArtefactHandler;
2525
import org.codehaus.groovy.grails.commons.GrailsApplication;
2626
import org.codehaus.groovy.grails.orm.hibernate.ConfigurableLocalSessionFactoryBean;
27+
import org.codehaus.groovy.grails.orm.hibernate.GrailsHibernateTransactionManager;
2728
import org.codehaus.groovy.grails.orm.hibernate.support.SpringLobHandlerDetectorFactoryBean;
2829
import org.codehaus.groovy.grails.orm.hibernate.validation.HibernateDomainClassValidator;
2930
import org.springframework.beans.MutablePropertyValues;
@@ -44,7 +45,6 @@
4445
import org.springframework.beans.factory.xml.XmlReaderContext;
4546
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
4647
import org.springframework.core.type.filter.AnnotationTypeFilter;
47-
import org.springframework.orm.hibernate3.HibernateTransactionManager;
4848
import org.springframework.util.StringUtils;
4949
import org.w3c.dom.Element;
5050

@@ -215,7 +215,7 @@ private AbstractBeanDefinition parseSessionFactory(Element element, String dataS
215215
}
216216
else {
217217
GenericBeanDefinition transactionManagerBean = new GenericBeanDefinition();
218-
transactionManagerBean.setBeanClass(HibernateTransactionManager.class);
218+
transactionManagerBean.setBeanClass(GrailsHibernateTransactionManager.class);
219219
transactionManagerBean.getPropertyValues().addPropertyValue("sessionFactory", new RuntimeBeanReference(sessionFactoryId));
220220

221221
targetRegistry.registerBeanDefinition("transactionManager", transactionManagerBean);

src/java/org/codehaus/groovy/grails/plugins/orm/hibernate/HibernatePluginSupport.groovy

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,14 @@ import org.codehaus.groovy.grails.commons.spring.GrailsRuntimeConfigurator
3535
import org.codehaus.groovy.grails.commons.spring.RuntimeSpringConfiguration
3636
import org.codehaus.groovy.grails.exceptions.GrailsDomainException
3737
import org.codehaus.groovy.grails.orm.hibernate.ConfigurableLocalSessionFactoryBean
38+
import org.codehaus.groovy.grails.orm.hibernate.GrailsHibernateTransactionManager
3839
import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsDomainBinder
3940
import org.codehaus.groovy.grails.orm.hibernate.cfg.GrailsHibernateUtil
4041
import org.codehaus.groovy.grails.orm.hibernate.cfg.HibernateNamedQueriesBuilder
4142
import org.codehaus.groovy.grails.orm.hibernate.events.PatchedDefaultFlushEventListener
4243
import org.codehaus.groovy.grails.orm.hibernate.metaclass.*
4344
import org.codehaus.groovy.grails.orm.hibernate.support.*
44-
import org.codehaus.groovy.grails.orm.hibernate.proxy.HibernateProxyHandler;
45+
import org.codehaus.groovy.grails.orm.hibernate.proxy.HibernateProxyHandler
4546
import org.codehaus.groovy.grails.orm.hibernate.validation.HibernateDomainClassValidator
4647
import org.codehaus.groovy.grails.orm.hibernate.validation.PersistentConstraintFactory
4748
import org.codehaus.groovy.grails.orm.hibernate.validation.UniqueConstraint
@@ -220,7 +221,7 @@ Using Grails' default naming strategy: '${GrailsDomainBinder.namingStrategy.getC
220221
'post-delete':eventTriggeringInterceptor]
221222
}
222223

223-
transactionManager(HibernateTransactionManager) {
224+
transactionManager(GrailsHibernateTransactionManager) {
224225
sessionFactory = sessionFactory
225226
}
226227
persistenceInterceptor(HibernatePersistenceContextInterceptor) {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
package org.codehaus.groovy.grails.orm.hibernate
2+
3+
class ReadOnlyTransactionTests extends AbstractGrailsHibernateTests {
4+
5+
protected void onSetUp() {
6+
gcl.parseClass '''
7+
class Gorpledanger {
8+
Long id
9+
Long version
10+
String name
11+
}
12+
13+
class GorpledangerService {
14+
static transactional = false
15+
16+
@org.springframework.transaction.annotation.Transactional(readOnly=true)
17+
void doNotModify(thing) {
18+
thing.name += 'wahoo'
19+
}
20+
}
21+
'''
22+
}
23+
24+
void testReadOnlyAnnotation() {
25+
def clazz = ga.getDomainClass('Gorpledanger').clazz
26+
def instance = clazz.newInstance()
27+
instance.name = 'foo'
28+
assertNotNull instance.save(flush: true)
29+
session.clear()
30+
31+
instance = clazz.get(instance.id)
32+
def service = appCtx.gorpledangerService
33+
service.doNotModify instance
34+
session.clear()
35+
36+
instance = clazz.get(instance.id)
37+
assertEquals 'foo', instance.name
38+
}
39+
}

0 commit comments

Comments
 (0)