diff --git a/pom.xml b/pom.xml
index 28e7260e25..f8d2694aab 100644
--- a/pom.xml
+++ b/pom.xml
@@ -5,7 +5,7 @@
org.springframework.data
spring-data-mongodb-parent
- 1.5.0.BUILD-SNAPSHOT
+ 1.5.0.DATAMONGO-884-SNAPSHOT
pom
Spring Data MongoDB
diff --git a/spring-data-mongodb-cross-store/pom.xml b/spring-data-mongodb-cross-store/pom.xml
index 69a19c150b..a2caf38f0d 100644
--- a/spring-data-mongodb-cross-store/pom.xml
+++ b/spring-data-mongodb-cross-store/pom.xml
@@ -6,7 +6,7 @@
org.springframework.data
spring-data-mongodb-parent
- 1.5.0.BUILD-SNAPSHOT
+ 1.5.0.DATAMONGO-884-SNAPSHOT
../pom.xml
@@ -48,7 +48,7 @@
org.springframework.data
spring-data-mongodb
- 1.5.0.BUILD-SNAPSHOT
+ 1.5.0.DATAMONGO-884-SNAPSHOT
diff --git a/spring-data-mongodb-distribution/pom.xml b/spring-data-mongodb-distribution/pom.xml
index 7302043a15..9a353bf88f 100644
--- a/spring-data-mongodb-distribution/pom.xml
+++ b/spring-data-mongodb-distribution/pom.xml
@@ -13,7 +13,7 @@
org.springframework.data
spring-data-mongodb-parent
- 1.5.0.BUILD-SNAPSHOT
+ 1.5.0.DATAMONGO-884-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb-log4j/pom.xml b/spring-data-mongodb-log4j/pom.xml
index 0415f6eb5e..df0124cc1d 100644
--- a/spring-data-mongodb-log4j/pom.xml
+++ b/spring-data-mongodb-log4j/pom.xml
@@ -5,7 +5,7 @@
org.springframework.data
spring-data-mongodb-parent
- 1.5.0.BUILD-SNAPSHOT
+ 1.5.0.DATAMONGO-884-SNAPSHOT
../pom.xml
diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml
index 6e84732769..03a9d4eaf7 100644
--- a/spring-data-mongodb/pom.xml
+++ b/spring-data-mongodb/pom.xml
@@ -11,7 +11,7 @@
org.springframework.data
spring-data-mongodb-parent
- 1.5.0.BUILD-SNAPSHOT
+ 1.5.0.DATAMONGO-884-SNAPSHOT
../pom.xml
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 e518dd8abf..90a6caab77 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
@@ -41,6 +41,7 @@
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
+import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
import com.mongodb.DB;
@@ -230,12 +231,81 @@ public Object intercept(Object obj, Method method, Object[] args, MethodProxy pr
return this.dbref;
}
- Object target = isObjectMethod(method) && Object.class.equals(method.getDeclaringClass()) ? obj
- : ensureResolved();
+ if (isObjectMethod(method) && Object.class.equals(method.getDeclaringClass())) {
+
+ if (ReflectionUtils.isToStringMethod(method)) {
+ return proxyToString(proxy);
+ }
+
+ if (ReflectionUtils.isEqualsMethod(method)) {
+ return proxyEquals(proxy, args[0]);
+ }
+
+ if (ReflectionUtils.isHashCodeMethod(method)) {
+ return proxyHashCode(proxy);
+ }
+ }
+
+ Object target = ensureResolved();
+
+ if (target == null) {
+ return null;
+ }
return method.invoke(target, args);
}
+ /**
+ * Returns a to string representation for the given {@code proxy}.
+ *
+ * @param proxy
+ * @return
+ */
+ private String proxyToString(Object proxy) {
+
+ StringBuilder description = new StringBuilder();
+ if (dbref != null) {
+ description.append(dbref.getRef());
+ description.append(":");
+ description.append(dbref.getId());
+ } else {
+ description.append(System.identityHashCode(proxy));
+ }
+ description.append("$").append(LazyLoadingProxy.class.getSimpleName());
+
+ return description.toString();
+ }
+
+ /**
+ * Returns the hashcode for the given {@code proxy}.
+ *
+ * @param proxy
+ * @return
+ */
+ private int proxyHashCode(Object proxy) {
+ return proxyToString(proxy).hashCode();
+ }
+
+ /**
+ * Performs an equality check for the given {@code proxy}.
+ *
+ * @param proxy
+ * @param that
+ * @return
+ */
+ private boolean proxyEquals(Object proxy, Object that) {
+
+ if (!(that instanceof LazyLoadingProxy)) {
+ return false;
+ }
+
+ if (that == proxy) {
+ return true;
+ }
+
+ return proxyToString(proxy).equals(that.toString());
+ }
+
/**
* Will trigger the resolution if the proxy is not resolved already or return a previously resolved result.
*
diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java
index c8ceb0306f..f3cc79f948 100644
--- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java
+++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/MongoTemplateTests.java
@@ -61,6 +61,7 @@
import org.springframework.data.mongodb.core.convert.CustomConversions;
import org.springframework.data.mongodb.core.convert.DbRefResolver;
import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver;
+import org.springframework.data.mongodb.core.convert.LazyLoadingProxy;
import org.springframework.data.mongodb.core.convert.MappingMongoConverter;
import org.springframework.data.mongodb.core.index.Index;
import org.springframework.data.mongodb.core.index.Index.Duplicates;
@@ -2550,6 +2551,35 @@ public void savingAndReassigningLazyLoadingProxies() {
assertThat(savedMessage.normalContent.text, is(content.text));
}
+ /**
+ * @see DATAMONGO-884
+ */
+ @Test
+ public void callingNonObjectMethodsOnLazyLoadingProxyShouldReturnNullIfUnderlyingDbrefWasDeletedInbetween() {
+
+ template.dropCollection(SomeTemplate.class);
+ template.dropCollection(SomeContent.class);
+
+ SomeContent content = new SomeContent();
+ content.id = "C1";
+ content.text = "BUBU";
+ template.save(content);
+
+ SomeTemplate tmpl = new SomeTemplate();
+ tmpl.id = "T1";
+ tmpl.content = content; // @DBRef(lazy=true) tmpl.content
+
+ template.save(tmpl);
+
+ SomeTemplate savedTmpl = template.findById(tmpl.id, SomeTemplate.class);
+
+ template.remove(content);
+
+ assertThat(savedTmpl.getContent().toString(), is("someContent:C1$LazyLoadingProxy"));
+ assertThat(savedTmpl.getContent(), is(instanceOf(LazyLoadingProxy.class)));
+ assertThat(savedTmpl.getContent().getText(), is(nullValue()));
+ }
+
static class DocumentWithDBRefCollection {
@Id public String id;
@@ -2730,7 +2760,7 @@ static class ObjectWithEnumValue {
EnumValue value;
}
- static class SomeTemplate {
+ public static class SomeTemplate {
String id;
@org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) SomeContent content;
@@ -2740,10 +2770,14 @@ public SomeContent getContent() {
}
}
- static class SomeContent {
+ public static class SomeContent {
String id;
String text;
+
+ public String getText() {
+ return text;
+ }
}
static class SomeMessage {
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 fc9914fd01..04c4a66eaf 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
@@ -310,6 +310,93 @@ public void lazyLoadingProxyForToStringObjectMethodOverridingDbref() {
assertProxyIsResolved(result.dbRefToToStringObjectMethodOverride, true);
}
+ /**
+ * @see DATAMONGO-884
+ */
+ @Test
+ public void callingToStringObjectMethodOnLazyLoadingDbrefShouldNotInitializeProxy() {
+
+ String id = "42";
+ String value = "bubu";
+ MappingMongoConverter converterSpy = spy(converter);
+ doReturn(new BasicDBObject("_id", id).append("value", value)).when(converterSpy).readRef((DBRef) any());
+
+ BasicDBObject dbo = new BasicDBObject();
+ WithObjectMethodOverrideLazyDbRefs lazyDbRefs = new WithObjectMethodOverrideLazyDbRefs();
+ lazyDbRefs.dbRefToPlainObject = new LazyDbRefTarget(id, value);
+ converterSpy.write(lazyDbRefs, dbo);
+
+ WithObjectMethodOverrideLazyDbRefs result = converterSpy.read(WithObjectMethodOverrideLazyDbRefs.class, dbo);
+
+ assertThat(result.dbRefToPlainObject, is(notNullValue()));
+ assertProxyIsResolved(result.dbRefToPlainObject, false);
+
+ // calling Object#toString does not initialize the proxy.
+ String proxyString = result.dbRefToPlainObject.toString();
+ assertThat(proxyString, is("lazyDbRefTarget" + ":" + id + "$LazyLoadingProxy"));
+ assertProxyIsResolved(result.dbRefToPlainObject, false);
+
+ // calling another method not declared on object triggers proxy initialization.
+ assertThat(result.dbRefToPlainObject.getValue(), is(value));
+ assertProxyIsResolved(result.dbRefToPlainObject, true);
+ }
+
+ /**
+ * @see DATAMONGO-884
+ */
+ @Test
+ public void equalsObjectMethodOnLazyLoadingDbrefShouldNotInitializeProxy() {
+
+ String id = "42";
+ String value = "bubu";
+ MappingMongoConverter converterSpy = spy(converter);
+ doReturn(new BasicDBObject("_id", id).append("value", value)).when(converterSpy).readRef((DBRef) any());
+
+ BasicDBObject dbo = new BasicDBObject();
+ WithObjectMethodOverrideLazyDbRefs lazyDbRefs = new WithObjectMethodOverrideLazyDbRefs();
+ lazyDbRefs.dbRefToPlainObject = new LazyDbRefTarget(id, value);
+ lazyDbRefs.dbRefToToStringObjectMethodOverride = new ToStringObjectMethodOverrideLazyDbRefTarget(id, value);
+ converterSpy.write(lazyDbRefs, dbo);
+
+ WithObjectMethodOverrideLazyDbRefs result = converterSpy.read(WithObjectMethodOverrideLazyDbRefs.class, dbo);
+
+ assertThat(result.dbRefToPlainObject, is(notNullValue()));
+ assertProxyIsResolved(result.dbRefToPlainObject, false);
+
+ assertThat(result.dbRefToPlainObject, is(equalTo(result.dbRefToPlainObject)));
+ assertThat(result.dbRefToPlainObject, is(not(equalTo(null))));
+ assertThat(result.dbRefToPlainObject, is(not(equalTo((Object) lazyDbRefs.dbRefToToStringObjectMethodOverride))));
+
+ assertProxyIsResolved(result.dbRefToPlainObject, false);
+ }
+
+ /**
+ * @see DATAMONGO-884
+ */
+ @Test
+ public void hashcodeObjectMethodOnLazyLoadingDbrefShouldNotInitializeProxy() {
+
+ String id = "42";
+ String value = "bubu";
+ MappingMongoConverter converterSpy = spy(converter);
+ doReturn(new BasicDBObject("_id", id).append("value", value)).when(converterSpy).readRef((DBRef) any());
+
+ BasicDBObject dbo = new BasicDBObject();
+ WithObjectMethodOverrideLazyDbRefs lazyDbRefs = new WithObjectMethodOverrideLazyDbRefs();
+ lazyDbRefs.dbRefToPlainObject = new LazyDbRefTarget(id, value);
+ lazyDbRefs.dbRefToToStringObjectMethodOverride = new ToStringObjectMethodOverrideLazyDbRefTarget(id, value);
+ converterSpy.write(lazyDbRefs, dbo);
+
+ WithObjectMethodOverrideLazyDbRefs result = converterSpy.read(WithObjectMethodOverrideLazyDbRefs.class, dbo);
+
+ assertThat(result.dbRefToPlainObject, is(notNullValue()));
+ assertProxyIsResolved(result.dbRefToPlainObject, false);
+
+ assertThat(result.dbRefToPlainObject.hashCode(), is(311365444));
+
+ assertProxyIsResolved(result.dbRefToPlainObject, false);
+ }
+
/**
* @see DATAMONGO-884
*/
@@ -513,6 +600,7 @@ public boolean equals(Object obj) {
static class WithObjectMethodOverrideLazyDbRefs {
+ @org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) LazyDbRefTarget dbRefToPlainObject;
@org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) ToStringObjectMethodOverrideLazyDbRefTarget dbRefToToStringObjectMethodOverride;
@org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) EqualsAndHashCodeObjectMethodOverrideLazyDbRefTarget dbRefEqualsAndHashcodeObjectMethodOverride2;
@org.springframework.data.mongodb.core.mapping.DBRef(lazy = true) EqualsAndHashCodeObjectMethodOverrideLazyDbRefTarget dbRefEqualsAndHashcodeObjectMethodOverride1;