diff --git a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java b/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java index d91bccfb2..4e133b7c8 100644 --- a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java +++ b/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2012 the original author or authors. + * Copyright 2012-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. @@ -38,4 +38,13 @@ class JpaPersistentEntityImpl extends BasicPersistentEntity information, Comparator comparator) { super(information, comparator); } + + /* + * (non-Javadoc) + * @see org.springframework.data.mapping.model.BasicPersistentEntity#returnPropertyIfBetterIdPropertyCandidateOrNull(org.springframework.data.mapping.PersistentProperty) + */ + @Override + protected JpaPersistentProperty returnPropertyIfBetterIdPropertyCandidateOrNull(JpaPersistentProperty property) { + return property.isIdProperty() ? property : null; + } } diff --git a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java b/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java index cf0864bda..9b49831d7 100644 --- a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java +++ b/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java @@ -1,5 +1,5 @@ /* - * Copyright 2012-2013 the original author or authors. + * Copyright 2012-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. @@ -23,6 +23,8 @@ import java.util.Collections; import java.util.HashSet; import java.util.Set; +import javax.persistence.Embeddable; +import javax.persistence.Embedded; import javax.persistence.EmbeddedId; import javax.persistence.Id; import javax.persistence.ManyToMany; @@ -30,8 +32,6 @@ import javax.persistence.ManyToOne; import javax.persistence.OneToMany; import javax.persistence.OneToOne; import javax.persistence.Transient; -import javax.persistence.metamodel.EmbeddableType; -import javax.persistence.metamodel.ManagedType; import javax.persistence.metamodel.Metamodel; import org.springframework.data.mapping.Association; @@ -58,6 +58,7 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty type = metamodel.managedType(getType()); - return !(type instanceof EmbeddableType); + metamodel.managedType(getType()); + return true; } catch (IllegalArgumentException o_O) { return false; } @@ -130,6 +131,10 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty PAB_POST_PROCESSOR = PersistenceAnnotationBeanPostProcessor.class; private static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager"; + private static final String JPA_MAPPING_CONTEXT_BEAN_NAME = "jpaMapppingContext"; /* * (non-Javadoc) @@ -73,40 +79,32 @@ public class JpaRepositoryConfigExtension extends RepositoryConfigurationExtensi /* * (non-Javadoc) - * @see org.springframework.data.repository.config14.RepositoryConfigurationExtensionSupport#postProcess(org.springframework.beans.factory.support.BeanDefinitionBuilder, org.springframework.data.repository.config14.XmlRepositoryConfigurationSource) + * @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#postProcess(org.springframework.beans.factory.support.BeanDefinitionBuilder, org.springframework.data.repository.config.RepositoryConfigurationSource) */ @Override - public void postProcess(BeanDefinitionBuilder builder, XmlRepositoryConfigurationSource config) { + public void postProcess(BeanDefinitionBuilder builder, RepositoryConfigurationSource source) { - Element element = config.getElement(); + String transactionManagerRef = source.getAttribute("transactionManagerRef"); + builder.addPropertyValue("transactionManager", + transactionManagerRef == null ? DEFAULT_TRANSACTION_MANAGER_BEAN_NAME : transactionManagerRef); - postProcess(builder, element.getAttribute("transaction-manager-ref"), - element.getAttribute("entity-manager-factory-ref"), config.getSource()); - } + String entityManagerFactoryRef = getEntityManagerFactoryRef(source); - /* - * (non-Javadoc) - * @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#postProcess(org.springframework.beans.factory.support.BeanDefinitionBuilder, org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource) - */ - @Override - public void postProcess(BeanDefinitionBuilder builder, AnnotationRepositoryConfigurationSource config) { - - AnnotationAttributes attributes = config.getAttributes(); - - postProcess(builder, attributes.getString("transactionManagerRef"), - attributes.getString("entityManagerFactoryRef"), config.getSource()); - } - - private void postProcess(BeanDefinitionBuilder builder, String transactionManagerRef, String entityManagerRef, - Object source) { - - transactionManagerRef = StringUtils.hasText(transactionManagerRef) ? transactionManagerRef - : DEFAULT_TRANSACTION_MANAGER_BEAN_NAME; - builder.addPropertyValue("transactionManager", transactionManagerRef); - - if (StringUtils.hasText(entityManagerRef)) { - builder.addPropertyValue("entityManager", getEntityManagerBeanDefinitionFor(entityManagerRef, source)); + if (entityManagerFactoryRef != null) { + builder.addPropertyValue("entityManager", getEntityManagerBeanDefinitionFor(entityManagerFactoryRef, source)); } + + builder.addPropertyReference("mappingContext", JPA_MAPPING_CONTEXT_BEAN_NAME); + } + + /** + * @param source + * @return + */ + private String getEntityManagerFactoryRef(RepositoryConfigurationSource source) { + + String entityManagerFactoryRef = source.getAttribute("entityManagerFactoryRef"); + return entityManagerFactoryRef == null ? "entityManagerFactory" : entityManagerFactoryRef; } /** @@ -143,10 +141,87 @@ public class JpaRepositoryConfigExtension extends RepositoryConfigurationExtensi registerWithSourceAndGeneratedBeanName(registry, new RootBeanDefinition( EntityManagerBeanDefinitionRegistrarPostProcessor.class), source); + BeanDefinition entityManagerBeanDefinitionFor = getEntityManagerBeanDefinitionFor( + getEntityManagerFactoryRef(configurationSource), source); + + BeanDefinitionBuilder builder = BeanDefinitionBuilder + .rootBeanDefinition(JpaMetamodelMappingContextFactoryBean.class); + builder.addPropertyValue("entityManager", entityManagerBeanDefinitionFor); + + AbstractBeanDefinition definition = builder.getBeanDefinition(); + definition.setSource(source); + registry.registerBeanDefinition(JPA_MAPPING_CONTEXT_BEAN_NAME, definition); + if (!hasBean(PAB_POST_PROCESSOR, registry) && !registry.containsBeanDefinition(AnnotationConfigUtils.PERSISTENCE_ANNOTATION_PROCESSOR_BEAN_NAME)) { registerWithSourceAndGeneratedBeanName(registry, new RootBeanDefinition(PAB_POST_PROCESSOR), source); } } + + /** + * {@link FactoryBean} to setup {@link JpaMetamodelMappingContext} instances from Spring configuration. + * + * @author Oliver Gierke + * @since 1.6 + */ + static class JpaMetamodelMappingContextFactoryBean extends AbstractFactoryBean { + + private EntityManager entityManager; + + /** + * Configures the {@link EntityManager} to use to create the {@link JpaMetamodelMappingContext}. + * + * @param entityManager must not be {@literal null}. + */ + public void setEntityManager(EntityManager entityManager) { + + Assert.notNull(entityManager, "EntityManager must not be null!"); + this.entityManager = entityManager; + } + + /* + * (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 { + + Metamodel metamodel = entityManager.getMetamodel(); + + Set> managedTypes = metamodel.getManagedTypes(); + Set> entitySources = new HashSet>(managedTypes.size()); + + for (ManagedType type : managedTypes) { + entitySources.add(type.getJavaType()); + } + + JpaMetamodelMappingContext context = new JpaMetamodelMappingContext(metamodel); + context.setInitialEntitySet(entitySources); + context.initialize(); + + return context; + } + + /* + * (non-Javadoc) + * @see org.springframework.beans.factory.config.AbstractFactoryBean#afterPropertiesSet() + */ + @Override + public void afterPropertiesSet() throws Exception { + + Assert.notNull(entityManager, "EntityManager must not be null!"); + + super.afterPropertiesSet(); + } + } } diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java index 4593c4b55..68ec233f9 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2012 the original author or authors. + * Copyright 2008-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. @@ -20,7 +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.mapping.context.MappingContext; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.support.RepositoryFactorySupport; import org.springframework.data.repository.core.support.TransactionalRepositoryFactoryBeanSupport; @@ -47,7 +47,15 @@ public class JpaRepositoryFactoryBean, S, ID extends @PersistenceContext public void setEntityManager(EntityManager entityManager) { this.entityManager = entityManager; - setMappingContext(new JpaMetamodelMappingContext(entityManager.getMetamodel())); + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.core.support.RepositoryFactoryBeanSupport#setMappingContext(org.springframework.data.mapping.context.MappingContext) + */ + @Override + public void setMappingContext(MappingContext mappingContext) { + super.setMappingContext(mappingContext); } /* diff --git a/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java b/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java index b99493ed9..32c4b0930 100644 --- a/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImplUnitTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 the original author or authors. + * Copyright 2013-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. @@ -18,6 +18,8 @@ package org.springframework.data.jpa.mapping; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; +import javax.persistence.Embeddable; +import javax.persistence.Embedded; import javax.persistence.OneToOne; import javax.persistence.Transient; import javax.persistence.metamodel.Metamodel; @@ -66,9 +68,44 @@ public class JpaPersistentPropertyImplUnitTests { assertThat(entity.getPersistentProperty("transientProp"), is(nullValue())); } + /** + * @see DATAJPA-484 + */ + @Test + public void considersEmbeddableAnEntity() { + assertThat(context.getPersistentEntity(SampleEmbeddable.class), is(notNullValue())); + } + + /** + * @see DATAJPA-484 + */ + @Test + public void considersEmbeddablePropertyAnAssociation() { + assertThat(entity.getPersistentProperty("embeddable").isAssociation(), is(true)); + } + + /** + * @see DATAJPA-484 + */ + @Test + public void considersEmbeddedPropertyAnAssociation() { + assertThat(entity.getPersistentProperty("embedded").isAssociation(), is(true)); + } + static class Sample { @OneToOne Sample other; @Transient String transientProp; + SampleEmbeddable embeddable; + @Embedded SampleEmbedded embedded; + } + + @Embeddable + static class SampleEmbeddable { + + } + + static class SampleEmbedded { + } } diff --git a/src/test/java/org/springframework/data/jpa/repository/config/AbstractRepositoryConfigTests.java b/src/test/java/org/springframework/data/jpa/repository/config/AbstractRepositoryConfigTests.java index d283a2b4f..6cb9d0573 100644 --- a/src/test/java/org/springframework/data/jpa/repository/config/AbstractRepositoryConfigTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/config/AbstractRepositoryConfigTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2013 the original author or authors. + * Copyright 2008-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. @@ -20,6 +20,7 @@ import static org.junit.Assert.*; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.jpa.mapping.JpaMetamodelMappingContext; import org.springframework.data.jpa.repository.sample.AuditableUserRepository; import org.springframework.data.jpa.repository.sample.RoleRepository; import org.springframework.data.jpa.repository.sample.UserRepository; @@ -33,14 +34,11 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) public abstract class AbstractRepositoryConfigTests { - @Autowired(required = false) - UserRepository userRepository; + @Autowired(required = false) UserRepository userRepository; + @Autowired(required = false) RoleRepository roleRepository; + @Autowired(required = false) AuditableUserRepository auditableUserRepository; - @Autowired(required = false) - RoleRepository roleRepository; - - @Autowired(required = false) - AuditableUserRepository auditableUserRepository; + @Autowired JpaMetamodelMappingContext mappingContext; /** * Asserts that context creation detects 3 repository beans. @@ -53,6 +51,9 @@ public abstract class AbstractRepositoryConfigTests { assertNotNull(auditableUserRepository); } + /** + * @see DATAJPA-330 + */ @Test public void repositoriesHaveExceptionTranslationApplied() { @@ -60,4 +61,12 @@ public abstract class AbstractRepositoryConfigTests { JpaRepositoriesRegistrarIntegrationTests.assertExceptionTranslationActive(roleRepository); JpaRepositoriesRegistrarIntegrationTests.assertExceptionTranslationActive(auditableUserRepository); } + + /** + * @see DATAJPA-??? + */ + @Test + public void exposesJpaMappingContext() { + assertNotNull(mappingContext); + } }