Java tutorial
/* * Copyright 2009 Lukas Lang * * 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 org.castor.jpa; import javax.persistence.EntityExistsException; import javax.persistence.EntityManager; import javax.persistence.EntityNotFoundException; import javax.persistence.EntityTransaction; import javax.persistence.FlushModeType; import javax.persistence.LockModeType; import javax.persistence.PersistenceException; import javax.persistence.Query; import javax.persistence.TransactionRequiredException; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.castor.jpa.proxy.ReferenceProxy; import org.exolab.castor.jdo.ClassNotPersistenceCapableException; import org.exolab.castor.jdo.Database; import org.exolab.castor.jdo.DuplicateIdentityException; import org.exolab.castor.jdo.ObjectNotFoundException; import org.exolab.castor.persist.spi.Identity; /** * Castor implementation of the {@link EntityManager} interface. * * @author lukas.lang * */ public final class CastorEntityManager implements EntityManager { /** * A logger. */ private final Log log = LogFactory.getLog(CastorEntityManager.class); /** * The Castor {@link Database} to use. */ private Database database; /** * Stays <code>true</code> until {@link EntityManager#close()} is called. */ private boolean open = true; /** * This {@link EntityManager}'s {@link EntityTransaction}. */ private EntityTransaction entityTransaction; /** * The {@link PersistenceContext} which manages entity life cycles. */ private PersistenceContext context; /** * Constructor taking a Castor {@link Database}. * * @param database * the Castor {@link Database} used by the {@link EntityManager}. */ public CastorEntityManager(final Database database) { this.database = database; this.context = new TransactionScopedPersistenceContext(); this.entityTransaction = new CastorEntityTransaction(this, database); } /** * Clear the persistence context, causing all managed entities to become * detached. Changes made to entities that have not been flushed to the * database will not be persisted. * * @throws IllegalStateException * if this EntityManager has been closed. */ public void clear() { // Check whether the entity manager is open. verifyOpenEntityManager(); // Invalidate context. invalidatePersistenceContext(); } /** * Close an application-managed EntityManager. After the close method has * been invoked, all methods on the EntityManager instance and any Query * objects obtained from it will throw the IllegalStateException except for * getTransaction and isOpen (which will return false). If this method is * called when the EntityManager is associated with an active transaction, * the persistence context remains managed until the transaction completes. * * TODO lukas.lang: Add test for a {@link Query} on a closed EM. * {@link EntityManager}. * * TODO lukas.lang: Add test with a running TX. * * @throws IllegalStateException * if the EntityManager is container-managed or has been already * closed.. */ public void close() { verifyOpenEntityManager(); // TODO lukas.lang: Should the Castor database get closed? // Close Castor database. // this.database.close(); // Close entity manager. this.open = false; } /** * Check if the instance belongs to the current persistence context. * * @param entity * @return <code>true</code> if the instance belongs to the current * persistence context. * @throws IllegalStateException * if this EntityManager has been closed. * @throws IllegalArgumentException * if not an entity */ public boolean contains(Object entity) { // Check whether the entity manager is open. verifyOpenEntityManager(); // Check whether the entity is persistence capable. verifyPersistenceCapable(entity.getClass()); return this.context.contains(entity); } /** * Create an instance of Query for executing a named query (in the Java * Persistence query language or in native SQL). * * @param name * the name of a query defined in metadata * @return the new query instance * @throws IllegalStateException * if this EntityManager has been closed. * @throws IllegalArgumentException * if a query has not been defined with the given name */ public Query createNamedQuery(String name) { // Check whether the entity manager is open. verifyOpenEntityManager(); //throw new UnsupportedOperationException("Not yet implemented!"); return new CastorQuery(name, this.database); } /** * Create an instance of Query for executing a native SQL statement, e.g., * for update or delete. * * @param sqlString * a native SQL query string * @return the new query instance * @throws IllegalStateException * if this EntityManager has been closed. */ public Query createNativeQuery(String sqlString) { // Check whether the entity manager is open. verifyOpenEntityManager(); throw new UnsupportedOperationException("Not yet implemented!"); } /** * Create an instance of Query for executing a native SQL query. * * @param sqlString * a native SQL query string * @param resultClass * the class of the resulting instance(s) * @return the new query instance * @throws IllegalStateException * if this EntityManager has been closed. */ @SuppressWarnings("unchecked") public Query createNativeQuery(String sqlString, Class resultClass) { // Check whether the entity manager is open. verifyOpenEntityManager(); throw new UnsupportedOperationException("Not yet implemented!"); } /** * Create an instance of Query for executing a native SQL query. * * @param sqlString * a native SQL query string * @param resultSetMapping * the name of the result set mapping * @return the new query instance * @throws IllegalStateException * if this EntityManager has been closed. */ public Query createNativeQuery(String sqlString, String resultSetMapping) { // Check whether the entity manager is open. verifyOpenEntityManager(); throw new UnsupportedOperationException("Not yet implemented!"); } /** * Create an instance of Query for executing a Java Persistence query * language statement. * * @param qlString * a Java Persistence query language query string * @return the new query instance * @throws IllegalStateException * if this EntityManager has been closed. * @throws IllegalArgumentException * if query string is not valid */ public Query createQuery(String qlString) { // Check whether the entity manager is open. verifyOpenEntityManager(); throw new UnsupportedOperationException("Not yet implemented!"); } /** * Find by primary key. * * @param entityClass * @param primaryKey * @return the found entity instance or null if the entity does not exist * @throws IllegalStateException * if this EntityManager has been closed. * @throws IllegalArgumentException * if the first argument does not denote an entity type or the * second argument is not a valid type for that entity's primary * key */ public <T> T find(Class<T> entityClass, Object primaryKey) { // Check whether the entity manager is open. verifyOpenEntityManager(); // Check whether a transaction is running. verifyRunningTransaction(); T entity = null; try { // Load the entity from the Castor database. entity = (T) this.database.load(entityClass, primaryKey); // Add the entity to the persistence context. if (entity != null) { this.context.manage(entity); } } catch (ObjectNotFoundException onfe) { if (log.isDebugEnabled()) { log.debug("Entity with primary key >" + primaryKey + "< not found."); } // Do nothing. } catch (ClassNotPersistenceCapableException npce) { log.error("Entity class >" + entityClass.getName() + "< not persistence capable.", npce); throw new IllegalArgumentException( "Entity class >" + entityClass.getName() + "< not persistence capable."); } catch (org.exolab.castor.jdo.PersistenceException e) { log.error("Could not load entity with id >" + primaryKey + "< from Castor database.", e); throw new IllegalArgumentException( "Could not load entity with id >" + primaryKey + "< from Castor database.", e); } // Return the object or null. return entity; } /** * Synchronize the persistence context to the underlying database. * * @throws IllegalStateException * if this EntityManager has been closed. * @throws TransactionRequiredException * if there is no transaction * @throws PersistenceException * if the flush fails */ public void flush() { // Check whether the entity manager is open. verifyOpenEntityManager(); // Check whether a transaction is running. verifyRunningTransaction(); throw new UnsupportedOperationException("Not yet implemented!"); } /** * Return the underlying provider object for the EntityManager, if * available. The result of this method is implementation specific. * * @throws IllegalStateException * if this EntityManager has been closed. */ public Object getDelegate() { // Check whether the entity manager is open. verifyOpenEntityManager(); throw new UnsupportedOperationException("Not yet implemented!"); } /** * Get the flush mode that applies to all objects contained in the * persistence context. * * @return flush mode * @throws IllegalStateException * if this EntityManager has been closed. */ public FlushModeType getFlushMode() { // Check whether the entity manager is open. verifyOpenEntityManager(); throw new UnsupportedOperationException("Not yet implemented!"); } /** * Get an instance, whose state may be lazily fetched. If the requested * instance does not exist in the database, throws * {@link EntityNotFoundException} when the instance state is first * accessed. (The persistence provider runtime is permitted to throw * {@link EntityNotFoundException} when {@link #getReference} is called.) * * The application should not expect that the instance state will be * available upon detachment, unless it was accessed by the application * while the entity manager was open. * * @param entityClass * @param primaryKey * @return the found entity instance * @throws IllegalStateException * if this EntityManager has been closed. * @throws IllegalArgumentException * if the first argument does not denote an entity type or the * second argument is not a valid type for that entity's primary * key * @throws EntityNotFoundException * if the entity state cannot be accessed */ public <T> T getReference(Class<T> entityClass, Object primaryKey) { // Check whether the entity manager is open. verifyOpenEntityManager(); // Check whether there is a a running transaction. verifyRunningTransaction(); // Check whether entity type is known. verifyPersistenceCapable(entityClass); // Return a proxied instance. return ReferenceProxy.createProxy(entityClass, new Identity(primaryKey), this); } /** * Returns the resource-level transaction object. The EntityTransaction * instance may be used serially to begin and commit multiple transactions. * * @return EntityTransaction instance * @throws IllegalStateException * if invoked on a JTA EntityManager. */ public EntityTransaction getTransaction() { return this.entityTransaction; } /** * Determine whether the EntityManager is open. * * @return true until the EntityManager has been closed. */ public boolean isOpen() { return this.open; } /** * Indicate to the EntityManager that a JTA transaction is active. This * method should be called on a JTA application managed EntityManager that * was created outside the scope of the active transaction to associate it * with the current JTA transaction. * * @throws IllegalStateException * if this EntityManager has been closed. * @throws TransactionRequiredException * if there is no transaction. */ public void joinTransaction() { // Check whether the entity manager is open. verifyOpenEntityManager(); // Check whether a transaction is running. verifyRunningTransaction(); throw new UnsupportedOperationException("Not yet implemented!"); } /** * Set the lock mode for an entity object contained in the persistence * context. * * @param entity * @param lockMode * @throws IllegalStateException * if this EntityManager has been closed. * @throws PersistenceException * if an unsupported lock call is made * @throws IllegalArgumentException * if the instance is not an entity or is a detached entity * @throws TransactionRequiredException * if there is no transaction */ public void lock(Object entity, LockModeType lockMode) { // Check whether the entity manager is open. verifyOpenEntityManager(); // Check whether a transaction is running. verifyRunningTransaction(); throw new UnsupportedOperationException("Not yet implemented!"); } /** * Merge the state of the given entity into the current persistence context. * * @param entity * @return the instance that the state was merged to * @throws IllegalStateException * if this EntityManager has been closed. * @throws IllegalArgumentException * if instance is not an entity or is a removed entity * @throws TransactionRequiredException * if invoked on a container-managed entity manager of type * PersistenceContextType.TRANSACTION and there is no * transaction. */ public <T> T merge(T entity) { // Check whether the entity manager is open. verifyOpenEntityManager(); // Check whether a transaction is running. verifyRunningTransaction(); if (this.context.contains(entity)) { // Entity is already managed. return entity; } try { // Merge entity. this.database.update(entity); // Add merged entity to persistence context. this.context.manage(entity); return entity; } catch (org.exolab.castor.jdo.PersistenceException e) { log.error("Could not merge entity.", e); throw new IllegalArgumentException("Could not merge entity.", e); } } /** * Make an entity instance managed and persistent. * * @param entity * @throws EntityExistsException * if the entity already exists. (The EntityExistsException may * be thrown when the persist operation is invoked, or the * EntityExistsException or another PersistenceException may be * thrown at flush or commit time.) * @throws IllegalStateException * if this EntityManager has been closed. * @throws IllegalArgumentException * if not an entity * @throws TransactionRequiredException * if invoked on a container-managed entity manager of type * PersistenceContextType.TRANSACTION and there is no * transaction. */ public void persist(Object entity) { // Check whether the entity manager is open. verifyOpenEntityManager(); // Check whether a transaction is running. verifyRunningTransaction(); if (this.context.contains(entity)) { // The entity is already managed within this persistence context. return; } try { // Create the entity. this.database.create(entity); // Add the entity to the set of managed entities. this.context.manage(entity); } catch (ClassNotPersistenceCapableException e) { log.error("Entity of type >" + entity.getClass().getName() + "< is not valid entity type.", e); throw new IllegalArgumentException( "Entity of type >" + entity.getClass().getName() + "< is not valid entity type.", e); } catch (DuplicateIdentityException e) { log.error("Entity of type " + entity.getClass().getName() + " already exists."); throw new EntityExistsException("Entity of type " + entity.getClass().getName() + " already exists.", e); } catch (org.exolab.castor.jdo.PersistenceException e) { throw new PersistenceException(e); } } /** * Refresh the state of the instance from the database, overwriting changes * made to the entity, if any. * * @param entity * @throws IllegalStateException * if this EntityManager has been closed. * @throws IllegalArgumentException * if not an entity or entity is not managed * @throws TransactionRequiredException * if invoked on a container-managed entity manager of type * PersistenceContextType.TRANSACTION and there is no * transaction. * @throws EntityNotFoundException * if the entity no longer exists in the database. */ public void refresh(Object entity) { // Check whether the entity manager is open. verifyOpenEntityManager(); // Check whether a transaction is running. verifyRunningTransaction(); throw new UnsupportedOperationException("Not yet implemented!"); } /** * Remove the entity instance. * * @param entity * @throws IllegalStateException * if this EntityManager has been closed. * @throws IllegalArgumentException * if not an entity or if a detached entity * @throws TransactionRequiredException * if invoked on a container-managed entity manager of type * PersistenceContextType.TRANSACTION and there is no * transaction. */ public void remove(Object entity) { // Check whether the entity manager is open. verifyOpenEntityManager(); // Check whether a transaction is running. verifyRunningTransaction(); // Check whether entity was already removed within this transaction. if (this.context.removed(entity)) { return; } // Check whether the given entity is managed. verifyEntityIsManaged(entity); try { // Remove entity from database. this.database.remove(entity); // Remove entity from the set of managed entities. this.context.remove(entity); } catch (org.exolab.castor.jdo.PersistenceException e) { log.error("Could not remove entity.", e); throw new IllegalArgumentException("Could not remove entity.", e); } } /** * Set the flush mode that applies to all objects contained in the * persistence context. * * @param flushMode * @throws IllegalStateException * if this EntityManager has been closed. */ public void setFlushMode(FlushModeType flushMode) { // Check whether the entity manager is open. verifyOpenEntityManager(); throw new UnsupportedOperationException("Not yet implemented!"); } /** * Invalidates the {@link PersistenceContext}. This method is called by a * {@link CastorEntityTransaction} when {@link EntityTransaction#commit()} * or {@link EntityTransaction#rollback()} is invoked. */ protected void invalidatePersistenceContext() { this.context = new TransactionScopedPersistenceContext(); } /** * Verifies that {@link EntityManager#isOpen()} returns true. * * @throws IllegalStateException * in case {@link EntityManager#close()} was called before. */ private void verifyOpenEntityManager() { if (!isOpen()) { throw new IllegalStateException("Method called on inactive EntityManager instance."); } } /** * Verifies that an {@link EntityTransaction} is active. */ private void verifyRunningTransaction() { if (!this.entityTransaction.isActive()) { throw new TransactionRequiredException("Method called while no EntityTransaction was running."); } } /** * Verifies whether the given entity is managed by this * {@link EntityManager}. * * @param entity * an entity. */ private void verifyEntityIsManaged(Object entity) { if (!this.context.contains(entity)) { // Entity is new or detached. throw new IllegalArgumentException("Entity of type >" + entity.getClass().getName() + "< is not managed by this EntityManager instance."); } } /** * Verifies whether a given entity is persistence capable within this * {@link EntityManager}. * * @param type an entities type. */ private void verifyPersistenceCapable(Class<?> type) { try { // Check whether type is known. this.database.getScope().getClassMolder(type); // No exception caught. Class is persistence capable. } catch (ClassNotPersistenceCapableException e) { log.error("Entity of type >" + type.getName() + "< not persistence capable!", e); throw new IllegalArgumentException("Entity of type >" + type.getName() + "< not persistence capable!", e); } } }