/** * Writes the given {@link Map} to the given {@link DBObject} considering the given {@link * TypeInformation}. * * @param obj must not be {@literal null}. * @param dbo must not be {@literal null}. * @param propertyType must not be {@literal null}. * @return */ protected DBObject writeMapInternal( Map<Object, Object> obj, DBObject dbo, TypeInformation<?> propertyType) { for (Map.Entry<Object, Object> entry : obj.entrySet()) { Object key = entry.getKey(); Object val = entry.getValue(); if (conversions.isSimpleType(key.getClass())) { String simpleKey = prepareMapKey(key); if (val == null || conversions.isSimpleType(val.getClass())) { writeSimpleInternal(val, dbo, simpleKey); } else if (val instanceof Collection || val.getClass().isArray()) { dbo.put( simpleKey, writeCollectionInternal( asCollection(val), propertyType.getMapValueType(), new BasicDBList())); } else { DBObject newDbo = new BasicDBObject(); TypeInformation<?> valueTypeInfo = propertyType.isMap() ? propertyType.getMapValueType() : ClassTypeInformation.OBJECT; writeInternal(val, newDbo, valueTypeInfo); dbo.put(simpleKey, newDbo); } } else { throw new MappingException("Cannot use a complex object as a key value."); } } return dbo; }
/** * Internal write conversion method which should be used for nested invocations. * * @param obj * @param dbo */ @SuppressWarnings("unchecked") protected void writeInternal( final Object obj, final DBObject dbo, final TypeInformation<?> typeHint) { if (null == obj) { return; } Class<?> entityType = obj.getClass(); Class<?> customTarget = conversions.getCustomWriteTarget(entityType, DBObject.class); if (customTarget != null) { DBObject result = conversionService.convert(obj, DBObject.class); dbo.putAll(result); return; } if (Map.class.isAssignableFrom(entityType)) { writeMapInternal((Map<Object, Object>) obj, dbo, ClassTypeInformation.MAP); return; } if (Collection.class.isAssignableFrom(entityType)) { writeCollectionInternal((Collection<?>) obj, ClassTypeInformation.LIST, (BasicDBList) dbo); return; } MongoPersistentEntity<?> entity = mappingContext.getPersistentEntity(entityType); writeInternal(obj, dbo, entity); addCustomTypeKeyIfNecessary(typeHint, obj, dbo); }
/** * Root entry method into write conversion. Adds a type discriminator to the {@link DBObject}. * Shouldn't be called for nested conversions. * * @see org.springframework.data.mongodb.core.core.convert.MongoWriter#write(java.lang.Object, * com.mongodb.DBObject) */ public void write(final Object obj, final DBObject dbo) { if (null == obj) { return; } Class<?> entityType = obj.getClass(); boolean handledByCustomConverter = conversions.getCustomWriteTarget(entityType, DBObject.class) != null; TypeInformation<? extends Object> type = ClassTypeInformation.from(entityType); if (!handledByCustomConverter && !(dbo instanceof BasicDBList)) { typeMapper.writeType(type, dbo); } Object target = obj instanceof LazyLoadingProxy ? ((LazyLoadingProxy) obj).getTarget() : obj; writeInternal(target, dbo, type); }
/** * Populates the given {@link BasicDBList} with values from the given {@link Collection}. * * @param source the collection to create a {@link BasicDBList} for, must not be {@literal null}. * @param type the {@link TypeInformation} to consider or {@literal null} if unknown. * @param sink the {@link BasicDBList} to write to. * @return */ private BasicDBList writeCollectionInternal( Collection<?> source, TypeInformation<?> type, BasicDBList sink) { TypeInformation<?> componentType = type == null ? null : type.getComponentType(); for (Object element : source) { Class<?> elementType = element == null ? null : element.getClass(); if (elementType == null || conversions.isSimpleType(elementType)) { sink.add(getPotentiallyConvertedSimpleWrite(element)); } else if (element instanceof Collection || elementType.isArray()) { sink.add(writeCollectionInternal(asCollection(element), componentType, new BasicDBList())); } else { BasicDBObject propDbObj = new BasicDBObject(); writeInternal(element, propDbObj, componentType); sink.add(propDbObj); } } return sink; }
@SuppressWarnings({"unchecked"}) protected void writePropertyInternal(Object obj, DBObject dbo, MongoPersistentProperty prop) { if (obj == null) { return; } DBObjectAccessor accessor = new DBObjectAccessor(dbo); TypeInformation<?> valueType = ClassTypeInformation.from(obj.getClass()); TypeInformation<?> type = prop.getTypeInformation(); if (valueType.isCollectionLike()) { DBObject collectionInternal = createCollection(asCollection(obj), prop); accessor.put(prop, collectionInternal); return; } if (valueType.isMap()) { DBObject mapDbObj = createMap((Map<Object, Object>) obj, prop); accessor.put(prop, mapDbObj); return; } if (prop.isDbReference()) { DBRef dbRefObj = null; /* * If we already have a LazyLoadingProxy, we use it's cached DBRef value instead of * unnecessarily initializing it only to convert it to a DBRef a few instructions later. */ if (obj instanceof LazyLoadingProxy) { dbRefObj = ((LazyLoadingProxy) obj).toDBRef(); } dbRefObj = dbRefObj != null ? dbRefObj : createDBRef(obj, prop); if (null != dbRefObj) { accessor.put(prop, dbRefObj); return; } } /* * If we have a LazyLoadingProxy we make sure it is initialized first. */ if (obj instanceof LazyLoadingProxy) { obj = ((LazyLoadingProxy) obj).getTarget(); } // Lookup potential custom target type Class<?> basicTargetType = conversions.getCustomWriteTarget(obj.getClass(), null); if (basicTargetType != null) { accessor.put(prop, conversionService.convert(obj, basicTargetType)); return; } Object existingValue = accessor.get(prop); BasicDBObject propDbObj = existingValue instanceof BasicDBObject ? (BasicDBObject) existingValue : new BasicDBObject(); addCustomTypeKeyIfNecessary(ClassTypeInformation.from(prop.getRawType()), obj, propDbObj); MongoPersistentEntity<?> entity = isSubtype(prop.getType(), obj.getClass()) ? mappingContext.getPersistentEntity(obj.getClass()) : mappingContext.getPersistentEntity(type); writeInternal(obj, propDbObj, entity); accessor.put(prop, propDbObj); }