diff --git a/spring-data-mongodb/pom.xml b/spring-data-mongodb/pom.xml index 94363c698..f9fa823df 100644 --- a/spring-data-mongodb/pom.xml +++ b/spring-data-mongodb/pom.xml @@ -18,11 +18,12 @@ 1.0.0.GA 1.3 + 1.5 - + org.springframework spring-tx @@ -50,7 +51,7 @@ spring-expression - + ${project.groupId} spring-data-commons @@ -144,6 +145,12 @@ test + + nl.jqno.equalsverifier + equalsverifier + ${equalsverifier} + test + diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/BasicQuery.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/BasicQuery.java index d278ec46b..ac36d0de7 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/BasicQuery.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/BasicQuery.java @@ -15,6 +15,8 @@ */ package org.springframework.data.mongodb.core.query; +import static org.springframework.util.ObjectUtils.*; + import com.mongodb.BasicDBObject; import com.mongodb.DBObject; import com.mongodb.util.JSON; @@ -25,6 +27,7 @@ import com.mongodb.util.JSON; * @author Thomas Risberg * @author Oliver Gierke * @author Christoph Strobl + * @author Thomas Darimont */ public class BasicQuery extends Query { @@ -97,4 +100,42 @@ public class BasicQuery extends Query { protected void setFieldsObject(DBObject fieldsObject) { this.fieldsObject = fieldsObject; } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.query.Query#equals(java.lang.Object) + */ + @Override + public boolean equals(Object o) { + + if (this == o) { + return true; + } + + if (!(o instanceof BasicQuery)) { + return false; + } + + BasicQuery that = (BasicQuery) o; + + return querySettingsEquals(that) && // + nullSafeEquals(fieldsObject, that.fieldsObject) && // + nullSafeEquals(queryObject, that.queryObject) && // + nullSafeEquals(sortObject, that.sortObject); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.mongodb.core.query.Query#hashCode() + */ + @Override + public int hashCode() { + + int result = super.hashCode(); + result = 31 * result + nullSafeHashCode(queryObject); + result = 31 * result + nullSafeHashCode(fieldsObject); + result = 31 * result + nullSafeHashCode(sortObject); + + return result; + } } diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java index 9156a342a..f400baefe 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Query.java @@ -385,12 +385,21 @@ public class Query { return false; } - Query that = (Query) obj; + return querySettingsEquals((Query) obj); + } + + /** + * Tests whether the settings of the given {@link Query} are equal to this query. + * + * @param that + * @return + */ + protected boolean querySettingsEquals(Query that) { boolean criteriaEqual = this.criteria.equals(that.criteria); - boolean fieldsEqual = this.fieldSpec == null ? that.fieldSpec == null : this.fieldSpec.equals(that.fieldSpec); - boolean sortEqual = this.sort == null ? that.sort == null : this.sort.equals(that.sort); - boolean hintEqual = this.hint == null ? that.hint == null : this.hint.equals(that.hint); + boolean fieldsEqual = nullSafeEquals(this.fieldSpec, that.fieldSpec); + boolean sortEqual = nullSafeEquals(this.sort, that.sort); + boolean hintEqual = nullSafeEquals(this.hint, that.hint); boolean skipEqual = this.skip == that.skip; boolean limitEqual = this.limit == that.limit; boolean metaEqual = nullSafeEquals(this.meta, that.meta); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/BasicQueryUnitTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/BasicQueryUnitTests.java index 5e4f3317e..1890267c3 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/BasicQueryUnitTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/BasicQueryUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2012 the original author or authors. + * Copyright 2011-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. @@ -18,6 +18,8 @@ package org.springframework.data.mongodb.core.query; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; import static org.springframework.data.mongodb.core.query.Criteria.*; +import nl.jqno.equalsverifier.EqualsVerifier; +import nl.jqno.equalsverifier.Warning; import org.junit.Test; import org.springframework.data.domain.Sort.Direction; @@ -29,6 +31,7 @@ import com.mongodb.DBObject; * Unit tests for {@link BasicQuery}. * * @author Oliver Gierke + * @author Thomas Darimont */ public class BasicQueryUnitTests { @@ -58,4 +61,80 @@ public class BasicQueryUnitTests { sortReference.put("lastname", 1); assertThat(query.getSortObject(), is(sortReference)); } + + /** + * @see DATAMONGO-1093 + */ + @Test + public void equalsContract() { + + BasicQuery query1 = new BasicQuery("{ \"name\" : \"Thomas\"}", "{\"name\":1, \"age\":1}"); + query1.setSortObject(new BasicDBObject("name", -1)); + + BasicQuery query2 = new BasicQuery("{ \"name\" : \"Oliver\"}", "{\"name\":1, \"address\":1}"); + query2.setSortObject(new BasicDBObject("name", 1)); + + EqualsVerifier.forExamples(query1, query2) // + .withRedefinedSuperclass() // + .suppress(Warning.NONFINAL_FIELDS, Warning.NULL_FIELDS, Warning.STRICT_INHERITANCE) // + .verify(); + } + + /** + * @see DATAMONGO-1093 + */ + @Test + public void handlesEqualsAndHashCodeCorrectlyForExactCopies() { + + String qry = "{ \"name\" : \"Thomas\"}"; + String fields = "{\"name\":1, \"age\":1}"; + + BasicQuery query1 = new BasicQuery(qry, fields); + query1.setSortObject(new BasicDBObject("name", -1)); + + BasicQuery query2 = new BasicQuery(qry, fields); + query2.setSortObject(new BasicDBObject("name", -1)); + + assertThat(query1, is(equalTo(query1))); + assertThat(query1, is(equalTo(query2))); + assertThat(query1.hashCode(), is(query2.hashCode())); + } + + /** + * @see DATAMONGO-1093 + */ + @Test + public void handlesEqualsAndHashCodeCorrectlyWhenBasicQuerySettingsDiffer() { + + String qry = "{ \"name\" : \"Thomas\"}"; + String fields = "{\"name\":1, \"age\":1}"; + + BasicQuery query1 = new BasicQuery(qry, fields); + query1.setSortObject(new BasicDBObject("name", -1)); + + BasicQuery query2 = new BasicQuery(qry, fields); + query2.setSortObject(new BasicDBObject("name", 1)); + + assertThat(query1, is(not(equalTo(query2)))); + assertThat(query1.hashCode(), is(not(query2.hashCode()))); + } + + /** + * @see DATAMONGO-1093 + */ + @Test + public void handlesEqualsAndHashCodeCorrectlyWhenQuerySettingsDiffer() { + + String qry = "{ \"name\" : \"Thomas\"}"; + String fields = "{\"name\":1, \"age\":1}"; + + BasicQuery query1 = new BasicQuery(qry, fields); + query1.getMeta().setComment("foo"); + + BasicQuery query2 = new BasicQuery(qry, fields); + query2.getMeta().setComment("bar"); + + assertThat(query1, is(not(equalTo(query2)))); + assertThat(query1.hashCode(), is(not(query2.hashCode()))); + } }