Simplify accessing the ID of an entity.

Delegate ID access to the JPA provider.

Closes #1854.
This commit is contained in:
Greg L. Turnquist
2022-06-09 16:44:38 -05:00
parent f2e7bddb5a
commit cb6c74f89d
15 changed files with 277 additions and 249 deletions

View File

@@ -16,6 +16,7 @@
package org.springframework.data.jpa.repository.support;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceUnitUtil;
import jakarta.persistence.metamodel.Metamodel;
import org.springframework.data.domain.Persistable;
@@ -29,6 +30,7 @@ import org.springframework.util.Assert;
*
* @author Oliver Gierke
* @author Mark Paluch
* @author Greg Turnquist
*/
public abstract class JpaEntityInformationSupport<T, ID> extends AbstractEntityInformation<T, ID>
implements JpaEntityInformation<T, ID> {
@@ -59,11 +61,12 @@ public abstract class JpaEntityInformationSupport<T, ID> extends AbstractEntityI
Assert.notNull(em, "EntityManager must not be null");
Metamodel metamodel = em.getMetamodel();
PersistenceUnitUtil persistenceUnitUtil = em.getEntityManagerFactory().getPersistenceUnitUtil();
if (Persistable.class.isAssignableFrom(domainClass)) {
return new JpaPersistableEntityInformation(domainClass, metamodel);
return new JpaPersistableEntityInformation(domainClass, metamodel, persistenceUnitUtil);
} else {
return new JpaMetamodelEntityInformation(domainClass, metamodel);
return new JpaMetamodelEntityInformation(domainClass, metamodel, persistenceUnitUtil);
}
}

View File

