diff --git a/src/main/java/eu/dozd/mongo/EntityCodec.java b/src/main/java/eu/dozd/mongo/EntityCodec.java index 4a152bf..33ab5e7 100644 --- a/src/main/java/eu/dozd/mongo/EntityCodec.java +++ b/src/main/java/eu/dozd/mongo/EntityCodec.java @@ -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. @@ -23,6 +21,8 @@ class EntityCodec implements CollectibleCodec { private final DocumentCodec documentCodec; private final BsonTypeClassMap bsonTypeClassMap; private final CodecRegistry registry; + private final List> ignoredTypes = new LinkedList<>(); + private final Map> typeCache = new HashMap<>(); public EntityCodec(Class clazz, EntityInfo info) { this.clazz = clazz; @@ -128,14 +128,7 @@ private List readGenericList(BsonReader bsonReader, DecoderContext decode bsonReader.readStartArray(); List list = new ArrayList<>(); while (bsonReader.readBsonType() != BsonType.END_OF_DOCUMENT) { - Codec codec; - try { - codec = registry.get(valueClazz); - } catch (CodecConfigurationException | MongoMapperException e) { - // No other way to check without catching exception. - codec = null; - } - + Codec codec = getCodecForType(valueClazz); V decode; if (codec != null) { decode = codec.decode(bsonReader, decoderContext); @@ -161,14 +154,7 @@ public Document decodeDocument(BsonReader bsonReader, DecoderContext decoder bsonReader.readStartDocument(); while (bsonReader.readBsonType() != BsonType.END_OF_DOCUMENT) { String fieldName = bsonReader.readName(); - Codec codec; - try { - codec = registry.get(valueClazz); - } catch (CodecConfigurationException | MongoMapperException e) { - // No other way to check without catching exception. - codec = null; - } - + Codec codec = getCodecForType(valueClazz); V decode; if (codec != null) { decode = codec.decode(bsonReader, decoderContext); @@ -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); @@ -244,6 +225,21 @@ private Object readValue(final BsonReader reader, final DecoderContext decoderCo return registry.get(bsonTypeClassMap.get(bsonType)).decode(reader, decoderContext); } + private Codec getCodecForType(Class 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 readList(final BsonReader reader, final DecoderContext decoderContext) { reader.readStartArray(); List list = new ArrayList<>(); diff --git a/src/main/java/eu/dozd/mongo/EntityInfo.java b/src/main/java/eu/dozd/mongo/EntityInfo.java index b6ba1c1..025ceae 100644 --- a/src/main/java/eu/dozd/mongo/EntityInfo.java +++ b/src/main/java/eu/dozd/mongo/EntityInfo.java @@ -18,6 +18,7 @@ class EntityInfo { protected final PropertyDescriptor[] descriptors; private final Map fields = new HashMap<>(); private final String entityName; + private final Map> typeCache = new HashMap<>(); EntityInfo(Class clazz) { entityName = clazz.getCanonicalName(); @@ -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)) { @@ -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."); } @@ -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)) { @@ -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."); } @@ -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) { diff --git a/src/main/java/eu/dozd/mongo/MapperCodecProvider.java b/src/main/java/eu/dozd/mongo/MapperCodecProvider.java index 2e60ef4..1d7a95c 100644 --- a/src/main/java/eu/dozd/mongo/MapperCodecProvider.java +++ b/src/main/java/eu/dozd/mongo/MapperCodecProvider.java @@ -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 entityMap = new HashMap<>(); diff --git a/src/test/java/eu/dozd/mongo/EntityInfoTest.java b/src/test/java/eu/dozd/mongo/EntityInfoTest.java index d685277..cd5fc56 100644 --- a/src/test/java/eu/dozd/mongo/EntityInfoTest.java +++ b/src/test/java/eu/dozd/mongo/EntityInfoTest.java @@ -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")); + } }