Skip to content

Commit 77f17b8

Browse files
committed
DATACMNS-637 - Performance improvements.
MappingContextTypeInformationMapper now caches detected type aliases. PreferredConstructor.isEnclosingClassParameter(…) now eagerly returns if the parameter itself is not an enclosing one and thus avoids a collection lookup and equals check. Moved equals check for the type to the very end of the equals check to increase the chances that other inequality guards kick in earlier. AbstractMappingContext now leaves the non-null-check for getPersistentEntity(…) to the factory method of ClassTypeInformation. We now pre-calculate the hash codes for TypeInformation implementations as far as possible as the instances are used as cache keys quite a lot. The same applies to AbstractPersistentProperty. BasicPersistentEntity now uses an ArrayList we sort during the verify() phase to mimic the previous behavior wich was implemented using a TreeSet as ArrayLists are way more performant when iterating over all elements which doWithProperties(…) is doing which is used quite a lot. BeanWrapper now avoids the getter lookup if field access is used. SimpleTypeHolder now uses a CopyOnWriteArrySet to leniently add types detected to be simple to the set of simple types to avoid ongoing checks against the inheritance hierarchy.
1 parent 3c0157d commit 77f17b8

File tree

11 files changed

+202
-62
lines changed

11 files changed

+202
-62
lines changed

src/main/java/org/springframework/data/convert/MappingContextTypeInformationMapper.java

Lines changed: 31 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2011-2014 the original author or authors.
2+
* Copyright 2011-2015 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.
@@ -15,12 +15,13 @@
1515
*/
1616
package org.springframework.data.convert;
1717

18-
import java.util.HashMap;
1918
import java.util.Map;
2019
import java.util.Map.Entry;
20+
import java.util.concurrent.ConcurrentHashMap;
2121