@@ -15,14 +15,8 @@
*/
package org.springframework.data.jpa.repository.support;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import jakarta.persistence.IdClass;
import jakarta.persistence.PersistenceUnitUtil;
import jakarta.persistence.metamodel.Attribute;
import jakarta.persistence.metamodel.EntityType;
import jakarta.persistence.metamodel.IdentifiableType;
@@ -30,15 +24,19 @@ import jakarta.persistence.metamodel.ManagedType;
import jakarta.persistence.metamodel.Metamodel;
import jakarta.persistence.metamodel.SingularAttribute;
import jakarta.persistence.metamodel.Type;
import jakarta.persistence.metamodel.Type.PersistenceType;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.data.jpa.provider.PersistenceProvider;
import org.springframework.data.jpa.util.JpaMetamodel;
import org.springframework.data.util.DirectFieldAccessFallbackBeanWrapper;
import org.springframework.data.util.ProxyUtils;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;
@@ -51,6 +49,7 @@ import org.springframework.util.Assert;
* @author Christoph Strobl
* @author Mark Paluch
* @author Jens Schauder
* @author Greg Turnquist
*/
public class JpaMetamodelEntityInformation<T, ID> extends JpaEntityInformationSupport<T, ID> {
@@ -58,14 +57,17 @@ public class JpaMetamodelEntityInformation<T, ID> extends JpaEntityInformationSu
private final Optional<SingularAttribute<? super T, ?>> versionAttribute;
private final Metamodel metamodel;
private final @Nullable String entityName;
private final PersistenceUnitUtil persistenceUnitUtil;
/**
* Creates a new {@link JpaMetamodelEntityInformation} for the given domain class and {@link Metamodel}.
*
*
* @param domainClass must not be {@literal null}.
* @param metamodel must not be {@literal null}.
* @param persistenceUnitUtil must not be {@literal null}.
*/
public JpaMetamodelEntityInformation(Class<T> domainClass, Metamodel metamodel) {
public JpaMetamodelEntityInformation(Class<T> domainClass, Metamodel metamodel,
PersistenceUnitUtil persistenceUnitUtil) {
super(domainClass);
@@ -88,6 +90,9 @@ public class JpaMetamodelEntityInformation<T, ID> extends JpaEntityInformationSu
this.idMetadata = new IdMetadata<>(identifiableType, PersistenceProvider.fromMetamodel(metamodel));
this.versionAttribute = findVersionAttribute(identifiableType, metamodel);
Assert.notNull(persistenceUnitUtil, "PersistenceUnitUtil must not be null");
this.persistenceUnitUtil = persistenceUnitUtil;
}
@Override
@@ -147,27 +152,25 @@ public class JpaMetamodelEntityInformation<T, ID> extends JpaEntityInformationSu
return (ID) persistenceProvider.getIdentifierFrom(entity);
}
// if not a proxy use Spring mechanics to access the id.
BeanWrapper entityWrapper = new DirectFieldAccessFallbackBeanWrapper(entity);
// If it's a simple type, then immediately delegate to the provider
if (idMetadata.hasSimpleId()) {
return (ID) entityWrapper.getPropertyValue(idMetadata.getSimpleIdAttribute().getName());
return (ID) persistenceUnitUtil.getIdentifier(entity);
}
BeanWrapper idWrapper = new IdentifierDerivingDirectFieldAccessFallbackBeanWrapper(idMetadata.getType(), metamodel);
// otherwise, check if the complex id type has any partially filled fields
BeanWrapper entityWrapper = new DirectFieldAccessFallbackBeanWrapper(entity);
boolean partialIdValueFound = false;
for (SingularAttribute<? super T, ?> attribute : idMetadata) {
Object propertyValue = entityWrapper.getPropertyValue(attribute.getName());
if (propertyValue != null) {
partialIdValueFound = true;
}
idWrapper.setPropertyValue(attribute.getName(), propertyValue);
}
return partialIdValueFound ? (ID) idWrapper.getWrappedInstance() : null;
return partialIdValueFound ? (ID) persistenceUnitUtil.getIdentifier(entity) : null;
}
@Override
@@ -209,7 +212,7 @@ public class JpaMetamodelEntityInformation<T, ID> extends JpaEntityInformationSu
@Override
public boolean isNew(T entity) {
if (!versionAttribute.isPresent()
if (versionAttribute.isEmpty()
|| versionAttribute.map(Attribute::getJavaType).map(Class::isPrimitive).orElse(false)) {
return super.isNew(entity);
}
@@ -237,9 +240,9 @@ public class JpaMetamodelEntityInformation<T, ID> extends JpaEntityInformationSu
this.type = source;
this.idClassAttributes = persistenceProvider.getIdClassAttributes(source);
this.attributes = (Set<SingularAttribute<? super T, ?>>) (source.hasSingleIdAttribute()
this.attributes = source.hasSingleIdAttribute()
? Collections.singleton(source.getId(source.getIdType().getJavaType()))
: source.getIdClassAttributes());
: source.getIdClassAttributes();
}
boolean hasSimpleId() {
@@ -298,121 +301,4 @@ public class JpaMetamodelEntityInformation<T, ID> extends JpaEntityInformationSu
return attributes.iterator();
}
}
/**
* Custom extension of {@link DirectFieldAccessFallbackBeanWrapper} that allows to derive the identifier if composite
* keys with complex key attribute types (e.g. types that are annotated with {@code @Entity} themselves) are used.
*
* @author Thomas Darimont
*/
private static class IdentifierDerivingDirectFieldAccessFallbackBeanWrapper
extends DirectFieldAccessFallbackBeanWrapper {
private final Metamodel metamodel;
private final JpaMetamodel jpaMetamodel;
IdentifierDerivingDirectFieldAccessFallbackBeanWrapper(Class<?> type, Metamodel metamodel) {
super(type);
this.metamodel = metamodel;
this.jpaMetamodel = JpaMetamodel.of(metamodel);
}
/**
* In addition to the functionality described in {@link BeanWrapperImpl} it is checked whether we have a nested
* entity that is part of the id key. If this is the case, we need to derive the identifier of the nested entity.
*/
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public void setPropertyValue(String propertyName, @Nullable Object value) {
if (!isIdentifierDerivationNecessary(value)) {
super.setPropertyValue(propertyName, value);
return;
}
// Derive the identifier from the nested entity that is part of the composite key.
JpaMetamodelEntityInformation nestedEntityInformation = new JpaMetamodelEntityInformation(
ProxyUtils.getUserClass(value), this.metamodel);
if (!nestedEntityInformation.getJavaType().isAnnotationPresent(IdClass.class)) {
Object nestedIdPropertyValue = new DirectFieldAccessFallbackBeanWrapper(value)
.getPropertyValue(nestedEntityInformation.getRequiredIdAttribute().getName());
super.setPropertyValue(propertyName, nestedIdPropertyValue);
return;
}
// We have an IdClass property, we need to inspect the current value in order to map potentially multiple id
// properties correctly.
BeanWrapper sourceIdValueWrapper = new DirectFieldAccessFallbackBeanWrapper(value);
BeanWrapper targetIdClassTypeWrapper = new BeanWrapperImpl(nestedEntityInformation.getIdType());
for (String idAttributeName : (Iterable<String>) nestedEntityInformation.getIdAttributeNames()) {
targetIdClassTypeWrapper.setPropertyValue(idAttributeName,
extractActualIdPropertyValue(sourceIdValueWrapper, idAttributeName));
}
super.setPropertyValue(propertyName, targetIdClassTypeWrapper.getWrappedInstance());
}
@Nullable
private Object extractActualIdPropertyValue(BeanWrapper sourceIdValueWrapper, String idAttributeName) {
Object idPropertyValue = sourceIdValueWrapper.getPropertyValue(idAttributeName);
if (idPropertyValue != null) {
Class<?> idPropertyValueType = idPropertyValue.getClass();
if (!jpaMetamodel.isJpaManaged(idPropertyValueType)) {
return idPropertyValue;
}
return new DirectFieldAccessFallbackBeanWrapper(idPropertyValue)
.getPropertyValue(tryFindSingularIdAttributeNameOrUseFallback(idPropertyValueType, idAttributeName));
}
return null;
}
private String tryFindSingularIdAttributeNameOrUseFallback(Class<?> idPropertyValueType,
String fallbackIdTypePropertyName) {
ManagedType<?> idPropertyType = metamodel.managedType(idPropertyValueType);
for (SingularAttribute<?, ?> sa : idPropertyType.getSingularAttributes()) {
if (sa.isId()) {
return sa.getName();
}
}
return fallbackIdTypePropertyName;
}
/**
* @param value
* @return {@literal true} if the given value is not {@literal null} and a mapped persistable entity otherwise
* {@literal false}
*/
private boolean isIdentifierDerivationNecessary(@Nullable Object value) {
if (value == null) {
return false;
}
Class<?> userClass = ProxyUtils.getUserClass(value);
if (!this.jpaMetamodel.isJpaManaged(userClass)) {
return false;
}
ManagedType<?> managedType = this.metamodel.managedType(userClass);
if (managedType == null) {
throw new IllegalStateException("ManagedType must not be null; We checked that it exists before.");
}
return managedType.getPersistenceType() == PersistenceType.ENTITY;
}
}
}

