Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EntityManagerFactory.withTransaction() as default implementation in the interface. #1

Open
wants to merge 2 commits into
base: 410
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
125 changes: 124 additions & 1 deletion api/src/main/java/jakarta/persistence/EntityManagerFactory.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2008, 2021 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2008, 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
Expand All @@ -17,6 +17,11 @@
package jakarta.persistence;

import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;

import jakarta.persistence.metamodel.Metamodel;
import jakarta.persistence.criteria.CriteriaBuilder;

Expand Down Expand Up @@ -221,4 +226,122 @@ public interface EntityManagerFactory extends AutoCloseable {
*/
public <T> void addNamedEntityGraph(String graphName, EntityGraph<T> entityGraph);

/**
* Create a new application-managed {@code EntityManager}, start a resource-local
* transaction, and call the given function, passing both the {@code EntityManager}
* and the {@code EntityTransaction}.
* The given function is responsible for finishing the transaction by calling {@code commit}
* or {@code rollback} method.
*
* @param work a function to be called in the scope of the transaction
*/
default void withTransaction(BiConsumer<EntityManager, EntityTransaction> work) {
try (EntityManager em = this.createEntityManager()) {
EntityTransaction t = em.getTransaction();
t.begin();
work.accept(em, t);
}
}

/**
* Create a new application-managed {@code EntityManager}, start a resource-local
* transaction, and call the given function, passing both the {@code EntityManager}
* and the {@code EntityTransaction}.
* The given function is responsible for finishing the transaction by calling {@code commit}
* or {@code rollback} method.
*
* @param work a function to be called in the scope of the transaction
* @return the value returned by the given function
*/
default <R> R withTransaction(BiFunction<EntityManager, EntityTransaction, R> work) {
try (EntityManager em = this.createEntityManager()) {
EntityTransaction t = em.getTransaction();
t.begin();
return work.apply(em, t);
}
}

/**
* Create a new application-managed {@code EntityManager}, start a resource-local
* transaction, and call the given function, passing the {@code EntityManager}.
* If the given function does not throw an exception, commit the transaction and
* return the result of the function. If the function does throw an exception,
* roll back the transaction and rethrow the exception. Finally, close the
* {@code EntityManager}.
*
* @param work a function to be called in the scope of the transaction
*/
default void withTransaction(TransactionVoidWork work) {
try (EntityManager em = this.createEntityManager()) {
EntityTransaction t = em.getTransaction();
try {
t.begin();
work.work(em);
t.commit();
} catch (Exception e) {
t.rollback();
throw new PersistenceException("Application-managed transaction failed", e);
}
}
}

/**
* Create a new application-managed {@code EntityManager}, start a resource-local
* transaction, and call the given function, passing the {@code EntityManager}.
* If the given function does not throw an exception, commit the transaction and
* return the result of the function. If the function does throw an exception,
* roll back the transaction and rethrow the exception. Finally, close the
* {@code EntityManager}.
*
* @param work a function to be called in the scope of the transaction
* @return the value returned by the given function
*/
default <R> R withTransaction(TransactionWork<R> work) {
try (EntityManager em = this.createEntityManager()) {
EntityTransaction t = em.getTransaction();
try {
t.begin();
R result = work.work(em);
t.commit();
return result;
} catch (Exception e) {
t.rollback();
throw new PersistenceException("Application-managed transaction failed", e);
}
}
}

/**
* A task that runs in a transaction, returns no result and may throw an exception.
* Implementors define a single method with {@link EntityManager} argument called {@code work}.
*/
@FunctionalInterface
interface TransactionVoidWork {
/**
* Executes the function. Throws an exception if unable to do so.
*
* @param em the application-managed {@link EntityManager} instance
* @throws Exception when unable to compute a result
*/
void work(EntityManager em) throws Exception;
}

/**
* A task that runs in a transaction, returns result and may throw an exception.
* Implementors define a single method with {@link EntityManager} argument called {@code work}.
*
* @param <R> the result type of method {@code work}
*/
@FunctionalInterface
interface TransactionWork<R> {
/**
* Executes the function. Throws an exception if unable to do so.
*
* @param em the application-managed {@link EntityManager} instance
* @return the computed result
* @throws Exception when unable to compute a result
*/
R work(EntityManager em) throws Exception;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -412,6 +412,17 @@ public interface EntityManagerFactory extends AutoCloseable {
*/
public <T> void addNamedEntityGraph(String graphName, EntityGraph<T> entityGraph);

/**
* Create a new application-managed {@code EntityManager}, start a resource-local
* transaction, and call the given function, passing the {@code EntityManager}.
* If the given function does not throw an exception, commit the transaction and
* return the result of the function. If the function does throw an exception,
* roll back the transaction and rethrow the exception. Finally, close the
* {@code EntityManager}.
*
* @param work a function to be called in the scope of the transaction
*/
public <R> R withTransaction(Function<EntityManager,R> work);
}
----

Expand Down Expand Up @@ -577,14 +588,13 @@ public class PasswordChanger {
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();

User user = (User)em.createQuery
("SELECT u FROM User u WHERE u.name=:name AND u.pass=:pass")
User user = em.createQuery
("SELECT u FROM User u WHERE u.name=:name AND u.pass=:pass", User.class)
.setParameter("name", args[0])
.setParameter("pass", args[1])
.getSingleResult();

if (user!=null)
user.setPassword(args[2]);
user.setPassword(args[2]);

em.getTransaction().commit();
em.close();
Expand All @@ -593,6 +603,48 @@ public class PasswordChanger {
}
----

=== The withTransaction method

The _withTransaction_ method of the _EntityManagerFactory_ provides
a shortcut for session and transaction management with an
application-managed _EntityManager_ and a resource-local
_EntityTransaction_.

[source,java]
----
entityManagerFactory.withTransaction( entityManager -> {
User user = em.createQuery
("SELECT u FROM User u WHERE u.name=:name AND u.pass=:pass", User.class)
.setParameter("name", args[0])
.setParameter("pass", args[1])
.getSingleResult();

user.setPassword(args[2]);

return null;
})
----

The argument function passed to _withTransaction_ must be called
and passed a new instance of _EntityManager_. When the function
returns or throws an exception, this _EntityManager_ must be closed
before _withTransaction_ returns.

The argument function passed to _withTransaction_ is executed in
the context of a resource-local transaction associated with this
new _EntityManager_. If the argument function throws an exception,
the transaction must be rolled back, and then the exception must be
rethrown by _withTransaction_. If the argument function returns,
then _withTransaction_ must attempt to commit the transaction.
If the attempt to commit the transaction fails, the exception must
be rethrown by _withTransaction_. Otherwise, _withTransaction_ must
return the value returned by the function, if any.

The application should not attempt to manage the resource-local
transaction directly. If the application calls any operations of
_EntityTransaction_ from within a call to _withTransaction_, the
behavior is undefined.

=== Container-managed Persistence Contexts [[a11791]]

When a container-managed entity manager is
Expand Down