Skip to content

Commit a50ee6c

Browse files
committed
DATACMNS-1547 - Properly remove partially populated PersistentEntity instances from cache.
We now remove partially populated PersistentEntity instances from the cache held in AbstractMappingContext as the creation could fail for other RuntimeExceptions other than a MappingException and we wouldn't want to keep the instances around in any case. Slight refactorings in the unit tests so that we can easily create MappingContext instances that reject certain types with certain exceptions.
1 parent 0a1bf26 commit a50ee6c

File tree

2 files changed

+78
-24
lines changed

2 files changed

+78
-24
lines changed

src/main/java/org/springframework/data/mapping/context/AbstractMappingContext.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ protected Optional<E> addPersistentEntity(TypeInformation<?> typeInformation) {
389389
entity.setPersistentPropertyAccessorFactory(persistentPropertyAccessorFactory);
390390
}
391391

392-
} catch (MappingException e) {
392+
} catch (RuntimeException e) {
393393
persistentEntities.remove(typeInformation);
394394
throw e;
395395
}

src/test/java/org/springframework/data/mapping/context/AbstractMappingContextUnitTests.java

+77-23
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,19 @@
1919
import static org.mockito.Mockito.*;
2020

2121
import groovy.lang.MetaClass;
22+
import lombok.AccessLevel;
23+
import lombok.EqualsAndHashCode;
24+
import lombok.RequiredArgsConstructor;
25+
import lombok.Value;
2226

2327
import java.time.LocalDateTime;
28+
import java.util.Arrays;
29+
import java.util.Collection;
2430
import java.util.Collections;
2531
import java.util.Iterator;
2632
import java.util.List;
2733
import java.util.TreeMap;
34+
import java.util.function.Supplier;
2835

2936
import org.junit.Before;
3037
import org.junit.Test;
@@ -57,32 +64,13 @@ public void setUp() {
5764
context.setSimpleTypeHolder(new SimpleTypeHolder(Collections.singleton(LocalDateTime.class), true));
5865
}
5966

60-
@Test(expected = MappingException.class) // DATACMNS-92
67+
@Test // DATACMNS-92
6168
public void doesNotAddInvalidEntity() {
6269

63-
context = new SampleMappingContext() {
64-
@Override
65-
@SuppressWarnings("unchecked")
66-
protected <S> BasicPersistentEntity<Object, SamplePersistentProperty> createPersistentEntity(
67-
TypeInformation<S> typeInformation) {
68-
return new BasicPersistentEntity<Object, SamplePersistentProperty>((TypeInformation<Object>) typeInformation) {
69-
@Override
70-
public void verify() {
71-
if (Unsupported.class.isAssignableFrom(getType())) {
72-
throw new MappingException("Unsupported type!");
73-
}
74-
}
75-
};
76-
}
77-
};
78-
79-
try {
80-
context.getPersistentEntity(Unsupported.class);
81-
} catch (MappingException e) {
82-
// expected
83-
}
70+
context = TypeRejectingMappingContext.rejecting(() -> new MappingException("Not supported!"), Unsupported.class);
8471

85-
context.getPersistentEntity(Unsupported.class);
72+
assertThatExceptionOfType(MappingException.class) //
73+
.isThrownBy(() -> context.getPersistentEntity(Unsupported.class));
8674
}
8775

8876
@Test
@@ -213,6 +201,21 @@ public void doesNotReturnPersistentEntityForCustomSimpleTypeProperty() {
213201
assertThat(context.getPersistentEntity(property)).isNull();
214202
}
215203

204+
@Test // DATACMNS-1574
205+
public void cleansUpCacheForRuntimeException() {
206+
207+
TypeRejectingMappingContext context = TypeRejectingMappingContext.rejecting(() -> new RuntimeException(),
208+
Unsupported.class);
209+
210+
assertThatExceptionOfType(RuntimeException.class) //
211+
.isThrownBy(() -> context.getPersistentEntity(Unsupported.class));
212+
213+
// Second lookup still throws the exception as the temporarily created entity was not cached
214+
215+
assertThatExceptionOfType(RuntimeException.class) //
216+
.isThrownBy(() -> context.getPersistentEntity(Unsupported.class));
217+
}
218+
216219
private static void assertHasEntityFor(Class<?> type, SampleMappingContext context, boolean expected) {
217220

218221
boolean found = false;
@@ -252,4 +255,55 @@ static class Base {
252255
static class Extension extends Base {
253256
@Id String foo;
254257
}
258+
259+
/**
260+
* Extension of {@link SampleMappingContext} to reject the creation of certain types with a configurable exception.
261+
*
262+
* @author Oliver Drotbohm
263+
*/
264+
@Value
265+
@EqualsAndHashCode(callSuper = false)
266+
@RequiredArgsConstructor(access = AccessLevel.PRIVATE)
267+
private static class TypeRejectingMappingContext extends SampleMappingContext {
268+
269+
Supplier<? extends RuntimeException> exception;
270+
Collection<Class<?>> rejectedTypes;
271+
272+
/**
273+
* Creates a new {@link TypeRejectingMappingContext} producing the given exceptions if any of the given types is
274+
* encountered.
275+
*
276+
* @param <T>
277+
* @param exception must not be {@literal null}.
278+
* @param types must not be {@literal null}.
279+
* @return
280+
*/
281+
public static <T extends RuntimeException> TypeRejectingMappingContext rejecting(Supplier<T> exception,
282+
Class<?>... types) {
283+
return new TypeRejectingMappingContext(exception, Arrays.asList(types));
284+
}
285+
286+
/*
287+
* (non-Javadoc)
288+
* @see org.springframework.data.mapping.context.SampleMappingContext#createPersistentEntity(org.springframework.data.util.TypeInformation)
289+
*/
290+
@Override
291+
protected <S> BasicPersistentEntity<Object, SamplePersistentProperty> createPersistentEntity(
292+
TypeInformation<S> typeInformation) {
293+
294+
return new BasicPersistentEntity<Object, SamplePersistentProperty>((TypeInformation<Object>) typeInformation) {
295+
296+
/*
297+
* (non-Javadoc)
298+
* @see org.springframework.data.mapping.model.BasicPersistentEntity#verify()
299+
*/
300+
@Override
301+
public void verify() {
302+
if (rejectedTypes.stream().anyMatch(it -> it.isAssignableFrom(getType()))) {
303+
throw exception.get();
304+
}
305+
}
306+
};
307+
}
308+
}
255309
}

0 commit comments

Comments
 (0)