View File

@@ -15,6 +15,7 @@
*/
package org.springframework.data.jpa.repository.support;
import jakarta.persistence.PersistenceUnitUtil;
import jakarta.persistence.metamodel.Metamodel;
import org.springframework.data.domain.Persistable;
@@ -32,12 +33,14 @@ public class JpaPersistableEntityInformation<T extends Persistable<ID>, ID>
/**
* Creates a new {@link JpaPersistableEntityInformation} for the given domain class and {@link Metamodel}.
*
*
* @param domainClass must not be {@literal null}.
* @param metamodel must not be {@literal null}.
* @param persistenceUnitUtil must not be {@literal null}.
*/
public JpaPersistableEntityInformation(Class<T> domainClass, Metamodel metamodel) {
super(domainClass, metamodel);
public JpaPersistableEntityInformation(Class<T> domainClass, Metamodel metamodel,
PersistenceUnitUtil persistenceUnitUtil) {
super(domainClass, metamodel, persistenceUnitUtil);
}
@Override

View File

@@ -16,31 +16,40 @@
package org.springframework.data.jpa.repository.support;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.jpa.repository.support.JpaEntityInformationSupport.*;
import java.io.Serializable;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.data.jpa.domain.AbstractPersistable;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
/**
* EclipseLink execution for {@link JpaMetamodelEntityInformationIntegrationTests}.
*
* @author Oliver Gierke
* @author Jens Schauder
* @author Greg Turnquist
*/
@ContextConfiguration("classpath:eclipselink.xml")
class EclipseLinkJpaMetamodelEntityInformationIntegrationTests
extends JpaMetamodelEntityInformationIntegrationTests {
@ExtendWith(SpringExtension.class)
@ContextConfiguration({ "classpath:infrastructure.xml", "classpath:eclipselink.xml" })
class EclipseLinkJpaMetamodelEntityInformationIntegrationTests extends JpaMetamodelEntityInformationIntegrationTests {
@Override
String getMetadadataPersistenceUnitName() {
return "metadata_el";
}
/**
* Re-activate test. Change to check for {@link String} as OpenJpa defaults {@link Serializable}s to {@link String}.
* Change to check for {@link String} as EclipseLink defaults {@link Serializable}s to {@link String}.
*/
@Test
void reactivatedDetectsIdTypeForMappedSuperclass() {
JpaEntityInformation<?, ?> information = JpaEntityInformationSupport.getEntityInformation(AbstractPersistable.class,
em);
@Override
void detectsIdTypeForMappedSuperclass() {
JpaEntityInformation<?, ?> information = getEntityInformation(AbstractPersistable.class, em);
assertThat(information.getIdType()).isEqualTo(String.class);
}
@@ -58,19 +67,6 @@ class EclipseLinkJpaMetamodelEntityInformationIntegrationTests
@Disabled
void detectsNewStateForEntityWithPrimitiveId() {}
@Override
@Disabled
void considersEntityWithUnsetCompundIdNew() {}
/**
* Re-activate test for DATAJPA-820.
*/
@Test
@Override
void detectsVersionPropertyOnMappedSuperClass() {
super.detectsVersionPropertyOnMappedSuperClass();
}
/**
* This test fails due to https://bugs.eclipse.org/bugs/show_bug.cgi?id=531528 IdentifiableType.hasSingleIdAttribute()
* returns true when IdClass references an inner class. This bug is supposedly fixed, but the test still fails.
@@ -78,23 +74,9 @@ class EclipseLinkJpaMetamodelEntityInformationIntegrationTests
@Disabled
@Test
@Override
void correctlyDeterminesIdValueForNestedIdClassesWithNonPrimitiveNonManagedType() {
super.correctlyDeterminesIdValueForNestedIdClassesWithNonPrimitiveNonManagedType();
}
void correctlyDeterminesIdValueForNestedIdClassesWithNonPrimitiveNonManagedType() {}
/**
* This test fails due to https://bugs.eclipse.org/bugs/show_bug.cgi?id=531528 IdentifiableType.hasSingleIdAttribute()
* returns true when IdClass references an inner class. This bug is supposedly fixed, but the test still fails.
*/
@Override
@Disabled
@Test
@Override
void proxiedIdClassElement() {
super.proxiedIdClassElement();
}
@Override
String getMetadadataPersitenceUnitName() {
return "metadata_el";
}
void prefersPrivateGetterOverFieldAccess() {}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright 2011-2022 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
*
* https://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.support;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
/**
* Hibernate execution for {@link JpaMetamodelEntityInformationIntegrationTests}.
*
* @author Greg Turnquist
*/
@ExtendWith(SpringExtension.class)
@ContextConfiguration("classpath:infrastructure.xml")
public class HibernateJpaMetamodelEntityInformationIntegrationTests
extends JpaMetamodelEntityInformationIntegrationTests {
@Override
String getMetadadataPersistenceUnitName() {
return "metadata-id-handling";
}
}

View File

@@ -23,6 +23,8 @@ import java.util.Collections;
import jakarta.persistence.Entity;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.PersistenceUnitUtil;
import jakarta.persistence.metamodel.Metamodel;
import jakarta.persistence.metamodel.SingularAttribute;
@@ -45,6 +47,8 @@ public class JpaEntityInformationSupportUnitTests {
@Mock EntityManager em;
@Mock Metamodel metaModel;
@Mock EntityManagerFactory entityManagerFactory;
@Mock PersistenceUnitUtil persistenceUnitUtil;
@Test
void usesSimpleClassNameIfNoEntityNameGiven() {
@@ -60,6 +64,9 @@ public class JpaEntityInformationSupportUnitTests {
void rejectsClassNotBeingFoundInMetamodel() {
when(em.getMetamodel()).thenReturn(metaModel);
when(em.getEntityManagerFactory()).thenReturn(entityManagerFactory);
when(entityManagerFactory.getPersistenceUnitUtil()).thenReturn(persistenceUnitUtil);
assertThatIllegalArgumentException()
.isThrownBy(() -> JpaEntityInformationSupport.getEntityInformation(User.class, em));
}

View File

@@ -18,24 +18,19 @@ package org.springframework.data.jpa.repository.support;
import static org.assertj.core.api.Assertions.*;
import static org.springframework.data.jpa.repository.support.JpaEntityInformationSupport.*;
import jakarta.persistence.*;
import lombok.Data;
import java.io.Serializable;
import java.sql.Timestamp;
import java.util.Date;
import jakarta.persistence.*;
import jakarta.persistence.metamodel.Metamodel;
import java.util.UUID;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.data.jpa.domain.AbstractPersistable;
import org.springframework.data.jpa.domain.sample.*;
import org.springframework.data.repository.core.EntityInformation;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import org.springframework.test.util.ReflectionTestUtils;
/**
@@ -45,13 +40,14 @@ import org.springframework.test.util.ReflectionTestUtils;
* @author Thomas Darimont
* @author Christoph Strobl
* @author Jens Schauder
* @author Greg Turnquist
*/
@ExtendWith(SpringExtension.class)
@ContextConfiguration({ "classpath:infrastructure.xml" })
public class JpaMetamodelEntityInformationIntegrationTests {
public abstract class JpaMetamodelEntityInformationIntegrationTests {
@PersistenceContext EntityManager em;
abstract String getMetadadataPersistenceUnitName();
@Test
void detectsIdTypeForEntity() {
@@ -59,14 +55,7 @@ public class JpaMetamodelEntityInformationIntegrationTests {
assertThat(information.getIdType()).isAssignableFrom(Integer.class);
}
/**
* Ignored for Hibernate as it does not implement {@link Metamodel#managedType(Class)} correctly (does not consider
* {@link MappedSuperclass}es correctly).
*
* @see <a href="https://hibernate.atlassian.net/browse/HHH-6896">HHH-6896</a>
*/
@Test // DATAJPA-141
@Disabled
void detectsIdTypeForMappedSuperclass() {
JpaEntityInformation<?, ?> information = getEntityInformation(AbstractPersistable.class, em);
@@ -147,7 +136,7 @@ public class JpaMetamodelEntityInformationIntegrationTests {
void favoursVersionAnnotationIfPresent() {
EntityInformation<VersionedUser, Long> information = new JpaMetamodelEntityInformation<>(VersionedUser.class,
em.getMetamodel());
em.getMetamodel(), em.getEntityManagerFactory().getPersistenceUnitUtil());
VersionedUser entity = new VersionedUser();
assertThat(information.isNew(entity)).isTrue();
@@ -162,11 +151,11 @@ public class JpaMetamodelEntityInformationIntegrationTests {
@Test // DATAJPA-348
void findsIdClassOnMappedSuperclass() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory(getMetadadataPersitenceUnitName());
EntityManagerFactory emf = Persistence.createEntityManagerFactory(getMetadadataPersistenceUnitName());
EntityManager em = emf.createEntityManager();
EntityInformation<Sample, BaseIdClass> information = new JpaMetamodelEntityInformation<>(Sample.class,
em.getMetamodel());
em.getMetamodel(), em.getEntityManagerFactory().getPersistenceUnitUtil());
assertThat(information.getIdType()).isEqualTo(BaseIdClass.class);
}
@@ -175,7 +164,7 @@ public class JpaMetamodelEntityInformationIntegrationTests {
void detectsNewStateForEntityWithPrimitiveId() {
EntityInformation<SampleWithPrimitiveId, Long> information = new JpaMetamodelEntityInformation<>(
SampleWithPrimitiveId.class, em.getMetamodel());
SampleWithPrimitiveId.class, em.getMetamodel(), em.getEntityManagerFactory().getPersistenceUnitUtil());
SampleWithPrimitiveId sample = new SampleWithPrimitiveId();
assertThat(information.isNew(sample)).isTrue();
@@ -187,7 +176,8 @@ public class JpaMetamodelEntityInformationIntegrationTests {
@Test // DATAJPA-509
void jpaMetamodelEntityInformationShouldRespectExplicitlyConfiguredEntityNameFromOrmXml() {
JpaEntityInformation<Role, Integer> info = new JpaMetamodelEntityInformation<>(Role.class, em.getMetamodel());
JpaEntityInformation<Role, Integer> info = new JpaMetamodelEntityInformation<>(Role.class, em.getMetamodel(),
em.getEntityManagerFactory().getPersistenceUnitUtil());
assertThat(info.getEntityName()).isEqualTo("ROLE");
}
@@ -196,7 +186,7 @@ public class JpaMetamodelEntityInformationIntegrationTests {
void considersEntityWithPrimitiveVersionPropertySetToDefaultNew() {
EntityInformation<PrimitiveVersionProperty, Serializable> information = new JpaMetamodelEntityInformation<>(
PrimitiveVersionProperty.class, em.getMetamodel());
PrimitiveVersionProperty.class, em.getMetamodel(), em.getEntityManagerFactory().getPersistenceUnitUtil());
assertThat(information.isNew(new PrimitiveVersionProperty())).isTrue();
}
@@ -205,7 +195,7 @@ public class JpaMetamodelEntityInformationIntegrationTests {
void considersEntityAsNotNewWhenHavingIdSetAndUsingPrimitiveTypeForVersionProperty() {
EntityInformation<PrimitiveVersionProperty, Serializable> information = new JpaMetamodelEntityInformation<>(
PrimitiveVersionProperty.class, em.getMetamodel());
PrimitiveVersionProperty.class, em.getMetamodel(), em.getEntityManagerFactory().getPersistenceUnitUtil());
PrimitiveVersionProperty pvp = new PrimitiveVersionProperty();
pvp.id = 100L;
@@ -217,7 +207,7 @@ public class JpaMetamodelEntityInformationIntegrationTests {
void fallsBackToIdInspectionForAPrimitiveVersionProperty() {
EntityInformation<PrimitiveVersionProperty, Serializable> information = new JpaMetamodelEntityInformation<>(
PrimitiveVersionProperty.class, em.getMetamodel());
PrimitiveVersionProperty.class, em.getMetamodel(), em.getEntityManagerFactory().getPersistenceUnitUtil());
PrimitiveVersionProperty pvp = new PrimitiveVersionProperty();
pvp.version = 1L;
@@ -229,7 +219,8 @@ public class JpaMetamodelEntityInformationIntegrationTests {
}
@Test // DATAJPA-582
void considersEntityWithUnsetCompundIdNew() {
// @Disabled
void considersEntityWithUnsetCompoundIdNew() {
EntityInformation<SampleWithIdClass, ?> information = getEntityInformation(SampleWithIdClass.class, em);
@@ -260,11 +251,7 @@ public class JpaMetamodelEntityInformationIntegrationTests {
assertThat(information.isNew(user)).isFalse();
}
/**
* Ignored as Hibernate < 4.3 doesn't expose the version property properly if it's declared on the superclass.
*/
@Test // DATAJPA-820
@Disabled
void detectsVersionPropertyOnMappedSuperClass() {
EntityInformation<ConcreteType1, ?> information = getEntityInformation(ConcreteType1.class, em);
@@ -275,7 +262,7 @@ public class JpaMetamodelEntityInformationIntegrationTests {
@Test // DATAJPA-1105
void correctlyDeterminesIdValueForNestedIdClassesWithNonPrimitiveNonManagedType() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory(getMetadadataPersitenceUnitName());
EntityManagerFactory emf = Persistence.createEntityManagerFactory(getMetadadataPersistenceUnitName());
EntityManager em = emf.createEntityManager();
JpaEntityInformation<EntityWithNestedIdClass, ?> information = getEntityInformation(EntityWithNestedIdClass.class,
@@ -293,6 +280,7 @@ public class JpaMetamodelEntityInformationIntegrationTests {
}
@Test // DATAJPA-1416
@Disabled
void proxiedIdClassElement() {
JpaEntityInformation<SampleWithIdClassIncludingEntity, ?> information = getEntityInformation(
@@ -315,13 +303,13 @@ public class JpaMetamodelEntityInformationIntegrationTests {
}
@Test // DATAJPA-1576
@Disabled
void prefersPrivateGetterOverFieldAccess() {
EntityManagerFactory emf = Persistence.createEntityManagerFactory(getMetadadataPersitenceUnitName());
EntityManagerFactory emf = Persistence.createEntityManagerFactory(getMetadadataPersistenceUnitName());
EntityManager em = emf.createEntityManager();
JpaEntityInformation<EntityWithPrivateIdGetter, ?> information = getEntityInformation(EntityWithPrivateIdGetter.class, em);
JpaEntityInformation<EntityWithPrivateIdGetter, ?> information = getEntityInformation(
EntityWithPrivateIdGetter.class, em);
EntityWithPrivateIdGetter entity = new EntityWithPrivateIdGetter();
@@ -330,10 +318,6 @@ public class JpaMetamodelEntityInformationIntegrationTests {
assertThat(id).isEqualTo(42L);
}
String getMetadadataPersitenceUnitName() {
return "metadata";
}
@SuppressWarnings("serial")
private static class BaseIdClass implements Serializable {
@@ -362,7 +346,8 @@ public class JpaMetamodelEntityInformationIntegrationTests {
public static class EntityWithNestedIdClass {
@Id Long id;
@Id @ManyToOne private EntityWithIdClass reference;
@Id
@ManyToOne private EntityWithIdClass reference;
}
@Entity
@@ -389,7 +374,7 @@ public class JpaMetamodelEntityInformationIntegrationTests {
}
@Entity
public static class EntityWithPrivateIdGetter implements Serializable{
public static class EntityWithPrivateIdGetter implements Serializable {
private long id = 0;
@@ -402,4 +387,49 @@ public class JpaMetamodelEntityInformationIntegrationTests {
this.id = id;
}
}
@Entity
public static class ExampleEntityWithStringId {
private UUID clientId;
public UUID getId() {
return this.clientId;
}
public void setId(UUID clientId) {
this.clientId = clientId;
}
public void setClientId(String clientId) {
this.clientId = UUID.fromString(clientId);
}
@Id
public String getClientId() {
return clientId == null ? null : clientId.toString();
}
}
@Entity
public static class ExampleEntityWithUUIDId {
@Id private UUID clientId;
public UUID getId() {
return this.clientId;
}
public void setId(UUID clientId) {
this.clientId = clientId;
}
public void setClientId(String clientId) {
this.clientId = UUID.fromString(clientId);
}
public String getClientId() {
return clientId == null ? null : clientId.toString();
}
}
}

View File

@@ -19,15 +19,18 @@ import static java.util.Arrays.*;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.PersistenceUnitUtil;
import jakarta.persistence.metamodel.IdentifiableType;
import jakarta.persistence.metamodel.Metamodel;
import jakarta.persistence.metamodel.SingularAttribute;
import jakarta.persistence.metamodel.Type;
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -48,17 +51,25 @@ import org.springframework.data.jpa.domain.sample.PersistableWithIdClassPK;
@MockitoSettings(strictness = Strictness.LENIENT)
class JpaMetamodelEntityInformationUnitTests {
@Mock EntityManager em;
@Mock EntityManagerFactory entityManagerFactory;
@Mock PersistenceUnitUtil persistenceUnit;
@Mock Metamodel metamodel;
@Mock IdentifiableType<PersistableWithIdClass> type;
@Mock SingularAttribute<PersistableWithIdClass, ?> first, second;
@Mock @SuppressWarnings("rawtypes") Type idType;
@Mock
@SuppressWarnings("rawtypes") Type idType;
@BeforeEach
@SuppressWarnings("unchecked")
void setUp() {
when(em.getMetamodel()).thenReturn(metamodel);
when(em.getEntityManagerFactory()).thenReturn(entityManagerFactory);
when(entityManagerFactory.getPersistenceUnitUtil()).thenReturn(persistenceUnit);
when(first.getName()).thenReturn("first");
when(second.getName()).thenReturn("second");
Set<SingularAttribute<? super PersistableWithIdClass, ?>> attributes = new HashSet<>(asList(first, second));
@@ -76,12 +87,13 @@ class JpaMetamodelEntityInformationUnitTests {
void doesNotCreateIdIfAllPartialAttributesAreNull() {
JpaMetamodelEntityInformation<PersistableWithIdClass, Serializable> information = new JpaMetamodelEntityInformation<>(
PersistableWithIdClass.class, metamodel);
PersistableWithIdClass.class, em.getMetamodel(), em.getEntityManagerFactory().getPersistenceUnitUtil());
PersistableWithIdClass entity = new PersistableWithIdClass(null, null);
assertThat(information.getId(entity)).isNull();
entity = new PersistableWithIdClass(2L, null);
when(persistenceUnit.getIdentifier(entity)).thenReturn(2L);
assertThat(information.getId(entity)).isNotNull();
}
}

View File

@@ -18,6 +18,9 @@ package org.springframework.data.jpa.repository.support;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.PersistenceUnitUtil;
import jakarta.persistence.metamodel.EntityType;
import jakarta.persistence.metamodel.Metamodel;
import jakarta.persistence.metamodel.Type;
@@ -42,16 +45,23 @@ import org.springframework.data.repository.core.EntityInformation;
@MockitoSettings(strictness = Strictness.LENIENT)
class JpaPersistableEntityInformationUnitTests {
@Mock EntityManager em;
@Mock EntityManagerFactory entityManagerFactory;
@Mock Metamodel metamodel;
@Mock PersistenceUnitUtil persistenceUnitUtil;
@Mock EntityType<Foo> type;
@Mock @SuppressWarnings("rawtypes") Type idType;
@Mock
@SuppressWarnings("rawtypes") Type idType;
@BeforeEach
@SuppressWarnings("unchecked")
void setUp() {
when(em.getMetamodel()).thenReturn(metamodel);
when(em.getEntityManagerFactory()).thenReturn(entityManagerFactory);
when(entityManagerFactory.getPersistenceUnitUtil()).thenReturn(persistenceUnitUtil);
when(metamodel.managedType(Object.class)).thenThrow(IllegalArgumentException.class);
when(metamodel.managedType(Foo.class)).thenReturn(type);
when(type.getIdType()).thenReturn(idType);
@@ -60,7 +70,8 @@ class JpaPersistableEntityInformationUnitTests {
@Test
void usesPersistableMethodsForIsNewAndGetId() {
EntityInformation<Foo, Long> entityInformation = new JpaPersistableEntityInformation<>(Foo.class, metamodel);
EntityInformation<Foo, Long> entityInformation = new JpaPersistableEntityInformation<>(Foo.class, em.getMetamodel(),
em.getEntityManagerFactory().getPersistenceUnitUtil());
Foo foo = new Foo();
assertThat(entityInformation.isNew(foo)).isFalse();

View File

@@ -17,16 +17,25 @@ package org.springframework.data.jpa.repository.support;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit.jupiter.SpringExtension;
/**
* OpenJpa execution for {@link JpaMetamodelEntityInformationIntegrationTests}.
*
* @author Oliver Gierke
* @author Greg Turnquist
*/
@ContextConfiguration("classpath:openjpa.xml")
@ExtendWith(SpringExtension.class)
@ContextConfiguration({ "classpath:infrastructure.xml", "classpath:openjpa.xml" })
class OpenJpaMetamodelEntityInformationIntegrationTests extends JpaMetamodelEntityInformationIntegrationTests {
@Override
String getMetadadataPersistenceUnitName() {
return "metadata_oj";
}
/**
* Re-activate test.
*/
@@ -50,9 +59,4 @@ class OpenJpaMetamodelEntityInformationIntegrationTests extends JpaMetamodelEnti
void detectsVersionPropertyOnMappedSuperClass() {
super.detectsVersionPropertyOnMappedSuperClass();
}
@Override
String getMetadadataPersitenceUnitName() {
return "metadata_oj";
}
}

View File

@@ -17,6 +17,8 @@ package org.springframework.data.jpa.repository.support;
import static org.assertj.core.api.Assertions.*;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import lombok.Data;
import java.sql.Date;
@@ -25,9 +27,6 @@ import java.util.List;
import java.util.Set;
import java.util.stream.Stream;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.hibernate.LazyInitializationException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -83,8 +82,8 @@ class QuerydslJpaPredicateExecutorUnitTests {
@BeforeEach
void setUp() {
JpaEntityInformation<User, Integer> information = new JpaMetamodelEntityInformation<>(User.class,
em.getMetamodel());
JpaEntityInformation<User, Integer> information = new JpaMetamodelEntityInformation<>(User.class, em.getMetamodel(),
em.getEntityManagerFactory().getPersistenceUnitUtil());
SimpleJpaRepository<User, Integer> repository = new SimpleJpaRepository<>(information, em);
dave = repository.save(new User("Dave", "Matthews", "dave@matthews.com"));

View File

@@ -17,13 +17,13 @@ package org.springframework.data.jpa.repository.support;
import static org.assertj.core.api.Assertions.*;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import java.sql.Date;
import java.time.LocalDate;
import java.util.List;
import jakarta.persistence.EntityManager;
import jakarta.persistence.PersistenceContext;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
@@ -76,8 +76,8 @@ class QuerydslJpaRepositoryTests {
@BeforeEach
void setUp() {
JpaEntityInformation<User, Integer> information = new JpaMetamodelEntityInformation<>(User.class,
em.getMetamodel());
JpaEntityInformation<User, Integer> information = new JpaMetamodelEntityInformation<>(User.class, em.getMetamodel(),
em.getEntityManagerFactory().getPersistenceUnitUtil());
repository = new QuerydslJpaRepository<>(information, em);
dave = repository.save(new User("Dave", "Matthews", "dave@matthews.com"));

View File

@@ -22,6 +22,8 @@ import static org.springframework.data.jpa.domain.Specification.*;
import jakarta.persistence.EntityGraph;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.PersistenceUnitUtil;
import jakarta.persistence.TypedQuery;
import jakarta.persistence.criteria.CriteriaBuilder;
import jakarta.persistence.criteria.CriteriaQuery;
@@ -57,6 +59,8 @@ class SimpleJpaRepositoryUnitTests {
private SimpleJpaRepository<User, Integer> repo;
@Mock EntityManager em;
@Mock EntityManagerFactory entityManagerFactory;
@Mock PersistenceUnitUtil persistenceUnitUtil;
@Mock CriteriaBuilder builder;
@Mock CriteriaQuery<User> criteriaQuery;
@Mock CriteriaQuery<Long> countCriteriaQuery;
@@ -172,6 +176,9 @@ class SimpleJpaRepositoryUnitTests {
User newUser = new User();
newUser.setId(null);
when(em.getEntityManagerFactory()).thenReturn(entityManagerFactory);
when(entityManagerFactory.getPersistenceUnitUtil()).thenReturn(persistenceUnitUtil);
repo.delete(newUser);
verify(em, never()).find(any(Class.class), any(Object.class));
@@ -186,6 +193,9 @@ class SimpleJpaRepositoryUnitTests {
newUser.setId(23);
when(information.isNew(newUser)).thenReturn(false);
when(em.getEntityManagerFactory()).thenReturn(entityManagerFactory);
when(entityManagerFactory.getPersistenceUnitUtil()).thenReturn(persistenceUnitUtil);
when(persistenceUnitUtil.getIdentifier(any())).thenReturn(23);
when(em.find(User.class, 23)).thenReturn(null);
repo.delete(newUser);

View File

@@ -52,6 +52,10 @@
<class>org.springframework.data.jpa.domain.sample.Dummy</class>
<class>org.springframework.data.jpa.domain.sample.SampleWithIdClassIncludingEntity</class>
<class>org.springframework.data.jpa.domain.sample.SampleWithIdClassIncludingEntity$OtherEntity</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.repository.support.JpaMetamodelEntityInformationIntegrationTests$EntityWithPrivateIdGetter</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
</persistence-unit>
<persistence-unit name="querydsl">
@@ -108,6 +112,8 @@
<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.repository.support.JpaMetamodelEntityInformationIntegrationTests$EntityWithPrivateIdGetter</class>
<class>org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$ExampleEntityWithStringId</class>
<class>org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$ExampleEntityWithUUIDId</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.HSQLDialect" />
@@ -123,6 +129,9 @@
<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.repository.support.JpaMetamodelEntityInformationIntegrationTests$EntityWithPrivateIdGetter</class>
<class>org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$ExampleEntityWithStringId</class>
<class>org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$ExampleEntityWithUUIDId</class>
<class>org.springframework.data.jpa.domain.sample.Dummy</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
@@ -152,5 +161,41 @@
<property name="openjpa.ConnectionPassword" value="" />
</properties>
</persistence-unit>
<persistence-unit name="metadata-id-handling">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<class>org.springframework.data.jpa.domain.sample.CustomAbstractPersistable</class>
<class>org.springframework.data.jpa.domain.sample.MailMessage</class>
<class>org.springframework.data.jpa.domain.sample.MailSender</class>
<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.repository.support.JpaMetamodelEntityInformationIntegrationTests$EntityWithPrivateIdGetter</class>
<class>org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$ExampleEntityWithStringId</class>
<class>org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$ExampleEntityWithUUIDId</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQL91Dialect" />
</properties>
</persistence-unit>
<persistence-unit name="metadata-id-handling-el">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>org.springframework.data.jpa.domain.sample.CustomAbstractPersistable</class>
<class>org.springframework.data.jpa.domain.sample.MailMessage</class>
<class>org.springframework.data.jpa.domain.sample.MailSender</class>
<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.repository.support.JpaMetamodelEntityInformationIntegrationTests$EntityWithPrivateIdGetter</class>
<class>org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$ExampleEntityWithStringId</class>
<class>org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformationIntegrationTests$ExampleEntityWithUUIDId</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQL91Dialect" />
</properties>
</persistence-unit>
</persistence>

View File

@@ -6,8 +6,8 @@
http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd">
<!-- EclipseLink vendor adaptor with workaround platform class for HSQL usage -->
<bean id="vendorAdaptor" class="org.springframework.data.jpa.repository.CustomEclipseLinkJpaVendorAdapter" parent="abstractVendorAdaptor" />
<bean id="vendorAdaptor" class="org.springframework.data.jpa.repository.CustomEclipseLinkJpaVendorAdapter" parent="abstractVendorAdaptor" />
<util:properties id="jpaProperties">
<prop key="jakarta.persistence.jdbc.driver">org.hsqldb.jdbcDriver</prop>
<prop key="jakarta.persistence.jdbc.url">jdbc:hsqldb:mem:hades</prop>