DATAKV-85 - Ported key-value infrastructure from Spring Data Commons.

This commit is contained in:
Oliver Gierke
2014-11-27 12:44:30 +01:00
parent 2ebac47e31
commit 42a13a7403
56 changed files with 7964 additions and 0 deletions

View File

@@ -0,0 +1,104 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.keyvalue;
import java.io.Serializable;
import org.springframework.data.annotation.Id;
import org.springframework.util.ObjectUtils;
import com.mysema.query.annotations.QueryEntity;
/**
* @author Christoph Strobl
*/
@QueryEntity
public class Person implements Serializable {
private @Id String id;
private String firstname;
private int age;
public Person(String firstname, int age) {
super();
this.firstname = firstname;
this.age = age;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
@Override
public String toString() {
return "Person [id=" + id + ", firstname=" + firstname + ", age=" + age + "]";
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + age;
result = prime * result + ObjectUtils.nullSafeHashCode(this.firstname);
result = prime * result + ObjectUtils.nullSafeHashCode(this.id);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Person)) {
return false;
}
Person other = (Person) obj;
if (!ObjectUtils.nullSafeEquals(this.id, other.id)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(this.firstname, other.firstname)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(this.age, other.age)) {
return false;
}
return true;
}
}

View File

@@ -0,0 +1,132 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.keyvalue.core;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.springframework.data.keyvalue.core.KeySpaceUtils.*;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.junit.Test;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.keyvalue.annotation.KeySpace;
import org.springframework.data.keyvalue.core.KeyValueTemplateUnitTests.AliasedEntity;
import org.springframework.data.keyvalue.core.KeyValueTemplateUnitTests.ClassWithDirectKeySpaceAnnotation;
import org.springframework.data.keyvalue.core.KeyValueTemplateUnitTests.EntityWithPersistentAnnotation;
import org.springframework.data.keyvalue.core.KeyValueTemplateUnitTests.Foo;
/**
* Unit tests for {@link KeySpaceUtils}.
*
* @author Christoph Strobl
* @author Oliver Gierke
*/
public class KeySpaceUtilsUnitTests {
/**
* @see DATACMNS-525
*/
@Test
public void shouldResolveKeySpaceDefaultValueCorrectly() {
assertThat(getKeySpace(EntityWithDefaultKeySpace.class), is((Object) "daenerys"));
}
/**
* @see DATACMNS-525
*/
@Test
public void shouldResolveKeySpaceCorrectly() {
assertThat(getKeySpace(EntityWithSetKeySpace.class), is((Object) "viserys"));
}
/**
* @see DATACMNS-525
*/
@Test
public void shouldReturnNullWhenNoKeySpaceFoundOnComposedPersistentAnnotation() {
assertThat(getKeySpace(AliasedEntity.class), nullValue());
}
/**
* @see DATACMNS-525
*/
@Test
public void shouldReturnNullWhenPersistentIsFoundOnNonComposedAnnotation() {
assertThat(getKeySpace(EntityWithPersistentAnnotation.class), nullValue());
}
/**
* @see DATACMNS-525
*/
@Test
public void shouldReturnNullWhenPersistentIsNotFound() {
assertThat(getKeySpace(Foo.class), nullValue());
}
/**
* @see DATACMNS-525
*/
@Test
public void shouldResolveInheritedKeySpaceCorrectly() {
assertThat(getKeySpace(EntityWithInheritedKeySpace.class), is((Object) "viserys"));
}
/**
* @see DATACMNS-525
*/
@Test
public void shouldResolveDirectKeySpaceAnnotationCorrectly() {
assertThat(getKeySpace(ClassWithDirectKeySpaceAnnotation.class), is((Object) "rhaegar"));
}
@PersistentAnnotationWithExplicitKeySpace
static class EntityWithDefaultKeySpace {
}
@PersistentAnnotationWithExplicitKeySpace(firstname = "viserys")
static class EntityWithSetKeySpace {
}
static class EntityWithInheritedKeySpace extends EntityWithSetKeySpace {
}
@Persistent
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
static @interface PersistentAnnotationWithExplicitKeySpace {
@KeySpace
String firstname() default "daenerys";
String lastnamne() default "targaryen";
}
@Persistent
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
static @interface ExplicitKeySpace {
@KeySpace
String name() default "";
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.keyvalue.core;
import static org.hamcrest.core.IsInstanceOf.*;
import static org.hamcrest.core.IsNull.*;
import static org.junit.Assert.*;
import java.util.NoSuchElementException;
import org.junit.Test;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.dao.DataRetrievalFailureException;
/**
* @author Christoph Strobl
*/
public class KeyValuePersistenceExceptionTranslatorUnitTests {
KeyValuePersistenceExceptionTranslator translator = new KeyValuePersistenceExceptionTranslator();
/**
* @see DATACMNS-525
*/
@Test
public void translateExeptionShouldReturnDataAccessExceptionWhenGivenOne() {
assertThat(translator.translateExceptionIfPossible(new DataRetrievalFailureException("booh")),
instanceOf(DataRetrievalFailureException.class));
}
/**
* @see DATACMNS-525
*/
@Test
public void translateExeptionShouldReturnNullWhenGivenNull() {
assertThat(translator.translateExceptionIfPossible(null), nullValue());
}
/**
* @see DATACMNS-525
*/
@Test
public void translateExeptionShouldTranslateNoSuchElementExceptionToDataRetrievalFailureException() {
assertThat(translator.translateExceptionIfPossible(new NoSuchElementException("")),
instanceOf(DataRetrievalFailureException.class));
}
/**
* @see DATACMNS-525
*/
@Test
public void translateExeptionShouldTranslateIndexOutOfBoundsExceptionToDataRetrievalFailureException() {
assertThat(translator.translateExceptionIfPossible(new IndexOutOfBoundsException("")),
instanceOf(DataRetrievalFailureException.class));
}
/**
* @see DATACMNS-525
*/
@Test
public void translateExeptionShouldTranslateIllegalStateExceptionToDataRetrievalFailureException() {
assertThat(translator.translateExceptionIfPossible(new IllegalStateException("")),
instanceOf(DataRetrievalFailureException.class));
}
/**
* @see DATACMNS-525
*/
@Test
public void translateExeptionShouldTranslateAnyJavaExceptionToUncategorizedKeyValueException() {
assertThat(translator.translateExceptionIfPossible(new UnsupportedOperationException("")),
instanceOf(UncategorizedKeyValueException.class));
}
/**
* @see DATACMNS-525
*/
@Test
public void translateExeptionShouldReturnNullForNonJavaExceptions() {
assertThat(translator.translateExceptionIfPossible(new NoSuchBeanDefinitionException("")), nullValue());
}
}

View File

@@ -0,0 +1,466 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.keyvalue.core;
import static org.hamcrest.collection.IsCollectionWithSize.*;
import static org.hamcrest.collection.IsEmptyCollection.*;
import static org.hamcrest.collection.IsIterableContainingInAnyOrder.*;
import static org.hamcrest.core.Is.*;
import static org.hamcrest.core.IsNull.*;
import static org.hamcrest.core.IsSame.*;
import static org.junit.Assert.*;
import java.io.Serializable;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.List;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.keyvalue.annotation.KeySpace;
import org.springframework.data.keyvalue.core.query.KeyValueQuery;
import org.springframework.data.map.MapKeyValueAdapter;
import org.springframework.util.ObjectUtils;
/**
* @author Christoph Strobl
* @author Oliver Gierke
*/
public class KeyValueTemplateTests {
static final Foo FOO_ONE = new Foo("one");
static final Foo FOO_TWO = new Foo("two");
static final Foo FOO_THREE = new Foo("three");
static final Bar BAR_ONE = new Bar("one");
static final ClassWithTypeAlias ALIASED = new ClassWithTypeAlias("super");
static final SubclassOfAliasedType SUBCLASS_OF_ALIASED = new SubclassOfAliasedType("sub");
static final KeyValueQuery<String> STRING_QUERY = new KeyValueQuery<String>("foo == 'two'");
KeyValueTemplate operations;
@Before
public void setUp() throws InstantiationException, IllegalAccessException {
this.operations = new KeyValueTemplate(new MapKeyValueAdapter());
}
@After
public void tearDown() throws Exception {
this.operations.destroy();
}
/**
* @see DATACMNS-525
*/
@Test
public void insertShouldNotThorwErrorWhenExecutedHavingNonExistingIdAndNonNullValue() {
operations.insert("1", FOO_ONE);
}
/**
* @see DATACMNS-525
*/
@Test(expected = IllegalArgumentException.class)
public void insertShouldThrowExceptionForNullId() {
operations.insert(null, FOO_ONE);
}
/**
* @see DATACMNS-525
*/
@Test(expected = IllegalArgumentException.class)
public void insertShouldThrowExceptionForNullObject() {
operations.insert("some-id", null);
}
/**
* @see DATACMNS-525
*/
@Test(expected = InvalidDataAccessApiUsageException.class)
public void insertShouldThrowExecptionWhenObjectOfSameTypeAlreadyExists() {
operations.insert("1", FOO_ONE);
operations.insert("1", FOO_TWO);
}
/**
* @see DATACMNS-525
*/
@Test
public void insertShouldWorkCorrectlyWhenObjectsOfDifferentTypesWithSameIdAreInserted() {
operations.insert("1", FOO_ONE);
operations.insert("1", BAR_ONE);
}
/**
* @see DATACMNS-525
*/
@Test
public void createShouldReturnSameInstanceGenerateId() {
ClassWithStringId source = new ClassWithStringId();
ClassWithStringId target = operations.insert(source);
assertThat(target, sameInstance(source));
}
/**
* @see DATACMNS-525
*/
@Test
public void createShouldRespectExistingId() {
ClassWithStringId source = new ClassWithStringId();
source.id = "one";
operations.insert(source);
assertThat(operations.findById("one", ClassWithStringId.class), is(source));
}
/**
* @see DATACMNS-525
*/
@Test
public void findByIdShouldReturnObjectWithMatchingIdAndType() {
operations.insert("1", FOO_ONE);
assertThat(operations.findById("1", Foo.class), is(FOO_ONE));
}
/**
* @see DATACMNS-525
*/
@Test
public void findByIdSouldReturnNullIfNoMatchingIdFound() {
operations.insert("1", FOO_ONE);
assertThat(operations.findById("2", Foo.class), nullValue());
}
/**
* @see DATACMNS-525
*/
@Test
public void findByIdShouldReturnNullIfNoMatchingTypeFound() {
operations.insert("1", FOO_ONE);
assertThat(operations.findById("1", Bar.class), nullValue());
}
/**
* @see DATACMNS-525
*/
@Test
public void findShouldExecuteQueryCorrectly() {
operations.insert("1", FOO_ONE);
operations.insert("2", FOO_TWO);
List<Foo> result = (List<Foo>) operations.find(STRING_QUERY, Foo.class);
assertThat(result, hasSize(1));
assertThat(result.get(0), is(FOO_TWO));
}
/**
* @see DATACMNS-525
*/
@Test
public void readShouldReturnEmptyCollectionIfOffsetOutOfRange() {
operations.insert("1", FOO_ONE);
operations.insert("2", FOO_TWO);
operations.insert("3", FOO_THREE);
assertThat(operations.findInRange(5, 5, Foo.class), empty());
}
/**
* @see DATACMNS-525
*/
@Test
public void updateShouldReplaceExistingObject() {
operations.insert("1", FOO_ONE);
operations.update("1", FOO_TWO);
assertThat(operations.findById("1", Foo.class), is(FOO_TWO));
}
/**
* @see DATACMNS-525
*/
@Test
public void updateShouldRespectTypeInformation() {
operations.insert("1", FOO_ONE);
operations.update("1", BAR_ONE);
assertThat(operations.findById("1", Foo.class), is(FOO_ONE));
}
/**
* @see DATACMNS-525
*/
@Test
public void deleteShouldRemoveObjectCorrectly() {
operations.insert("1", FOO_ONE);
operations.delete("1", Foo.class);
assertThat(operations.findById("1", Foo.class), nullValue());
}
/**
* @see DATACMNS-525
*/
@Test
public void deleteReturnsNullWhenNotExisting() {
operations.insert("1", FOO_ONE);
assertThat(operations.delete("2", Foo.class), nullValue());
}
/**
* @see DATACMNS-525
*/
@Test
public void deleteReturnsRemovedObject() {
operations.insert("1", FOO_ONE);
assertThat(operations.delete("1", Foo.class), is(FOO_ONE));
}
/**
* @see DATACMNS-525
*/
@Test(expected = IllegalArgumentException.class)
public void deleteThrowsExceptionWhenIdCannotBeExctracted() {
operations.delete(FOO_ONE);
}
/**
* @see DATACMNS-525
*/
@Test
public void countShouldReturnZeroWhenNoElementsPresent() {
assertThat(operations.count(Foo.class), is(0L));
}
/**
* @see DATACMNS-525
*/
@Test
public void insertShouldRespectTypeAlias() {
operations.insert("1", ALIASED);
operations.insert("2", SUBCLASS_OF_ALIASED);
assertThat(operations.findAll(ALIASED.getClass()), containsInAnyOrder(ALIASED, SUBCLASS_OF_ALIASED));
}
static class Foo implements Serializable {
private static final long serialVersionUID = -8912754229220128922L;
String foo;
public Foo(String foo) {
this.foo = foo;
}
public String getFoo() {
return foo;
}
@Override
public int hashCode() {
return ObjectUtils.nullSafeHashCode(this.foo);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Foo)) {
return false;
}
Foo other = (Foo) obj;
return ObjectUtils.nullSafeEquals(this.foo, other.foo);
}
}
static class Bar implements Serializable {
private static final long serialVersionUID = 196011921826060210L;
String bar;
public Bar(String bar) {
this.bar = bar;
}
public String getBar() {
return bar;
}
@Override
public int hashCode() {
return ObjectUtils.nullSafeHashCode(this.bar);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Bar)) {
return false;
}
Bar other = (Bar) obj;
return ObjectUtils.nullSafeEquals(this.bar, other.bar);
}
}
static class ClassWithStringId implements Serializable {
private static final long serialVersionUID = -7481030649267602830L;
@Id String id;
String value;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ObjectUtils.nullSafeHashCode(this.id);
result = prime * result + ObjectUtils.nullSafeHashCode(this.value);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof ClassWithStringId)) {
return false;
}
ClassWithStringId other = (ClassWithStringId) obj;
if (!ObjectUtils.nullSafeEquals(this.id, other.id)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(this.value, other.value)) {
return false;
}
return true;
}
}
@ExplicitKeySpace(name = "aliased")
static class ClassWithTypeAlias implements Serializable {
private static final long serialVersionUID = -5921943364908784571L;
@Id String id;
String name;
public ClassWithTypeAlias(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ObjectUtils.nullSafeHashCode(this.id);
result = prime * result + ObjectUtils.nullSafeHashCode(this.name);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof ClassWithTypeAlias)) {
return false;
}
ClassWithTypeAlias other = (ClassWithTypeAlias) obj;
if (!ObjectUtils.nullSafeEquals(this.id, other.id)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(this.name, other.name)) {
return false;
}
return true;
}
}
static class SubclassOfAliasedType extends ClassWithTypeAlias {
private static final long serialVersionUID = -468809596668871479L;
public SubclassOfAliasedType(String name) {
super(name);
}
}
@Persistent
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
static @interface ExplicitKeySpace {
@KeySpace
String name() default "";
}
}