2222
import org.springframework.data.mapping.PersistentEntity;
2323
import org.springframework.data.mapping.context.MappingContext;
24+
import org.springframework.data.util.CacheValue;
2425
import org.springframework.data.util.ClassTypeInformation;
2526
import org.springframework.data.util.TypeInformation;
2627
import org.springframework.util.Assert;
@@ -34,7 +35,7 @@
3435
*/
3536
public class MappingContextTypeInformationMapper implements TypeInformationMapper {
3637

37-
private final Map<ClassTypeInformation<?>, Object> typeMap;
38+
private final Map<ClassTypeInformation<?>, CacheValue<Object>> typeMap;
3839
private final MappingContext<? extends PersistentEntity<?, ?>, ?> mappingContext;
3940

4041
/**
@@ -47,7 +48,7 @@ public MappingContextTypeInformationMapper(MappingContext<? extends PersistentEn
4748

4849
Assert.notNull(mappingContext);
4950

50-
this.typeMap = new HashMap<ClassTypeInformation<?>, Object>();
51+
this.typeMap = new ConcurrentHashMap<ClassTypeInformation<?>, CacheValue<Object>>();
5152
this.mappingContext = mappingContext;
5253

5354
for (PersistentEntity<?, ?> entity : mappingContext.getPersistentEntities()) {
@@ -61,10 +62,10 @@ public MappingContextTypeInformationMapper(MappingContext<? extends PersistentEn
6162
*/
6263
public Object createAliasFor(TypeInformation<?> type) {
6364

64-
Object key = typeMap.get(type);
65+
CacheValue<Object> key = typeMap.get(type);
6566

6667
if (key != null) {
67-
return key;
68+
return key.getValue();
6869
}
6970

7071
PersistentEntity<?, ?> entity = mappingContext.getPersistentEntity(type);
@@ -87,34 +88,44 @@ public Object createAliasFor(TypeInformation<?> type) {
8788
*/
8889
private void safelyAddToCache(ClassTypeInformation<?> key, Object alias) {
8990

90-
if (alias == null) {
91+
CacheValue<Object> aliasToBeCached = CacheValue.ofNullable(alias);
92+
93+
if (alias == null && !typeMap.containsKey(key)) {
94+
typeMap.put(key, aliasToBeCached);
9195
return;
9296
}
9397

94-
Object existingAlias = typeMap.get(key);
98+
CacheValue<Object> alreadyCachedAlias = typeMap.get(key);
9599

96100
// Reject second alias for same type
97101

98-
if (existingAlias != null && !alias.equals(existingAlias)) {
102+
if (alreadyCachedAlias != null && alreadyCachedAlias.isPresent() && !alreadyCachedAlias.hasValue(alias)) {
99103
throw new IllegalArgumentException(String.format(
100-
"Trying to register alias '%s', but found already registered alias '%s' for type %s!", alias, existingAlias,
101-
key));
104+
"Trying to register alias '%s', but found already registered alias '%s' for type %s!", alias,
105+
alreadyCachedAlias, key));
102106
}
103107

104108
// Reject second type for same alias
105109

106-
if (typeMap.containsValue(alias)) {
110+
if (typeMap.containsValue(aliasToBeCached)) {
111+
112+
for (Entry<ClassTypeInformation<?>, CacheValue<Object>> entry : typeMap.entrySet()) {
113+
114+
CacheValue<Object> value = entry.getValue();
107115

108-
for (Entry<ClassTypeInformation<?>, Object> entry : typeMap.entrySet()) {
109-
if (entry.getValue().equals(alias) && !entry.getKey().equals(key)) {
116+
if (!value.isPresent()) {
117+
continue;
118+
}
119+
120+
if (value.hasValue(alias) && !entry.getKey().equals(key)) {
110121
throw new IllegalArgumentException(String.format(
111122
"Detected existing type mapping of %s to alias '%s' but attempted to bind the same alias to %s!", key,
112123
alias, entry.getKey()));
113124
}
114125
}
115126
}
116127

117-
typeMap.put(key, alias);
128+
typeMap.put(key, aliasToBeCached);
118129
}
119130

120131
/*
@@ -127,8 +138,11 @@ public ClassTypeInformation<?> resolveTypeFrom(Object alias) {
127138
return null;
128139
}
129140

130-
for (Entry<ClassTypeInformation<?>, Object> entry : typeMap.entrySet()) {
131-
if (entry.getValue().equals(alias)) {
141+
for (Entry<ClassTypeInformation<?>, CacheValue<Object>> entry : typeMap.entrySet()) {
142+
143+
CacheValue<Object> cachedAlias = entry.getValue();
144+
145+
if (cachedAlias.hasValue(alias)) {
132146
return entry.getKey();
133147
}
134148
}

src/main/java/org/springframework/data/mapping/PreferredConstructor.java

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2011-2014 the original author or authors.
2+
* Copyright 2011-2015 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.
@@ -168,11 +168,11 @@ public boolean isEnclosingClassParameter(Parameter<?, P> parameter) {
168168

169169
Assert.notNull(parameter);
170170

171-
if (parameters.isEmpty()) {
171+
if (parameters.isEmpty() || !parameter.isEnclosingClassParameter()) {
172172
return false;
173173
}
174174

175-
return parameters.get(0).equals(parameter) && parameter.isEnclosingClassParameter();
175+
return parameters.get(0).equals(parameter);
176176
}
177177

178178
/**
@@ -312,10 +312,10 @@ public boolean equals(Object obj) {
312312

313313
boolean nameEquals = this.name == null ? that.name == null : this.name.equals(that.name);
314314
boolean keyEquals = this.key == null ? that.key == null : this.key.equals(that.key);
315-
boolean typeEquals = nullSafeEquals(this.type, that.type);
316315
boolean entityEquals = this.entity == null ? that.entity == null : this.entity.equals(that.entity);
317316

318-
return nameEquals && keyEquals && typeEquals && entityEquals;
317+
// Explicitly delay equals check on type as it might be expensive
318+
return nameEquals && keyEquals && entityEquals && this.type.equals(that.type);
319319
}
320320

321321
/*
@@ -329,8 +329,8 @@ public int hashCode() {
329329

330330
result += 31 * nullSafeHashCode(this.name);
331331
result += 31 * nullSafeHashCode(this.key);
332-
result += 31 * nullSafeHashCode(this.type);
333332
result += 31 * nullSafeHashCode(this.entity);
333+
result += 31 * this.type.hashCode();
334334

335335
return result;
336336
}

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

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2011-2014 by the original author(s).
2+
* Copyright 2011-2015 by the original author(s).
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.
@@ -137,7 +137,6 @@ public Collection<E> getPersistentEntities() {
137137
* @see org.springframework.data.mapping.model.MappingContext#getPersistentEntity(java.lang.Class)
138138
*/
139139
public E getPersistentEntity(Class<?> type) {
140-
Assert.notNull(type);
141140
return getPersistentEntity(ClassTypeInformation.from(type));
142141
}
143142

src/main/java/org/springframework/data/mapping/model/AbstractPersistentProperty.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2011-2014 the original author or authors.
2+
* Copyright 2011-2015 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.
@@ -52,6 +52,7 @@ public abstract class AbstractPersistentProperty<P extends PersistentProperty<P>
5252
protected final Association<P> association;
5353
protected final PersistentEntity<?, P> owner;
5454
private final SimpleTypeHolder simpleTypeHolder;
55+
private final int hashCode;
5556

5657
public AbstractPersistentProperty(Field field, PropertyDescriptor propertyDescriptor, PersistentEntity<?, P> owner,
5758
SimpleTypeHolder simpleTypeHolder) {
@@ -67,6 +68,7 @@ public AbstractPersistentProperty(Field field, PropertyDescriptor propertyDescri
6768
this.association = isAssociation() ? createAssociation() : null;
6869
this.owner = owner;
6970
this.simpleTypeHolder = simpleTypeHolder;
71+
this.hashCode = this.field == null ? this.propertyDescriptor.hashCode() : this.field.hashCode();
7072
}
7173

7274
protected abstract Association<P> createAssociation();
@@ -315,7 +317,7 @@ public boolean equals(Object obj) {
315317
*/
316318
@Override
317319
public int hashCode() {
318-
return this.field == null ? this.propertyDescriptor.hashCode() : this.field.hashCode();
320+
return this.hashCode;
319321
}
320322

321323
/*

src/main/java/org/springframework/data/mapping/model/BasicPersistentEntity.java

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2011-2013 by the original author(s).
2+
* Copyright 2011-2015 by the original author(s).
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.
@@ -17,9 +17,12 @@
1717

1818
import java.io.Serializable;
1919
import java.lang.annotation.Annotation;
20+
import java.util.ArrayList;
21+
import java.util.Collections;
2022
import java.util.Comparator;
2123
import java.util.HashMap;
2224
import java.util.HashSet;
25+
import java.util.List;
2326
import java.util.Map;
2427
import java.util.Set;
2528
import java.util.TreeSet;
@@ -50,7 +53,8 @@ public class BasicPersistentEntity<T, P extends PersistentProperty<P>> implement
5053

5154
private final PreferredConstructor<T, P> constructor;
5255
private final TypeInformation<T> information;
53-
private final Set<P> properties;
56+
private final List<P> properties;
57+
private final Comparator<P> comparator;
5458
private final Set<Association<P>> associations;
5559

5660
private final Map<String, P> propertyCache;
@@ -81,7 +85,8 @@ public BasicPersistentEntity(TypeInformation<T> information, Comparator<P> compa
8185
Assert.notNull(information);
8286

8387
this.information = information;
84-
this.properties = comparator == null ? new HashSet<P>() : new TreeSet<P>(comparator);
88+
this.properties = new ArrayList<P>();
89+
this.comparator = comparator;
8590
this.constructor = new PreferredConstructorDiscoverer<T, P>(information, this).getConstructor();
8691
this.associations = comparator == null ? new HashSet<Association<P>>() : new TreeSet<Association<P>>(
8792
new AssociationComparator<P>(comparator));
@@ -169,6 +174,11 @@ public boolean hasVersionProperty() {
169174
public void addPersistentProperty(P property) {
170175

171176
Assert.notNull(property);
177+
178+
if (properties.contains(property)) {
179+
return;
180+
}
181+
172182
properties.add(property);
173183

174184
if (!propertyCache.containsKey(property.getName())) {
@@ -217,7 +227,10 @@ protected P returnPropertyIfBetterIdPropertyCandidateOrNull(P property) {
217227
* @see org.springframework.data.mapping.MutablePersistentEntity#addAssociation(org.springframework.data.mapping.model.Association)
218228
*/
219229
public void addAssociation(Association<P> association) {
220-
associations.add(association);
230+
231+
if (!associations.contains(association)) {
232+
associations.add(association);
233+
}
221234
}
222235

223236
/*
@@ -357,11 +370,15 @@ public <A extends Annotation> A findAnnotation(Class<A> annotationType) {
357370
return annotation;
358371
}
359372

360-
/* (non-Javadoc)
373+
/*
374+
* (non-Javadoc)
361375
* @see org.springframework.data.mapping.MutablePersistentEntity#verify()
362376
*/
363377
public void verify() {
364378

379+
if (comparator != null) {
380+
Collections.sort(properties, comparator);
381+
}
365382
}
366383

367384
/**

src/main/java/org/springframework/data/mapping/model/BeanWrapper.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2011-2014 by the original author(s).
2+
* Copyright 2011-2015 by the original author(s).
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.
@@ -116,15 +116,17 @@ public <S> S getProperty(PersistentProperty<?> property, Class<? extends S> type
116116
try {
117117

118118
Object obj = null;
119-
Method getter = property.getGetter();
120119

121120
if (!property.usePropertyAccess()) {
122121

123122
Field field = property.getField();
124123
ReflectionUtils.makeAccessible(field);
125124
obj = ReflectionUtils.getField(field, bean);
125+
}
126+
127+
Method getter = property.getGetter();
126128

127-
} else if (property.usePropertyAccess() && getter != null) {
129+
if (property.usePropertyAccess() && getter != null) {
128130

129131
ReflectionUtils.makeAccessible(getter);
130132
obj = ReflectionUtils.invokeMethod(getter, bean);

src/main/java/org/springframework/data/mapping/model/SimpleTypeHolder.java

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2011 by the original author(s).
2+
* Copyright 2011-2015 by the original author(s).
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.
@@ -20,6 +20,7 @@
2020
import java.util.HashSet;
2121
import java.util.Locale;
2222
import java.util.Set;
23+
import java.util.concurrent.CopyOnWriteArraySet;
2324

2425
import org.springframework.util.Assert;
2526

@@ -86,7 +87,7 @@ public SimpleTypeHolder() {
8687
public SimpleTypeHolder(Set<? extends Class<?>> customSimpleTypes, boolean registerDefaults) {
8788

8889
Assert.notNull(customSimpleTypes);
89-
this.simpleTypes = new HashSet<Class<?>>(customSimpleTypes);
90+
this.simpleTypes = new CopyOnWriteArraySet<Class<?>>(customSimpleTypes);
9091

9192
if (registerDefaults) {
9293
this.simpleTypes.addAll(DEFAULTS);
@@ -104,7 +105,7 @@ public SimpleTypeHolder(Set<? extends Class<?>> customSimpleTypes, SimpleTypeHol
104105
Assert.notNull(customSimpleTypes);
105106
Assert.notNull(source);
106107

107-
this.simpleTypes = new HashSet<Class<?>>(customSimpleTypes);
108+
this.simpleTypes = new CopyOnWriteArraySet<Class<?>>(customSimpleTypes);
108109
this.simpleTypes.addAll(source.simpleTypes);
109110
}
110111

@@ -118,12 +119,13 @@ public boolean isSimpleType(Class<?> type) {
118119

119120
Assert.notNull(type);
120121

121-
if (Object.class.equals(type)) {
122+
if (Object.class.equals(type) || simpleTypes.contains(type)) {
122123
return true;
123124
}
124125

125126
for (Class<?> clazz : simpleTypes) {
126127
if (clazz.isAssignableFrom(type)) {
128+
simpleTypes.add(type);
127129
return true;
128130
}
129131
}

0 commit comments

Comments
 (0)