Skip to content

Commit

Permalink
jakartaee/persistence#490 - add getReference(detachedEntity) to Entit…
Browse files Browse the repository at this point in the history
…yManager

jakartaee/persistence#451 - add PersistenceUnitUtil.getVersion()

Signed-off-by: Tomáš Kraus <tomas.kraus@oracle.com>
  • Loading branch information
Tomas-Kraus authored and lukasj committed Dec 20, 2023
1 parent 1822761 commit a6bff23
Show file tree
Hide file tree
Showing 12 changed files with 284 additions and 21 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,7 @@ public boolean isStoredInObject() {
* INTERNAL:
* Retrieves the lock value from the object.
*/
protected Object lockValueFromObject(Object domainObject) {
public Object lockValueFromObject(Object domainObject) {
// PERF: If mapping with a direct mapping get from cached mapping.
if (this.lockMapping != null) {
return this.lockMapping.getAttributeValueFromObject(domainObject);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,8 @@ public class ExceptionLocalizationResource extends ListResourceBundle {
{ "jaxb_helper_invalid_target_for_marshaller", "The provided target Class [{0}] must be one of EclipseLink JAXBMarshaller or EclipseLink XMLMarshaller." },
{ "jaxb_helper_invalid_target_for_binder", "The provided target Class [{0}] must be one of EclipseLink JAXBBinder or EclipseLink XMLBinder." },
{ "jpa_persistence_util_non_persistent_class", "PersistenceUtil.getIdentifier(entity) was called with object [{0}] which is not a persistent object." },
{ "jpa_persistence_util_get_version_non_persistent_class", "PersistenceUtil.getVersion(entity) was called with object [{0}] which is not a persistent object." },
{ "jpa_persistence_util_get_version_no_version_in_class", "PersistenceUtil.getVersion(entity) was called with object [{0}] which has no version attribute." },
{ "metamodel_identifiable_type_has_no_idclass_attribute", "No @IdClass attributes exist on the IdentifiableType [{0}]. There still may be one or more @Id or an @EmbeddedId on type." },
{ "metamodel_identifiable_no_version_attribute_present", "No @Version attribute exists on the identifiable type [{0}]." },
{ "metamodel_identifiable_no_id_attribute_present", "No @Id attribute exists on the identifiable type [{0}]." },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6153,32 +6153,45 @@ public Set<Object> getChangeTrackedHardList() {
return this.changeTrackedHardList;
}

@Override
public <T> T getReference(T entity) {
ClassDescriptor descriptor = getDescriptor(entity);
return getReferenceInternal(descriptor.getJavaClass(),
descriptor.getObjectBuilder()
.extractPrimaryKeyFromObject(entity, this),
descriptor);
}

/**
* Get an instance, whose state may be lazily fetched.
* If the requested instance does not exist in the database, null is returned, or the object will fail when accessed.
* The instance will be lazy when it does not exist in the cache, and supports fetch groups.
* @param id The primary key of the object, either as a List, singleton, IdClass or an instance of the object.
*/
@Override
public Object getReference(Class<?> theClass, Object id) {
ClassDescriptor descriptor = getDescriptor(theClass);
public <T> T getReference(Class<T> theClass, Object id) {
return getReferenceInternal(theClass, id, getDescriptor(theClass));
}

// Get an instance, whose state may be lazily fetched.
public <T> T getReferenceInternal(Class<T> theClass, Object id, ClassDescriptor descriptor) {
if (descriptor == null || descriptor.isDescriptorTypeAggregate()) {
throw new IllegalArgumentException(ExceptionLocalization.buildMessage("unknown_bean_class", new Object[] { theClass }));
}
Object reference;
if (id == null) { //gf721 - check for null PK
throw new IllegalArgumentException(ExceptionLocalization.buildMessage("null_pk"));
}
Object reference;
Object primaryKey;
if (id instanceof List) {
if (descriptor.getCachePolicy().getCacheKeyType() == CacheKeyType.ID_VALUE) {
if (((List)id).isEmpty()) {
if (((List<?>)id).isEmpty()) {
primaryKey = null;
} else {
primaryKey = ((List)id).get(0);
primaryKey = ((List<?>)id).get(0);
}
} else {
primaryKey = new CacheId(((List)id).toArray());
primaryKey = new CacheId(((List<?>)id).toArray());
}
} else if (id instanceof CacheId) {
primaryKey = id;
Expand Down Expand Up @@ -6217,7 +6230,7 @@ public Object getReference(Class<?> theClass, Object id) {
query.setIsExecutionClone(true);
reference = executeQuery(query);
}
return reference;
return theClass.cast(reference);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -689,12 +689,28 @@ enum CommitOrderType {
void writeChanges();

/**
* Get an instance, whose state may be lazily fetched.
* Get an entity instance, whose state may be lazily fetched.
* If the requested instance does not exist in the database, null is returned, or the object will fail when accessed.
* The instance will be lazy when it does not exist in the cache, and supports fetch groups.
* @param primaryKey - The primary key of the object, either as a List, singleton, IdClass or an instance of the object.
*
* @param theClass instance class
* @param primaryKey the primary key of the object, either as a List, singleton, IdClass or an instance of the object
* @param <T> the entity type
*
* @return a reference to the entity instance
*/
<T> T getReference(Class<T> theClass, Object primaryKey);

/**
* Get an entity instance, whose state may be lazily fetched.
* If the requested instance does not exist in the database, null is returned, or the object will fail when accessed.
* The instance will be lazy when it does not exist in the cache, and supports fetch groups.
*
* @param entity persistent or detached entity instance
* @param <T> the entity type
* @return a reference to the entity instance
*/
Object getReference(Class<?> theClass, Object primaryKey);
<T> T getReference(T entity);

/**
* ADVANCED:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0,
* or the Eclipse Distribution License v. 1.0 which is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
*/
package org.eclipse.persistence.testing.models.jpa.persistence32;

import jakarta.persistence.Entity;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import jakarta.persistence.Version;

@Entity
@Table(name="PERSISTENCE32_VERSION_ENTITY")
public class VersionEntity {

// ID is assigned in tests to avoid collisions
@Id
private int id;

@Version
private int version;

private String name;

public VersionEntity() {
}

public VersionEntity(int id, int version, String name) {
this.id = id;
this.version = version;
this.name = name;
}

public int getId() {
return id;
}

public void setId(int id) {
this.id = id;
}

public int getVersion() {
return version;
}

public void setVersion(int version) {
this.version = version;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
<class>org.eclipse.persistence.testing.models.jpa.persistence32.Pokemon</class>
<class>org.eclipse.persistence.testing.models.jpa.persistence32.Type</class>
<class>org.eclipse.persistence.testing.models.jpa.persistence32.SyntaxEntity</class>
<class>org.eclipse.persistence.testing.models.jpa.persistence32.VersionEntity</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="jakarta.persistence.schema-generation.scripts.action" value="none"/>
Expand All @@ -45,6 +46,7 @@
<class>org.eclipse.persistence.testing.models.jpa.persistence32.Pokemon</class>
<class>org.eclipse.persistence.testing.models.jpa.persistence32.Type</class>
<class>org.eclipse.persistence.testing.models.jpa.persistence32.SyntaxEntity</class>
<class>org.eclipse.persistence.testing.models.jpa.persistence32.VersionEntity</class>
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="jakarta.persistence.schema-generation.database.action" value="verify"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@
import org.eclipse.persistence.descriptors.FetchGroupManager;
import org.eclipse.persistence.jpa.JpaEntityManagerFactory;
import org.eclipse.persistence.testing.models.jpa.persistence32.Pokemon;
import org.eclipse.persistence.testing.models.jpa.persistence32.Team;
import org.eclipse.persistence.testing.models.jpa.persistence32.Trainer;
import org.eclipse.persistence.testing.models.jpa.persistence32.Trainer_;
import org.eclipse.persistence.testing.models.jpa.persistence32.Type;
import org.eclipse.persistence.testing.models.jpa.persistence32.VersionEntity;

/**
* Verify jakarta.persistence 3.2 API changes in {@link jakarta.persistence.EntityManagerFactory}.
Expand All @@ -58,7 +60,9 @@ public static Test suite() {
new EntityManagerFactoryTest("testLoadEntityNamedAttribute"),
new EntityManagerFactoryTest("testVerifyPokemonFetchGroups"),
new EntityManagerFactoryTest("testIsLoadedEntity"),
new EntityManagerFactoryTest("testLoadEntity")
new EntityManagerFactoryTest("testLoadEntity"),
new EntityManagerFactoryTest("testGetVersionOnEntityWithVersion"),
new EntityManagerFactoryTest("testGetVersionOnEntityWithoutVersion")
);
}

Expand Down Expand Up @@ -228,6 +232,7 @@ public void testCreateConflictingConfiguredEntityManagerFactory() {
}
}

// Test <E> boolean PersistenceUnitUtil#isLoaded(E, Attribute<? super E, ?>) on lazy entity reference
public void testIsLoadedEntityAttribute() {
Trainer t = emf.callInTransaction(
em -> em.createQuery("SELECT t FROM Trainer t WHERE t.name = :name", Trainer.class)
Expand All @@ -238,6 +243,7 @@ public void testIsLoadedEntityAttribute() {
assertFalse("Entity lazy attribute Trainer.team should not be loaded", util.isLoaded(t, Trainer_.team));
}

// Test <E> void PersistenceUnitUtil#load(E, Attribute<? super E, ?>) on lazy entity reference
public void testLoadEntityAttribute() {
Trainer t = emf.callInTransaction(
em -> em.createQuery("SELECT t FROM Trainer t WHERE t.name = :name", Trainer.class)
Expand All @@ -250,6 +256,7 @@ public void testLoadEntityAttribute() {
assertTrue("Entity lazy attribute Trainer.team should be loaded after load call", util.isLoaded(t, Trainer_.team));
}

// Test <E> boolean PersistenceUnitUtil#isLoaded(Object, String) on lazy entity reference
public void testIsLoadedEntityNamedAttribute() {
Trainer t = emf.callInTransaction(
em -> em.createQuery("SELECT t FROM Trainer t WHERE t.name = :name", Trainer.class)
Expand All @@ -260,6 +267,7 @@ public void testIsLoadedEntityNamedAttribute() {
assertFalse("Entity lazy attribute Trainer.team should not be loaded", util.isLoaded(t, "team"));
}

// Test <E> void PersistenceUnitUtil#load(Object, String) on lazy entity reference
public void testLoadEntityNamedAttribute() {
Trainer t = emf.callInTransaction(
em -> em.createQuery("SELECT t FROM Trainer t WHERE t.name = :name", Trainer.class)
Expand All @@ -274,6 +282,7 @@ public void testLoadEntityNamedAttribute() {
util.isLoaded(t, "team"));
}

// Verify Pokemon entity fetch groups: custom FetchTypes FetchGroup must be defined
public void testVerifyPokemonFetchGroups() {
if (isWeavingEnabled()) {
ClassDescriptor pokemonsDescriptor = getPersistenceUnitServerSession().getDescriptor(Pokemon.class);
Expand All @@ -283,6 +292,7 @@ public void testVerifyPokemonFetchGroups() {
}
}

// Test boolean PersistenceUnitUtil#isLoaded(Object) on non fully loaded entity using FetchTypes FetchGroup
public void testIsLoadedEntity() {
if (isWeavingEnabled()) {
Pokemon ekans = new Pokemon(6, TRAINERS[2], "Ekans", List.of(TYPES[4]));
Expand All @@ -308,6 +318,7 @@ public void testIsLoadedEntity() {
}
}

// Test boolean PersistenceUnitUtil#load(Object) on non fully loaded entity using FetchTypes FetchGroup
public void testLoadEntity() {
if (isWeavingEnabled()) {
Pokemon arbok = new Pokemon(7, TRAINERS[2], "Arbok", List.of(TYPES[4]));
Expand Down Expand Up @@ -335,6 +346,30 @@ public void testLoadEntity() {
}
}

// Test Object PersistenceUnitUtil#getVersion(Object) on entity with version attribute
public void testGetVersionOnEntityWithVersion() {
VersionEntity entity = new VersionEntity(1, 1, "Entity");
emf.runInTransaction(em -> em.persist(entity));
Integer version = (Integer) emf.getPersistenceUnitUtil().getVersion(entity);
assertNotNull(version);
assertEquals(entity.getVersion(), version.intValue());
}

// Test Object PersistenceUnitUtil#getVersion(Object) on entity without version attribute
// Shall throw IllegalArgumentException
public void testGetVersionOnEntityWithoutVersion() {
Team entity = new Team(100, "Team");
emf.runInTransaction(em -> em.persist(entity));
try {
emf.getPersistenceUnitUtil().getVersion(entity);
fail("PersistenceUnitUtil#getVersion(Entity) on entity without version shall throw IllegalArgumentException");
} catch (IllegalArgumentException iae) {
assertTrue(
"Unexpected exception message: " + iae.getLocalizedMessage(),
iae.getMessage().contains("which has no version attribute"));
}
}

private static PersistenceConfiguration createPersistenceConfiguration(JpaEntityManagerFactory emf, String puName) {
PersistenceConfiguration configuration = new PersistenceConfiguration(puName);
configuration.properties(emf.getProperties());
Expand Down

0 comments on commit a6bff23

Please sign in to comment.