Skip to content

Commit df18e91

Browse files
committed
Revised PersistenceExceptionTranslationInterceptor to lazily retrieve PersistenceExceptionTranslator beans on demand
Issue: SPR-10894
1 parent baa698e commit df18e91

File tree

2 files changed

+70
-56
lines changed

2 files changed

+70
-56
lines changed

spring-tx/src/main/java/org/springframework/dao/support/PersistenceExceptionTranslationInterceptor.java

Lines changed: 31 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -47,10 +47,12 @@
4747
public class PersistenceExceptionTranslationInterceptor
4848
implements MethodInterceptor, BeanFactoryAware, InitializingBean {
4949

50-
private PersistenceExceptionTranslator persistenceExceptionTranslator;
50+
private volatile PersistenceExceptionTranslator persistenceExceptionTranslator;
5151

5252
private boolean alwaysTranslate = false;
5353

54+
private ListableBeanFactory beanFactory;
55+
5456

5557
/**
5658
* Create a new PersistenceExceptionTranslationInterceptor.
@@ -63,10 +65,11 @@ public PersistenceExceptionTranslationInterceptor() {
6365
/**
6466
* Create a new PersistenceExceptionTranslationInterceptor
6567
* for the given PersistenceExceptionTranslator.
66-
* @param persistenceExceptionTranslator the PersistenceExceptionTranslator to use
68+
* @param pet the PersistenceExceptionTranslator to use
6769
*/
68-
public PersistenceExceptionTranslationInterceptor(PersistenceExceptionTranslator persistenceExceptionTranslator) {
69-
setPersistenceExceptionTranslator(persistenceExceptionTranslator);
70+
public PersistenceExceptionTranslationInterceptor(PersistenceExceptionTranslator pet) {
71+
Assert.notNull(pet, "PersistenceExceptionTranslator must not be null");
72+
this.persistenceExceptionTranslator = pet;
7073
}
7174

7275
/**
@@ -76,7 +79,8 @@ public PersistenceExceptionTranslationInterceptor(PersistenceExceptionTranslator
7679
* PersistenceExceptionTranslators from
7780
*/
7881
public PersistenceExceptionTranslationInterceptor(ListableBeanFactory beanFactory) {
79-
this.persistenceExceptionTranslator = detectPersistenceExceptionTranslators(beanFactory);
82+
Assert.notNull(beanFactory, "ListableBeanFactory must not be null");
83+
this.beanFactory = beanFactory;
8084
}
8185

8286

@@ -87,7 +91,6 @@ public PersistenceExceptionTranslationInterceptor(ListableBeanFactory beanFactor
8791
* @see #detectPersistenceExceptionTranslators
8892
*/
8993
public void setPersistenceExceptionTranslator(PersistenceExceptionTranslator pet) {
90-
Assert.notNull(pet, "PersistenceExceptionTranslator must not be null");
9194
this.persistenceExceptionTranslator = pet;
9295
}
9396

@@ -115,19 +118,37 @@ public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
115118
throw new IllegalArgumentException(
116119
"Cannot use PersistenceExceptionTranslator autodetection without ListableBeanFactory");
117120
}
118-
this.persistenceExceptionTranslator =
119-
detectPersistenceExceptionTranslators((ListableBeanFactory) beanFactory);
121+
this.beanFactory = (ListableBeanFactory) beanFactory;
120122
}
121123
}
122124

