DATAJPA-1105 - Fixed support for nested IdClass with non mapped non primitive type.

When obtaining a multipart id from an entity we have to check for each part of the id if it is another entity.

Before this change that check was done be checking if the type is a primitive or wrapper type, which fails to properly classify e.g. String. This caused us to try to get an id from a String instance which of course failed because String is not a managed type. Now we make the necessary distinction based on JpaMetamodel.isJpaManaged(type).

Changes in the persistence.xml beyond adding entities for tests are required to make the tests work again with EclipseLink. This didn't cause problems in the past because all tests that actually access the database and use the changed persistence context are disabled for EclipseLink. The new test had to be get disabled for EclipseLink though, due to another bug in EclipseLink which prevents the usage of inner classes in @IdClass annotations.

See also: https://bugs.eclipse.org/bugs/show_bug.cgi?id=531528
Original pull request: #251.
This commit is contained in:
Jens Schauder
2018-02-21 11:39:30 +01:00
committed by Oliver Gierke
parent 65f0020636
commit 63ceb6da71
4 changed files with 89 additions and 2 deletions

View File

@@ -35,10 +35,10 @@ import javax.persistence.metamodel.Type.PersistenceType;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.jpa.util.JpaMetamodel;
import org.springframework.data.util.DirectFieldAccessFallbackBeanWrapper;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Implementation of {@link org.springframework.data.repository.core.EntityInformation} that uses JPA {@link Metamodel}
@@ -48,6 +48,7 @@ import org.springframework.util.ClassUtils;
* @author Thomas Darimont
* @author Christoph Strobl
* @author Mark Paluch
* @author Jens Schauder
*/
public class JpaMetamodelEntityInformation<T, ID> extends JpaEntityInformationSupport<T, ID> {
@@ -319,10 +320,12 @@ public class JpaMetamodelEntityInformation<T, ID> extends JpaEntityInformationSu
extends DirectFieldAccessFallbackBeanWrapper {
private final Metamodel metamodel;
private final JpaMetamodel jpaMetamodel;
IdentifierDerivingDirectFieldAccessFallbackBeanWrapper(Class<?> type, Metamodel metamodel) {
super(type);
this.metamodel = metamodel;
this.jpaMetamodel = new JpaMetamodel(metamodel);
}
/**
@@ -374,7 +377,7 @@ public class JpaMetamodelEntityInformation<T, ID> extends JpaEntityInformationSu
Class<? extends Object> idPropertyValueType = idPropertyValue.getClass();
if (ClassUtils.isPrimitiveOrWrapper(idPropertyValueType)) {
if (!jpaMetamodel.isJpaManaged(idPropertyValueType)) {
return idPropertyValue;
}

View File

@@ -28,6 +28,7 @@ import org.springframework.test.context.ContextConfiguration;
* EclipseLink execution for {@link JpaMetamodelEntityInformationIntegrationTests}.
*
* @author Oliver Gierke
* @author Jens Schauder
*/
@ContextConfiguration("classpath:eclipselink.xml")
public class EclipseLinkJpaMetamodelEntityInformationIntegrationTests
@@ -69,6 +70,14 @@ public class EclipseLinkJpaMetamodelEntityInformationIntegrationTests
super.detectsVersionPropertyOnMappedSuperClass();
}
/**
* Ignored due to https://bugs.eclipse.org/bugs/show_bug.cgi?id=531528 EclipseLink doesn't support
* {@link javax.persistence.IdClass} referencing inner classes.
*/
@Override
@Ignore
public void correctlyDeterminesIdValueForNestedIdClassesWithNonPrimitiveNonManagedType() {}
@Override
protected String getMetadadataPersitenceUnitName() {
return "metadata_el";

View File

@@ -19,6 +19,8 @@ import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.springframework.data.jpa.repository.support.JpaEntityInformationSupport.*;
import lombok.Data;
import java.io.Serializable;
import java.sql.Timestamp;
import java.util.Date;
@@ -42,6 +44,7 @@ import org.springframework.test.util.ReflectionTestUtils;
* @author Oliver Gierke
* @author Thomas Darimont
* @author Christoph Strobl
* @author Jens Schauder
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({ "classpath:infrastructure.xml" })
@@ -258,6 +261,26 @@ public class JpaMetamodelEntityInformationIntegrationTests {
assertThat(ReflectionTestUtils.getField(information, "versionAttribute"), is(notNullValue()));
}
@Test // DATAJPA-1105
public void correctlyDeterminesIdValueForNestedIdClassesWithNonPrimitiveNonManagedType() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory(getMetadadataPersitenceUnitName());
EntityManager em = emf.createEntityManager();
JpaEntityInformation<EntityWithNestedIdClass, ?> information = getEntityInformation(EntityWithNestedIdClass.class,
em);
EntityWithNestedIdClass entity = new EntityWithNestedIdClass();
entity.id = 23L;
entity.reference = new EntityWithIdClass();
entity.reference.id1 = "one";
entity.reference.id2 = "two";
Object id = information.getId(entity);
assertThat(id, is(notNullValue()));
}
protected String getMetadadataPersitenceUnitName() {
return "metadata";
}
@@ -283,4 +306,36 @@ public class JpaMetamodelEntityInformationIntegrationTests {
public static class Sample extends Identifiable {
}
@Entity
@Access(AccessType.FIELD)
@IdClass(EntityWithNestedIdClassPK.class)
public static class EntityWithNestedIdClass {
@Id Long id;
@Id @ManyToOne private EntityWithIdClass reference;
}
@Entity
@Access(AccessType.FIELD)
@IdClass(EntityWithIdClassPK.class)
public static class EntityWithIdClass {
@Id String id1;
@Id String id2;
}
@Data
public static class EntityWithIdClassPK implements Serializable {
String id1;
String id2;
}
@Data
public static class EntityWithNestedIdClassPK implements Serializable {
Long id;
EntityWithIdClassPK reference;
}
}

View File

@@ -94,6 +94,12 @@
<class>org.springframework.data.jpa.domain.sample.MailUser</class>
<class>org.springframework.data.jpa.domain.sample.User</class>
<class>org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$Sample</class>
<class>
org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$EntityWithNestedIdClass
</class>
<class>
org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$EntityWithIdClass
</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" />
@@ -107,8 +113,22 @@
<class>org.springframework.data.jpa.domain.sample.MailUser</class>
<class>org.springframework.data.jpa.domain.sample.User</class>
<class>org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$Sample</class>
<class>
org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$EntityWithNestedIdClass
</class>
<class>
org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$EntityWithIdClass
</class>
<class>org.springframework.data.jpa.domain.sample.Dummy</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="javax.persistence.jdbc.driver" value="org.hsqldb.jdbcDriver"/>
<property name="javax.persistence.jdbc.url" value="jdbc:hsqldb:mem:test"/>
<property name="javax.persistence.jdbc.user" value="sa"/>
<property name="javax.persistence.jdbc.password" value=""/>
<property name="eclipselink.ddl-generation" value="create-tables"/>
<property name="eclipselink.ddl-generation.output-mode" value="database"/>
</properties>
</persistence-unit>
<persistence-unit name="metadata_oj">
<provider>org.apache.openjpa.persistence.PersistenceProviderImpl</provider>