Skip to content

Commit

Permalink
JPA #414 - Add EntityManagerFactory.runInTransaction()/callInTransact…
Browse files Browse the repository at this point in the history
…ion()

JPA #433 - Add EntityManager.runWithConnection()/callWithConnection()

Signed-off-by: Tomáš Kraus <[email protected]>
  • Loading branch information
Tomas-Kraus committed Aug 25, 2023
1 parent 46f49c8 commit 71a1e97
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 43 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ public class ExceptionLocalizationResource extends ListResourceBundle {
{ "bean_definition_vector_arguments_are_of_different_sizes", "Bean definition vector arguments are of different sizes" },
{ "missing_toplink_bean_definition_for", "Missing TopLink bean definition for {0}" },
{ "argument_collection_was_null", "Argument collection was null" },
{ "entity_manager_with_connection_failed", "Execution of user code failed: {0}"},
{ "no_entities_retrieved_for_get_single_result", "getSingleResult() did not retrieve any entities." },
{ "no_entities_retrieved_for_get_reference", "Could not find Entity for id: {0}" },
{ "too_many_results_for_get_single_result", "More than one result was returned from Query.getSingleResult()" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,7 @@ public class LoggingLocalizationResource extends ListResourceBundle {
{ "osgi_initializer", "Using OSGi initializer: [{0}]."},
{ "entity_manager_ignores_nonjta_data_source", "Persistence unit uses JTA, therefore the EntityManager ignores non jta data source. "},
{ "entity_manager_ignores_jta_data_source", "Persistence unit does not use JTA, therefore the EntityManager ignores jta data source. "},
{ "entity_manager_has_multiple_connections", "Persistence unit has multiple connections, returning the first one from the list."},
{ "problem_registering_mbean", "Problem while registering MBean: {0}" },
{ "problem_unregistering_mbean", "Problem while unregistering MBean: {0}" },
{ "session_key_for_mbean_name_is_null", "Session name used for the MBean registration cannot be null." },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
@NamedQuery(name="Pokemon.get", query="SELECT p FROM Pokemon p WHERE p.id = :id")
public class Pokemon {

// ID is assigned in tests to avoid collisions
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;

private String name;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@

package org.eclipse.persistence.testing.tests.jpa.persistence32;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityTransaction;
import junit.framework.Test;
Expand All @@ -20,6 +26,7 @@
import org.eclipse.persistence.jpa.JpaEntityManagerFactory;
import org.eclipse.persistence.testing.framework.jpa.junit.JUnitTestCase;
import org.eclipse.persistence.testing.models.jpa.persistence32.Persistence32TableCreator;
import org.eclipse.persistence.testing.models.jpa.persistence32.Pokemon;
import org.eclipse.persistence.testing.models.jpa.persistence32.Type;

public class EntityManagerFactoryTest extends JUnitTestCase {
Expand Down Expand Up @@ -54,10 +61,10 @@ public static Test suite() {
TestSuite suite = new TestSuite();
suite.setName("EntityManagerFactoryTest");
suite.addTest(new EntityManagerFactoryTest("testSetup"));
// suite.addTest(new EntityManagerFactoryTest("testWithApplicationManagedTransaction"));
// suite.addTest(new EntityManagerFactoryTest("testWithApplicationManagedTransactionVoid"));
// suite.addTest(new EntityManagerFactoryTest("testWithUserManagedTransaction"));
// suite.addTest(new EntityManagerFactoryTest("testWithUserManagedTransactionVoid"));
suite.addTest(new EntityManagerFactoryTest("testCallInTransaction"));
suite.addTest(new EntityManagerFactoryTest("testRunInTransaction"));
suite.addTest(new EntityManagerFactoryTest("testRunWithConnection"));
suite.addTest(new EntityManagerFactoryTest("testCallWithConnection"));
return suite;
}

Expand Down Expand Up @@ -97,84 +104,114 @@ public void testSetup() {
EntityTransaction et = em.getTransaction();
try {
et.begin();

for (int i = 1; i < TYPES.length; i++) {
em.persist(TYPES[i]);
}
et.commit();
} catch (Exception e) {
et.rollback();
throw e;
}
}
}

// TODO-API-3.2 - rewrite using new API
/*
public void testWithApplicationManagedTransaction() {
Pokemon pokemon = emf.withTransaction((em -> {
public void testCallInTransaction() {
Pokemon pokemon = emf.callInTransaction((em -> {
Map<Integer, Type> types = new HashMap<>(24);
em.createNamedQuery("Type.all", Type.class)
.getResultList()
.forEach(type -> types.put(type.getId(), type));
Pokemon newPokemon = new Pokemon("Pidgey", List.of(types.get(1), types.get(3)));
Pokemon newPokemon = new Pokemon(1, "Pidgey", List.of(types.get(1), types.get(3)));
em.persist(newPokemon);
return newPokemon;
}));
verifyObjectInEntityManager(pokemon, getPersistenceUnitName());
}

public void testWithApplicationManagedTransactionVoid() {
public void testRunInTransaction() {
Pokemon[] pokemon = new Pokemon[1];
emf.withTransaction((em -> {
emf.runInTransaction((em -> {
Map<Integer, Type> types = new HashMap<>(24);
em.createNamedQuery("Type.all", Type.class)
.getResultList()
.forEach(type -> types.put(type.getId(), type));
Pokemon newPokemon = new Pokemon("Beedrill", List.of(types.get(7), types.get(4)));
Pokemon newPokemon = new Pokemon(2, "Beedrill", List.of(types.get(7), types.get(4)));
em.persist(newPokemon);
pokemon[0] = newPokemon;
}));
verifyObjectInEntityManager(pokemon[0], getPersistenceUnitName());
}

public void testWithUserManagedTransaction() {
Pokemon pokemon = emf.withTransaction(((em, et) -> {
public void testRunWithConnection() {
Pokemon[] pokemon = new Pokemon[1];
try (EntityManager em = emf.createEntityManager()) {
EntityTransaction et = em.getTransaction();
try {
Map<Integer, Type> types = new HashMap<>(24);
em.createNamedQuery("Type.all", Type.class)
.getResultList()
.forEach(type -> types.put(type.getId(), type));
Pokemon newPokemon = new Pokemon("Caterpie", List.of(types.get(7)));
em.persist(newPokemon);
et.begin();
em.<Connection>runWithConnection(
connection -> {
Pokemon newPokemon = new Pokemon(3, "Squirtle", List.of(TYPES[10]));
try (PreparedStatement stmt = connection.prepareStatement(
"INSERT INTO PERSISTENCE32_POKEMON (ID, NAME) VALUES(?, ?)")) {
stmt.setInt(1, newPokemon.getId());
stmt.setString(2, newPokemon.getName());
stmt.executeUpdate();
}
try (PreparedStatement stmt = connection.prepareStatement(
"INSERT INTO PERSISTENCE32_POKEMON_TYPE (POKEMON_ID, TYPE_ID) VALUES(?, ?)")) {
for (Type type : newPokemon.getTypes()) {
stmt.setInt(1, newPokemon.getId());
stmt.setInt(2, type.getId());
stmt.executeUpdate();
}
}
pokemon[0] = newPokemon;
}
);
et.commit();
return newPokemon;
} catch (Exception e) {
et.rollback();
throw e;
}
}));
verifyObjectInEntityManager(pokemon, getPersistenceUnitName());
}
Pokemon dbPokemon = createEntityManager().find(Pokemon.class, 3);
assertEquals(pokemon[0], dbPokemon);
}

public void testWithUserManagedTransactionVoid() {
Pokemon[] pokemon = new Pokemon[1];
emf.withTransaction(((em, et) -> {
public void testCallWithConnection() {
Pokemon pokemon;
try (EntityManager em = emf.createEntityManager()) {
EntityTransaction et = em.getTransaction();
try {
Map<Integer, Type> types = new HashMap<>(24);
em.createNamedQuery("Type.all", Type.class)
.getResultList()
.forEach(type -> types.put(type.getId(), type));
Pokemon newPokemon = new Pokemon("Squirtle", List.of(types.get(11)));
em.persist(newPokemon);
et.begin();
pokemon = em.<Connection, Pokemon>callWithConnection(
connection -> {
Pokemon newPokemon = new Pokemon(4, "Caterpie", List.of(TYPES[6]));
try (PreparedStatement stmt = connection.prepareStatement(
"INSERT INTO PERSISTENCE32_POKEMON (ID, NAME) VALUES(?, ?)")) {
stmt.setInt(1, newPokemon.getId());
stmt.setString(2, newPokemon.getName());
stmt.executeUpdate();
}
try (PreparedStatement stmt = connection.prepareStatement(
"INSERT INTO PERSISTENCE32_POKEMON_TYPE (POKEMON_ID, TYPE_ID) VALUES(?, ?)")) {
for (Type type : newPokemon.getTypes()) {
stmt.setInt(1, newPokemon.getId());
stmt.setInt(2, type.getId());
stmt.executeUpdate();
}
}
return newPokemon;
}
);
et.commit();
pokemon[0] = newPokemon;
} catch (Exception e) {
et.rollback();
throw e;
}
}));
verifyObjectInEntityManager(pokemon[0], getPersistenceUnitName());
}
Pokemon dbPokemon = createEntityManager().find(Pokemon.class, 4);
assertEquals(pokemon, dbPokemon);
}
*/

}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
import jakarta.persistence.EntityGraph;
import jakarta.persistence.EntityManager;
import jakarta.persistence.EntityManagerFactory;
import jakarta.persistence.EntityTransaction;
import jakarta.persistence.FlushModeType;
import jakarta.persistence.PersistenceException;
import jakarta.persistence.PersistenceUnitUtil;
Expand Down Expand Up @@ -913,7 +914,29 @@ public <T> void addNamedEntityGraph(String graphName, EntityGraph<T> entityGraph
// TODO-API-3.2
@Override
public void runInTransaction(Consumer<EntityManager> work) {
throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet");
try (EntityManager em = createEntityManager()) {
switch (setupImpl.getPersistenceUnitInfo().getTransactionType()) {
case JTA:
em.joinTransaction();
work.accept(em);
return;
case RESOURCE_LOCAL:
EntityTransaction et = em.getTransaction();
et.begin();
try {
work.accept(em);
et.commit();
return;
} catch (Exception e) {
et.rollback();
throw e;
}
// This may happen only when JPA gets new transaction type
default:
throw new IllegalStateException(
"Unknown transaction type " + setupImpl.getPersistenceUnitInfo().getTransactionType().name());
}
}
}

/**
Expand Down Expand Up @@ -945,7 +968,28 @@ public void runInTransaction(Consumer<EntityManager> work) {
// TODO-API-3.2
@Override
public <R> R callInTransaction(Function<EntityManager, R> work) {
throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet");
try (EntityManager em = createEntityManager()) {
switch (setupImpl.getPersistenceUnitInfo().getTransactionType()) {
case JTA:
em.joinTransaction();
return work.apply(em);
case RESOURCE_LOCAL:
EntityTransaction et = em.getTransaction();
et.begin();
try {
R result = work.apply(em);
et.commit();
return result;
} catch (Exception e) {
et.rollback();
throw e;
}
// This may happen only when JPA gets new transaction type
default:
throw new IllegalStateException(
"Unknown transaction type " + setupImpl.getPersistenceUnitInfo().getTransactionType().name());
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -1623,7 +1623,19 @@ public jakarta.persistence.EntityTransaction getTransaction() {
// TODO-API-3.2
@Override
public <C> void runWithConnection(ConnectionConsumer<C> action) {
throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet");
if (getAbstractSession().getAccessors().size() > 1) {
getAbstractSession().log(SessionLog.WARNING, SessionLog.CONNECTION, "entity_manager_has_multiple_connections");
}
@SuppressWarnings("unchecked")
C connection = (C) getAbstractSession().getAccessor().getDatasourceConnection();
try {
action.accept(connection);
} catch (Exception e) {
transaction.setRollbackOnlyInternal();
throw new PersistenceException(
ExceptionLocalization.buildMessage(
"entity_manager_with_connection_failed", new String[] {e.getLocalizedMessage()}), e);
}
}

/**
Expand All @@ -1648,7 +1660,19 @@ public <C> void runWithConnection(ConnectionConsumer<C> action) {
// TODO-API-3.2
@Override
public <C, T> T callWithConnection(ConnectionFunction<C, T> function) {
throw new UnsupportedOperationException("Jakarta Persistence 3.2 API was not implemented yet");
if (getAbstractSession().getAccessors().size() > 1) {
getAbstractSession().log(SessionLog.WARNING, SessionLog.CONNECTION, "entity_manager_has_multiple_connections");
}
@SuppressWarnings("unchecked")
C connection = (C) getAbstractSession().getAccessor().getDatasourceConnection();
try {
return function.apply(connection);
} catch (Exception e) {
transaction.setRollbackOnlyInternal();
throw new PersistenceException(
ExceptionLocalization.buildMessage(
"entity_manager_with_connection_failed", new String[] {e.getLocalizedMessage()}), e);
}
}

/**
Expand Down

0 comments on commit 71a1e97

Please sign in to comment.