123125
@Override
124126
public void afterPropertiesSet() {
125-
if (this.persistenceExceptionTranslator == null) {
127+
if (this.persistenceExceptionTranslator == null && this.beanFactory == null) {
126128
throw new IllegalArgumentException("Property 'persistenceExceptionTranslator' is required");
127129
}
128130
}
129131

130132

133+
@Override
134+
public Object invoke(MethodInvocation mi) throws Throwable {
135+
try {
136+
return mi.proceed();
137+
}
138+
catch (RuntimeException ex) {
139+
// Let it throw raw if the type of the exception is on the throws clause of the method.
140+
if (!this.alwaysTranslate && ReflectionUtils.declaresException(mi.getMethod(), ex.getClass())) {
141+
throw ex;
142+
}
143+
else {
144+
if (this.persistenceExceptionTranslator == null) {
145+
this.persistenceExceptionTranslator = detectPersistenceExceptionTranslators(this.beanFactory);
146+
}
147+
throw DataAccessUtils.translateIfNecessary(ex, this.persistenceExceptionTranslator);
148+
}
149+
}
150+
}
151+
131152
/**
132153
* Detect all PersistenceExceptionTranslators in the given BeanFactory.
133154
* @param beanFactory the ListableBeanFactory to obtaining all
@@ -140,32 +161,11 @@ protected PersistenceExceptionTranslator detectPersistenceExceptionTranslators(L
140161
// Find all translators, being careful not to activate FactoryBeans.
141162
Map<String, PersistenceExceptionTranslator> pets = BeanFactoryUtils.beansOfTypeIncludingAncestors(
142163
beanFactory, PersistenceExceptionTranslator.class, false, false);
143-
if (pets.isEmpty()) {
144-
throw new IllegalStateException(
145-
"No persistence exception translators found in bean factory. Cannot perform exception translation.");
146-
}
147164
ChainedPersistenceExceptionTranslator cpet = new ChainedPersistenceExceptionTranslator();
148165
for (PersistenceExceptionTranslator pet : pets.values()) {
149166
cpet.addDelegate(pet);
150167
}
151168
return cpet;
152169
}
153170

154-
155-
@Override
156-
public Object invoke(MethodInvocation mi) throws Throwable {
157-
try {
158-
return mi.proceed();
159-
}
160-
catch (RuntimeException ex) {
161-
// Let it throw raw if the type of the exception is on the throws clause of the method.
162-
if (!this.alwaysTranslate && ReflectionUtils.declaresException(mi.getMethod(), ex.getClass())) {
163-
throw ex;
164-
}
165-
else {
166-
throw DataAccessUtils.translateIfNecessary(ex, this.persistenceExceptionTranslator);
167-
}
168-
}
169-
}
170-
171171
}

spring-tx/src/test/java/org/springframework/dao/annotation/PersistenceExceptionTranslationPostProcessorTests.java

Lines changed: 39 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2013 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,6 +16,8 @@
1616

1717
package org.springframework.dao.annotation;
1818

19+
import javax.persistence.PersistenceException;
20+
1921
import junit.framework.TestCase;
2022
import org.aspectj.lang.JoinPoint;
2123
import org.aspectj.lang.annotation.Aspect;
@@ -25,38 +27,23 @@
2527
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
2628
import org.springframework.aop.framework.Advised;
2729
import org.springframework.aop.support.AopUtils;
28-
import org.springframework.beans.BeansException;
2930
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
3031
import org.springframework.beans.factory.support.RootBeanDefinition;
3132
import org.springframework.context.support.GenericApplicationContext;
33+
import org.springframework.dao.DataAccessException;
34+
import org.springframework.dao.DataAccessResourceFailureException;
3235
import org.springframework.dao.annotation.PersistenceExceptionTranslationAdvisorTests.RepositoryInterface;
3336
import org.springframework.dao.annotation.PersistenceExceptionTranslationAdvisorTests.RepositoryInterfaceImpl;
3437
import org.springframework.dao.annotation.PersistenceExceptionTranslationAdvisorTests.StereotypedRepositoryInterfaceImpl;
35-
import org.springframework.dao.support.ChainedPersistenceExceptionTranslator;
38+
import org.springframework.dao.support.PersistenceExceptionTranslator;
3639
import org.springframework.stereotype.Repository;
3740

3841
/**
39-
* Unit tests for PersistenceExceptionTranslationPostProcessor. Does not test translation; there are separate unit tests
40-
* for the Spring AOP Advisor. Just checks whether proxying occurs correctly, as a unit test should.
41-
*
4242
* @author Rod Johnson
43+
* @author Juergen Hoeller
4344
*/
4445
public class PersistenceExceptionTranslationPostProcessorTests extends TestCase {
4546

46-
public void testFailsWithNoPersistenceExceptionTranslators() {
47-
GenericApplicationContext gac = new GenericApplicationContext();
48-
gac.registerBeanDefinition("translator",
49-
new RootBeanDefinition(PersistenceExceptionTranslationPostProcessor.class));
50-
gac.registerBeanDefinition("proxied", new RootBeanDefinition(StereotypedRepositoryInterfaceImpl.class));
51-
try {
52-
gac.refresh();
53-
fail("Should fail with no translators");
54-
}
55-
catch (BeansException ex) {
56-
// Ok
57-
}
58-
}
59-
6047
public void testProxiesCorrectly() {
6148
GenericApplicationContext gac = new GenericApplicationContext();
6249
gac.registerBeanDefinition("translator",
@@ -66,8 +53,8 @@ public void testProxiesCorrectly() {
6653
gac.registerBeanDefinition("classProxied", new RootBeanDefinition(RepositoryWithoutInterface.class));
6754
gac.registerBeanDefinition("classProxiedAndAdvised",
6855
new RootBeanDefinition(RepositoryWithoutInterfaceAndOtherwiseAdvised.class));
69-
gac.registerBeanDefinition("chainedTranslator",
70-
new RootBeanDefinition(ChainedPersistenceExceptionTranslator.class));
56+
gac.registerBeanDefinition("myTranslator",
57+
new RootBeanDefinition(MyPersistenceExceptionTranslator.class));
7158
gac.registerBeanDefinition("proxyCreator",
7259
BeanDefinitionBuilder.rootBeanDefinition(AnnotationAwareAspectJAutoProxyCreator.class).
7360
addPropertyValue("order", 50).getBeanDefinition());
@@ -84,8 +71,15 @@ public void testProxiesCorrectly() {
8471

8572
Additional rwi2 = (Additional) gac.getBean("classProxiedAndAdvised");
8673
assertTrue(AopUtils.isAopProxy(rwi2));
87-
rwi2.additionalMethod();
74+
rwi2.additionalMethod(false);
8875
checkWillTranslateExceptions(rwi2);
76+
try {
77+
rwi2.additionalMethod(true);
78+
fail("Should have thrown DataAccessResourceFailureException");
79+
}
80+
catch (DataAccessResourceFailureException ex) {
81+
assertEquals("my failure", ex.getMessage());
82+
}
8983
}
9084

9185
protected void checkWillTranslateExceptions(Object o) {
@@ -99,26 +93,46 @@ protected void checkWillTranslateExceptions(Object o) {
9993
fail("No translation");
10094
}
10195

96+
10297
@Repository
10398
public static class RepositoryWithoutInterface {
10499

105100
public void nameDoesntMatter() {
106101
}
107102
}
108103

104+
109105
public interface Additional {
110106

111-
void additionalMethod();
107+
void additionalMethod(boolean fail);
112108
}
113109

110+
114111
public static class RepositoryWithoutInterfaceAndOtherwiseAdvised extends StereotypedRepositoryInterfaceImpl
115112
implements Additional {
116113

117114
@Override
118-
public void additionalMethod() {
115+
public void additionalMethod(boolean fail) {
116+
if (fail) {
117+
throw new PersistenceException("my failure");
118+
}
119119
}
120120
}
121121

122+
123+
public static class MyPersistenceExceptionTranslator implements PersistenceExceptionTranslator {
124+
125+
126+
@Override
127+
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
128+
if (ex instanceof PersistenceException) {
129+
return new DataAccessResourceFailureException(ex.getMessage());
130+
}
131+
return null;
132+
}
133+
}
134+
135+
122136
@Aspect
123137
public static class LogAllAspect {
124138

0 commit comments

Comments
 (0)