View File

@@ -0,0 +1,609 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.keyvalue.core;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.collection.IsIterableContainingInAnyOrder.*;
import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import java.io.Serializable;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Arrays;
import java.util.Collection;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.annotation.TypeAlias;
import org.springframework.data.keyvalue.annotation.KeySpace;
import org.springframework.data.keyvalue.core.query.KeyValueQuery;
import org.springframework.util.ObjectUtils;
/**
* @author Christoph Strobl
*/
@RunWith(MockitoJUnitRunner.class)
public class KeyValueTemplateUnitTests {
private static final Foo FOO_ONE = new Foo("one");
private static final Foo FOO_TWO = new Foo("two");
private static final ClassWithTypeAlias ALIASED = new ClassWithTypeAlias("super");
private static final SubclassOfAliasedType SUBCLASS_OF_ALIASED = new SubclassOfAliasedType("sub");
private static final KeyValueQuery<String> STRING_QUERY = new KeyValueQuery<String>("foo == 'two'");
private @Mock KeyValueAdapter adapterMock;
private KeyValueTemplate template;
@Before
public void setUp() throws InstantiationException, IllegalAccessException {
this.template = new KeyValueTemplate(adapterMock);
}
/**
* @see DATACMNS-525
*/
@Test(expected = IllegalArgumentException.class)
public void shouldThrowExceptionWhenCreatingNewTempateWithNullAdapter() {
new KeyValueTemplate(null);
}
/**
* @see DATACMNS-525
*/
@Test(expected = IllegalArgumentException.class)
public void shouldThrowExceptionWhenCreatingNewTempateWithNullMappingContext() {
new KeyValueTemplate(adapterMock, null);
}
/**
* @see DATACMNS-525
*/
@Test
public void insertShouldLookUpValuesBeforeInserting() {
template.insert("1", FOO_ONE);
verify(adapterMock, times(1)).contains("1", Foo.class.getName());
}
/**
* @see DATACMNS-525
*/
@Test
public void insertShouldInsertUseClassNameAsDefaultKeyspace() {
template.insert("1", FOO_ONE);
verify(adapterMock, times(1)).put("1", FOO_ONE, Foo.class.getName());
}
/**
* @see DATACMNS-525
*/
@Test(expected = InvalidDataAccessApiUsageException.class)
public void insertShouldThrowExceptionWhenObectWithIdAlreadyExists() {
when(adapterMock.contains(anyString(), anyString())).thenReturn(true);
template.insert("1", FOO_ONE);
}
/**
* @see DATACMNS-525
*/
@Test(expected = IllegalArgumentException.class)
public void insertShouldThrowExceptionForNullId() {
template.insert(null, FOO_ONE);
}
/**
* @see DATACMNS-525
*/
@Test(expected = IllegalArgumentException.class)
public void insertShouldThrowExceptionForNullObject() {
template.insert("some-id", null);
}
/**
* @see DATACMNS-525
*/
@Test
public void insertShouldGenerateId() {
ClassWithStringId target = template.insert(new ClassWithStringId());
assertThat(target.id, notNullValue());
}
/**
* @see DATACMNS-525
*/
@Test(expected = IllegalArgumentException.class)
public void insertShouldThrowErrorWhenIdCannotBeResolved() {
template.insert(FOO_ONE);
}
/**
* @see DATACMNS-525
*/
@Test
public void insertShouldReturnSameInstanceGenerateId() {
ClassWithStringId source = new ClassWithStringId();
ClassWithStringId target = template.insert(source);
assertThat(target, sameInstance(source));
}
/**
* @see DATACMNS-525
*/
@Test
public void insertShouldRespectExistingId() {
ClassWithStringId source = new ClassWithStringId();
source.id = "one";
template.insert(source);
verify(adapterMock, times(1)).put("one", source, ClassWithStringId.class.getName());
}
/**
* @see DATACMNS-525
*/
@Test
public void findByIdShouldReturnNullWhenNoElementsPresent() {
assertNull(template.findById("1", Foo.class));
}
/**
* @see DATACMNS-525
*/
@Test
public void findByIdShouldReturnObjectWithMatchingIdAndType() {
template.findById("1", Foo.class);
verify(adapterMock, times(1)).get("1", Foo.class.getName());
}
/**
* @see DATACMNS-525
*/
@Test(expected = IllegalArgumentException.class)
public void findByIdShouldThrowExceptionWhenGivenNullId() {
template.findById((Serializable) null, Foo.class);
}
/**
* @see DATACMNS-525
*/
@Test
public void findAllOfShouldReturnEntireCollection() {
template.findAll(Foo.class);
verify(adapterMock, times(1)).getAllOf(Foo.class.getName());
}
/**
* @see DATACMNS-525
*/
@Test(expected = IllegalArgumentException.class)
public void findAllOfShouldThrowExceptionWhenGivenNullType() {
template.findAll(null);
}
/**
* @see DATACMNS-525
*/
@Test
public void findShouldCallFindOnAdapterToResolveMatching() {
template.find(STRING_QUERY, Foo.class);
verify(adapterMock, times(1)).find(STRING_QUERY, Foo.class.getName());
}
/**
* @see DATACMNS-525
*/
@Test
@SuppressWarnings("rawtypes")
public void findInRangeShouldRespectOffset() {
ArgumentCaptor<KeyValueQuery> captor = ArgumentCaptor.forClass(KeyValueQuery.class);
template.findInRange(1, 5, Foo.class);
verify(adapterMock, times(1)).find(captor.capture(), eq(Foo.class.getName()));
assertThat(captor.getValue().getOffset(), is(1));
assertThat(captor.getValue().getRows(), is(5));
assertThat(captor.getValue().getCritieria(), nullValue());
}
/**
* @see DATACMNS-525
*/
@Test
public void updateShouldReplaceExistingObject() {
template.update("1", FOO_TWO);
verify(adapterMock, times(1)).put("1", FOO_TWO, Foo.class.getName());
}
/**
* @see DATACMNS-525
*/
@Test(expected = IllegalArgumentException.class)
public void updateShouldThrowExceptionWhenGivenNullId() {
template.update(null, FOO_ONE);
}
/**
* @see DATACMNS-525
*/
@Test(expected = IllegalArgumentException.class)
public void updateShouldThrowExceptionWhenGivenNullObject() {
template.update("1", null);
}
/**
* @see DATACMNS-525
*/
@Test
public void updateShouldUseExtractedIdInformation() {
ClassWithStringId source = new ClassWithStringId();
source.id = "some-id";
template.update(source);
verify(adapterMock, times(1)).put(source.id, source, ClassWithStringId.class.getName());
}
/**
* @see DATACMNS-525
*/
@Test(expected = InvalidDataAccessApiUsageException.class)
public void updateShouldThrowErrorWhenIdInformationCannotBeExtracted() {
template.update(FOO_ONE);
}
/**
* @see DATACMNS-525
*/
@Test
public void deleteShouldRemoveObjectCorrectly() {
template.delete("1", Foo.class);
verify(adapterMock, times(1)).delete("1", Foo.class.getName());
}
/**
* @see DATACMNS-525
*/
@Test
public void deleteRemovesObjectUsingExtractedId() {
ClassWithStringId source = new ClassWithStringId();
source.id = "some-id";
template.delete(source);
verify(adapterMock, times(1)).delete("some-id", ClassWithStringId.class.getName());
}
/**
* @see DATACMNS-525
*/
@Test(expected = IllegalArgumentException.class)
public void deleteThrowsExceptionWhenIdCannotBeExctracted() {
template.delete(FOO_ONE);
}
/**
* @see DATACMNS-525
*/
@Test
public void countShouldReturnZeroWhenNoElementsPresent() {
template.count(Foo.class);
}
/**
* @see DATACMNS-525
*/
@Test
@SuppressWarnings({ "rawtypes", "unchecked" })
public void countShouldReturnCollectionSize() {
Collection foo = Arrays.asList(FOO_ONE, FOO_ONE);
when(adapterMock.getAllOf(Foo.class.getName())).thenReturn(foo);
assertThat(template.count(Foo.class), is(2L));
}
/**
* @see DATACMNS-525
*/
@Test(expected = IllegalArgumentException.class)
public void countShouldThrowErrorOnNullType() {
template.count(null);
}
/**
* @see DATACMNS-525
*/
@Test
public void insertShouldRespectTypeAlias() {
template.insert("1", ALIASED);
verify(adapterMock, times(1)).put("1", ALIASED, "aliased");
}
/**
* @see DATACMNS-525
*/
@Test
public void insertShouldRespectTypeAliasOnSubClass() {
template.insert("1", SUBCLASS_OF_ALIASED);
verify(adapterMock, times(1)).put("1", SUBCLASS_OF_ALIASED, "aliased");
}
/**
* @see DATACMNS-525
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
@Test
public void findAllOfShouldRespectTypeAliasAndFilterNonMatchingTypes() {
Collection foo = Arrays.asList(ALIASED, SUBCLASS_OF_ALIASED);
when(adapterMock.getAllOf("aliased")).thenReturn(foo);
assertThat(template.findAll(SUBCLASS_OF_ALIASED.getClass()), containsInAnyOrder(SUBCLASS_OF_ALIASED));
}
/**
* @see DATACMNS-525
*/
@Test
public void insertSouldRespectTypeAliasAndFilterNonMatching() {
template.insert("1", ALIASED);
assertThat(template.findById("1", SUBCLASS_OF_ALIASED.getClass()), nullValue());
}
/**
* @see DATACMNS-525
*/
@Test(expected = IllegalArgumentException.class)
public void setttingNullPersistenceExceptionTranslatorShouldThrowException() {
template.setExceptionTranslator(null);
}
static class Foo {
String foo;
public Foo(String foo) {
this.foo = foo;
}
public String getFoo() {
return foo;
}
@Override
public int hashCode() {
return ObjectUtils.nullSafeHashCode(this.foo);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Foo)) {
return false;
}
Foo other = (Foo) obj;
return ObjectUtils.nullSafeEquals(this.foo, other.foo);
}
}
static class Bar {
String bar;
public Bar(String bar) {
this.bar = bar;
}
public String getBar() {
return bar;
}
@Override
public int hashCode() {
return ObjectUtils.nullSafeHashCode(this.bar);
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof Bar)) {
return false;
}
Bar other = (Bar) obj;
return ObjectUtils.nullSafeEquals(this.bar, other.bar);
}
}
static class ClassWithStringId {
@Id String id;
String value;
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ObjectUtils.nullSafeHashCode(this.id);
result = prime * result + ObjectUtils.nullSafeHashCode(this.value);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof ClassWithStringId)) {
return false;
}
ClassWithStringId other = (ClassWithStringId) obj;
if (!ObjectUtils.nullSafeEquals(this.id, other.id)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(this.value, other.value)) {
return false;
}
return true;
}
}
@ExplicitKeySpace(name = "aliased")
static class ClassWithTypeAlias {
@Id String id;
String name;
public ClassWithTypeAlias(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ObjectUtils.nullSafeHashCode(this.id);
result = prime * result + ObjectUtils.nullSafeHashCode(this.name);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof ClassWithTypeAlias)) {
return false;
}
ClassWithTypeAlias other = (ClassWithTypeAlias) obj;
if (!ObjectUtils.nullSafeEquals(this.id, other.id)) {
return false;
}
if (!ObjectUtils.nullSafeEquals(this.name, other.name)) {
return false;
}
return true;
}
}
static class SubclassOfAliasedType extends ClassWithTypeAlias {
public SubclassOfAliasedType(String name) {
super(name);
}
}
@Persistent
static class EntityWithPersistentAnnotation {
}
@TypeAlias("foo")
static class AliasedEntity {
}
@KeySpace("rhaegar")
static class ClassWithDirectKeySpaceAnnotation {
}
@Persistent
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
private static @interface ExplicitKeySpace {
@KeySpace
String name() default "";
}
}

