/*
* Copyright 2013-2017 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.jpa.mapping;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.*;
import java.util.Collections;
import java.util.Optional;
import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import javax.persistence.Embedded;
import javax.persistence.ManyToOne;
import javax.persistence.OneToOne;
import javax.persistence.Transient;
import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.Metamodel;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.springframework.data.annotation.AccessType.Type;
import org.springframework.data.annotation.Version;
import org.springframework.data.util.ClassTypeInformation;
import org.springframework.data.util.TypeInformation;
/**
* Unit tests for {@link JpaPersistentPropertyImpl}.
*
* @author Oliver Gierke
* @author Greg Turnquist
*/
@RunWith(MockitoJUnitRunner.class)
public class JpaPersistentPropertyImplUnitTests {
@Mock Metamodel model;
JpaMetamodelMappingContext context;
JpaPersistentEntity<?> entity;
@Before
public void setUp() {
context = new JpaMetamodelMappingContext(Collections.singleton(model));
entity = context.getRequiredPersistentEntity(Sample.class);
}
@Test // DATAJPA-284
public void considersOneToOneMappedPropertyAnAssociation() {
JpaPersistentProperty property = entity.getRequiredPersistentProperty("other");
assertThat(property.isAssociation(), is(true));
}
@Test // DATAJPA-376
public void considersJpaTransientFieldsAsTransient() {
assertThat(entity.getPersistentProperty("transientProp"), is(Optional.empty()));
}
@Test // DATAJPA-484
public void considersEmbeddableAnEntity() {
assertThat(context.getPersistentEntity(SampleEmbeddable.class), is(notNullValue()));
}
@Test // DATAJPA-484
public void considersEmbeddablePropertyAnAssociation() {
assertThat(entity.getRequiredPersistentProperty("embeddable").isAssociation(), is(true));
}
@Test // DATAJPA-484
public void considersEmbeddedPropertyAnAssociation() {
assertThat(entity.getRequiredPersistentProperty("embedded").isAssociation(), is(true));
}
@Test // DATAJPA-619
public void considersPropertyLevelAccessTypeDefinitions() {
assertThat(getProperty(PropertyLevelPropertyAccess.class, "field").usePropertyAccess(), is(false));
assertThat(getProperty(PropertyLevelPropertyAccess.class, "property").usePropertyAccess(), is(true));
}
@Test // DATAJPA-619
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));
}
@Test // DATAJPA-619
public void considersJpaAccessDefinitionAnnotations() {
assertThat(getProperty(TypeLevelPropertyAccess.class, "id").usePropertyAccess(), is(true));
}
@Test // DATAJPA-619
public void springDataAnnotationTrumpsJpaIfBothOnTypeLevel() {
assertThat(getProperty(CompetingTypeLevelAnnotations.class, "id").usePropertyAccess(), is(false));
}
@Test // DATAJPA-619
public void springDataAnnotationTrumpsJpaIfBothOnPropertyLevel() {
assertThat(getProperty(CompetingPropertyLevelAnnotations.class, "id").usePropertyAccess(), is(false));
}
@Test // DATAJPA-605
public void detectsJpaVersionAnnotation() {
assertThat(getProperty(JpaVersioned.class, "version").isVersionProperty(), is(true));
}
@Test // DATAJPA-664
@SuppressWarnings("rawtypes")
public void considersTargetEntityTypeForPropertyType() {
JpaPersistentProperty property = getProperty(SpecializedAssociation.class, "api");
assertThat(property.getType(), is(typeCompatibleWith(Api.class)));
assertThat(property.getActualType(), is(typeCompatibleWith(Implementation.class)));
Iterable<? extends TypeInformation<?>> entityType = property.getPersistentEntityType();
assertThat(entityType.iterator().hasNext(), is(true));
assertThat(entityType.iterator().next(), is((TypeInformation) ClassTypeInformation.from(Implementation.class)));
}
@Test // DATAJPA-716
public void considersNonUpdateablePropertyNotWriteable() {
assertThat(getProperty(WithReadOnly.class, "name").isWritable(), is(false));
assertThat(getProperty(WithReadOnly.class, "updatable").isWritable(), is(true));
}
@Test // DATAJPA-904
public void isEntityWorksEvenWithManagedTypeWithNullJavaType() {
ManagedType<?> managedType = mock(ManagedType.class);
doReturn(Collections.singleton(managedType)).when(model).getManagedTypes();
assertThat(getProperty(Sample.class, "other").isEntity(), is(false));
}
@Test // DATAJPA-1064
public void simplePropertyIsNotConsideredAnAssociation() {
JpaPersistentEntityImpl<?> entity = context.getRequiredPersistentEntity(WithReadOnly.class);
JpaPersistentProperty property = entity.getRequiredPersistentProperty("updatable");
assertThat(property.isAssociation()).isFalse();
}
private JpaPersistentProperty getProperty(Class<?> ownerType, String propertyName) {
JpaPersistentEntity<?> entity = context.getRequiredPersistentEntity(ownerType);
return entity.getRequiredPersistentProperty(propertyName);
}
static class Sample {
@OneToOne Sample other;
@Transient String transientProp;
SampleEmbeddable embeddable;
@Embedded SampleEmbedded embedded;
}
@Embeddable
static class SampleEmbeddable {
}
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;
}
}
static class SpringDataVersioned {
@Version long version;
}
static class JpaVersioned {
@javax.persistence.Version long version;
}
static class SpecializedAssociation {
@ManyToOne(targetEntity = Implementation.class) Api api;
}
static interface Api {}
static class Implementation {}
static class WithReadOnly {
@Column(updatable = false) String name;
String updatable;
}
}