Skip to content

Create PersistentEntity only for user class in case of proxy instance. #2486

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

<groupId>org.springframework.data</groupId>
<artifactId>spring-data-commons</artifactId>
<version>2.6.0-SNAPSHOT</version>
<version>2.6.0-GH-2485-SNAPSHOT</version>

<name>Spring Data Core</name>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -360,6 +360,17 @@ protected Optional<E> addPersistentEntity(TypeInformation<?> typeInformation) {
return persistentEntity;
}

if(typeInformation.isProxyTypeInformation()) {

TypeInformation<?> userTypeInformation = typeInformation.getUserTypeInformation();
persistentEntity = persistentEntities.get(userTypeInformation);

if (persistentEntity != null) {
persistentEntities.put(typeInformation, persistentEntity);
return persistentEntity;
}
}

} finally {
read.unlock();
}
Expand All @@ -369,7 +380,10 @@ protected Optional<E> addPersistentEntity(TypeInformation<?> typeInformation) {
try {

write.lock();
entity = doAddPersistentEntity(typeInformation);
entity = doAddPersistentEntity(typeInformation.getUserTypeInformation());
if(typeInformation.isProxyTypeInformation()) {
persistentEntities.put(typeInformation, Optional.of(entity));
}

} catch (BeansException e) {
throw new MappingException(e.getMessage(), e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import org.springframework.util.Assert;
import org.springframework.util.ConcurrentReferenceHashMap;
import org.springframework.util.ConcurrentReferenceHashMap.ReferenceType;
import org.springframework.util.ObjectUtils;

/**
* {@link TypeInformation} for a plain {@link Class}.
Expand Down Expand Up @@ -177,4 +178,26 @@ public TypeInformation<? extends S> specialize(ClassTypeInformation<?> type) {
public String toString() {
return type.getName();
}

@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}

if (!super.equals(o)) {
return false;
}

ClassTypeInformation<?> that = (ClassTypeInformation<?>) o;
return ObjectUtils.nullSafeEquals(type, that.type);
}

@Override
public int hashCode() {

int result = super.hashCode();
result = 31 * result + ObjectUtils.nullSafeHashCode(type);
return result;
}
}
23 changes: 23 additions & 0 deletions src/main/java/org/springframework/data/util/TypeInformation.java
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,29 @@ default TypeInformation<?> getRequiredMapValueType() {
*/
Class<S> getType();

/**
* Returns the user type of the property if proxied.
*
* @return the unpacked (if proxied) type of the property.
* @see ProxyUtils#getUserClass(Class)
* @since 2.6
*/
default TypeInformation<?> getUserTypeInformation() {

Class<?> userType = ProxyUtils.getUserClass(getType());
return userType.equals(getType()) ? this : ClassTypeInformation.from(userType);
}

/**
* Returns if {@link #getType()} refers to a proxy or user class.
*
* @return true if type is a proxy.
* @since 2.6
*/
default boolean isProxyTypeInformation() {
return !this.equals(getUserTypeInformation());
}

/**
* Returns a {@link ClassTypeInformation} to represent the {@link TypeInformation} of the raw type of the current
* instance.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,14 @@
import java.util.TreeMap;
import java.util.function.Supplier;

import org.aopalliance.aop.Advice;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.aop.Advisor;
import org.springframework.aop.SpringProxy;
import org.springframework.aop.TargetSource;
import org.springframework.aop.framework.Advised;
import org.springframework.aop.framework.AopConfigException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.springframework.data.annotation.Id;
Expand All @@ -54,6 +60,7 @@
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.StreamUtils;
import org.springframework.data.util.TypeInformation;
import org.springframework.lang.Nullable;

/**
* Unit test for {@link AbstractMappingContext}.
Expand Down Expand Up @@ -323,6 +330,40 @@ void shouldNotCreatePersistentEntityForMapButItsGenericTypeArguments() {
.doesNotContain(List.class, Map.class, String.class, Integer.class);
}

@Test // GH-2485
void contextSeesUserTypeBeforeProxy() {

SampleMappingContext context = new SampleMappingContext();
BasicPersistentEntity<Object, SamplePersistentProperty> persistentEntity = context.getPersistentEntity(Base.class);
persistentEntity.getTypeInformation().getType().equals(Base.class);

assertThat(context.hasPersistentEntityFor(Base.class)).isTrue();
assertThat(context.hasPersistentEntityFor(Base$$SpringProxy$873fa2e.class)).isFalse();

BasicPersistentEntity<Object, SamplePersistentProperty> persistentEntityForProxy = context.getPersistentEntity(Base$$SpringProxy$873fa2e.class);
persistentEntityForProxy.getTypeInformation().getType().equals(Base.class);
assertThat(context.hasPersistentEntityFor(Base$$SpringProxy$873fa2e.class)).isTrue();

assertThat(context.getPersistentEntities()).hasSize(1); // only one distinct instance
}

@Test // GH-2485
void contextSeesProxyBeforeUserType() {

SampleMappingContext context = new SampleMappingContext();

BasicPersistentEntity<Object, SamplePersistentProperty> persistentEntityForProxy = context.getPersistentEntity(Base$$SpringProxy$873fa2e.class);
persistentEntityForProxy.getTypeInformation().getType().equals(Base.class);

assertThat(context.hasPersistentEntityFor(Base$$SpringProxy$873fa2e.class)).isTrue();
assertThat(context.hasPersistentEntityFor(Base.class)).isTrue();

BasicPersistentEntity<Object, SamplePersistentProperty> persistentEntity = context.getPersistentEntity(Base.class);
persistentEntity.getTypeInformation().getType().equals(Base.class);

assertThat(context.getPersistentEntities()).hasSize(1); // only one distinct instance
}

private static void assertHasEntityFor(Class<?> type, SampleMappingContext context, boolean expected) {

boolean found = false;
Expand Down Expand Up @@ -505,4 +546,123 @@ class WithMap {
Map<MapKey, Integer> mapOfKeyToPerson;
}

static class Base$$SpringProxy$873fa2e extends Base implements SpringProxy, Advised {

@Override
public boolean isFrozen() {
return false;
}

@Override
public boolean isProxyTargetClass() {
return false;
}

@Override
public Class<?>[] getProxiedInterfaces() {
return new Class[0];
}

@Override
public boolean isInterfaceProxied(Class<?> intf) {
return false;
}

@Override
public void setTargetSource(TargetSource targetSource) {

}

@Override
public TargetSource getTargetSource() {
return null;
}

@Override
public void setExposeProxy(boolean exposeProxy) {

}

@Override
public boolean isExposeProxy() {
return false;
}

@Override
public void setPreFiltered(boolean preFiltered) {

}

@Override
public boolean isPreFiltered() {
return false;
}

@Override
public Advisor[] getAdvisors() {
return new Advisor[0];
}

@Override
public void addAdvisor(Advisor advisor) throws AopConfigException {

}

@Override
public void addAdvisor(int pos, Advisor advisor) throws AopConfigException {

}

@Override
public boolean removeAdvisor(Advisor advisor) {
return false;
}

@Override
public void removeAdvisor(int index) throws AopConfigException {

}

@Override
public int indexOf(Advisor advisor) {
return 0;
}

@Override
public boolean replaceAdvisor(Advisor a, Advisor b) throws AopConfigException {
return false;
}

@Override
public void addAdvice(Advice advice) throws AopConfigException {

}

@Override
public void addAdvice(int pos, Advice advice) throws AopConfigException {

}

@Override
public boolean removeAdvice(Advice advice) {
return false;
}

@Override
public int indexOf(Advice advice) {
return 0;
}

@Override
public String toProxyConfigString() {
return null;
}

@Nullable
@Override
public Class<?> getTargetClass() {
return null;
}
}

}
Loading