View File

@@ -0,0 +1,226 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.keyvalue.core;
import static java.lang.Integer.*;
import static org.hamcrest.CoreMatchers.*;
import static org.hamcrest.number.OrderingComparison.*;
import static org.junit.Assert.*;
import java.util.Comparator;
import org.junit.Test;
import org.springframework.expression.spel.standard.SpelExpressionParser;
/**
* Unit tests for {@link SpelPropertyComparator}.
*
* @author Christoph Strobl
* @author Oliver Gierke
*/
public class SpelPropertyComperatorUnitTests {
private static final SpelExpressionParser PARSER = new SpelExpressionParser();
private static final SomeType ONE = new SomeType("one", Integer.valueOf(1), 1);
private static final SomeType TWO = new SomeType("two", Integer.valueOf(2), 2);
private static final WrapperType WRAPPER_ONE = new WrapperType("w-one", ONE);
private static final WrapperType WRAPPER_TWO = new WrapperType("w-two", TWO);
/**
* @see DATACMNS-525
*/
@Test
public void shouldCompareStringAscCorrectly() {
Comparator<SomeType> comparator = new SpelPropertyComparator<SomeType>("stringProperty", PARSER);
assertThat(comparator.compare(ONE, TWO), is(ONE.getStringProperty().compareTo(TWO.getStringProperty())));
}
/**
* @see DATACMNS-525
*/
@Test
public void shouldCompareStringDescCorrectly() {
Comparator<SomeType> comparator = new SpelPropertyComparator<SomeType>("stringProperty", PARSER).desc();
assertThat(comparator.compare(ONE, TWO), is(TWO.getStringProperty().compareTo(ONE.getStringProperty())));
}
/**
* @see DATACMNS-525
*/
@Test
public void shouldCompareIntegerAscCorrectly() {
Comparator<SomeType> comparator = new SpelPropertyComparator<SomeType>("integerProperty", PARSER);
assertThat(comparator.compare(ONE, TWO), is(ONE.getIntegerProperty().compareTo(TWO.getIntegerProperty())));
}
/**
* @see DATACMNS-525
*/
@Test
public void shouldCompareIntegerDescCorrectly() {
Comparator<SomeType> comparator = new SpelPropertyComparator<SomeType>("integerProperty", PARSER).desc();
assertThat(comparator.compare(ONE, TWO), is(TWO.getIntegerProperty().compareTo(ONE.getIntegerProperty())));
}
/**
* @see DATACMNS-525
*/
@Test
public void shouldComparePrimitiveIntegerAscCorrectly() {
Comparator<SomeType> comparator = new SpelPropertyComparator<SomeType>("primitiveProperty", PARSER);
assertThat(comparator.compare(ONE, TWO),
is(valueOf(ONE.getPrimitiveProperty()).compareTo(valueOf(TWO.getPrimitiveProperty()))));
}
/**
* @see DATACMNS-525
*/
@Test
public void shouldNotFailOnNullValues() {
Comparator<SomeType> comparator = new SpelPropertyComparator<SomeType>("stringProperty", PARSER);
assertThat(comparator.compare(ONE, new SomeType(null, null, 2)), is(1));
}
/**
* @see DATACMNS-525
*/
@Test
public void shouldComparePrimitiveIntegerDescCorrectly() {
Comparator<SomeType> comparator = new SpelPropertyComparator<SomeType>("primitiveProperty", PARSER).desc();
assertThat(comparator.compare(ONE, TWO),
is(valueOf(TWO.getPrimitiveProperty()).compareTo(valueOf(ONE.getPrimitiveProperty()))));
}
/**
* @see DATACMNS-525
*/
@Test
public void shouldSortNullsFirstCorrectly() {
Comparator<SomeType> comparator = new SpelPropertyComparator<SomeType>("stringProperty", PARSER).nullsFirst();
assertThat(comparator.compare(ONE, new SomeType(null, null, 2)), equalTo(1));
}
/**
* @see DATACMNS-525
*/
@Test
public void shouldSortNullsLastCorrectly() {
Comparator<SomeType> comparator = new SpelPropertyComparator<SomeType>("stringProperty", PARSER).nullsLast();
assertThat(comparator.compare(ONE, new SomeType(null, null, 2)), equalTo(-1));
}
/**
* @see DATACMNS-525
*/
@Test
public void shouldCompareNestedTypesCorrectly() {
Comparator<WrapperType> comparator = new SpelPropertyComparator<WrapperType>("nestedType.stringProperty", PARSER);
assertThat(comparator.compare(WRAPPER_ONE, WRAPPER_TWO), is(WRAPPER_ONE.getNestedType().getStringProperty()
.compareTo(WRAPPER_TWO.getNestedType().getStringProperty())));
}
/**
* @see DATACMNS-525
*/
@Test
public void shouldCompareNestedTypesCorrectlyWhenOneOfThemHasNullValue() {
SpelPropertyComparator<WrapperType> comparator = new SpelPropertyComparator<WrapperType>(
"nestedType.stringProperty", PARSER);
assertThat(comparator.compare(WRAPPER_ONE, new WrapperType("two", null)), is(greaterThanOrEqualTo(1)));
}
static class WrapperType {
private String stringPropertyWrapper;
private SomeType nestedType;
public WrapperType(String stringPropertyWrapper, SomeType nestedType) {
this.stringPropertyWrapper = stringPropertyWrapper;
this.nestedType = nestedType;
}
public String getStringPropertyWrapper() {
return stringPropertyWrapper;
}
public void setStringPropertyWrapper(String stringPropertyWrapper) {
this.stringPropertyWrapper = stringPropertyWrapper;
}
public SomeType getNestedType() {
return nestedType;
}
public void setNestedType(SomeType nestedType) {
this.nestedType = nestedType;
}
}
static class SomeType {
public SomeType() {
}
public SomeType(String stringProperty, Integer integerProperty, int primitiveProperty) {
this.stringProperty = stringProperty;
this.integerProperty = integerProperty;
this.primitiveProperty = primitiveProperty;
}
String stringProperty;
Integer integerProperty;
int primitiveProperty;
public String getStringProperty() {
return stringProperty;
}
public void setStringProperty(String stringProperty) {
this.stringProperty = stringProperty;
}
public Integer getIntegerProperty() {
return integerProperty;
}
public void setIntegerProperty(Integer integerProperty) {
this.integerProperty = integerProperty;
}
public int getPrimitiveProperty() {
return primitiveProperty;
}
public void setPrimitiveProperty(int primitiveProperty) {
this.primitiveProperty = primitiveProperty;
}
}
}

