DATAJPA-274 - Introduce JPA Metamodel based MappingContext implementation.

Implemented rudimentary MappingContext to expose a JpaPersistentEntity through RepositoryFactoryInformation.
This commit is contained in:
Oliver Gierke
2012-11-28 18:15:49 +01:00
parent 69bcc3d887
commit 258af43397
9 changed files with 418 additions and 7 deletions

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2012 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 java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import javax.persistence.metamodel.Metamodel;
import org.springframework.data.mapping.context.AbstractMappingContext;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.data.util.TypeInformation;
import org.springframework.util.Assert;
/**
* {@link MappingContext} implementation based on a Jpa {@link Metamodel}.
*
* @author Oliver Gierke
* @since 1.3
*/
public class JpaMetamodelMappingContext extends
AbstractMappingContext<JpaPersistentEntityImpl<?>, JpaPersistentProperty> {
private final Metamodel model;
/**
* Creates a new JPA {@link Metamodel} based {@link MappingContext}.
*
* @param model must not be {@literal null}.
*/
public JpaMetamodelMappingContext(Metamodel model) {
Assert.notNull(model, "JPA Metamodel must not be null!");
this.model = model;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.AbstractMappingContext#createPersistentEntity(org.springframework.data.util.TypeInformation)
*/
@Override
protected <T> JpaPersistentEntityImpl<?> createPersistentEntity(TypeInformation<T> typeInformation) {
return new JpaPersistentEntityImpl<T>(typeInformation, null);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.AbstractMappingContext#createPersistentProperty(java.lang.reflect.Field, java.beans.PropertyDescriptor, org.springframework.data.mapping.model.MutablePersistentEntity, org.springframework.data.mapping.model.SimpleTypeHolder)
*/
@Override
protected JpaPersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor,
JpaPersistentEntityImpl<?> owner, SimpleTypeHolder simpleTypeHolder) {
return new JpaPersistentPropertyImpl(model, field, descriptor, owner, simpleTypeHolder);
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.context.AbstractMappingContext#shouldCreatePersistentEntityFor(org.springframework.data.util.TypeInformation)
*/
@Override
protected boolean shouldCreatePersistentEntityFor(TypeInformation<?> type) {
try {
model.managedType(type.getType());
return true;
} catch (IllegalArgumentException o_O) {
return false;
}
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2012 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 org.springframework.data.mapping.PersistentEntity;
/**
* Interface for a JPA-specific entity.
*
* @author Oliver Gierke
* @since 1.3
*/
public interface JpaPersistentEntity<T> extends PersistentEntity<T, JpaPersistentProperty> {
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2012 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 java.util.Comparator;
import org.springframework.data.mapping.model.BasicPersistentEntity;
import org.springframework.data.util.TypeInformation;
/**
* Implementation of {@link JpaPersistentEntity}.
*
* @author Oliver Gierke
* @since 1.3
*/
class JpaPersistentEntityImpl<T> extends BasicPersistentEntity<T, JpaPersistentProperty> implements
JpaPersistentEntity<T> {
/**
* Creates a new {@link JpaPersistentEntityImpl} using the given {@link TypeInformation} and {@link Comparator}.
*
* @param information must not be {@literal null}.
* @param comparator must not be {@literal null}.
*/
public JpaPersistentEntityImpl(TypeInformation<T> information, Comparator<JpaPersistentProperty> comparator) {
super(information, comparator);
}
}

View File

@@ -0,0 +1,28 @@
/*
* Copyright 2012 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 org.springframework.data.mapping.PersistentProperty;
/**
* Interface for a JPA-specific {@link PersistentProperty}.
*
* @author Oliver Gierke
* @since 1.3
*/
public interface JpaPersistentProperty extends PersistentProperty<JpaPersistentProperty> {
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright 2012 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 java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.EmbeddedId;
import javax.persistence.Id;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.metamodel.EmbeddableType;
import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.Metamodel;
import org.springframework.data.mapping.Association;
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
import org.springframework.data.mapping.model.SimpleTypeHolder;
/**
* {@link JpaPersistentProperty} implementation usind a JPA {@link Metamodel}.
*
* @author Oliver Gierke
* @since 1.3
*/
class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty<JpaPersistentProperty> implements
JpaPersistentProperty {
private static final Collection<Class<? extends Annotation>> ASSOCIATION_ANNOTATIONS;
private static final Collection<Class<? extends Annotation>> ID_ANNOTATIONS;
static {
Set<Class<? extends Annotation>> annotations = new HashSet<Class<? extends Annotation>>();
annotations.add(OneToMany.class);
annotations.add(ManyToMany.class);
annotations.add(ManyToOne.class);
ASSOCIATION_ANNOTATIONS = Collections.unmodifiableSet(annotations);
annotations = new HashSet<Class<? extends Annotation>>();
annotations.add(Id.class);
annotations.add(EmbeddedId.class);
ID_ANNOTATIONS = annotations;
}
private final Metamodel metamodel;
/**
* Creates a new {@link JpaPersistentPropertyImpl}
*
* @param metamodel must not be {@literal null}.
* @param field must not be {@literal null}.
* @param propertyDescriptor can be {@literal null}.
* @param owner must not be {@literal null}.
* @param simpleTypeHolder must not be {@literal null}.
*/
public JpaPersistentPropertyImpl(Metamodel metamodel, Field field, PropertyDescriptor propertyDescriptor,
PersistentEntity<?, JpaPersistentProperty> owner, SimpleTypeHolder simpleTypeHolder) {
super(field, propertyDescriptor, owner, simpleTypeHolder);
this.metamodel = metamodel;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.AnnotationBasedPersistentProperty#isIdProperty()
*/
@Override
public boolean isIdProperty() {
for (Class<? extends Annotation> annotation : ID_ANNOTATIONS) {
if (field.getAnnotation(annotation) != null) {
return true;
}
}
return false;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.AbstractPersistentProperty#isEntity()
*/
@Override
public boolean isEntity() {
try {
ManagedType<?> type = metamodel.managedType(getType());
return !(type instanceof EmbeddableType);
} catch (IllegalArgumentException o_O) {
return false;
}
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.AnnotationBasedPersistentProperty#isAssociation()
*/
@Override
public boolean isAssociation() {
for (Class<? extends Annotation> annotationType : ASSOCIATION_ANNOTATIONS) {
if (field.getAnnotation(annotationType) != null) {
return true;
}
}
return false;
}
/*
* (non-Javadoc)
* @see org.springframework.data.mapping.model.AbstractPersistentProperty#createAssociation()
*/
@Override
protected Association<JpaPersistentProperty> createAssociation() {
return new Association<JpaPersistentProperty>(this, null);
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2008-2011 the original author or authors.
* Copyright 2008-2012 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.
@@ -50,6 +50,7 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport {
public JpaRepositoryFactory(EntityManager entityManager) {
Assert.notNull(entityManager);
this.entityManager = entityManager;
this.extractor = PersistenceProvider.fromEntityManager(entityManager);
this.lockModePostProcessor = LockModeRepositoryPostProcessor.INSTANCE;
@@ -59,14 +60,10 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport {
/*
* (non-Javadoc)
*
* @see
* org.springframework.data.repository.support.RepositoryFactorySupport#
* getTargetRepository(java.lang.Class)
* @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getTargetRepository(org.springframework.data.repository.core.RepositoryMetadata)
*/
@Override
protected Object getTargetRepository(RepositoryMetadata metadata) {
return getTargetRepository(metadata, entityManager);
}

View File

@@ -20,6 +20,7 @@ import java.io.Serializable;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.data.jpa.mapping.JpaMetamodelMappingContext;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import org.springframework.data.repository.core.support.TransactionalRepositoryFactoryBeanSupport;
@@ -45,8 +46,8 @@ public class JpaRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends
*/
@PersistenceContext
public void setEntityManager(EntityManager entityManager) {
this.entityManager = entityManager;
setMappingContext(new JpaMetamodelMappingContext(entityManager.getMetamodel()));
}
/*

View File

@@ -0,0 +1,87 @@
/*
* Copyright 2012 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.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.data.jpa.domain.sample.User;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* Integration tests for {@link JpaMetamodelMappingContext}.
*
* @author Oliver Gierke
* @since 1.3
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:infrastructure.xml")
public class JpaMetamodelMappingContextIntegrationTests {
JpaMetamodelMappingContext context;
@PersistenceContext
EntityManager em;
@Before
public void setUp() {
context = new JpaMetamodelMappingContext(em.getMetamodel());
}
@Test
public void setsUpMappingContextCorrectly() {
JpaPersistentEntityImpl<?> entity = context.getPersistentEntity(User.class);
assertThat(entity, is(notNullValue()));
}
@Test
public void detectsIdProperty() {
JpaPersistentEntityImpl<?> entity = context.getPersistentEntity(User.class);
assertThat(entity.getIdProperty(), is(notNullValue()));
}
@Test
public void detectsAssociation() {
JpaPersistentEntityImpl<?> entity = context.getPersistentEntity(User.class);
assertThat(entity, is(notNullValue()));
JpaPersistentProperty property = entity.getPersistentProperty("manager");
assertThat(property.isAssociation(), is(true));
}
@Test
public void detectsPropertyIsEntity() {
JpaPersistentEntityImpl<?> entity = context.getPersistentEntity(User.class);
assertThat(entity, is(notNullValue()));
JpaPersistentProperty property = entity.getPersistentProperty("manager");
assertThat(property.isEntity(), is(true));
property = entity.getPersistentProperty("lastname");
assertThat(property.isEntity(), is(false));
}
}

View File

@@ -24,6 +24,7 @@ import java.util.HashMap;
import java.util.Map;
import javax.persistence.EntityManager;
import javax.persistence.metamodel.Metamodel;
import org.junit.Before;
import org.junit.Test;
@@ -60,6 +61,8 @@ public class JpaRepositoryFactoryBeanUnitTests {
PersistenceExceptionTranslator translator;
@Mock
Repository<?, ?> repository;
@Mock
Metamodel metamodel;
@Before
@SuppressWarnings("unchecked")
@@ -70,6 +73,7 @@ public class JpaRepositoryFactoryBeanUnitTests {
when(beanFactory.getBeansOfType(eq(PersistenceExceptionTranslator.class), anyBoolean(), anyBoolean())).thenReturn(
beans);
when(factory.getRepository(any(Class.class), any(Object.class))).thenReturn(repository);
when(entityManager.getMetamodel()).thenReturn(metamodel);
// Setup standard factory configuration
factoryBean = new DummyJpaRepositoryFactoryBean<SimpleSampleRepository, User, Integer>();