/* * #%L * Wisdom-Framework * %% * Copyright (C) 2013 - 2014 Wisdom Framework * %% * 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. * #L% */ package org.wisdom.framework.jpa; import javax.persistence.*; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.metamodel.Metamodel; import javax.transaction.Synchronization; import javax.transaction.Transaction; import javax.transaction.TransactionManager; import java.util.Map; /** * A Transactional Entity Manager delegates all requests to another Entity * Manager. The delegate is created when it is needed for the first time in a * transaction and later reused. The delegate is automatically closed at the end * of the transaction. */ @SuppressWarnings("rawtypes") class TransactionalEntityManager implements EntityManager { private final TransactionManager transactionManager; private final EntityManagerFactory entityManagerFactory; private final PersistenceUnitComponent unit; final ThreadLocal<EntityManager> perThreadEntityManager = new ThreadLocal<>(); volatile boolean open = true; //TODO Manage transaction manager in the request scope. public TransactionalEntityManager(TransactionManager tm, EntityManagerFactory emf, PersistenceUnitComponent unit) { this.transactionManager = tm; this.entityManagerFactory = emf; this.unit = unit; } /** * The delegated methods call this method to get the delegate. This method * verifies if we're still open, if there already is an Entity Manager for * this thread and otherwise creates it and enlists it for auto close at the * current transaction. * * @return an Entity Manager */ private EntityManager getEM() throws IllegalStateException { if (!open) { throw new IllegalStateException("The JPA bridge has closed"); } try { // Do we already have one on this thread? EntityManager em = perThreadEntityManager.get(); if (em != null) { return em; } // Nope, so we need to check if there actually is a transaction final Transaction transaction = transactionManager.getTransaction(); if (transaction == null) { throw new TransactionRequiredException("Cannot create an EM since no transaction active"); } em = entityManagerFactory.createEntityManager(); try { // Register a callback at the end of the transaction transaction.registerSynchronization(new Synchronization() { @Override public void beforeCompletion() { if (!open) { throw new IllegalStateException( "The Transaction Entity Manager was closed in the mean time"); } } @Override public void afterCompletion(int arg0) { EntityManager em = perThreadEntityManager.get(); perThreadEntityManager.set(null); em.close(); } }); } catch (Exception e) { em.close(); throw new IllegalStateException("Registering synchronization to close EM", e); } // Make it available for later calls on this thread perThreadEntityManager.set(em); // And make sure it joins the current transaction. em.joinTransaction(); return em; } catch (Exception e) { throw new IllegalStateException("Error while retrieving entity manager", e); } } void shutdown() { open = false; } /** * We automatically close so ignore. */ @Override public void close() { } @Override public void clear() { getEM().clear(); } @Override public boolean contains(Object arg0) { return getEM().contains(arg0); } @Override public <T> TypedQuery<T> createNamedQuery(String arg0, Class<T> arg1) { return getEM().createNamedQuery(arg0, arg1); } @Override public Query createNamedQuery(String arg0) { return getEM().createNamedQuery(arg0); } @Override public Query createNativeQuery(String arg0, Class arg1) { return getEM().createNativeQuery(arg0, arg1); } @Override public Query createNativeQuery(String arg0, String arg1) { return getEM().createNativeQuery(arg0, arg1); } @Override public Query createNativeQuery(String arg0) { return getEM().createNativeQuery(arg0); } @Override public <T> TypedQuery<T> createQuery(CriteriaQuery<T> arg0) { return getEM().createQuery(arg0); } @Override public <T> TypedQuery<T> createQuery(String arg0, Class<T> arg1) { return getEM().createQuery(arg0, arg1); } @Override public Query createQuery(String arg0) { return getEM().createQuery(arg0); } @Override public void detach(Object arg0) { getEM().detach(arg0); } @Override public <T> T find(Class<T> arg0, Object arg1, LockModeType arg2, Map<String, Object> arg3) { return getEM().find(arg0, arg1, arg2, arg3); } @Override public <T> T find(Class<T> arg0, Object arg1, LockModeType arg2) { return getEM().find(arg0, arg1, arg2); } @Override public <T> T find(Class<T> arg0, Object arg1, Map<String, Object> arg2) { return getEM().find(arg0, arg1, arg2); } @Override public <T> T find(Class<T> arg0, Object arg1) { return getEM().find(arg0, arg1); } @Override public void flush() { getEM().flush(); } @Override public CriteriaBuilder getCriteriaBuilder() { return getEM().getCriteriaBuilder(); } @Override public Object getDelegate() { return getEM().getDelegate(); } @Override public EntityManagerFactory getEntityManagerFactory() { return getEM().getEntityManagerFactory(); } @Override public FlushModeType getFlushMode() { return getEM().getFlushMode(); } @Override public LockModeType getLockMode(Object arg0) { return getEM().getLockMode(arg0); } @Override public Metamodel getMetamodel() { return getEM().getMetamodel(); } @Override public Map<String, Object> getProperties() { return getEM().getProperties(); } @Override public <T> T getReference(Class<T> arg0, Object arg1) { return getEM().getReference(arg0, arg1); } @Override public EntityTransaction getTransaction() { return getEM().getTransaction(); } @Override public boolean isOpen() { return getEM().isOpen(); } @Override public void joinTransaction() { getEM().joinTransaction(); } @Override public void lock(Object arg0, LockModeType arg1, Map<String, Object> arg2) { getEM().lock(arg0, arg1, arg2); } @Override public void lock(Object arg0, LockModeType arg1) { getEM().lock(arg0, arg1); } @Override public <T> T merge(T arg0) { return getEM().merge(arg0); } @Override public void persist(Object arg0) { getEM().persist(arg0); } @Override public void refresh(Object arg0, LockModeType arg1, Map<String, Object> arg2) { getEM().refresh(arg0, arg1, arg2); } @Override public void refresh(Object arg0, LockModeType arg1) { getEM().refresh(arg0, arg1); } @Override public void refresh(Object arg0, Map<String, Object> arg1) { getEM().refresh(arg0, arg1); } @Override public void refresh(Object arg0) { getEM().refresh(arg0); } @Override public void remove(Object arg0) { getEM().remove(arg0); } @Override public void setFlushMode(FlushModeType arg0) { getEM().setFlushMode(arg0); } @Override public void setProperty(String arg0, Object arg1) { getEM().setProperty(arg0, arg1); } @Override public <T> T unwrap(Class<T> arg0) { return getEM().unwrap(arg0); } }