DATAJPA-619 - JpaPersistentPropertyImpl now considers JPA access definition annotations.
JpaPersistentPropertyImpl now looks up both Spring Data and JPA access type definition annotations from both property and type level. Property-level definitions trump type-level ones, Spring Data ones trump JPA ones if defined on the same level. In support of DATAREST-269 to be able to work around id access issues when working with proxies created by JPA providers. Related ticket: DATAREST-269.
This commit is contained in:
@@ -23,6 +23,8 @@ import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.Access;
|
||||
import javax.persistence.AccessType;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Embedded;
|
||||
import javax.persistence.EmbeddedId;
|
||||
@@ -34,6 +36,7 @@ import javax.persistence.OneToOne;
|
||||
import javax.persistence.Transient;
|
||||
import javax.persistence.metamodel.Metamodel;
|
||||
|
||||
import org.springframework.data.annotation.AccessType.Type;
|
||||
import org.springframework.data.mapping.Association;
|
||||
import org.springframework.data.mapping.PersistentEntity;
|
||||
import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
|
||||
@@ -71,6 +74,7 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty<JpaPer
|
||||
}
|
||||
|
||||
private final Metamodel metamodel;
|
||||
private final Boolean usePropertyAccess;
|
||||
|
||||
/**
|
||||
* Creates a new {@link JpaPersistentPropertyImpl}
|
||||
@@ -89,6 +93,7 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty<JpaPer
|
||||
Assert.notNull(metamodel, "Metamodel must not be null!");
|
||||
|
||||
this.metamodel = metamodel;
|
||||
this.usePropertyAccess = detectPropertyAccess();
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -159,4 +164,45 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty<JpaPer
|
||||
protected Association<JpaPersistentProperty> createAssociation() {
|
||||
return new Association<JpaPersistentProperty>(this, null);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see org.springframework.data.mapping.model.AnnotationBasedPersistentProperty#usePropertyAccess()
|
||||
*/
|
||||
@Override
|
||||
public boolean usePropertyAccess() {
|
||||
return usePropertyAccess != null ? usePropertyAccess : super.usePropertyAccess();
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks up both Spring Data's and JPA's access type definition annotations on the property or type level to determine
|
||||
* the access type to be used. Will consider property-level annotations over type-level ones, favoring the Spring Data
|
||||
* ones over the JPA ones if found on the same level. Returns {@literal null} if no explicit annotation can be found
|
||||
* falling back to the defaults implemented in the super class.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private Boolean detectPropertyAccess() {
|
||||
|
||||
org.springframework.data.annotation.AccessType accessType = findAnnotation(org.springframework.data.annotation.AccessType.class);
|
||||
|
||||
if (accessType != null) {
|
||||
return Type.PROPERTY.equals(accessType.value());
|
||||
}
|
||||
|
||||
Access access = findAnnotation(Access.class);
|
||||
|
||||
if (access != null) {
|
||||
return AccessType.PROPERTY.equals(access.value());
|
||||
}
|
||||
|
||||
accessType = findPropertyOrOwnerAnnotation(org.springframework.data.annotation.AccessType.class);
|
||||
|
||||
if (accessType != null) {
|
||||
return Type.PROPERTY.equals(accessType.value());
|
||||
}
|
||||
|
||||
access = findPropertyOrOwnerAnnotation(Access.class);
|
||||
return access == null ? null : AccessType.PROPERTY.equals(access.value());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.springframework.data.jpa.mapping;
|
||||
import static org.hamcrest.CoreMatchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import javax.persistence.Access;
|
||||
import javax.persistence.AccessType;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Embedded;
|
||||
import javax.persistence.OneToOne;
|
||||
@@ -29,6 +31,7 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.springframework.data.annotation.AccessType.Type;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link JpaPersistentPropertyImpl}.
|
||||
@@ -92,6 +95,59 @@ public class JpaPersistentPropertyImplUnitTests {
|
||||
assertThat(entity.getPersistentProperty("embedded").isAssociation(), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-619
|
||||
*/
|
||||
@Test
|
||||
public void considersPropertyLevelAccessTypeDefinitions() {
|
||||
|
||||
assertThat(getProperty(PropertyLevelPropertyAccess.class, "field").usePropertyAccess(), is(false));
|
||||
assertThat(getProperty(PropertyLevelPropertyAccess.class, "property").usePropertyAccess(), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-619
|
||||
*/
|
||||
@Test
|
||||
public void propertyLevelAccessTypeTrumpsTypeLevelDefinition() {
|
||||
|
||||
assertThat(getProperty(PropertyLevelDefinitionTrumpsTypeLevelOne.class, "field").usePropertyAccess(), is(false));
|
||||
assertThat(getProperty(PropertyLevelDefinitionTrumpsTypeLevelOne.class, "property").usePropertyAccess(), is(true));
|
||||
|
||||
assertThat(getProperty(PropertyLevelDefinitionTrumpsTypeLevelOne2.class, "field").usePropertyAccess(), is(false));
|
||||
assertThat(getProperty(PropertyLevelDefinitionTrumpsTypeLevelOne2.class, "property").usePropertyAccess(), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-619
|
||||
*/
|
||||
@Test
|
||||
public void considersJpaAccessDefinitionAnnotations() {
|
||||
assertThat(getProperty(TypeLevelPropertyAccess.class, "id").usePropertyAccess(), is(true));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-619
|
||||
*/
|
||||
@Test
|
||||
public void springDataAnnotationTrumpsJpaIfBothOnTypeLevel() {
|
||||
assertThat(getProperty(CompetingTypeLevelAnnotations.class, "id").usePropertyAccess(), is(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-619
|
||||
*/
|
||||
@Test
|
||||
public void springDataAnnotationTrumpsJpaIfBothOnPropertyLevel() {
|
||||
assertThat(getProperty(CompetingPropertyLevelAnnotations.class, "id").usePropertyAccess(), is(false));
|
||||
}
|
||||
|
||||
private JpaPersistentProperty getProperty(Class<?> ownerType, String propertyName) {
|
||||
|
||||
JpaPersistentEntity<?> entity = context.getPersistentEntity(ownerType);
|
||||
return entity.getPersistentProperty(propertyName);
|
||||
}
|
||||
|
||||
static class Sample {
|
||||
|
||||
@OneToOne Sample other;
|
||||
@@ -108,4 +164,81 @@ public class JpaPersistentPropertyImplUnitTests {
|
||||
static class SampleEmbedded {
|
||||
|
||||
}
|
||||
|
||||
@Access(AccessType.PROPERTY)
|
||||
static class TypeLevelPropertyAccess {
|
||||
|
||||
private String id;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
static class PropertyLevelPropertyAccess {
|
||||
|
||||
String field;
|
||||
String property;
|
||||
|
||||
/**
|
||||
* @return the property
|
||||
*/
|
||||
@org.springframework.data.annotation.AccessType(Type.PROPERTY)
|
||||
public String getProperty() {
|
||||
return property;
|
||||
}
|
||||
}
|
||||
|
||||
@Access(AccessType.FIELD)
|
||||
static class PropertyLevelDefinitionTrumpsTypeLevelOne {
|
||||
|
||||
String field;
|
||||
String property;
|
||||
|
||||
/**
|
||||
* @return the property
|
||||
*/
|
||||
@org.springframework.data.annotation.AccessType(Type.PROPERTY)
|
||||
public String getProperty() {
|
||||
return property;
|
||||
}
|
||||
}
|
||||
|
||||
@org.springframework.data.annotation.AccessType(Type.PROPERTY)
|
||||
static class PropertyLevelDefinitionTrumpsTypeLevelOne2 {
|
||||
|
||||
@Access(AccessType.FIELD) String field;
|
||||
String property;
|
||||
|
||||
/**
|
||||
* @return the property
|
||||
*/
|
||||
public String getProperty() {
|
||||
return property;
|
||||
}
|
||||
}
|
||||
|
||||
@org.springframework.data.annotation.AccessType(Type.FIELD)
|
||||
@Access(AccessType.PROPERTY)
|
||||
static class CompetingTypeLevelAnnotations {
|
||||
|
||||
private String id;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
|
||||
@org.springframework.data.annotation.AccessType(Type.FIELD)
|
||||
@Access(AccessType.PROPERTY)
|
||||
static class CompetingPropertyLevelAnnotations {
|
||||
|
||||
private String id;
|
||||
|
||||
@org.springframework.data.annotation.AccessType(Type.FIELD)
|
||||
@Access(AccessType.PROPERTY)
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user