diff --git a/src/main/java/org/springframework/data/gemfire/repository/query/DefaultGemfireEntityInformation.java b/src/main/java/org/springframework/data/gemfire/repository/query/DefaultGemfireEntityInformation.java index 056ea1f1..c04eb292 100644 --- a/src/main/java/org/springframework/data/gemfire/repository/query/DefaultGemfireEntityInformation.java +++ b/src/main/java/org/springframework/data/gemfire/repository/query/DefaultGemfireEntityInformation.java @@ -18,16 +18,23 @@ package org.springframework.data.gemfire.repository.query; import java.io.Serializable; import org.springframework.data.gemfire.mapping.GemfirePersistentEntity; +import org.springframework.data.mapping.PersistentProperty; +import org.springframework.data.repository.core.support.AbstractEntityInformation; import org.springframework.data.repository.core.support.DelegatingEntityInformation; -import org.springframework.data.repository.core.support.ReflectionEntityInformation; +import org.springframework.util.Assert; +import org.springframework.util.ReflectionUtils; /** - * Implementation of {@link GemfireEntityInformation} using reflection to lookup region names. - * + * Implementation of {@link GemfireEntityInformation} using reflection to lookup entity properties and type information. + *

* @author Oliver Gierke + * @author John Blum + * @see org.springframework.data.gemfire.mapping.GemfirePersistentEntity + * @see org.springframework.data.gemfire.repository.query.GemfireEntityInformation + * @see org.springframework.data.repository.core.support.DelegatingEntityInformation */ public class DefaultGemfireEntityInformation extends DelegatingEntityInformation -implements GemfireEntityInformation { + implements GemfireEntityInformation { private final GemfirePersistentEntity entity; @@ -37,7 +44,8 @@ implements GemfireEntityInformation { * @param entity must not be {@literal null}. */ public DefaultGemfireEntityInformation(GemfirePersistentEntity entity) { - super(new ReflectionEntityInformation(entity.getType())); + //super(new ReflectionEntityInformation(entity.getType())); + super(new PersistentEntityBasedEntityInformation(entity)); this.entity = entity; } @@ -49,4 +57,45 @@ implements GemfireEntityInformation { public String getRegionName() { return entity.getRegionName(); } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.core.support.AbstractEntityInformation + */ + protected static class PersistentEntityBasedEntityInformation + extends AbstractEntityInformation { + + private final GemfirePersistentEntity persistentEntity; + + public PersistentEntityBasedEntityInformation(final GemfirePersistentEntity persistentEntity) { + super(persistentEntity.getType()); + this.persistentEntity = persistentEntity; + } + + protected GemfirePersistentEntity getPersistentEntity() { + return persistentEntity; + } + + protected PersistentProperty getEntityIdProperty() { + Assert.state(getPersistentEntity().hasIdProperty(), String.format( + "The ID property of entity type (%1$s) was not properly discovered!", + getPersistentEntity().getType())); + + return getPersistentEntity().getIdProperty(); + } + + @SuppressWarnings("unchecked") + public ID getId(final T entity) { + PersistentProperty idProperty = getEntityIdProperty(); + + return (ID) (idProperty.usePropertyAccess() ? ReflectionUtils.invokeMethod(idProperty.getGetter(), entity) + : ReflectionUtils.getField(idProperty.getField(), entity)); + } + + @SuppressWarnings("unchecked") + public Class getIdType() { + return (Class) getEntityIdProperty().getRawType(); + } + } + } diff --git a/src/test/java/org/springframework/data/gemfire/repository/query/DefaultGemfireEntityInformationTest.java b/src/test/java/org/springframework/data/gemfire/repository/query/DefaultGemfireEntityInformationTest.java new file mode 100644 index 00000000..a4d8ae42 --- /dev/null +++ b/src/test/java/org/springframework/data/gemfire/repository/query/DefaultGemfireEntityInformationTest.java @@ -0,0 +1,161 @@ +/* + * Copyright 2010-2013 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.gemfire.repository.query; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; + +import java.io.Serializable; + +import org.junit.Before; +import org.junit.Test; +import org.springframework.data.annotation.Id; +import org.springframework.data.gemfire.mapping.GemfireMappingContext; +import org.springframework.data.gemfire.mapping.GemfirePersistentEntity; +import org.springframework.data.gemfire.repository.sample.Algorithm; +import org.springframework.data.gemfire.repository.sample.Animal; +import org.springframework.data.mapping.context.MappingContext; + +/** + * The DefaultGemfireEntityInformationTest class is a test suite of test cases testing the contract and functionality + * of the DefaultGemfireEntityInformation class used to extract entity information during persistence/mapping operations + * during data access to the underlying data store (GemFire). + *

+ * @author John Blum + * @see org.junit.Test + * @see org.springframework.data.gemfire.repository.query.DefaultGemfireEntityInformation + * @since 1.4.0 + */ +public class DefaultGemfireEntityInformationTest { + + private MappingContext mappingContext; + + @Before + public void setup() { + mappingContext = new GemfireMappingContext(); + } + + protected Algorithm createAlgorithm(final String name) { + return new Algorithm() { + public String getName() { + return name; + } + }; + } + + protected Animal createAnimal(final Long id, final String name) { + Animal animal = new Animal(); + animal.setId(id); + animal.setName(name); + return animal; + } + + protected GemfireEntityInformation createEntityInformation( + final GemfirePersistentEntity persistentEntity) { + return new DefaultGemfireEntityInformation(persistentEntity); + } + + @SuppressWarnings("unchecked") + protected GemfirePersistentEntity createPersistentEntity(final Class domainEntityType) { + return (GemfirePersistentEntity) mappingContext.getPersistentEntity(domainEntityType); + } + + @Test + public void testInterfaceBasedEntity() { + GemfireEntityInformation entityInfo = createEntityInformation( + createPersistentEntity(Algorithm.class)); + + assertNotNull(entityInfo); + assertEquals("Algorithms", entityInfo.getRegionName()); + assertTrue(Algorithm.class.isAssignableFrom(entityInfo.getJavaType())); + assertEquals(String.class, entityInfo.getIdType()); + assertEquals("Quick Sort", entityInfo.getId(createAlgorithm("Quick Sort"))); + } + + @Test + public void testClassBasedEntity() { + GemfireEntityInformation entityInfo = createEntityInformation( + createPersistentEntity(Animal.class)); + + assertNotNull(entityInfo); + assertEquals("Animal", entityInfo.getRegionName()); + assertEquals(Animal.class, entityInfo.getJavaType()); + assertEquals(Long.class, entityInfo.getIdType()); + assertEquals(new Long(1l), entityInfo.getId(createAnimal(1l, "Tyger"))); + } + + @Test + public void testConfusedDomainEntityHavingLongId() { + GemfireEntityInformation entityInfo = createEntityInformation( + createPersistentEntity(MyConfusedDomainEntity.class)); + + assertNotNull(entityInfo); + assertEquals("MyConfusedDomainEntity", entityInfo.getRegionName()); + assertEquals(MyConfusedDomainEntity.class, entityInfo.getJavaType()); + assertEquals(Long.class, entityInfo.getIdType()); + assertEquals(new Long(123l), entityInfo.getId(new MyConfusedDomainEntity(123l))); + } + + @Test + // Uh, WOW! + public void testConfusedDomainEntityHavingStringId() { + GemfireEntityInformation entityInfo = createEntityInformation( + createPersistentEntity(MyConfusedDomainEntity.class)); + + assertNotNull(entityInfo); + assertEquals("MyConfusedDomainEntity", entityInfo.getRegionName()); + assertEquals(MyConfusedDomainEntity.class, entityInfo.getJavaType()); + //assertEquals(String.class, entityInfo.getIdType()); + assertTrue(Long.class.equals(entityInfo.getIdType())); + //assertEquals("123", entityInfo.getId(new MyConfusedDomainEntity(123l))); + assertEquals(123l, entityInfo.getId(new MyConfusedDomainEntity("123"))); + } + + @SuppressWarnings("unused") + protected static class MyConfusedDomainEntity { + + @Id private Long id; + + protected MyConfusedDomainEntity() { + this((Long) null); + } + + protected MyConfusedDomainEntity(final Long id) { + this.id = id; + } + + protected MyConfusedDomainEntity(final String id) { + setId(id); + } + + @Id + protected String getId() { + return String.valueOf(id); + } + + protected final void setId(String id) { + try { + this.id = Long.valueOf(id); + } + catch (NumberFormatException e) { + this.id = null; + } + } + } + +}