Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
Copyright (C) 2014-2015 Philip Helger (www.helger.com) philip[at]helger[dot]com 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 http://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 com.helger.db.jpa;
 
 
 
 
JPA enabled manager with transaction handling etc. The IHasEntityManager required in the constructor should be a request singleton that ensures one javax.persistence.EntityManager per thread. The main javax.persistence.EntityManager objects are usually create from a subclass of AbstractGlobalEntityManagerFactory.

Author(s):
Philip Helger
 
 public class JPAEnabledManager
 {
  
By default the entity manager is not locked (changed in 3.0.0)
 
   public static final boolean DEFAULT_SYNC_ENTITY_MGR = false;
  
By default nested transactions are not allowed
 
   public static final boolean DEFAULT_ALLOW_NESTED_TRANSACTIONS = false;
  
By default no transaction is used for select statements
 
   public static final boolean DEFAULT_USE_TRANSACTIONS_FOR_SELECT = false;
  
The default execution time after which a warning is emitted
 
   public static final int DEFAULT_EXECUTION_WARN_TIME_MS = 1000;
 
   private static final Logger s_aLogger = LoggerFactory.getLogger (JPAEnabledManager.class);
   private static final IMutableStatisticsHandlerCounter s_aStatsCounterTransactions = StatisticsManager.getCounterHandler (JPAEnabledManager.class.getName () +
                                                                                                                            "$transactions");
   private static final IMutableStatisticsHandlerCounter s_aStatsCounterRollback = StatisticsManager.getCounterHandler (JPAEnabledManager.class.getName () +
                                                                                                                        "$rollback");
   private static final IMutableStatisticsHandlerCounter s_aStatsCounterSuccess = StatisticsManager.getCounterHandler (JPAEnabledManager.class.getName () +
                                                                                                                       "$success");
   private static final IMutableStatisticsHandlerCounter s_aStatsCounterError = StatisticsManager.getCounterHandler (JPAEnabledManager.class.getName () +
                                                                                                                     "$error");
   private static final IMutableStatisticsHandlerTimer s_aStatsTimerExecutionSuccess = StatisticsManager.getTimerHandler (JPAEnabledManager.class.getName () +
                                                                                                                          "$execSuccess");
   private static final IMutableStatisticsHandlerTimer s_aStatsTimerExecutionError = StatisticsManager.getTimerHandler (JPAEnabledManager.class.getName () +
                                                                                                                        "$execError");
 
   protected static final ReadWriteLock s_aRWLock = new ReentrantReadWriteLock ();
 
 
   public JPAEnabledManager (@Nonnull final IHasEntityManager aEntityManagerProvider)
   {
     ValueEnforcer.notNull (aEntityManagerProvider"EntityManagerProvider");
      = aEntityManagerProvider;
   }
  public final boolean isSyncEntityMgr ()
  {
    return .get ();
  }

  
Set whether the entity manager should be synchronized upon each access

Parameters:
bSyncEntityMgr true to enable sync, false to disable sync
  public final void setSyncEntityMgr (final boolean bSyncEntityMgr)
  {
    .set (bSyncEntityMgr);
  }
  public final boolean isAllowNestedTransactions ()
  {
    return .get ();
  }

  
Allow nested transaction

Parameters:
bAllowNestedTransactions true to enable nested transaction
  public final void setAllowNestedTransactions (final boolean bAllowNestedTransactions)
  {
    .set (bAllowNestedTransactions);
  }

  

Returns:
true if transactions should be used for selecting, false if this can be done without transactions
  public final boolean isUseTransactionsForSelect ()
  {
    return .get ();
  }

  
Use transactions for select statements?

Parameters:
bUseTransactionsForSelect true to enable the usage of transactions for select statements.
  public final void setUseTransactionsForSelect (final boolean bUseTransactionsForSelect)
  {
    .set (bUseTransactionsForSelect);
  }

  

Returns:
Get the entity manager to be used. Must not be null.
  protected final EntityManager getEntityManager ()
  {
  }

  
Set a custom exception handler that is called in case performing some operation fails.

Parameters:
aExceptionCallback The exception handler to be set. May be null to indicate no custom exception handler.
  public static final void setCustomExceptionCallback (@Nullable final IExceptionCallback <ThrowableaExceptionCallback)
  {
    .writeLock ().lock ();
    try
    {
       = aExceptionCallback;
    }
    finally
    {
      .writeLock ().unlock ();
    }
  }

  
Get the custom exception handler.

Returns:
null if non is set
  {
    .readLock ().lock ();
    try
    {
      return ;
    }
    finally
    {
      .readLock ().unlock ();
    }
  }

  
Invoke the custom exception handler (if present)

Parameters:
t The exception that occurred.
  private static void _invokeCustomExceptionCallback (@Nonnull final Throwable t)
  {
    final IExceptionCallback <ThrowableaExceptionCallback = getCustomExceptionCallback ();
    if (aExceptionCallback == null)
    {
      // By default: log something :)
      .error ("Failed to perform something in a JPAEnabledManager!"t);
    }
    else
      try
      {
        aExceptionCallback.onException (t);
      }
      catch (final Throwable t2)
      {
        .error ("Error in JPAEnabledManager custom exception handler " + aExceptionCallbackt2);
      }
  }

  

Returns:
The milliseconds after which a warning is emitted, if an SQL statement takes longer to execute.
  public static final int getDefaultExecutionWarnTime ()
  {
    return .get ();
  }

  
Set the milli seconds duration on which a warning should be emitted, if a single SQL execution too at least that long.

Parameters:
nMillis The number of milli seconds. Must be &ge; 0.
  public static final void setDefaultExecutionWarnTime (final int nMillis)
  {
    ValueEnforcer.isGE0 (nMillis"Milliseconds");
    .set (nMillis);
  }
  public static final void setExecutionTimeExceededHandler (@Nullable final IExecutionTimeExceededCallback aExecutionTimeExceededHandler)
  {
    .writeLock ().lock ();
    try
    {
       = aExecutionTimeExceededHandler;
    }
    finally
    {
      .writeLock ().unlock ();
    }
  }

  
Get the custom exception handler.

Returns:
null if non is set
  {
    .readLock ().lock ();
    try
    {
    }
    finally
    {
      .readLock ().unlock ();
    }
  }
  public static final void onExecutionTimeExceeded (@Nonnull final String sMsg,
                                                    @Nonnegative final long nExecutionMillis)
  {
    if (aHdl != null)
      try
      {
        // Invoke the handler
        aHdl.onExecutionTimeExceeded (sMsgnExecutionMillis);
      }
      catch (final Throwable t)
      {
        .error ("Failed to invoke exceution time exceeded handler " + aHdlt);
      }
  }
  public static final JPAExecutionResult <?> doInTransaction (@Nonnull @WillNotClose final EntityManager aEntityMgr,
                                                              final boolean bAllowNestedTransactions,
                                                              @Nonnull final Runnable aRunnable)
  {
    return doInTransaction (aEntityMgrbAllowNestedTransactions, AdapterRunnableToCallable.createAdapter (aRunnable));
  }
  public static final JPAExecutionResult <?> doInTransaction (@Nonnull @WillNotClose final EntityManager aEntityMgr,
                                                              final boolean bAllowNestedTransactions,
                                                              @Nonnull final IThrowingRunnable aRunnable)
  {
    return doInTransaction (aEntityMgr,
                            bAllowNestedTransactions,
                            AdapterThrowingRunnableToCallable.createAdapter (aRunnable));
  }
  public final JPAExecutionResult <?> doInTransaction (@Nonnull final IThrowingRunnable aRunnable)
  {
    // Create entity manager
    final EntityManager aEntityMgr = getEntityManager ();
    if (!isSyncEntityMgr ())
    {
      // No synchronization required
      return doInTransaction (aEntityMgrisAllowNestedTransactions (), aRunnable);
    }
    // Sync on the whole entity manager, to have a cross-manager
    // synchronization!
    synchronized (aEntityMgr)
    {
      return doInTransaction (aEntityMgrisAllowNestedTransactions (), aRunnable);
    }
  }
  public final JPAExecutionResult <?> doInTransaction (@Nonnull final Runnable aRunnable)
  {
    return doInTransaction (new AdapterRunnableToThrowingRunnable (aRunnable));
  }
  public static final <T> JPAExecutionResult <T> doInTransaction (@Nonnull @WillNotClose final EntityManager aEntityMgr,
                                                                  final boolean bAllowNestedTransactions,
                                                                  @Nonnull final Callable <T> aCallable)
  {
    final StopWatch aSW = StopWatch.createdStarted ();
    final EntityTransaction aTransaction = aEntityMgr.getTransaction ();
    final boolean bTransactionRequired = !bAllowNestedTransactions || !aTransaction.isActive ();
    if (bTransactionRequired)
    {
      aTransaction.begin ();
    }
    try
    {
      // Execute whatever you want to do
      final T ret = aCallable.call ();
      // And if no exception was thrown, commit it
      if (bTransactionRequired)
        aTransaction.commit ();
      return JPAExecutionResult.createSuccess (ret);
    }
    catch (final Throwable t)
    {
      return JPAExecutionResult.<T> createFailure (t);
    }
    finally
    {
      if (bTransactionRequired)
        if (aTransaction.isActive ())
        {
          // We got an exception -> rollback
          aTransaction.rollback ();
          .warn ("Rolled back transaction for callable " + aCallable);
          .increment ();
        }
      if (aSW.getMillis () > getDefaultExecutionWarnTime ())
        onExecutionTimeExceeded ("Callback: " +
                                 aSW.getMillis () +
                                 " ms; transaction: " +
                                 bTransactionRequired +
                                 "; Execution of callable in transaction took too long: " +
                                 aCallable.toString (),
                                 aSW.getMillis ());
    }
  }
  public final <T> JPAExecutionResult <T> doInTransaction (@Nonnull final Callable <T> aCallable)
  {
    // Create entity manager
    final EntityManager aEntityMgr = getEntityManager ();
    if (!isSyncEntityMgr ())
    {
      // No synchronization required
      return doInTransaction (aEntityMgrisAllowNestedTransactions (), aCallable);
    }
    // Sync on the whole entity manager, to have a cross-manager
    // synchronization!
    synchronized (aEntityMgr)
    {
      return doInTransaction (aEntityMgrisAllowNestedTransactions (), aCallable);
    }
  }

  
Perform a select, without a transaction

Parameters:
aCallable The callable
Returns:
The return of the callable or null upon success
  public static final <T> JPAExecutionResult <T> doSelectStatic (@Nonnull final Callable <T> aCallable)
  {
    ValueEnforcer.notNull (aCallable"Callable");
    final StopWatch aSW = StopWatch.createdStarted ();
    try
    {
      // Call callback
      final T ret = aCallable.call ();
      return JPAExecutionResult.createSuccess (ret);
    }
    catch (final Throwable t)
    {
      return JPAExecutionResult.<T> createFailure (t);
    }
    finally
    {
      if (aSW.getMillis () > getDefaultExecutionWarnTime ())
        onExecutionTimeExceeded ("Execution of select took too long: " + aCallable.toString (), aSW.getMillis ());
    }
  }

  
Run a read-only query. By default no transaction is used, and the entity manager is synchronized.

Parameters:
aCallable The callable to execute.
Returns:
A non-null result of the select.
  public final <T> JPAExecutionResult <T> doSelect (@Nonnull final Callable <T> aCallable)
  {
    {
      // Use transactions for select statement!
      return doInTransaction (aCallable);
    }
    // Ensure that only one transaction is active for all users!
    final EntityManager aEntityMgr = getEntityManager ();
    if (!isSyncEntityMgr ())
    {
      // No synchronization required
      return doSelectStatic (aCallable);
    }
    // Sync on the whole entity manager, to have a cross-manager
    // synchronization!
    synchronized (aEntityMgr)
    {
      return doSelectStatic (aCallable);
    }
  }

  
Helper method to handle the execution of "SELECT COUNT(...) ..." SQL statements. To be invoked inside a doSelect(java.util.concurrent.Callable) or doSelectStatic(java.util.concurrent.Callable) method.

Parameters:
aQuery The SELECT COUNT query
Returns:
a non-negative row count
  public static final Number getSelectCountResultObj (@Nonnull final Query aQuery)
  {
    final Number ret = (NumberaQuery.getSingleResult ();
    return ret != null ? ret : Integer.valueOf (0);
  }

  
Helper method to handle the execution of "SELECT COUNT(...) ..." SQL statements. To be invoked inside a doSelect(java.util.concurrent.Callable) or doSelectStatic(java.util.concurrent.Callable) method.

Parameters:
aQuery The SELECT COUNT query
Returns:
a non-negative row count
  public static final long getSelectCountResult (@Nonnull final Query aQuery)
  {
    return getSelectCountResultObj (aQuery).longValue ();
  }
New to GrepCode? Check out our FAQ X