DATAJPA-820 - Try to discover more version properties on superclasses.
Tweaked the lookup of a version property on a mapped superclass. Hibernate doesn't expose itself as being very supportive in that: on versions below 4.3 the method primarily intended to look it up (IdentifiableType.getVersion(…)) expects you to hand in exactly the type of the property you're trying to find in the first place. Awesome, not. If this fails, we now explicitly traverse the singular attributes and recursively traverse super types. Unfortunately, on the Hibernate version broken as defined above, the check for attribute.isVersion() fails even for the version property as the implementation holds all singular attributes with one for the version property which is not marked as such. tl;dr; - everyone trying to use @Version on a mapped superclass and a primitive identifier in an entity on Hibernate 4.3 will still have to implement Persistable to make sure EntityManager.persist(…) is used for new entities.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2014 the original author or authors.
|
||||
* Copyright 2011-2015 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.
|
||||
@@ -66,6 +66,7 @@ public class JpaMetamodelEntityInformation<T, ID extends Serializable> extends J
|
||||
this.metamodel = metamodel;
|
||||
|
||||
ManagedType<T> type = metamodel.managedType(domainClass);
|
||||
|
||||
if (type == null) {
|
||||
throw new IllegalArgumentException("The given domain class can not be found in the given Metamodel!");
|
||||
}
|
||||
@@ -76,8 +77,10 @@ public class JpaMetamodelEntityInformation<T, ID extends Serializable> extends J
|
||||
throw new IllegalArgumentException("The given domain class does not contain an id attribute!");
|
||||
}
|
||||
|
||||
this.idMetadata = new IdMetadata<T>((IdentifiableType<T>) type);
|
||||
this.versionAttribute = findVersionAttribute(type);
|
||||
IdentifiableType<T> identifiableType = (IdentifiableType<T>) type;
|
||||
|
||||
this.idMetadata = new IdMetadata<T>(identifiableType);
|
||||
this.versionAttribute = findVersionAttribute(identifiableType, metamodel);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -93,9 +96,18 @@ public class JpaMetamodelEntityInformation<T, ID extends Serializable> extends J
|
||||
* Returns the version attribute of the given {@link ManagedType} or {@literal null} if none available.
|
||||
*
|
||||
* @param type must not be {@literal null}.
|
||||
* @param metamodel must not be {@literal null}.
|
||||
* @return
|
||||
*/
|
||||
private static <T> SingularAttribute<? super T, ?> findVersionAttribute(ManagedType<T> type) {
|
||||
@SuppressWarnings("unchecked")
|
||||
private static <T> SingularAttribute<? super T, ?> findVersionAttribute(IdentifiableType<T> type,
|
||||
Metamodel metamodel) {
|
||||
|
||||
try {
|
||||
return type.getVersion(Object.class);
|
||||
} catch (IllegalArgumentException o_O) {
|
||||
// Needs workarounds as the method is implemented with a strict type check on e.g. Hibernate < 4.3
|
||||
}
|
||||
|
||||
Set<SingularAttribute<? super T, ?>> attributes = type.getSingularAttributes();
|
||||
|
||||
@@ -105,7 +117,21 @@ public class JpaMetamodelEntityInformation<T, ID extends Serializable> extends J
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
Class<?> superType = type.getJavaType().getSuperclass();
|
||||
|
||||
try {
|
||||
|
||||
ManagedType<?> managedSuperType = metamodel.managedType(superType);
|
||||
|
||||
if (!(managedSuperType instanceof IdentifiableType)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (SingularAttribute<? super T, ?>) findVersionAttribute((IdentifiableType<T>) managedSuperType, metamodel);
|
||||
|
||||
} catch (IllegalArgumentException o_O) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -219,8 +245,8 @@ public class JpaMetamodelEntityInformation<T, ID extends Serializable> extends J
|
||||
public IdMetadata(IdentifiableType<T> source) {
|
||||
|
||||
this.type = source;
|
||||
this.attributes = (Set<SingularAttribute<? super T, ?>>) (source.hasSingleIdAttribute() ? Collections
|
||||
.singleton(source.getId(source.getIdType().getJavaType())) : source.getIdClassAttributes());
|
||||
this.attributes = (Set<SingularAttribute<? super T, ?>>) (source.hasSingleIdAttribute()
|
||||
? Collections.singleton(source.getId(source.getIdType().getJavaType())) : source.getIdClassAttributes());
|
||||
}
|
||||
|
||||
public boolean hasSimpleId() {
|
||||
@@ -275,8 +301,8 @@ public class JpaMetamodelEntityInformation<T, ID extends Serializable> extends J
|
||||
*
|
||||
* @author Thomas Darimont
|
||||
*/
|
||||
private static class IdentifierDerivingDirectFieldAccessFallbackBeanWrapper extends
|
||||
DirectFieldAccessFallbackBeanWrapper {
|
||||
private static class IdentifierDerivingDirectFieldAccessFallbackBeanWrapper
|
||||
extends DirectFieldAccessFallbackBeanWrapper {
|
||||
|
||||
private final Metamodel metamodel;
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-2015 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,19 +18,22 @@ package org.springframework.data.jpa.domain.sample;
|
||||
import javax.persistence.GeneratedValue;
|
||||
import javax.persistence.Id;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
import javax.persistence.Version;
|
||||
|
||||
/**
|
||||
* @author Thomas Darimont
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@MappedSuperclass
|
||||
public abstract class AbstractMappedType {
|
||||
|
||||
@Id @GeneratedValue Long id;
|
||||
@Version Long version;
|
||||
String attribute1;
|
||||
|
||||
public AbstractMappedType() {}
|
||||
|
||||
public AbstractMappedType(String attribute1) {
|
||||
this.attribute1 = attribute1;
|
||||
}
|
||||
|
||||
@Id @GeneratedValue Long id;
|
||||
String attribute1;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-2015 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.
|
||||
@@ -30,16 +30,16 @@ import org.springframework.test.context.ContextConfiguration;
|
||||
* @author Oliver Gierke
|
||||
*/
|
||||
@ContextConfiguration("classpath:eclipselink.xml")
|
||||
public class EclipseLinkJpaMetamodelEntityInformationIntegrationTests extends
|
||||
JpaMetamodelEntityInformationIntegrationTests {
|
||||
public class EclipseLinkJpaMetamodelEntityInformationIntegrationTests
|
||||
extends JpaMetamodelEntityInformationIntegrationTests {
|
||||
|
||||
/**
|
||||
* Re-activate test. Change to check for {@link String} as OpenJpa defaults {@link Serializable}s to {@link String}.
|
||||
*/
|
||||
@Test
|
||||
public void reactivatedDetectsIdTypeForMappedSuperclass() {
|
||||
JpaEntityInformation<?, ?> information = JpaEntityInformationSupport.getEntityInformation(
|
||||
AbstractPersistable.class, em);
|
||||
JpaEntityInformation<?, ?> information = JpaEntityInformationSupport.getEntityInformation(AbstractPersistable.class,
|
||||
em);
|
||||
assertEquals(String.class, information.getIdType());
|
||||
}
|
||||
|
||||
@@ -61,6 +61,14 @@ public class EclipseLinkJpaMetamodelEntityInformationIntegrationTests extends
|
||||
@Ignore
|
||||
public void considersEntityWithUnsetCompundIdNew() {}
|
||||
|
||||
/**
|
||||
* Re-activate test for DATAJPA-820.
|
||||
*/
|
||||
@Test
|
||||
public void detectsVersionPropertyOnMappedSuperClass() {
|
||||
super.detectsVersionPropertyOnMappedSuperClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMetadadataPersitenceUnitName() {
|
||||
return "metadata_el";
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2011-2014 the original author or authors.
|
||||
* Copyright 2011-2015 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.
|
||||
@@ -39,6 +39,7 @@ import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.springframework.data.jpa.domain.AbstractPersistable;
|
||||
import org.springframework.data.jpa.domain.sample.ConcreteType1;
|
||||
import org.springframework.data.jpa.domain.sample.PersistableWithIdClass;
|
||||
import org.springframework.data.jpa.domain.sample.PersistableWithIdClassPK;
|
||||
import org.springframework.data.jpa.domain.sample.PrimitiveVersionProperty;
|
||||
@@ -51,6 +52,7 @@ import org.springframework.data.jpa.domain.sample.VersionedUser;
|
||||
import org.springframework.data.repository.core.EntityInformation;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
/**
|
||||
* Integration tests for {@link JpaMetamodelEntityInformation}.
|
||||
@@ -105,7 +107,8 @@ public class JpaMetamodelEntityInformationIntegrationTests {
|
||||
|
||||
PersistableWithIdClass entity = new PersistableWithIdClass(2L, 4L);
|
||||
|
||||
JpaEntityInformation<PersistableWithIdClass, ?> information = getEntityInformation(PersistableWithIdClass.class, em);
|
||||
JpaEntityInformation<PersistableWithIdClass, ?> information = getEntityInformation(PersistableWithIdClass.class,
|
||||
em);
|
||||
Object id = information.getId(entity);
|
||||
|
||||
assertThat(id, is(instanceOf(PersistableWithIdClassPK.class)));
|
||||
@@ -260,6 +263,19 @@ public class JpaMetamodelEntityInformationIntegrationTests {
|
||||
assertThat(information.isNew(user), is(false));
|
||||
}
|
||||
|
||||
/**
|
||||
* @see DATAJPA-820 - Ignored as Hibernate < 4.3 doesn't expose the version property properly if it's declared on the
|
||||
* superclass.
|
||||
*/
|
||||
@Test
|
||||
@Ignore
|
||||
public void detectsVersionPropertyOnMappedSuperClass() {
|
||||
|
||||
EntityInformation<ConcreteType1, ?> information = getEntityInformation(ConcreteType1.class, em);
|
||||
|
||||
assertThat(ReflectionTestUtils.getField(information, "versionAttribute"), is(notNullValue()));
|
||||
}
|
||||
|
||||
protected String getMetadadataPersitenceUnitName() {
|
||||
return "metadata";
|
||||
}
|
||||
|
||||
@@ -45,17 +45,12 @@ import org.springframework.data.jpa.domain.sample.PersistableWithIdClassPK;
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class JpaMetamodelEntityInformationUnitTests {
|
||||
|
||||
@Mock
|
||||
Metamodel metamodel;
|
||||
@Mock Metamodel metamodel;
|
||||
|
||||
@Mock
|
||||
IdentifiableType<PersistableWithIdClass> type;
|
||||
@Mock
|
||||
SingularAttribute<PersistableWithIdClass, ?> first, second;
|
||||
@Mock IdentifiableType<PersistableWithIdClass> type;
|
||||
@Mock SingularAttribute<PersistableWithIdClass, ?> first, second;
|
||||
|
||||
@Mock
|
||||
@SuppressWarnings("rawtypes")
|
||||
Type idType;
|
||||
@Mock @SuppressWarnings("rawtypes") Type idType;
|
||||
|
||||
@Before
|
||||
@SuppressWarnings("unchecked")
|
||||
@@ -68,6 +63,7 @@ public class JpaMetamodelEntityInformationUnitTests {
|
||||
|
||||
when(type.getIdClassAttributes()).thenReturn(attributes);
|
||||
|
||||
when(metamodel.managedType(Object.class)).thenThrow(IllegalArgumentException.class);
|
||||
when(metamodel.managedType(PersistableWithIdClass.class)).thenReturn(type);
|
||||
|
||||
when(type.getIdType()).thenReturn(idType);
|
||||
|
||||
@@ -39,20 +39,17 @@ import org.springframework.data.repository.core.EntityInformation;
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class JpaPersistableEntityInformationUnitTests {
|
||||
|
||||
@Mock
|
||||
Metamodel metamodel;
|
||||
@Mock Metamodel metamodel;
|
||||
|
||||
@Mock
|
||||
EntityType<Foo> type;
|
||||
@Mock EntityType<Foo> type;
|
||||
|
||||
@Mock
|
||||
@SuppressWarnings("rawtypes")
|
||||
Type idType;
|
||||
@Mock @SuppressWarnings("rawtypes") Type idType;
|
||||
|
||||
@Before
|
||||
@SuppressWarnings("unchecked")
|
||||
public void setUp() {
|
||||
|
||||
when(metamodel.managedType(Object.class)).thenThrow(IllegalArgumentException.class);
|
||||
when(metamodel.managedType(Foo.class)).thenReturn(type);
|
||||
when(type.getIdType()).thenReturn(idType);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2013 the original author or authors.
|
||||
* Copyright 2013-2015 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.
|
||||
@@ -42,6 +42,14 @@ public class OpenJpaMetamodelEntityInformationIntegrationTests extends JpaMetamo
|
||||
@Ignore
|
||||
public void findsIdClassOnMappedSuperclass() {}
|
||||
|
||||
/**
|
||||
* Re-activate test for DATAJPA-820.
|
||||
*/
|
||||
@Test
|
||||
public void detectsVersionPropertyOnMappedSuperClass() {
|
||||
super.detectsVersionPropertyOnMappedSuperClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getMetadadataPersitenceUnitName() {
|
||||
return "metadata_oj";
|
||||
|
||||
Reference in New Issue
Block a user