Skip to content

Commit

Permalink
DATAJPA-576 - Improvements in JpaMappingContext setup in configuration.
Browse files Browse the repository at this point in the history
Refactored JpaMappingContext to be able to work with multiple instances of Metamodel. This removes the need to wire a dedicated one in JpaMetamodelContextFactoryBean and thus makes it independent of a particular EntityManager(Factory). This significantly eases the implementation of the configuration integration.
  • Loading branch information
odrotbohm committed Jul 16, 2014
1 parent 3467d92 commit 1998874
Show file tree
Hide file tree
Showing 8 changed files with 224 additions and 145 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.util.Collections;
import java.util.Set;

import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.Metamodel;

import org.springframework.data.mapping.context.AbstractMappingContext;
Expand All @@ -35,17 +38,22 @@
public class JpaMetamodelMappingContext extends
AbstractMappingContext<JpaPersistentEntityImpl<?>, JpaPersistentProperty> {

private final Metamodel model;
private final Set<Metamodel> models;

/**
* Creates a new JPA {@link Metamodel} based {@link MappingContext}.
*
* @param model must not be {@literal null}.
* @param models must not be {@literal null} or empty.
*/
public JpaMetamodelMappingContext(Metamodel model) {
public JpaMetamodelMappingContext(Set<Metamodel> models) {

Assert.notNull(models, "JPA metamodel must not be null!");
Assert.notEmpty(models, "At least one JPA metamodel must be present!");
this.models = models;
}

Assert.notNull(model, "JPA Metamodel must not be null!");
this.model = model;
public JpaMetamodelMappingContext(Metamodel model) {
this(Collections.singleton(model));
}

/*
Expand All @@ -64,7 +72,9 @@ protected <T> JpaPersistentEntityImpl<?> createPersistentEntity(TypeInformation<
@Override
protected JpaPersistentProperty createPersistentProperty(Field field, PropertyDescriptor descriptor,
JpaPersistentEntityImpl<?> owner, SimpleTypeHolder simpleTypeHolder) {
return new JpaPersistentPropertyImpl(model, field, descriptor, owner, simpleTypeHolder);

Metamodel metamodel = getMetamodelFor(owner.getType());
return new JpaPersistentPropertyImpl(metamodel, field, descriptor, owner, simpleTypeHolder);
}

/*
Expand All @@ -73,12 +83,35 @@ protected JpaPersistentProperty createPersistentProperty(Field field, PropertyDe
*/
@Override
protected boolean shouldCreatePersistentEntityFor(TypeInformation<?> type) {
return getMetamodelFor(type.getType()) != null;
}

/**
* Returns the {@link Metamodel} aware of the given type.
*
* @param type
* @return
*/
private Metamodel getMetamodelFor(Class<?> type) {

for (Metamodel model : models) {

try {
model.managedType(type.getType());
return true;
} catch (IllegalArgumentException o_O) {
return false;
try {
model.managedType(type);
return model;
} catch (IllegalArgumentException o_O) {

// Fall back to inspect *all* managed types manually as Metamodel.managedType(…) only
// returns for entities, embeddables and managed supperclasses.

for (ManagedType<?> managedType : model.getManagedTypes()) {
if (type.equals(managedType.getJavaType())) {
return model;
}
}
}
}

return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import org.springframework.data.mapping.PersistentEntity;
import org.springframework.data.mapping.model.AnnotationBasedPersistentProperty;
import org.springframework.data.mapping.model.SimpleTypeHolder;
import org.springframework.util.Assert;

/**
* {@link JpaPersistentProperty} implementation usind a JPA {@link Metamodel}.
Expand Down Expand Up @@ -84,6 +85,9 @@ public JpaPersistentPropertyImpl(Metamodel metamodel, Field field, PropertyDescr
PersistentEntity<?, JpaPersistentProperty> owner, SimpleTypeHolder simpleTypeHolder) {

super(field, propertyDescriptor, owner, simpleTypeHolder);

Assert.notNull(metamodel, "Metamodel must not be null!");

this.metamodel = metamodel;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public class AuditingBeanDefinitionParser implements BeanDefinitionParser {
private static final String AUDITING_BFPP_CLASS_NAME = "org.springframework.data.jpa.domain.support.AuditingBeanFactoryPostProcessor";

private final AuditingHandlerBeanDefinitionParser auditingHandlerParser = new AuditingHandlerBeanDefinitionParser(
JpaRepositoryConfigExtension.JPA_MAPPING_CONTEXT_BEAN_NAME);
BeanDefinitionNames.JPA_MAPPING_CONTEXT_BEAN_NAME);
private final SpringConfiguredBeanDefinitionParser springConfiguredParser = new SpringConfiguredBeanDefinitionParser();

/*
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright 2014 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.repository.config;

/**
* Helper class to manage bean definition names in a single place.
*
* @author Oliver Gierke
*/
class BeanDefinitionNames {

public static final String JPA_MAPPING_CONTEXT_BEAN_NAME = "jpaMapppingContext";
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
package org.springframework.data.jpa.repository.config;

import static org.springframework.data.jpa.domain.support.AuditingBeanFactoryPostProcessor.*;
import static org.springframework.data.jpa.repository.config.BeanDefinitionNames.*;

import java.lang.annotation.Annotation;

Expand All @@ -29,6 +30,7 @@
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport;
import org.springframework.data.auditing.config.AuditingConfiguration;
import org.springframework.data.config.ParsingUtils;
import org.springframework.data.jpa.domain.support.AuditingBeanFactoryPostProcessor;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
Expand Down Expand Up @@ -62,6 +64,17 @@ protected String getAuditingHandlerBeanName() {
return "jpaAuditingHandler";
}

/*
* (non-Javadoc)
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAuditHandlerBeanDefinitionBuilder(org.springframework.data.auditing.config.AuditingConfiguration)
*/
@Override
protected BeanDefinitionBuilder getAuditHandlerBeanDefinitionBuilder(AuditingConfiguration configuration) {

BeanDefinitionBuilder builder = super.getAuditHandlerBeanDefinitionBuilder(configuration);
return builder.addConstructorArgReference(JPA_MAPPING_CONTEXT_BEAN_NAME);
}

/*
* (non-Javadoc)
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#registerBeanDefinitions(org.springframework.core.type.AnnotationMetadata, org.springframework.beans.factory.support.BeanDefinitionRegistry)
Expand All @@ -86,6 +99,11 @@ public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanD
protected void registerAuditListenerBeanDefinition(BeanDefinition auditingHandlerDefinition,
BeanDefinitionRegistry registry) {

if (!registry.containsBeanDefinition(JPA_MAPPING_CONTEXT_BEAN_NAME)) {
registry.registerBeanDefinition(JPA_MAPPING_CONTEXT_BEAN_NAME, //
new RootBeanDefinition(JpaMetamodelMappingContextFactoryBean.class));
}

BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(AuditingEntityListener.class);
builder.addPropertyValue("auditingHandler",
ParsingUtils.getObjectFactoryBeanDefinition(getAuditingHandlerBeanName(), null));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package org.springframework.data.jpa.repository.config;

import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.EntityManagerFactory;
import javax.persistence.metamodel.ManagedType;
import javax.persistence.metamodel.Metamodel;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.config.AbstractFactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.jpa.mapping.JpaMetamodelMappingContext;

/**
* {@link FactoryBean} to setup {@link JpaMetamodelMappingContext} instances from Spring configuration.
*
* @author Oliver Gierke
* @since 1.6
*/
class JpaMetamodelMappingContextFactoryBean extends AbstractFactoryBean<JpaMetamodelMappingContext> implements
ApplicationContextAware {

private ListableBeanFactory beanFactory;

/*
* (non-Javadoc)
* @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.beanFactory = applicationContext;
}

/*
* (non-Javadoc)
* @see org.springframework.beans.factory.config.AbstractFactoryBean#getObjectType()
*/
@Override
public Class<?> getObjectType() {
return JpaMetamodelMappingContext.class;
}

/*
* (non-Javadoc)
* @see org.springframework.beans.factory.config.AbstractFactoryBean#createInstance()
*/
@Override
protected JpaMetamodelMappingContext createInstance() throws Exception {

Set<Metamodel> models = getMetamodels();
Set<Class<?>> entitySources = new HashSet<Class<?>>();

for (Metamodel metamodel : models) {

for (ManagedType<?> type : metamodel.getManagedTypes()) {

Class<?> javaType = type.getJavaType();

if (javaType != null) {
entitySources.add(javaType);
}
}
}

JpaMetamodelMappingContext context = new JpaMetamodelMappingContext(models);
context.setInitialEntitySet(entitySources);
context.initialize();

return context;
}

/**
* Obtains all {@link Metamodel} instances of the current {@link ApplicationContext}.
*
* @return
*/
private Set<Metamodel> getMetamodels() {

Collection<EntityManagerFactory> factories = BeanFactoryUtils.beansOfTypeIncludingAncestors(beanFactory,
EntityManagerFactory.class).values();
Set<Metamodel> metamodels = new HashSet<Metamodel>(factories.size());

for (EntityManagerFactory emf : factories) {
metamodels.add(emf.getMetamodel());
}

return metamodels;
}
}
Loading

0 comments on commit 1998874

Please sign in to comment.