org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of eclipselink Show documentation
Show all versions of eclipselink Show documentation
EclipseLink build based upon Git transaction f2b9fc5
/*
* Copyright (c) 1998, 2020 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
*/
// Contributors:
// Oracle - initial API and implementation from Oracle TopLink
// 07/13/2012-2.5 Guy Pelletier
// - 350487: JPA 2.1 Specification defined support for Stored Procedure Calls
// 08/24/2012-2.5 Guy Pelletier
// - 350487: JPA 2.1 Specification defined support for Stored Procedure Calls
// 09/13/2013-2.5 Guy Pelletier
// - 350487: JPA 2.1 Specification defined support for Stored Procedure Calls
package org.eclipse.persistence.internal.jpa.transaction;
import java.util.Map;
import java.util.WeakHashMap;
import jakarta.persistence.RollbackException;
import org.eclipse.persistence.exceptions.TransactionException;
import org.eclipse.persistence.internal.jpa.QueryImpl;
import org.eclipse.persistence.internal.jpa.transaction.EntityTransactionWrapper;
import org.eclipse.persistence.internal.localization.ExceptionLocalization;
/**
* JDK 1.5 version of the EntityTransaction. Differs from base version only in that
* it takes a JDK 1.5 version of the EntityTransactionWrapper.
*
* @see org.eclipse.persistence.internal.jpa.transaction.EntityTransactionImpl
*/
public class EntityTransactionImpl implements jakarta.persistence.EntityTransaction {
/**
* Keep a weak reference to the open queries that are executed in this entity manager.
*/
protected WeakHashMap openQueriesMap;
protected EntityTransactionWrapper wrapper;
protected boolean active = false;
protected boolean rollbackOnly = false;
protected TransactionFinalizer finalizer;
/** PERF: Avoid finalization if not required by the application, and finalizers have major concurrency affects. */
public static boolean isFinalizedRequired = false;
public EntityTransactionImpl(EntityTransactionWrapper wrapper) {
this.wrapper = wrapper;
if (isFinalizedRequired) {
this.finalizer = new TransactionFinalizer();
}
}
/**
* Within a transaction track any open queries that will need to be closed
* on commit or rollback.
*/
/**
* Queries that leave the connection and are executed against this entity
* manager will be added here. On rollback or commit any left over open
* queries should be closed.
*
* @param query
*/
public void addOpenQuery(QueryImpl query) {
if (openQueriesMap == null) {
openQueriesMap = new WeakHashMap();
}
openQueriesMap.put(query, query);
}
/**
* Start the current transaction. This can only be invoked if
* {@link #isActive()} returns false
.
*
* @throws IllegalStateException
* if isActive() is true.
*/
@Override
public void begin() {
if (isActive()) {
throw new IllegalStateException(TransactionException.transactionIsActive().getMessage());
}
//bug307445 : Throw IllegalStateException if entityManager was closed
this.wrapper.getEntityManager().verifyOpen();
// always extended
this.wrapper.localUOW = this.wrapper.getEntityManager().getActivePersistenceContext(null);
this.wrapper.localUOW.setShouldTerminateTransaction(false);
this.active = true;
}
/**
* Open queries within a transaction will be closed on commit or rollback
* if they haven't already been closed.
*/
protected void closeOpenQueries() {
if (openQueriesMap != null) {
for (QueryImpl openQuery : openQueriesMap.keySet()) {
openQuery.close();
}
}
}
/**
* Commit the current transaction, writing any un-flushed changes to the
* database. This can only be invoked if {@link #isActive()} returns
* true
.
*
* @throws IllegalStateException
* if isActive() is false.
*/
@Override
public void commit() {
if (!isActive()) {
throw new IllegalStateException(TransactionException.transactionNotActive().getMessage());
}
// Make sure any open queries are closed.
closeOpenQueries();
try {
if (this.wrapper.localUOW != null) {
this.wrapper.localUOW.setShouldTerminateTransaction(true);
if (!this.rollbackOnly) {
if (this.wrapper.localUOW.shouldResumeUnitOfWorkOnTransactionCompletion()) {
this.wrapper.localUOW.commitAndResume();
return;
} else {
this.wrapper.localUOW.commit();
// all change sets and are cleared, but the cache is
// kept
this.wrapper.localUOW.clearForClose(false);
}
} else {
throw new RollbackException(ExceptionLocalization.buildMessage("rollback_because_of_rollback_only"));
}
}
} catch (RuntimeException exception) {
try {
if (this.wrapper.localUOW != null) {
this.wrapper.getEntityManager().removeExtendedPersistenceContext();
this.wrapper.localUOW.release();
this.wrapper.localUOW.getParent().release();
}
} catch (Exception ignore) {} // Throw first exception.
if (exception instanceof RollbackException) {
throw exception;
} else if (exception instanceof org.eclipse.persistence.exceptions.OptimisticLockException) {
throw new RollbackException(new jakarta.persistence.OptimisticLockException(exception));
} else {
throw new RollbackException(exception);
}
} finally {
this.active = false;
this.rollbackOnly = false;
this.wrapper.setLocalUnitOfWork(null);
}
}
/**
* Roll back the current transaction, discarding any changes that have
* happened in this transaction. This can only be invoked if
* {@link #isActive()} returns true
.
*
* @throws IllegalStateException
* if isActive() is false.
*/
@Override
public void rollback() {
if (!isActive()) {
throw new IllegalStateException(TransactionException.transactionNotActive().getMessage());
}
// Make sure any open queries are closed.
closeOpenQueries();
try {
if (wrapper.getLocalUnitOfWork() != null) {
this.wrapper.localUOW.setShouldTerminateTransaction(true);
this.wrapper.localUOW.release();
this.wrapper.localUOW.getParent().release();
}
} finally {
this.active = false;
this.rollbackOnly = false;
this.wrapper.getEntityManager().removeExtendedPersistenceContext();
this.wrapper.setLocalUnitOfWork(null);
}
}
/**
* Mark the current transaction so that the only possible outcome of the
* transaction is for the transaction to be rolled back.
*
* @throws IllegalStateException
* if isActive() is false.
*/
@Override
public void setRollbackOnly() {
if (!isActive()) {
throw new IllegalStateException(TransactionException.transactionNotActive().getMessage());
}
this.rollbackOnly = true;
}
/**
* Return the weak reference to the open queries.
*/
protected Map getOpenQueriesMap() {
if (openQueriesMap == null) {
openQueriesMap = new WeakHashMap();
}
return openQueriesMap;
}
/**
* Determine whether the current transaction has been marked for rollback.
*
* @throws IllegalStateException
* if isActive() is false.
*/
@Override
public boolean getRollbackOnly() {
if (!isActive()) {
throw new IllegalStateException(TransactionException.transactionNotActive().getMessage());
}
return this.rollbackOnly;
}
/**
* Check to see if the current transaction is in progress.
*/
@Override
public boolean isActive() {
return this.active;
}
class TransactionFinalizer {
/**
* Here incase a user does not commit or rollback an enityTransaction
* but just throws it away. If we do not rollback the txn the connection
* will go back into the pool.
*/
@Override
protected void finalize() throws Throwable {
if (isActive()) {
rollback();
}
}
}
}