View File

@@ -0,0 +1,247 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.keyvalue.repository;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import java.util.Arrays;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.Persistent;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.keyvalue.core.KeyValueOperations;
import org.springframework.data.keyvalue.repository.support.SimpleKeyValueRepository;
import org.springframework.data.repository.core.support.ReflectionEntityInformation;
/**
* @author Christoph Strobl
*/
@RunWith(MockitoJUnitRunner.class)
public class SimpleKeyValueRepositoryUnitTests {
private SimpleKeyValueRepository<Foo, String> repo;
private @Mock KeyValueOperations opsMock;
@Before
public void setUp() {
ReflectionEntityInformation<Foo, String> ei = new ReflectionEntityInformation<Foo, String>(Foo.class);
repo = new SimpleKeyValueRepository<Foo, String>(ei, opsMock);
}
/**
* @see DATACMNS-525
*/
@Test
public void saveNewWithNumericId() {
ReflectionEntityInformation<WithNumericId, Integer> ei = new ReflectionEntityInformation<WithNumericId, Integer>(
WithNumericId.class);
SimpleKeyValueRepository<WithNumericId, Integer> temp = new SimpleKeyValueRepository<WithNumericId, Integer>(ei,
opsMock);
WithNumericId withNumericId = new WithNumericId();
temp.save(withNumericId);
verify(opsMock, times(1)).insert(eq(withNumericId));
}
/**
* @see DATACMNS-525
*/
@Test
public void testDoubleSave() {
Foo foo = new Foo("one");
repo.save(foo);
foo.id = "1";
repo.save(foo);
verify(opsMock, times(1)).insert(eq(foo));
verify(opsMock, times(1)).update(eq(foo.getId()), eq(foo));
}
/**
* @see DATACMNS-525
*/
@Test
public void multipleSave() {
Foo one = new Foo("one");
Foo two = new Foo("one");
repo.save(Arrays.asList(one, two));
verify(opsMock, times(1)).insert(eq(one));
verify(opsMock, times(1)).insert(eq(two));
}
/**
* @see DATACMNS-525
*/
@Test
public void deleteEntity() {
Foo one = repo.save(new Foo("one"));
repo.delete(one);
verify(opsMock, times(1)).delete(eq(one.getId()), eq(Foo.class));
}
/**
* @see DATACMNS-525
*/
@Test
public void deleteById() {
repo.delete("one");
verify(opsMock, times(1)).delete(eq("one"), eq(Foo.class));
}
/**
* @see DATACMNS-525
*/
@Test
public void deleteAll() {
repo.deleteAll();
verify(opsMock, times(1)).delete(eq(Foo.class));
}
/**
* @see DATACMNS-525
*/
@Test
public void findAllIds() {
repo.findAll(Arrays.asList("one", "two", "three"));
verify(opsMock, times(3)).findById(anyString(), eq(Foo.class));
}
/**
* @see DATACMNS-525
*/
@Test
public void findAllWithPageableShouldDelegateToOperationsCorrectlyWhenPageableDoesNotContainSort() {
repo.findAll(new PageRequest(10, 15));
verify(opsMock, times(1)).findInRange(eq(150), eq(15), isNull(Sort.class), eq(Foo.class));
}
/**
* @see DATACMNS-525
*/
@Test
public void findAllWithPageableShouldDelegateToOperationsCorrectlyWhenPageableContainsSort() {
Sort sort = new Sort("for", "bar");
repo.findAll(new PageRequest(10, 15, sort));
verify(opsMock, times(1)).findInRange(eq(150), eq(15), eq(sort), eq(Foo.class));
}
/**
* @see DATACMNS-525
*/
@Test
public void findAllShouldFallbackToFindAllOfWhenGivenNullPageable() {
repo.findAll((Pageable) null);
verify(opsMock, times(1)).findAll(eq(Foo.class));
}
static class Foo {
private @Id String id;
private Long longValue;
private String name;
private Bar bar;
public Foo() {
}
public Foo(String name) {
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Long getLongValue() {
return longValue;
}
public void setLongValue(Long longValue) {
this.longValue = longValue;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Bar getBar() {
return bar;
}
public void setBar(Bar bar) {
this.bar = bar;
}
}
static class Bar {
private String bar;
public String getBar() {
return bar;
}
public void setBar(String bar) {
this.bar = bar;
}
}
@Persistent
static class WithNumericId {
@Id Integer id;
}
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.keyvalue.repository.config;
import static org.hamcrest.core.IsNull.*;
import static org.junit.Assert.*;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.annotation.Id;
import org.springframework.data.keyvalue.core.KeyValueOperations;
import org.springframework.data.keyvalue.core.KeyValueTemplate;
import org.springframework.data.map.MapKeyValueAdapter;
import org.springframework.data.repository.CrudRepository;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author Christoph Strobl
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
public class KeyValueRepositoryRegistrarIntegrationTests {
@Configuration
@EnableKeyValueRepositories(considerNestedRepositories = true)
static class Config {
@Bean
public KeyValueOperations keyValueTemplate() {
return new KeyValueTemplate(new MapKeyValueAdapter());
}
}
@Autowired PersonRepository repo;
/**
* @see DATACMNS-525
*/
@Test
public void shouldEnableKeyValueRepositoryCorrectly() {
assertThat(repo, notNullValue());
}
static class Person {
@Id String id;
String firstname;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
}
static interface PersonRepository extends CrudRepository<Person, String> {
List<Person> findByFirstname(String firstname);
}
}

View File

@@ -0,0 +1,566 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.keyvalue.repository.query;
import static org.hamcrest.core.Is.*;
import static org.junit.Assert.*;
import java.lang.reflect.Method;
import java.util.Date;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.data.annotation.Id;
import org.springframework.data.keyvalue.core.query.KeyValueQuery;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.query.ParametersParameterAccessor;
import org.springframework.data.repository.query.QueryMethod;
import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.expression.spel.standard.SpelExpression;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.util.ObjectUtils;
/**
* @author Christoph Strobl
*/
@RunWith(MockitoJUnitRunner.class)
public class SpelQueryCreatorUnitTests {
static final DateTimeFormatter FORMATTER = ISODateTimeFormat.dateTimeNoMillis().withZoneUTC();
static final Person RICKON = new Person("rickon", 4);
static final Person BRAN = new Person("bran", 9)//
.skinChanger(true)//
.bornAt(FORMATTER.parseDateTime("2013-01-31T06:00:00Z").toDate());
static final Person ARYA = new Person("arya", 13);
static final Person ROBB = new Person("robb", 16)//
.named("stark")//
.bornAt(FORMATTER.parseDateTime("2010-09-20T06:00:00Z").toDate());
static final Person JON = new Person("jon", 17).named("snow");
@Mock RepositoryMetadata metadataMock;
/**
* @see DATACMNS-525
*/
@Test
public void equalsReturnsTrueWhenMatching() throws Exception {
assertThat(evaluate("findByFirstname", BRAN.firstname).against(BRAN), is(true));
}
/**
* @see DATACMNS-525
*/
@Test
public void equalsReturnsFalseWhenNotMatching() throws Exception {
assertThat(evaluate("findByFirstname", BRAN.firstname).against(RICKON), is(false));
}
/**
* @see DATACMNS-525
*/
@Test
public void isTrueAssertedPropertlyWhenTrue() throws Exception {
assertThat(evaluate("findBySkinChangerIsTrue").against(BRAN), is(true));
}
/**
* @see DATACMNS-525
*/
@Test
public void isTrueAssertedPropertlyWhenFalse() throws Exception {
assertThat(evaluate("findBySkinChangerIsTrue").against(RICKON), is(false));
}
/**
* @see DATACMNS-525
*/
@Test
public void isFalseAssertedPropertlyWhenTrue() throws Exception {
assertThat(evaluate("findBySkinChangerIsFalse").against(BRAN), is(false));
}
/**
* @see DATACMNS-525
*/
@Test
public void isFalseAssertedPropertlyWhenFalse() throws Exception {
assertThat(evaluate("findBySkinChangerIsFalse").against(RICKON), is(true));
}
/**
* @see DATACMNS-525
*/
@Test
public void isNullAssertedPropertlyWhenAttributeIsNull() throws Exception {
assertThat(evaluate("findByLastnameIsNull").against(BRAN), is(true));
}
/**
* @see DATACMNS-525
*/
@Test
public void isNullAssertedPropertlyWhenAttributeIsNotNull() throws Exception {
assertThat(evaluate("findByLastnameIsNull").against(ROBB), is(false));
}
/**
* @see DATACMNS-525
*/
@Test
public void isNotNullFalseTrueWhenAttributeIsNull() throws Exception {
assertThat(evaluate("findByLastnameIsNotNull").against(BRAN), is(false));
}
/**
* @see DATACMNS-525
*/
@Test
public void isNotNullReturnsTrueAttributeIsNotNull() throws Exception {
assertThat(evaluate("findByLastnameIsNotNull").against(ROBB), is(true));
}
/**
* @see DATACMNS-525
*/
@Test
public void startsWithReturnsTrueWhenMatching() throws Exception {
assertThat(evaluate("findByFirstnameStartingWith", "r").against(ROBB), is(true));
}
/**
* @see DATACMNS-525
*/
@Test
public void startsWithReturnsFalseWhenNotMatching() throws Exception {
assertThat(evaluate("findByFirstnameStartingWith", "r").against(BRAN), is(false));
}
/**
* @see DATACMNS-525
*/
@Test
public void likeReturnsTrueWhenMatching() throws Exception {
assertThat(evaluate("findByFirstnameLike", "ob").against(ROBB), is(true));
}
/**
* @see DATACMNS-525
*/
@Test
public void likeReturnsFalseWhenNotMatching() throws Exception {
assertThat(evaluate("findByFirstnameLike", "ra").against(ROBB), is(false));
}
/**
* @see DATACMNS-525
*/
@Test
public void endsWithReturnsTrueWhenMatching() throws Exception {
assertThat(evaluate("findByFirstnameEndingWith", "bb").against(ROBB), is(true));
}
/**
* @see DATACMNS-525
*/
@Test
public void endsWithReturnsFalseWhenNotMatching() throws Exception {
assertThat(evaluate("findByFirstnameEndingWith", "an").against(ROBB), is(false));
}
/**
* @see DATACMNS-525
*/
@Test(expected = InvalidDataAccessApiUsageException.class)
public void startsWithIgnoreCaseReturnsTrueWhenMatching() throws Exception {
assertThat(evaluate("findByFirstnameIgnoreCase", "R").against(ROBB), is(true));
}
/**
* @see DATACMNS-525
*/
@Test
public void greaterThanReturnsTrueForHigherValues() throws Exception {
assertThat(evaluate("findByAgeGreaterThan", BRAN.age).against(ROBB), is(true));
}
/**
* @see DATACMNS-525
*/
@Test
public void greaterThanReturnsFalseForLowerValues() throws Exception {
assertThat(evaluate("findByAgeGreaterThan", BRAN.age).against(RICKON), is(false));
}
/**
* @see DATACMNS-525
*/
@Test
public void afterReturnsTrueForHigherValues() throws Exception {
assertThat(evaluate("findByBirthdayAfter", ROBB.birthday).against(BRAN), is(true));
}
/**
* @see DATACMNS-525
*/
@Test
public void afterReturnsFalseForLowerValues() throws Exception {
assertThat(evaluate("findByBirthdayAfter", BRAN.birthday).against(ROBB), is(false));
}
/**
* @see DATACMNS-525
*/
@Test
public void greaterThanEaualsReturnsTrueForHigherValues() throws Exception {
assertThat(evaluate("findByAgeGreaterThanEqual", BRAN.age).against(ROBB), is(true));
}
/**
* @see DATACMNS-525
*/
@Test
public void greaterThanEaualsReturnsTrueForEqualValues() throws Exception {
assertThat(evaluate("findByAgeGreaterThanEqual", BRAN.age).against(BRAN), is(true));
}
/**
* @see DATACMNS-525
*/
@Test
public void greaterThanEqualsReturnsFalseForLowerValues() throws Exception {
assertThat(evaluate("findByAgeGreaterThanEqual", BRAN.age).against(RICKON), is(false));
}
/**
* @see DATACMNS-525
*/
@Test
public void lessThanReturnsTrueForHigherValues() throws Exception {
assertThat(evaluate("findByAgeLessThan", BRAN.age).against(ROBB), is(false));
}
/**
* @see DATACMNS-525
*/
@Test
public void lessThanReturnsFalseForLowerValues() throws Exception {
assertThat(evaluate("findByAgeLessThan", BRAN.age).against(RICKON), is(true));
}
/**
* @see DATACMNS-525
*/
@Test
public void beforeReturnsTrueForLowerValues() throws Exception {
assertThat(evaluate("findByBirthdayBefore", BRAN.birthday).against(ROBB), is(true));
}
/**
* @see DATACMNS-525
*/
@Test
public void beforeReturnsFalseForHigherValues() throws Exception {
assertThat(evaluate("findByBirthdayBefore", ROBB.birthday).against(BRAN), is(false));
}
/**
* @see DATACMNS-525
*/
@Test
public void lessThanEaualsReturnsTrueForHigherValues() throws Exception {
assertThat(evaluate("findByAgeLessThanEqual", BRAN.age).against(ROBB), is(false));
}
/**
* @see DATACMNS-525
*/
@Test
public void lessThanEaualsReturnsTrueForEqualValues() throws Exception {
assertThat(evaluate("findByAgeLessThanEqual", BRAN.age).against(BRAN), is(true));
}
/**
* @see DATACMNS-525
*/
@Test
public void lessThanEqualsReturnsFalseForLowerValues() throws Exception {
assertThat(evaluate("findByAgeLessThanEqual", BRAN.age).against(RICKON), is(true));
}
/**
* @see DATACMNS-525
*/
@Test
public void betweenEqualsReturnsTrueForValuesInBetween() throws Exception {
assertThat(evaluate("findByAgeBetween", BRAN.age, ROBB.age).against(ARYA), is(true));
}
/**
* @see DATACMNS-525
*/
@Test
public void betweenEqualsReturnsFalseForHigherValues() throws Exception {
assertThat(evaluate("findByAgeBetween", BRAN.age, ROBB.age).against(JON), is(false));
}
/**
* @see DATACMNS-525
*/
@Test
public void betweenEqualsReturnsFalseForLowerValues() throws Exception {
assertThat(evaluate("findByAgeBetween", BRAN.age, ROBB.age).against(RICKON), is(false));
}
/**
* @see DATACMNS-525
*/
@Test
public void connectByAndReturnsTrueWhenAllPropertiesMatching() throws Exception {
assertThat(evaluate("findByAgeGreaterThanAndLastname", BRAN.age, JON.lastname).against(JON), is(true));
}
/**
* @see DATACMNS-525
*/
@Test
public void connectByAndReturnsFalseWhenOnlyFewPropertiesMatch() throws Exception {
assertThat(evaluate("findByAgeGreaterThanAndLastname", BRAN.age, JON.lastname).against(ROBB), is(false));
}
/**
* @see DATACMNS-525
*/
@Test
public void connectByOrReturnsTrueWhenOnlyFewPropertiesMatch() throws Exception {
assertThat(evaluate("findByAgeGreaterThanOrLastname", BRAN.age, JON.lastname).against(ROBB), is(true));
}
/**
* @see DATACMNS-525
*/
@Test
public void connectByOrReturnsTrueWhenAllPropertiesMatch() throws Exception {
assertThat(evaluate("findByAgeGreaterThanOrLastname", BRAN.age, JON.lastname).against(JON), is(true));
}
/**
* @see DATACMNS-525
*/
@Test
public void regexReturnsTrueWhenMatching() throws Exception {
assertThat(evaluate("findByLastnameMatches", "^s.*w$").against(JON), is(true));
}
/**
* @see DATACMNS-525
*/
@Test
public void regexReturnsFalseWhenNotMatching() throws Exception {
assertThat(evaluate("findByLastnameMatches", "^s.*w$").against(ROBB), is(false));
}
private Evaluation evaluate(String methodName, Object... args) throws Exception {
return new Evaluation((SpelExpression) createQueryForMethodWithArgs(methodName, args).getCritieria());
}
private KeyValueQuery<SpelExpression> createQueryForMethodWithArgs(String methodName, Object... args)
throws NoSuchMethodException, SecurityException {
Class<?>[] argTypes = new Class<?>[args.length];
if (!ObjectUtils.isEmpty(args)) {
for (int i = 0; i < args.length; i++) {
argTypes[i] = args[i].getClass();
}
}
Method method = PersonRepository.class.getMethod(methodName, argTypes);
PartTree partTree = new PartTree(method.getName(), method.getReturnType());
SpelQueryCreator creator = new SpelQueryCreator(partTree, new ParametersParameterAccessor(new QueryMethod(method,
metadataMock).getParameters(), args));
KeyValueQuery<SpelExpression> q = creator.createQuery();
q.getCritieria().setEvaluationContext(new StandardEvaluationContext(args));
return q;
}
static interface PersonRepository {
// Type.SIMPLE_PROPERTY
Person findByFirstname(String firstname);
// Type.TRUE
Person findBySkinChangerIsTrue();
// Type.FALSE
Person findBySkinChangerIsFalse();
// Type.IS_NULL
Person findByLastnameIsNull();
// Type.IS_NOT_NULL
Person findByLastnameIsNotNull();
// Type.STARTING_WITH
Person findByFirstnameStartingWith(String firstanme);
Person findByFirstnameIgnoreCase(String firstanme);
// Type.AFTER
Person findByBirthdayAfter(Date date);
// Type.GREATHER_THAN
Person findByAgeGreaterThan(Integer age);
// Type.GREATER_THAN_EQUAL
Person findByAgeGreaterThanEqual(Integer age);
// Type.BEFORE
Person findByBirthdayBefore(Date date);
// Type.LESS_THAN
Person findByAgeLessThan(Integer age);
// Type.LESS_THAN_EQUAL
Person findByAgeLessThanEqual(Integer age);
// Type.BETWEEN
Person findByAgeBetween(Integer low, Integer high);
// Type.LIKE
Person findByFirstnameLike(String firstname);
// Type.ENDING_WITH
Person findByFirstnameEndingWith(String firstname);
Person findByAgeGreaterThanAndLastname(Integer age, String lastname);
Person findByAgeGreaterThanOrLastname(Integer age, String lastname);
// Type.REGEX
Person findByLastnameMatches(String lastname);
}
static class Evaluation {
SpelExpression expression;
Object candidate;
public Evaluation(SpelExpression expresison) {
this.expression = expresison;
}
public Boolean against(Object candidate) {
this.candidate = candidate;
return evaluate();
}
private boolean evaluate() {
expression.getEvaluationContext().setVariable("it", candidate);
return expression.getValue(Boolean.class);
}
}
static class Person {
private @Id String id;
private String firstname, lastname;
private int age;
private boolean isSkinChanger = false;
private Date birthday;
public Person() {}
public Person(String firstname, int age) {
super();
this.firstname = firstname;
this.age = age;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getFirstname() {
return firstname;
}
public void setFirstname(String firstname) {
this.firstname = firstname;
}
public String getLastname() {
return lastname;
}
public void setLastname(String lastname) {
this.lastname = lastname;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public boolean isSkinChanger() {
return isSkinChanger;
}
public void setSkinChanger(boolean isSkinChanger) {
this.isSkinChanger = isSkinChanger;
}
public Person skinChanger(boolean isSkinChanger) {
this.isSkinChanger = isSkinChanger;
return this;
}
public Person named(String lastname) {
this.lastname = lastname;
return this;
}
public Person bornAt(Date date) {
this.birthday = date;
return this;
}
}
}

View File

@@ -0,0 +1,122 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.keyvalue.repository.support;
import static org.hamcrest.collection.IsArrayWithSize.*;
import static org.junit.Assert.*;
import static org.springframework.data.keyvalue.repository.support.KeyValueQueryDslUtils.*;
import org.hamcrest.collection.IsArrayContainingInOrder;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.NullHandling;
import org.springframework.data.keyvalue.Person;
import org.springframework.data.keyvalue.QPerson;
import org.springframework.data.querydsl.SimpleEntityPathResolver;
import com.mysema.query.types.EntityPath;
import com.mysema.query.types.OrderSpecifier;
import com.mysema.query.types.path.PathBuilder;
/**
* @author Christoph Strobl
* @author Thomas Darimont
*/
public class QueryDslUtilsUnitTests {
private EntityPath<Person> path;
private PathBuilder<Person> builder;
@Before
public void setUp() {
this.path = SimpleEntityPathResolver.INSTANCE.createPath(Person.class);
this.builder = new PathBuilder<Person>(path.getType(), path.getMetadata());
}
/**
* @see DATACMNS-525
*/
@Test(expected = IllegalArgumentException.class)
public void toOrderSpecifierThrowsExceptioOnNullPathBuilder() {
toOrderSpecifier(new Sort("firstname"), null);
}
/**
* @see DATACMNS-525
*/
@Test
public void toOrderSpecifierReturnsEmptyArrayWhenSortIsNull() {
assertThat(toOrderSpecifier(null, builder), arrayWithSize(0));
}
/**
* @see DATACMNS-525
*/
@Test
public void toOrderSpecifierConvertsSimpleAscSortCorrectly() {
Sort sort = new Sort(Direction.ASC, "firstname");
OrderSpecifier<?>[] specifiers = toOrderSpecifier(sort, builder);
assertThat(specifiers, IsArrayContainingInOrder.<OrderSpecifier<?>> arrayContaining(QPerson.person.firstname.asc()));
}
/**
* @see DATACMNS-525
*/
@Test
public void toOrderSpecifierConvertsSimpleDescSortCorrectly() {
Sort sort = new Sort(Direction.DESC, "firstname");
OrderSpecifier<?>[] specifiers = toOrderSpecifier(sort, builder);
assertThat(specifiers,
IsArrayContainingInOrder.<OrderSpecifier<?>> arrayContaining(QPerson.person.firstname.desc()));
}
/**
* @see DATACMNS-525
*/
@Test
public void toOrderSpecifierConvertsSortCorrectlyAndRetainsArgumentOrder() {
Sort sort = new Sort(Direction.DESC, "firstname").and(new Sort(Direction.ASC, "age"));
OrderSpecifier<?>[] specifiers = toOrderSpecifier(sort, builder);
assertThat(specifiers, IsArrayContainingInOrder.<OrderSpecifier<?>> arrayContaining(
QPerson.person.firstname.desc(), QPerson.person.age.asc()));
}
/**
* @see DATACMNS-525
*/
@Test
public void toOrderSpecifierConvertsSortWithNullHandlingCorrectly() {
Sort sort = new Sort(new Sort.Order(Direction.DESC, "firstname", NullHandling.NULLS_LAST));
OrderSpecifier<?>[] specifiers = toOrderSpecifier(sort, builder);
assertThat(specifiers,
IsArrayContainingInOrder.<OrderSpecifier<?>> arrayContaining(QPerson.person.firstname.desc().nullsLast()));
}
}

View File

@@ -0,0 +1,96 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.map;
import static org.hamcrest.core.IsEqual.*;
import static org.hamcrest.core.IsInstanceOf.*;
import static org.junit.Assert.*;
import java.io.Serializable;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListMap;
import org.junit.Test;
import org.springframework.core.CollectionFactory;
/**
* @author Christoph Strobl
*/
public class MapKeyValueAdapterFactoryUnitTests {
/**
* @see DATACMNS-525
*/
@Test
public void shouldDefaultToConcurrentHashMapWhenTypeIsNull() {
assertThat(new MapKeyValueAdapterFactory(null).getAdapter().getKeySpaceMap("foo"),
instanceOf(ConcurrentHashMap.class));
}
/**
* @see DATACMNS-525
*/
@Test
public void shouldDefaultToCollecitonUtilsDefaultForInterfaceTypes() {
assertThat(new MapKeyValueAdapterFactory(Map.class).getAdapter().getKeySpaceMap("foo"),
instanceOf(CollectionFactory.createMap(Map.class, 0).getClass()));
}
/**
* @see DATACMNS-525
*/
@Test
public void shouldUseConcreteMapTypeWhenInstantiable() {
assertThat(new MapKeyValueAdapterFactory(ConcurrentSkipListMap.class).getAdapter().getKeySpaceMap("foo"),
instanceOf(CollectionFactory.createMap(ConcurrentSkipListMap.class, 0).getClass()));
}
/**
* @see DATACMNS-525
*/
@Test
public void shouldPopulateAdapterWithValues() {
MapKeyValueAdapterFactory factory = new MapKeyValueAdapterFactory();
factory.setInitialValuesForKeyspace("foo", Collections.singletonMap("1", "STANIS"));
factory.setInitialValuesForKeyspace("bar", Collections.singletonMap("1", "ROBERT"));
assertThat((String) factory.getAdapter().get("1", "foo"), equalTo("STANIS"));
assertThat((String) factory.getAdapter().get("1", "bar"), equalTo("ROBERT"));
}
/**
* @see DATACMNS-525
*/
@Test(expected = IllegalArgumentException.class)
public void shouldThrowExceptionWhenSettingValuesForNullKeySpace() {
new MapKeyValueAdapterFactory().setInitialValuesForKeyspace(null, Collections.<Serializable, Object> emptyMap());
}
/**
* @see DATACMNS-525
*/
@Test(expected = IllegalArgumentException.class)
public void shouldThrowExceptionWhenSettingNullValuesForKeySpace() {
new MapKeyValueAdapterFactory().setInitialValuesForKeyspace("foo", null);
}
}

View File

@@ -0,0 +1,223 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.map;
import static org.hamcrest.collection.IsIterableContainingInAnyOrder.*;
import static org.hamcrest.core.Is.*;
import static org.hamcrest.core.IsEqual.*;
import static org.hamcrest.core.IsNull.*;
import static org.junit.Assert.*;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.map.MapKeyValueAdapter;
import org.springframework.util.ObjectUtils;
/**
* @author Christoph Strobl
*/
public class MapKeyValueAdapterUnitTests {
private static final String COLLECTION_1 = "collection-1";
private static final String COLLECTION_2 = "collection-2";
private static final String STRING_1 = new String("1");
private Object object1 = new SimpleObject("one");
private Object object2 = new SimpleObject("two");
private MapKeyValueAdapter adapter;
@Before
public void setUp() {
this.adapter = new MapKeyValueAdapter();
}
/**
* @see DATACMNS-525
*/
@Test(expected = IllegalArgumentException.class)
public void putShouldThrowExceptionWhenAddingNullId() {
adapter.put(null, object1, COLLECTION_1);
}
/**
* @see DATACMNS-525
*/
@Test(expected = IllegalArgumentException.class)
public void putShouldThrowExceptionWhenCollectionIsNullValue() {
adapter.put("1", object1, null);
}
/**
* @see DATACMNS-525
*/
@Test
public void putReturnsNullWhenNoObjectForIdPresent() {
assertThat(adapter.put("1", object1, COLLECTION_1), nullValue());
}
/**
* @see DATACMNS-525
*/
@Test
public void putShouldReturnPreviousObjectForIdWhenAddingNewOneWithSameIdPresent() {
adapter.put("1", object1, COLLECTION_1);
assertThat(adapter.put("1", object2, COLLECTION_1), equalTo(object1));
}
/**
* @see DATACMNS-525
*/
@Test(expected = IllegalArgumentException.class)
public void containsShouldThrowExceptionWhenIdIsNull() {
adapter.contains(null, COLLECTION_1);
}
/**
* @see DATACMNS-525
*/
@Test(expected = IllegalArgumentException.class)
public void containsShouldThrowExceptionWhenTypeIsNull() {
adapter.contains("", null);
}
/**
* @see DATACMNS-525
*/
@Test
public void containsShouldReturnFalseWhenNoElementsPresent() {
assertThat(adapter.contains("1", COLLECTION_1), is(false));
}
/**
* @see DATACMNS-525
*/
@Test
public void containShouldReturnTrueWhenElementWithIdPresent() {
adapter.put("1", object1, COLLECTION_1);
assertThat(adapter.contains("1", COLLECTION_1), is(true));
}
/**
* @see DATACMNS-525
*/
@Test
public void getShouldReturnNullWhenNoElementWithIdPresent() {
assertThat(adapter.get("1", COLLECTION_1), nullValue());
}
/**
* @see DATACMNS-525
*/
@Test
public void getShouldReturnElementWhenMatchingIdPresent() {
adapter.put("1", object1, COLLECTION_1);
assertThat(adapter.get("1", COLLECTION_1), is(object1));
}
/**
* @see DATACMNS-525
*/
@Test(expected = IllegalArgumentException.class)
public void getShouldThrowExceptionWhenIdIsNull() {
adapter.get(null, COLLECTION_1);
}
/**
* @see DATACMNS-525
*/
@Test(expected = IllegalArgumentException.class)
public void getShouldThrowExceptionWhenTypeIsNull() {
adapter.get("1", null);
}
/**
* @see DATACMNS-525
*/
@Test
public void getAllOfShouldReturnAllValuesOfGivenCollection() {
adapter.put("1", object1, COLLECTION_1);
adapter.put("2", object2, COLLECTION_1);
adapter.put("3", STRING_1, COLLECTION_2);
assertThat(adapter.getAllOf(COLLECTION_1), containsInAnyOrder(object1, object2));
}
/**
* @see DATACMNS-525
*/
@Test(expected = IllegalArgumentException.class)
public void getAllOfShouldThrowExceptionWhenTypeIsNull() {
adapter.getAllOf(null);
}
/**
* @see DATACMNS-525
*/
@Test
public void deleteShouldReturnNullWhenGivenIdThatDoesNotExist() {
assertThat(adapter.delete("1", COLLECTION_1), nullValue());
}
/**
* @see DATACMNS-525
*/
@Test
public void deleteShouldReturnDeletedObject() {
adapter.put("1", object1, COLLECTION_1);
assertThat(adapter.delete("1", COLLECTION_1), is(object1));
}
static class SimpleObject {
protected String stringValue;
public SimpleObject() {}
SimpleObject(String value) {
this.stringValue = value;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * ObjectUtils.nullSafeHashCode(this.stringValue);
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (obj == null) {
return false;
}
if (!(obj instanceof SimpleObject)) {
return false;
}
SimpleObject that = (SimpleObject) obj;
return ObjectUtils.nullSafeEquals(this.stringValue, that.stringValue);
}
}
}

View File

@@ -0,0 +1,138 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.map;
import static org.hamcrest.collection.IsCollectionWithSize.*;
import static org.hamcrest.collection.IsIterableContainingInAnyOrder.*;
import static org.hamcrest.collection.IsIterableContainingInOrder.*;
import static org.hamcrest.core.Is.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.keyvalue.Person;
import org.springframework.data.keyvalue.QPerson;
import org.springframework.data.querydsl.QSort;
import org.springframework.data.querydsl.QueryDslPredicateExecutor;
/**
* @author Christoph Strobl
*/
public class QueryDslMapRepositoryUnitTests extends SimpleKeyValueRepositoryUnitTests {
/**
* @see DATACMNS-525
*/
@Test
public void findOneIsExecutedCorrectly() {
repository.save(LENNISTERS);
Person result = getQPersonRepo().findOne(QPerson.person.firstname.eq(CERSEI.getFirstname()));
assertThat(result, is(CERSEI));
}
/**
* @see DATACMNS-525
*/
@Test
public void findAllIsExecutedCorrectly() {
repository.save(LENNISTERS);
Iterable<Person> result = getQPersonRepo().findAll(QPerson.person.age.eq(CERSEI.getAge()));
assertThat(result, containsInAnyOrder(CERSEI, JAIME));
}
/**
* @see DATACMNS-525
*/
@Test
public void findWithPaginationWorksCorrectly() {
repository.save(LENNISTERS);
Page<Person> page1 = getQPersonRepo().findAll(QPerson.person.age.eq(CERSEI.getAge()), new PageRequest(0, 1));
assertThat(page1.getTotalElements(), is(2L));
assertThat(page1.getContent(), hasSize(1));
assertThat(page1.hasNext(), is(true));
Page<Person> page2 = ((QPersonRepository) repository).findAll(QPerson.person.age.eq(CERSEI.getAge()),
page1.nextPageable());
assertThat(page2.getTotalElements(), is(2L));
assertThat(page2.getContent(), hasSize(1));
assertThat(page2.hasNext(), is(false));
}
/**
* @see DATACMNS-525
*/
@Test
public void findAllUsingOrderSpecifierWorksCorrectly() {
repository.save(LENNISTERS);
Iterable<Person> result = getQPersonRepo().findAll(QPerson.person.age.eq(CERSEI.getAge()),
QPerson.person.firstname.desc());
assertThat(result, contains(JAIME, CERSEI));
}
/**
* @see DATACMNS-525
*/
@Test
public void findAllUsingPageableWithSortWorksCorrectly() {
repository.save(LENNISTERS);
Iterable<Person> result = getQPersonRepo().findAll(QPerson.person.age.eq(CERSEI.getAge()),
new PageRequest(0, 10, Direction.DESC, "firstname"));
assertThat(result, contains(JAIME, CERSEI));
}
/**
* @see DATACMNS-525
*/
@Test
public void findAllUsingPagableWithQSortWorksCorrectly() {
repository.save(LENNISTERS);
Iterable<Person> result = getQPersonRepo().findAll(QPerson.person.age.eq(CERSEI.getAge()),
new PageRequest(0, 10, new QSort(QPerson.person.firstname.desc())));
assertThat(result, contains(JAIME, CERSEI));
}
@Override
protected Class<? extends PersonRepository> getRepositoryClass() {
return QPersonRepository.class;
}
QPersonRepository getQPersonRepo() {
return ((QPersonRepository) repository);
}
static interface QPersonRepository extends PersonRepository, QueryDslPredicateExecutor<Person> {
}
}

View File

@@ -0,0 +1,171 @@
/*
* Copyright 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.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.data.map;
import static org.hamcrest.collection.IsIterableContainingInAnyOrder.*;
import static org.hamcrest.collection.IsIterableContainingInOrder.*;
import static org.hamcrest.core.Is.*;
import static org.junit.Assert.*;
import java.util.Arrays;
import java.util.List;
import org.hamcrest.collection.IsCollectionWithSize;
import org.hamcrest.collection.IsIterableContainingInOrder;
import org.junit.Before;
import org.junit.Test;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.keyvalue.Person;
import org.springframework.data.keyvalue.core.KeyValueTemplate;
import org.springframework.data.keyvalue.repository.KeyValueRepository;
import org.springframework.data.keyvalue.repository.support.KeyValueRepositoryFactory;
import org.springframework.data.map.MapKeyValueAdapter;
import org.springframework.data.repository.CrudRepository;
/**
* @author Christoph Strobl
*/
public class SimpleKeyValueRepositoryUnitTests {
protected static final Person CERSEI = new Person("cersei", 19);
protected static final Person JAIME = new Person("jaime", 19);
protected static final Person TYRION = new Person("tyrion", 17);
protected static List<Person> LENNISTERS = Arrays.asList(CERSEI, JAIME, TYRION);
protected PersonRepository repository;
protected KeyValueTemplate template = new KeyValueTemplate(new MapKeyValueAdapter());
@Before
public void setup() {
this.repository = new KeyValueRepositoryFactory(template).getRepository(getRepositoryClass());
}
/**
* @see DATACMNS-525
*/
@Test
public void findBy() {
this.repository.save(LENNISTERS);
assertThat(this.repository.findByAge(19), containsInAnyOrder(CERSEI, JAIME));
}
/**
* @see DATACMNS-525
*/
@Test
public void combindedFindUsingAnd() {
this.repository.save(LENNISTERS);
assertThat(this.repository.findByFirstnameAndAge(JAIME.getFirstname(), 19), containsInAnyOrder(JAIME));
}
/**
* @see DATACMNS-525
*/
@Test
public void findPage() {
this.repository.save(LENNISTERS);
Page<Person> page = this.repository.findByAge(19, new PageRequest(0, 1));
assertThat(page.hasNext(), is(true));
assertThat(page.getTotalElements(), is(2L));
assertThat(page.getContent(), IsCollectionWithSize.hasSize(1));
Page<Person> next = this.repository.findByAge(19, page.nextPageable());
assertThat(next.hasNext(), is(false));
assertThat(next.getTotalElements(), is(2L));
assertThat(next.getContent(), IsCollectionWithSize.hasSize(1));
}
/**
* @see DATACMNS-525
*/
@Test
public void findByConnectingOr() {
this.repository.save(LENNISTERS);
assertThat(this.repository.findByAgeOrFirstname(19, TYRION.getFirstname()),
containsInAnyOrder(CERSEI, JAIME, TYRION));
}
/**
* @see DATACMNS-525
*/
@Test
public void singleEntityExecution() {
this.repository.save(LENNISTERS);
assertThat(this.repository.findByAgeAndFirstname(TYRION.getAge(), TYRION.getFirstname()), is(TYRION));
}
/**
* @see DATACMNS-525
*/
@Test
public void findAllShouldRespectSort() {
this.repository.save(LENNISTERS);
assertThat(this.repository.findAll(new Sort(new Sort.Order(Direction.ASC, "age"), new Sort.Order(Direction.DESC,
"firstname"))), IsIterableContainingInOrder.contains(TYRION, JAIME, CERSEI));
}
/**
* @see DATACMNS-525
*/
@Test
public void derivedFinderShouldRespectSort() {
repository.save(LENNISTERS);
List<Person> result = repository.findByAgeGreaterThanOrderByAgeAscFirstnameDesc(2);
assertThat(result, contains(TYRION, JAIME, CERSEI));
}
protected Class<? extends PersonRepository> getRepositoryClass() {
return PersonRepository.class;
}
public static interface PersonRepository extends CrudRepository<Person, String>, KeyValueRepository<Person, String> {
List<Person> findByAge(int age);
List<Person> findByFirstname(String firstname);
List<Person> findByFirstnameAndAge(String firstname, int age);
Page<Person> findByAge(int age, Pageable page);
List<Person> findByAgeOrFirstname(int age, String firstname);
Person findByAgeAndFirstname(int age, String firstname);
List<Person> findByAgeGreaterThanOrderByAgeAscFirstnameDesc(int age);
}
}