Skip to content

Commit

Permalink
Performance improvement for entity decoding.
Browse files Browse the repository at this point in the history
Signed-off-by: Zdenek Dolezal <[email protected]>
  • Loading branch information
dozd committed Apr 22, 2017
1 parent 33cb007 commit 1be2cec
Show file tree
Hide file tree
Showing 4 changed files with 49 additions and 29 deletions.
48 changes: 22 additions & 26 deletions src/main/java/eu/dozd/mongo/EntityCodec.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@
import org.bson.codecs.configuration.CodecRegistry;
import org.bson.types.ObjectId;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.*;

/**
* Codec used to decode and encode registered entities.
Expand All @@ -23,6 +21,8 @@ class EntityCodec<T> implements CollectibleCodec<T> {
private final DocumentCodec documentCodec;
private final BsonTypeClassMap bsonTypeClassMap;
private final CodecRegistry registry;
private final List<Class<?>> ignoredTypes = new LinkedList<>();
private final Map<String, Class<?>> typeCache = new HashMap<>();

public EntityCodec(Class<T> clazz, EntityInfo info) {
this.clazz = clazz;
Expand Down Expand Up @@ -128,14 +128,7 @@ private <V> List<V> readGenericList(BsonReader bsonReader, DecoderContext decode
bsonReader.readStartArray();
List<V> list = new ArrayList<>();
while (bsonReader.readBsonType() != BsonType.END_OF_DOCUMENT) {
Codec<V> codec;
try {
codec = registry.get(valueClazz);
} catch (CodecConfigurationException | MongoMapperException e) {
// No other way to check without catching exception.
codec = null;
}

Codec<V> codec = getCodecForType(valueClazz);
V decode;
if (codec != null) {
decode = codec.decode(bsonReader, decoderContext);
Expand All @@ -161,14 +154,7 @@ public <V> Document decodeDocument(BsonReader bsonReader, DecoderContext decoder
bsonReader.readStartDocument();
while (bsonReader.readBsonType() != BsonType.END_OF_DOCUMENT) {
String fieldName = bsonReader.readName();
Codec<V> codec;
try {
codec = registry.get(valueClazz);
} catch (CodecConfigurationException | MongoMapperException e) {
// No other way to check without catching exception.
codec = null;
}

Codec<V> codec = getCodecForType(valueClazz);
V decode;
if (codec != null) {
decode = codec.decode(bsonReader, decoderContext);
Expand Down Expand Up @@ -218,14 +204,9 @@ private Object readValue(final BsonReader reader, final DecoderContext decoderCo
}

Codec<?> codec = null;
if (fieldName != null && !fieldName.equals(ID_FIELD)) {
if (fieldName != null && !fieldName.equals(ID_FIELD) && info.hasField(fieldName)) {
// Check whether there is special codec for given field.
try {
codec = registry.get(info.getFieldType(fieldName));
} catch (CodecConfigurationException | MongoMapperException e) {
// No other way to check without catching exception.
codec = null;
}
codec = getCodecForType(info.getFieldType(fieldName));

if (codec != null) {
return codec.decode(reader, decoderContext);
Expand All @@ -244,6 +225,21 @@ private Object readValue(final BsonReader reader, final DecoderContext decoderCo
return registry.get(bsonTypeClassMap.get(bsonType)).decode(reader, decoderContext);
}

private <V> Codec<V> getCodecForType(Class<V> fieldType) {
if (ignoredTypes.contains(fieldType)) {
return null;
}

try {
return registry.get(fieldType);
} catch (CodecConfigurationException | MongoMapperException e) {
// No other way to check without catching exception.
// Cache types without codec to improve performance
ignoredTypes.add(fieldType);
}
return null;
}

private List<Object> readList(final BsonReader reader, final DecoderContext decoderContext) {
reader.readStartArray();
List<Object> list = new ArrayList<>();
Expand Down
22 changes: 20 additions & 2 deletions src/main/java/eu/dozd/mongo/EntityInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ class EntityInfo {
protected final PropertyDescriptor[] descriptors;
private final Map<String, PropertyDescriptor> fields = new HashMap<>();
private final String entityName;
private final Map<String, Class<?>> typeCache = new HashMap<>();

EntityInfo(Class<?> clazz) {
entityName = clazz.getCanonicalName();
Expand Down Expand Up @@ -76,10 +77,15 @@ Class<?> getGenericListValueType(String fieldName) {
if (!fields.containsKey(fieldName)) {
throw new IllegalArgumentException("Field " + fieldName + " not found.");
}

if (!isGenericList(fieldName)) {
throw new MongoMapperException("Field " + fieldName + " is not a generic list.");
}

if (typeCache.containsKey(fieldName)) {
return typeCache.get(fieldName);
}

PropertyDescriptor pd = getField(fieldName);
Type type = pd.getReadMethod().getGenericReturnType();
if (!(type instanceof ParameterizedType)) {
Expand All @@ -90,7 +96,9 @@ Class<?> getGenericListValueType(String fieldName) {
String className = pt.getActualTypeArguments()[0].getTypeName();

try {
return Class.forName(className);
Class<?> aClass = Class.forName(className);
typeCache.put(fieldName, aClass);
return aClass;
} catch (ClassNotFoundException e) {
throw new MongoMapperException("Class " + className + " not found.");
}
Expand All @@ -105,6 +113,10 @@ Class<?> getMapValueType(String fieldName) {
throw new MongoMapperException("Field " + fieldName + " is not a map.");
}

if (typeCache.containsKey(fieldName)) {
return typeCache.get(fieldName);
}

PropertyDescriptor pd = getField(fieldName);
Type type = pd.getReadMethod().getGenericReturnType();
if (!(type instanceof ParameterizedType)) {
Expand All @@ -119,7 +131,9 @@ Class<?> getMapValueType(String fieldName) {
}

try {
return Class.forName(className);
Class<?> aClass = Class.forName(className);
typeCache.put(fieldName, aClass);
return aClass;
} catch (ClassNotFoundException e) {
throw new MongoMapperException("Class " + className + " not found.");
}
Expand All @@ -129,6 +143,10 @@ Class<?> getFieldType(String field) {
return getField(field).getPropertyType();
}

boolean hasField(String field) {
return fields.keySet().contains(field);
}

void setValue(Object o, String field, Object v) {
Method writeMethod = getField(field).getWriteMethod();
if (writeMethod == null) {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/eu/dozd/mongo/MapperCodecProvider.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import java.util.Map;

/**
* Mongo codec provider for mapped entites. Should be passed to Mongo configuration with other providers.
* Mongo codec provider for mapped entities. Should be passed to Mongo configuration with other providers.
*/
public class MapperCodecProvider implements CodecProvider {
private Map<Class, EntityInfo> entityMap = new HashMap<>();
Expand Down
6 changes: 6 additions & 0 deletions src/test/java/eu/dozd/mongo/EntityInfoTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,10 @@ public void testGetEntityName() throws Exception {
public void testEntityMapValue() throws Exception {
Assert.assertEquals(Integer.class, info.getMapValueType("map"));
}

@Test
public void testHasFields() throws Exception {
Assert.assertTrue(info.hasField("name"));
Assert.assertFalse(info.hasField("name2"));
}
}

0 comments on commit 1be2cec

Please sign in to comment.