diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefResolver.java index 88149ed65..18ae6fa58 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefResolver.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DbRefResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 the original author or authors. + * Copyright 2013-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,10 +24,16 @@ import com.mongodb.DBRef; * Used to resolve associations annotated with {@link org.springframework.data.mongodb.core.mapping.DBRef}. * * @author Thomas Darimont + * @author Oliver Gierke + * @since 1.4 */ public interface DbRefResolver { /** + * Resolves the given {@link DBRef} into an object of the given {@link MongoPersistentProperty}'s type. The method + * might return a proxy object for the {@link DBRef} or resolve it immediately. In both cases the + * {@link DbRefResolverCallback} will be used to obtain the actual backing object. + * * @param property will never be {@literal null}. * @param dbref the {@link DBRef} to resolve. * @param callback will never be {@literal null}. diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolver.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolver.java index cf4974b0d..5fa81c952 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolver.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/DefaultDbRefResolver.java @@ -51,6 +51,7 @@ import com.mongodb.DBRef; * * @author Thomas Darimont * @author Oliver Gierke + * @since 1.4 */ public class DefaultDbRefResolver implements DbRefResolver { @@ -109,7 +110,7 @@ public class DefaultDbRefResolver implements DbRefResolver { * eventually resolve the value of the property. * * @param property must not be {@literal null}. - * @param dbref + * @param dbref can be {@literal null}. * @param callback must not be {@literal null}. * @return */ @@ -144,7 +145,9 @@ public class DefaultDbRefResolver implements DbRefResolver { } /** - * @param property + * Returns whether the property shall be resolved lazily. + * + * @param property must not be {@literal null}. * @return */ private boolean isLazyDbRef(MongoPersistentProperty property) { @@ -157,12 +160,12 @@ public class DefaultDbRefResolver implements DbRefResolver { * guaranteed to be performed only once. * * @author Thomas Darimont + * @author Oliver Gierke */ static class LazyLoadingInterceptor implements MethodInterceptor, org.springframework.cglib.proxy.MethodInterceptor, Serializable { - private static final Method initializeMethod; - private static final Method toDBRefMethod; + private static final Method INITIALIZE_METHOD, TO_DBREF_METHOD; private final DbRefResolverCallback callback; private final MongoPersistentProperty property; @@ -174,8 +177,8 @@ public class DefaultDbRefResolver implements DbRefResolver { static { try { - initializeMethod = LazyLoadingProxy.class.getMethod("initialize"); - toDBRefMethod = LazyLoadingProxy.class.getMethod("toDBRef"); + INITIALIZE_METHOD = LazyLoadingProxy.class.getMethod("initialize"); + TO_DBREF_METHOD = LazyLoadingProxy.class.getMethod("toDBRef"); } catch (Exception e) { throw new RuntimeException(e); } @@ -186,7 +189,7 @@ public class DefaultDbRefResolver implements DbRefResolver { * {@link PersistenceExceptionTranslator} and {@link DbRefResolverCallback}. * * @param property must not be {@literal null}. - * @param dbref + * @param dbref can be {@literal null}. * @param callback must not be {@literal null}. */ public LazyLoadingInterceptor(MongoPersistentProperty property, DBRef dbref, @@ -209,11 +212,11 @@ public class DefaultDbRefResolver implements DbRefResolver { @Override public Object invoke(MethodInvocation invocation) throws Throwable { - if (invocation.getMethod().equals(initializeMethod)) { + if (invocation.getMethod().equals(INITIALIZE_METHOD)) { return ensureResolved(); } - if (invocation.getMethod().equals(toDBRefMethod)) { + if (invocation.getMethod().equals(TO_DBREF_METHOD)) { return this.dbref; } @@ -227,17 +230,22 @@ public class DefaultDbRefResolver implements DbRefResolver { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { - if (method.equals(initializeMethod)) { + if (INITIALIZE_METHOD.equals(method)) { return ensureResolved(); } - if (method.equals(toDBRefMethod)) { + if (TO_DBREF_METHOD.equals(method)) { return this.dbref; } return ReflectionUtils.isObjectMethod(method) ? method.invoke(obj, args) : method.invoke(ensureResolved(), args); } + /** + * Will trigger the resolution if the proxy is not resolved already or return a previously resolved result. + * + * @return + */ private Object ensureResolved() { if (!resolved) { @@ -248,16 +256,28 @@ public class DefaultDbRefResolver implements DbRefResolver { return this.result; } + /** + * Callback method for serialization. + * + * @param out + * @throws IOException + */ private void writeObject(ObjectOutputStream out) throws IOException { ensureResolved(); out.writeObject(this.result); } + /** + * Callback method for deserialization. + * + * @param in + * @throws IOException + */ private void readObject(ObjectInputStream in) throws IOException { try { - this.resolved = true; // Object is guaranteed to be resolved after serializations + this.resolved = true; this.result = in.readObject(); } catch (ClassNotFoundException e) { throw new LazyLoadingException("Could not deserialize result", e); @@ -265,6 +285,8 @@ public class DefaultDbRefResolver implements DbRefResolver { } /** + * Resolves the proxy into its backing object. + * * @return */ private synchronized Object resolve() { @@ -284,14 +306,6 @@ public class DefaultDbRefResolver implements DbRefResolver { return result; } - - public boolean isResolved() { - return resolved; - } - - public Object getResult() { - return result; - } } /** diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java index 2d65ca9f8..00a78f1b7 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/convert/MappingMongoConverter.java @@ -262,11 +262,11 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App entity.doWithAssociations(new AssociationHandler() { public void doWithAssociation(Association association) { - MongoPersistentProperty inverseProp = association.getInverse(); + MongoPersistentProperty property = association.getInverse(); - Object value = dbo.get(inverseProp.getName()); + Object value = dbo.get(property.getName()); DBRef dbref = value instanceof DBRef ? (DBRef) value : null; - Object obj = dbRefResolver.resolveDbRef(inverseProp, dbref, new DbRefResolverCallback() { + Object obj = dbRefResolver.resolveDbRef(property, dbref, new DbRefResolverCallback() { @Override public Object resolve(MongoPersistentProperty property) { @@ -274,7 +274,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App } }); - wrapper.setProperty(inverseProp, obj); + wrapper.setProperty(property, obj); } }); @@ -446,6 +446,7 @@ public class MappingMongoConverter extends AbstractMongoConverter implements App } dbRefObj = dbRefObj != null ? dbRefObj : createDBRef(obj, prop); + if (null != dbRefObj) { accessor.put(prop, dbRefObj); return; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java index 7d626ab37..ea7408538 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/DbRefMappingMongoConverterUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 the original author or authors. + * Copyright 2013-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -50,6 +50,8 @@ import com.mongodb.DBObject; import com.mongodb.DBRef; /** + * Unit tests dor {@link DbRefMappingMongoConverterUnitTests}. + * * @author Oliver Gierke */ @RunWith(MockitoJUnitRunner.class) @@ -345,6 +347,7 @@ public class DbRefMappingMongoConverterUnitTests { } } + @SuppressWarnings("serial") static class LazyDbRefTargetWithPeristenceConstructor extends LazyDbRefTarget { boolean persistenceConstructorCalled; @@ -362,6 +365,7 @@ public class DbRefMappingMongoConverterUnitTests { } } + @SuppressWarnings("serial") static class LazyDbRefTargetWithPeristenceConstructorWithoutDefaultConstructor extends LazyDbRefTarget { boolean persistenceConstructorCalled; diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/LazyLoadingTestUtils.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/LazyLoadingTestUtils.java index 2d5b6c690..d6de71ff1 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/LazyLoadingTestUtils.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/convert/LazyLoadingTestUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 the original author or authors. + * Copyright 2013-2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,6 +21,7 @@ import static org.junit.Assert.*; import org.springframework.aop.framework.Advised; import org.springframework.cglib.proxy.Factory; import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver.LazyLoadingInterceptor; +import org.springframework.test.util.ReflectionTestUtils; /** * Utility class to test proxy handling for lazy loading. @@ -39,8 +40,8 @@ public class LazyLoadingTestUtils { public static void assertProxyIsResolved(Object target, boolean expected) { LazyLoadingInterceptor interceptor = extractInterceptor(target); - assertThat(interceptor.isResolved(), is(expected)); - assertThat(interceptor.getResult(), is(expected ? notNullValue() : nullValue())); + assertThat(ReflectionTestUtils.getField(interceptor, "resolved"), is((Object) expected)); + assertThat(ReflectionTestUtils.getField(interceptor, "result"), is(expected ? notNullValue() : nullValue())); } private static LazyLoadingInterceptor extractInterceptor(Object proxy) {