/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package com.caucho.amber.manager; import java.lang.reflect.Method; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import javax.persistence.EntityExistsException; import javax.persistence.EntityManager; import javax.persistence.EntityManagerFactory; 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.RollbackException; import javax.persistence.TransactionRequiredException; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.metamodel.Metamodel; import javax.sql.DataSource; import javax.transaction.Status; import javax.transaction.Synchronization; import javax.transaction.Transaction; import com.caucho.amber.AmberException; import com.caucho.amber.AmberObjectNotFoundException; import com.caucho.amber.AmberQuery; import com.caucho.amber.AmberRuntimeException; import com.caucho.amber.cfg.EntityResultConfig; import com.caucho.amber.cfg.NamedNativeQueryConfig; import com.caucho.amber.cfg.SqlResultSetMappingConfig; import com.caucho.amber.collection.AmberCollection; import com.caucho.amber.entity.AmberCompletion; import com.caucho.amber.entity.AmberEntityHome; import com.caucho.amber.entity.CacheableEntityItem; import com.caucho.amber.entity.Entity; import com.caucho.amber.entity.EntityFactory; import com.caucho.amber.entity.EntityItem; import com.caucho.amber.entity.EntityKey; import com.caucho.amber.entity.EntityState; import com.caucho.amber.entity.RowInsertCompletion; import com.caucho.amber.entity.RowInvalidateCompletion; import com.caucho.amber.query.AbstractQuery; import com.caucho.amber.query.QueryCacheKey; import com.caucho.amber.query.QueryParser; import com.caucho.amber.query.ResultSetCacheChunk; import com.caucho.amber.query.UserQuery; import com.caucho.amber.table.AmberTable; import com.caucho.amber.type.EntityType; import com.caucho.config.ConfigException; import com.caucho.ejb.EJBExceptionWrapper; import com.caucho.jdbc.JdbcMetaData; import com.caucho.util.L10N; import com.caucho.util.LruCache; /** * The entity manager from a entity manager proxy. */ public class AmberConnection implements Synchronization, EntityManager { private static final L10N L = new L10N(AmberConnection.class); private static final Logger log = Logger.getLogger(AmberConnection.class.getName()); private static final Entity []NULL_ENTITIES = new Entity[0]; private AmberPersistenceUnit _persistenceUnit; private boolean _isRegistered; private boolean _isThreadConnection; private Entity []_entities = new Entity[32]; private int _entitiesTop; private Entity []_txEntities = NULL_ENTITIES; private int _txEntitiesTop; private ArrayList<AmberCompletion> _completionList = new ArrayList<AmberCompletion>(); private ArrayList<AmberCollection> _queries = new ArrayList<AmberCollection>(); private EntityTransaction _trans; private long _xid; private boolean _isInTransaction; private boolean _isXA; private boolean _isExtended; private boolean _isAppManaged; private Connection _conn; private Connection _readConn; private boolean _isAutoCommit = true; private int _depth; private LruCache<String,PreparedStatement> _preparedStatementMap = new LruCache<String,PreparedStatement>(32); private ArrayList<Statement> _statements = new ArrayList<Statement>(); private EntityKey _entityKey = new EntityKey(); private QueryCacheKey _queryKey = new QueryCacheKey(); private ArrayList<Entity> _mergingEntities = new ArrayList<Entity>(); private boolean _isFlushAllowed = true; /** * Creates a manager instance. */ AmberConnection(AmberPersistenceUnit persistenceUnit, boolean isExtended, boolean isAppManaged) { _persistenceUnit = persistenceUnit; _isExtended = isExtended; _isAppManaged = isAppManaged; } /** * Creates a manager instance. */ AmberConnection(AmberPersistenceUnit persistenceUnit, boolean isExtended) { this(persistenceUnit, isExtended, false); } /** * Returns the persistence unit. */ public AmberPersistenceUnit getPersistenceUnit() { return _persistenceUnit; } /** * Returns true for JPA. */ public boolean isJPA() { return _persistenceUnit.isJPA(); } /** * Set true for a threaded connection. */ public void initThreadConnection() { _isThreadConnection = true; initJta(); } public void initJta() { // non-jta connections do not register with the local transaction if (_persistenceUnit.isJta()) register(); } /** * Makes the instance managed. */ public void persist(Object entityObject) { RuntimeException exn = null; try { if (entityObject == null) return; Entity entity = checkEntityType(entityObject, "persist"); checkTransactionRequired("persist"); persistInternal(entity); // XXX: check spec. for JTA vs. non-JTA behavior and add QA. // ejb30/persistence/ee/packaging/ejb/resource_local/test14 if (! _persistenceUnit.isJta()) flushInternal(); } catch (RuntimeException e) { exn = e; } catch (SQLException e) { exn = new IllegalStateException(e); } catch (Exception e) { exn = new EJBExceptionWrapper(e); } if (exn != null) { if (! _persistenceUnit.isJta()) { if (_trans != null) _trans.setRollbackOnly(); } throw exn; } } /** * Makes the instance managed called * from cascading operations. */ public Object persistFromCascade(Object o) { // jpa/0h25, jpa/0i5e try { if (o == null) return null; Entity entity = (Entity) o; // jpa/0h25 return persistInternal(entity); } catch (EntityExistsException e) { log.log(Level.FINER, e.toString(), e); // This is not an issue. It is the cascading // operation trying to persist the source // entity from the destination end. return o; } catch (RuntimeException e) { throw e; } catch (SQLException e) { throw new IllegalStateException(e); } catch (Exception e) { throw new EJBExceptionWrapper(e); } } /** * Merges the state of the entity into the current context. */ public <T> T merge(T entityT) { RuntimeException exn = null; try { flushInternal(); // Cannot flush before the merge is complete. _isFlushAllowed = false; entityT = recursiveMerge(entityT); } catch (RuntimeException e) { exn = e; } catch (Exception e) { exn = new EJBExceptionWrapper(e); } finally { _isFlushAllowed = true; try { flushInternal(); } catch (RuntimeException e) { if (exn == null) exn = e; } catch (Exception e) { if (exn == null) exn = new EJBExceptionWrapper(e); } finally { _mergingEntities.clear(); } } // jpa/0o42, jpa/0o44 if (exn != null) throw exn; return entityT; } /** * Remove the instance. */ public void remove(Object entity) { try { if (entity == null) return; Entity instance = checkEntityType(entity, "remove"); checkTransactionRequired("remove"); if (log.isLoggable(Level.FINER)) log.log(Level.FINER, L.l("removing entity class " + instance.getClass().getName() + " PK: " + instance.__caucho_getPrimaryKey() + " state: " + instance.__caucho_getEntityState())); EntityState state = instance.__caucho_getEntityState(); if (EntityState.P_DELETING.ordinal() <= state.ordinal()) { if (log.isLoggable(Level.FINER)) log.log(Level.FINER, L.l("remove is ignoring entity in state " + state)); return; } // jpa/0k12 if (instance.__caucho_getConnection() == null) { if (instance.__caucho_getEntityType() == null) { if (log.isLoggable(Level.FINER)) log.log(Level.FINER, L.l("remove is ignoring entity; performing only cascade post-remove")); // Ignore this entity; only post-remove child entities. instance.__caucho_cascadePostRemove(this); // jpa/0ga7 return; } else throw new IllegalArgumentException(L.l("remove() operation can only be applied to a managed entity. This entity instance '{0}' PK: '{1}' is detached which means it was probably removed or needs to be merged.", instance.getClass().getName(), instance.__caucho_getPrimaryKey())); } // jpa/0h25, jpa/0i5e // Do not flush dependent objects for cascading persistence // when this entity is being removed. instance.__caucho_setEntityState(EntityState.P_DELETING); if (log.isLoggable(Level.FINER)) log.log(Level.FINER, L.l("remove is flushing any lazy cascading operation")); // jpa/1620 // In particular, required for cascading persistence, since the cascade // is lazy until flush // jpa/0h25 flushInternal(); // Cannot flush since the delete is lazy until flush, i.e.: // remove(A); // (*) __caucho_flush() // remove(B); // (*) would break a FK constraint if B has a reference to A. // jpa/0h26 updateFlushPriority(instance); // jpa/0h25, jpa/0i5e // Restores original state. instance.__caucho_setEntityState(state); Object oldEntity; oldEntity = getEntity(instance.getClass(), instance.__caucho_getPrimaryKey()); // jpa/0ga4 if (oldEntity == null) throw new IllegalArgumentException(L.l("remove() operation can only be applied to a managed entity instance.")); if (log.isLoggable(Level.FINER)) log.log(Level.FINER, L.l("remove is performing cascade pre-remove")); // Pre-remove child entities. instance.__caucho_cascadePreRemove(this); if (log.isLoggable(Level.FINER)) log.log(Level.FINER, L.l("remove is performing delete on the target entity")); delete(instance); // jpa/0o30: flushes the owning side delete. // XXX: Cannot flush since the delete is lazy until flush. // jpa/0h25 // instance.__caucho_flush(); if (log.isLoggable(Level.FINER)) log.log(Level.FINER, L.l("remove is performing cascade post-remove")); // jpa/0o30 // Post-remove child entities. instance.__caucho_cascadePostRemove(this); if (log.isLoggable(Level.FINER)) log.log(Level.FINER, L.l("DONE successful remove for entity class " + instance.getClass().getName() + " PK: " + instance.__caucho_getPrimaryKey())); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new EJBExceptionWrapper(e); } } /** * Find by the primary key. */ public <T> T find(Class<T> entityClass, Object primaryKey) { // Do not flush while an entity is being loaded or merged. boolean oldIsFlushAllowed = _isFlushAllowed; try { // Do not flush while loading an entity. // Broken relationships would not pass the flush validation. _isFlushAllowed = false; T entity = (T) load(entityClass, primaryKey, true); // jpa/0j07 /* if (! isActiveTransaction()) { // jpa/0o00 detach(); } */ return entity; } catch (AmberObjectNotFoundException e) { // JPA: should not throw at all, returns null only. // log.log(Level.FINER, e.toString(), e); return null; } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new EJBExceptionWrapper(e); } finally { _isFlushAllowed = oldIsFlushAllowed; } } /** * Find by the primary key. */ public <T> T getReference(Class<T> entityClass, Object primaryKey) throws EntityNotFoundException, IllegalArgumentException { T reference = null; try { // XXX: only needs to get a reference. reference = (T) load(entityClass, primaryKey, false); if (reference == null) throw new EntityNotFoundException(L.l("entity with primary key {0} not found in getReference()", primaryKey)); /* if (! (entityClass.isAssignableFrom(Entity.class))) throw new IllegalArgumentException(L.l("getReference() operation can only be applied to an entity class")); */ return reference; } catch (EntityNotFoundException e) { throw e; } catch (RuntimeException e) { throw new IllegalArgumentException(e); } catch (Exception e) { throw new EJBExceptionWrapper(e); } } /** * Clears the connection */ public void clear() { _entitiesTop = 0; _txEntitiesTop = 0; } /** * Creates a query. */ public Query createQuery(String sql) { try { AbstractQuery queryProgram = parseQuery(sql, false); return new QueryImpl(queryProgram, this); } catch (RuntimeException e) { throw new IllegalArgumentException(e); } catch (Exception e) { throw new EJBExceptionWrapper(e); } } /** * Creates an instance of the named query */ public Query createNamedQuery(String name) { String sql = _persistenceUnit.getNamedQuery(name); if (sql != null) return createQuery(sql); NamedNativeQueryConfig nativeQuery = _persistenceUnit.getNamedNativeQuery(name); sql = nativeQuery.getQuery(); String resultSetMapping = nativeQuery.getResultSetMapping(); if (! ((resultSetMapping == null) || "".equals(resultSetMapping))) return createNativeQuery(sql, resultSetMapping); Class resultClass = nativeQuery.getResultClass(); AmberEntityHome entityHome = _persistenceUnit.getEntityHome(resultClass.getName()); EntityType entityType = entityHome.getEntityType(); try { return createNativeQuery(sql, entityType.getInstanceClass()); } catch (Exception e) { throw new IllegalArgumentException(e); } } /** * Creates an instance of the named query */ public Query createNativeQuery(String sql) { sql = sql.trim(); char ch = sql.charAt(0); if (ch == 'S' || ch == 's') throw new UnsupportedOperationException(L.l("createNativeQuery(String sql) is not supported for select statements. Please use createNativeQuery(String sql, String map) or createNativeQuery(String sql, Class cl) to map the result to scalar values or bean classes.")); return createInternalNativeQuery(sql); } /** * Creates an instance of the named query */ public Query createNativeQuery(String sql, String map) { // jpa/0y1- SqlResultSetMappingConfig resultSet; resultSet = _persistenceUnit.getSqlResultSetMapping(map); if (resultSet == null) throw new IllegalArgumentException(L.l("createNativeQuery() cannot create a native query for a result set named '{0}'", map)); return createInternalNativeQuery(sql, resultSet); } /** * Creates an instance of the native query */ public Query createNativeQuery(String sql, Class type) { SqlResultSetMappingConfig resultSet = new SqlResultSetMappingConfig(); EntityResultConfig entityResult = new EntityResultConfig(); entityResult.setEntityClass(type.getName()); resultSet.addEntityResult(entityResult); return createInternalNativeQuery(sql, resultSet); } /** * Refresh the state of the instance from the database. */ public void refresh(Object entity) { try { if (entity == null) return; if (! (entity instanceof Entity)) throw new IllegalArgumentException(L.l("refresh() operation can only be applied to an entity instance. This object is of class '{0}'", entity.getClass().getName())); checkTransactionRequired("refresh"); Entity instance = (Entity) entity; String className = instance.getClass().getName(); Object pk = instance.__caucho_getPrimaryKey(); Entity oldEntity = getEntity(instance.getClass(), pk); if (oldEntity != null) { EntityState state = instance.__caucho_getEntityState(); if (state.ordinal() <= EntityState.TRANSIENT.ordinal() || EntityState.P_DELETING.ordinal() <= state.ordinal()) { throw new IllegalArgumentException(L.l("refresh() operation can only be applied to a managed entity instance. The entity state is '{0}' for object of class '{0}' with PK '{1}'", className, pk, state == EntityState.TRANSIENT ? "TRANSIENT" : "DELETING or DELETED")); } } else throw new IllegalArgumentException(L.l("refresh() operation can only be applied to a managed entity instance. There was no managed instance of class '{0}' with PK '{1}'", className, pk)); // Reset and refresh state. instance.__caucho_expire(); instance.__caucho_makePersistent(this, (EntityType) null); instance.__caucho_retrieve_eager(this); } catch (SQLException e) { throw new AmberRuntimeException(e); } } /** * Returns the flush mode. */ public FlushModeType getFlushMode() { return FlushModeType.AUTO; } /** * Sets the extended type. */ public void setExtended(boolean isExtended) { _isExtended = isExtended; } /** * Returns the flush mode. */ public void setFlushMode(FlushModeType mode) { throw new UnsupportedOperationException(); } /** * Locks the object. */ public void lock(Object entity, LockModeType lockMode) { throw new UnsupportedOperationException(); } /** * Returns the transaction. */ public EntityTransaction getTransaction() { if (_isXA) throw new IllegalStateException(L.l("Cannot call EntityManager.getTransaction() inside a distributed transaction.")); if (_trans == null) _trans = new EntityTransactionImpl(); return _trans; } /** * Returns true if open. */ public boolean isOpen() { return _persistenceUnit != null; } /** * Registers with the local transaction. */ void register() { if (! _isRegistered) { /* if (! _isAppManaged) UserTransactionProxy.getInstance().enlistCloseResource(this); UserTransactionProxy.getInstance().enlistBeginResource(this); */ } _isRegistered = true; } /** * Joins the transaction. */ public void joinTransaction() { // XXX: jpa/0s46, jpa/0s47 _isInTransaction = true; } /** * Gets the delegate. */ public Object getDelegate() { throw new UnsupportedOperationException(); } /** * Closes the context. */ public void close() { if (_persistenceUnit == null) { // jpa/0s45 throw new IllegalStateException("Entity manager is already closed."); } try { if (_isThreadConnection) _persistenceUnit.removeThreadConnection(); _isRegistered = false; cleanup(); } catch (Exception e) { log.log(Level.FINER, e.toString(), e); } finally { _persistenceUnit = null; } } /** * Returns the amber manager. */ public AmberPersistenceUnit getAmberManager() { return _persistenceUnit; } /** * Registers a collection. */ public void register(AmberCollection query) { _queries.add(query); } /** * Adds a completion */ public void addCompletion(AmberCompletion completion) { if (! _completionList.contains(completion)) _completionList.add(completion); } /** * Returns true if a transaction is active or * this persistence context is extended. */ public boolean isActiveTransaction() { return _isInTransaction || _isExtended; } /** * Returns true if a transaction is active. */ public boolean isInTransaction() { return _isInTransaction; } /** * Returns the cache chunk size. */ public int getCacheChunkSize() { return 32; } public Object load(Class cl, Object key, boolean isEager) throws AmberException { if (_persistenceUnit == null) throw new IllegalStateException(L.l("AmberConnection is closed")); if (log.isLoggable(Level.FINER)) log.finer(L.l("{0}[1] amber loading entity class", cl.getSimpleName(), key)); Entity entity = null; if (key == null) return null; // ejb/0d01, jpa/0gh0, jpa/0g0k, jpa/0j5f // if (shouldRetrieveFromCache()) entity = getEntity(cl, key); if (entity != null) { // jpa/0s2d: if it contains such entity and we have // PersistenceContextType.TRANSACTION, the entity is // managed and we can just return it (otherwise it would // be detached and not be found in _entities). // XXX: for PersistenceContextType.EXTENDED??? return entity; } _entityKey.init(cl, key); EntityItem cacheItem = loadCacheItem(cl, key, null); if (cacheItem == null) return null; /* boolean isLoad = true; // jpa/0h13 as a negative test. if (isActiveTransaction()) isLoad = isEager; */ // jpa/0o03 boolean isLoad = isEager; try { entity = cacheItem.createEntity(this, key); if (entity == null) return null; // The entity is added for eager loading addInternalEntity(entity); boolean isXA = isActiveTransaction(); // jpa/0l48: inheritance loading optimization. // jpa/0h20: no transaction, copy from the existing cache item. // jpa/0l42: loading optimization. if (isLoad) { entity.__caucho_retrieve_eager(this); } else if (isXA) { // jpa/0v33: within a transaction, cannot copy from cache. entity.__caucho_retrieve_self(this); } } catch (SQLException e) { if (_persistenceUnit.isJPA()) { log.log(Level.FINER, e.toString(), e); return null; } throw new AmberObjectNotFoundException(L.l("{0}[{1}] is an unknown amber object", cl.getName(), key), e); } catch (AmberObjectNotFoundException e) { // 0g0q: if the entity is not found, removes it from context. if (entity != null) removeEntity(entity); if (_persistenceUnit.isJPA()) return null; throw e; } Entity txEntity = getTransactionEntity(entity.getClass(), entity.__caucho_getPrimaryKey()); // XXX: jpa/0v33 if (txEntity != null) setTransactionalState(txEntity); return entity; } public EntityItem loadCacheItem(Class cl, Object key, AmberEntityHome entityHome) throws AmberException { _entityKey.init(cl, key); EntityItem cacheItem = _persistenceUnit.getEntity(_entityKey); if (cacheItem != null) return cacheItem; if (entityHome == null) entityHome = _persistenceUnit.getEntityHome(cl.getName()); if (entityHome == null) { throw new IllegalArgumentException(L.l("'{0}' is an unknown class in persistence-unit '{1}'. find() operation can only be applied if the entity class is specified in the scope of a persistence unit.", cl.getName(), _persistenceUnit.getName())); } cacheItem = entityHome.findEntityItem(this, key); if (cacheItem == null) { if (_persistenceUnit.isJPA()) return null; // ejb/0604 throw new AmberObjectNotFoundException("amber find: no matching object " + cl.getName() + "[" + key + "]"); } if (cacheItem instanceof CacheableEntityItem) cacheItem = _persistenceUnit.putEntity(cl, key, cacheItem); return cacheItem; } /** * Loads the object based on the class and primary key. */ public Object load(String entityName, Object key) throws AmberException { AmberEntityHome entityHome = _persistenceUnit.getEntityHome(entityName); if (entityHome == null) return null; Entity entity = null; // XXX: ejb/0d01 // jpa/0y14 if (shouldRetrieveFromCache()) entity = getEntity(entityHome.getJavaClass(), key); if (entity != null) return entity; try { entityHome.init(); } catch (ConfigException e) { throw new AmberException(e); } entity = (Entity) find(entityHome.getEntityType().getInstanceClass(), key); // addEntity(entity); return entity; } /** * Returns the entity for the connection. */ public Entity getEntity(EntityItem item) { Entity itemEntity = item.getEntity(); Class cl = itemEntity.getClass(); Object pk = itemEntity.__caucho_getPrimaryKey(); Entity entity = getEntity(cl, pk); if (entity != null) { if (entity.__caucho_getEntityState().isManaged()) return entity; // else // jpa/0g40: the copy object was created at some point in // findEntityItem, but it is still not loaded. } else { try { entity = item.createEntity(this, pk); } catch (SQLException e) { throw new AmberRuntimeException(e); } /* // Create a new entity for the given class and primary key. try { entity = (Entity) cl.newInstance(); } catch (Exception e) { throw new AmberRuntimeException(e); } */ // entity.__caucho_setEntityState(EntityState.P_NON_TRANSACTIONAL); // entity.__caucho_setPrimaryKey(pk); // jpa/1000: avoids extra allocations. addInternalEntity(entity); } // jpa/0l43 //_persistenceUnit.copyFromCacheItem(this, entity, item); // jpa/0l4a entity.__caucho_retrieve_eager(this); return entity; } /** * Returns the entity for the connection. */ public Entity getEntityLazy(EntityItem item) { Entity itemEntity = item.getEntity(); Class cl = itemEntity.getClass(); Object pk = itemEntity.__caucho_getPrimaryKey(); Entity entity = getEntity(cl, pk); if (entity != null) { if (entity.__caucho_getEntityState().isManaged()) return entity; // else // jpa/0g40: the copy object was created at some point in // findEntityItem, but it is still not loaded. } else { try { entity = item.createEntity(this, pk); } catch (SQLException e) { throw new AmberRuntimeException(e); } /* // Create a new entity for the given class and primary key. try { entity = (Entity) cl.newInstance(); } catch (Exception e) { throw new AmberRuntimeException(e); } */ // entity.__caucho_setEntityState(EntityState.P_NON_TRANSACTIONAL); // entity.__caucho_setPrimaryKey(pk); // jpa/1000: avoids extra allocations. addInternalEntity(entity); } return entity; } /** * Loads the object based on itself. */ public Object makePersistent(Object obj) throws SQLException { Entity entity = (Entity) obj; // check to see if exists if (entity == null) throw new NullPointerException(); Class cl = entity.getClass(); // Entity oldEntity = getEntity(cl, entity.__caucho_getPrimaryKey()); AmberEntityHome entityHome; entityHome = _persistenceUnit.getEntityHome(entity.getClass().getName()); if (entityHome == null) throw new AmberException(L.l("{0}: entity has no matching home", entity.getClass().getName())); entityHome.makePersistent(entity, this, false); return entity; } /** * Loads the object with the given class. */ public Entity loadLazy(Class cl, String name, Object key) { return loadLazy(cl.getName(), name, key); } /** * Loads the object with the given class. */ public Entity loadLazy(String className, String name, Object key) { if (key == null) return null; try { AmberEntityHome home = _persistenceUnit.getEntityHome(name); if (home == null) throw new RuntimeException(L.l("no matching home for {0}", className)); home.init(); Object obj = load(home.getEntityType().getInstanceClass(), key, false); Entity entity = (Entity) obj; return entity; } catch (SQLException e) { log.log(Level.WARNING, e.toString(), e); return null; } catch (ConfigException e) { throw new AmberRuntimeException(e); } } /** * Loads the object with the given class. */ public EntityItem findEntityItem(String name, Object key) { try { AmberEntityHome home = _persistenceUnit.getEntityHome(name); if (home == null) throw new RuntimeException(L.l("no matching home for {0}", name)); home.init(); return loadCacheItem(home.getJavaClass(), key, home); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new AmberRuntimeException(e); } } /** * Loads the object with the given class. */ public EntityItem setEntityItem(String name, Object key, EntityItem item) { try { AmberEntityHome home = _persistenceUnit.getEntityHome(name); if (home == null) throw new RuntimeException(L.l("no matching home for {0}", name)); home.init(); return home.setEntityItem(key, item); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new AmberRuntimeException(e); } } /** * Loads the object with the given class. * * @param name the class name. * @param key the key. */ public Entity loadFromHome(String name, Object key) { try { AmberEntityHome home = _persistenceUnit.getEntityHome(name); if (home == null) throw new RuntimeException(L.l("no matching home for {0}", name)); home.init(); // jpa/0ge4, jpa/0o04, jpa/0o0b, jpa/0o0c: bidirectional optimization. return (Entity) load(home.getEntityType().getInstanceClass(), key, true); } catch (AmberObjectNotFoundException e) { if (_persistenceUnit.isJPA()) { if (log.isLoggable(Level.FINER)) log.log(Level.FINER, e.toString(), e); // jpa/0h29 return null; } throw e; } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new AmberRuntimeException(e); } } /** * Loads the object with the given class. */ public Object loadProxy(String name, Object key) { if (key == null) return null; AmberEntityHome home = _persistenceUnit.getEntityHome(name); if (home == null) throw new RuntimeException(L.l("no matching home for {0}", name)); return loadProxy(home.getEntityType(), key); } /** * Loads the object with the given class. */ public Object loadProxy(EntityType type, Object key) { if (key == null) return null; Entity entity = getEntity(type.getInstanceClass(), key); if (entity != null) { // jpa/0m30 return entity; } try { AmberEntityHome home = type.getHome(); EntityItem item = home.findEntityItem(this, key); if (item == null) return null; EntityFactory factory = home.getEntityFactory(); Object newEntity = factory.getEntity(this, item); if (_persistenceUnit.isJPA()) { // jpa/0h29: eager loading. Entity instance = (Entity) newEntity; setTransactionalState(instance); } return newEntity; } catch (SQLException e) { log.log(Level.WARNING, e.toString(), e); return null; } } /** * Loads the CMP 2.1 object for the given entityItem */ public Object loadProxy(EntityItem entityItem) { Entity itemEntity = entityItem.getEntity(); /* Class cl = itemEntity.getClass(); Object pk = itemEntity.__caucho_getPrimaryKey(); Entity entity = getEntity(cl.getName(), pk); if (entity != null) { // jpa/0m30 return entity; } */ AmberEntityHome home = entityItem.getEntityHome(); EntityFactory factory = home.getEntityFactory(); Object newEntity = factory.getEntity(this, entityItem); return newEntity; } /** * Loads the object based on the class and primary key. */ public Object load(Class cl, long intKey) throws AmberException { AmberEntityHome entityHome = _persistenceUnit.getEntityHome(cl.getName()); if (entityHome == null) return null; Object key = entityHome.toObjectKey(intKey); return load(cl, key, true); } /** * Loads the object based on the class and primary key. */ public Object loadLazy(Class cl, long intKey) throws AmberException { AmberEntityHome entityHome = _persistenceUnit.getEntityHome(cl.getName()); if (entityHome == null) return null; Object key = entityHome.toObjectKey(intKey); return loadLazy(cl, cl.getName(), key); } /** * Matches the entity. */ public Entity getEntity(Class cl, Object key) { Entity []entities = _entities; for (int i = _entitiesTop - 1; i >= 0; i--) { Entity entity = entities[i]; if (entity.__caucho_match(cl, key)) { return entity; } } return null; } public Entity getEntity(int index) { return _entities[index]; } /** * Returns the context entity that corresponds to the * entity passed in. The entity passed in is normally a * cache entity but might be the context entity itself * when we want to make sure the reference is to an * entity in the persistence context. */ public Entity getEntity(Entity entity) { if (entity == null) return null; return getEntity(entity.getClass(), entity.__caucho_getPrimaryKey()); } public Entity getSubEntity(Class cl, Object key) { Entity []entities = _entities; // jpa/0l43 for (int i = _entitiesTop - 1; i >= 0; i--) { Entity entity = entities[i]; if (entity.__caucho_getPrimaryKey().equals(key)) { if (cl.isAssignableFrom(entity.getClass())) return entity; } } return null; } /** * Gets the cache item referenced by __caucho_item * from an entity of class/subclass cl. */ public EntityItem getSubEntityCacheItem(Class cl, Object key) { Entity []entities = _entities; // jpa/0l4a for (int i = _entitiesTop - 1; i >= 0; i--) { Entity entity = entities[i]; if (entity.__caucho_getPrimaryKey().equals(key)) { if (cl.isAssignableFrom(entity.getClass())) return entity.__caucho_getCacheItem(); } } return null; } public Entity getTransactionEntity(Class cl, Object key) { Entity []entities = _txEntities; for (int i = _txEntitiesTop - 1; i >= 0; i--) { Entity entity = entities[i]; if (entity.__caucho_match(cl, key)) { return entity; } } return null; } public Entity getTransactionEntity(int index) { return _txEntities[index]; } /** * Adds a new entity for the given class name and key. * The new entity object is supposed to be used as a * copy from cache. This avoids the cache entity to * be added to the context. * * @return null - if the entity is already in the context. * otherwise, it returns the new entity added to * the context. */ public Entity addNewEntity(Class cl, Object key) throws InstantiationException, IllegalAccessException { // jpa/0l43 Entity entity = getSubEntity(cl, key); // If the entity is already in the context, it returns null. if (entity != null) return null; if (_persistenceUnit.isJPA()) { // XXX: needs to create based on the discriminator with inheritance. // Create a new entity for the given class and primary key. entity = (Entity) cl.newInstance(); // jpa/0s2d entity.__caucho_setEntityState(EntityState.P_NON_TRANSACTIONAL); } else { // HelperBean__Amber -> HelperBean String className = cl.getSuperclass().getName(); AmberEntityHome entityHome = _persistenceUnit.getEntityHome(className); if (entityHome == null) { if (log.isLoggable(Level.FINER)) log.log(Level.FINER, L.l("Amber.addNewEntity: home not found for entity (class: '{0}' PK: '{1}')", className, key)); return null; } EntityFactory factory = entityHome.getEntityFactory(); // TestBean__EJB Object value = factory.getEntity(key); Method cauchoGetBeanMethod = entityHome.getCauchoGetBeanMethod(); if (cauchoGetBeanMethod != null) { try { // Bean entity = (Entity) cauchoGetBeanMethod.invoke(value, new Object[0]); // entity.__caucho_makePersistent(aConn, item); } catch (Exception e) { log.log(Level.FINER, e.toString(), e); } } if (entity == null) { throw new IllegalStateException(L.l("AmberConnection.addNewEntity unable to instantiate new entity with cauchoGetBeanMethod")); } } entity.__caucho_setPrimaryKey(key); addInternalEntity(entity); return entity; } /** * Adds a new entity for the given class name and key. */ public Entity loadEntity(Class cl, Object key, boolean isEager) { if (key == null) return null; // jpa/0l43 Entity entity = getSubEntity(cl, key); // If the entity is already in the context, return it if (entity != null) return entity; if (_persistenceUnit.isJPA()) { // XXX: needs to create based on the discriminator with inheritance. // Create a new entity for the given class and primary key. try { entity = (Entity) load(cl, key, isEager); } catch (AmberException e) { throw new AmberRuntimeException(e); } } else { // HelperBean__Amber -> HelperBean String className = cl.getSuperclass().getName(); AmberEntityHome entityHome = _persistenceUnit.getEntityHome(className); if (entityHome == null) { if (log.isLoggable(Level.FINER)) log.log(Level.FINER, L.l("Amber.addNewEntity: home not found for entity (class: '{0}' PK: '{1}')", className, key)); return null; } EntityFactory factory = entityHome.getEntityFactory(); // TestBean__EJB Object value = factory.getEntity(key); Method cauchoGetBeanMethod = entityHome.getCauchoGetBeanMethod(); if (cauchoGetBeanMethod != null) { try { // Bean entity = (Entity) cauchoGetBeanMethod.invoke(value, new Object[0]); // entity.__caucho_makePersistent(aConn, item); } catch (Exception e) { log.log(Level.FINER, e.toString(), e); } } if (entity == null) { throw new IllegalStateException(L.l("AmberConnection.addNewEntity unable to instantiate new entity with cauchoGetBeanMethod")); } entity.__caucho_setPrimaryKey(key); addInternalEntity(entity); } return entity; } /** * Removes an entity. */ public boolean removeEntity(Entity entity) { removeEntityImpl(entity); if (isActiveTransaction()) removeTxEntity(entity); return true; } /** * Loads the object based on itself. */ public boolean contains(Object obj) { if (obj == null) return false; if (! (obj instanceof Entity)) throw new IllegalArgumentException(L.l("contains() operation can only be applied to an entity instance.")); Entity entity = (Entity) obj; // jpa/11a8 if (entity.__caucho_getConnection() != this) { return false; } EntityState state = entity.__caucho_getEntityState(); if (isInTransaction() && ! state.isTransactional()) { // jpa/11a6, jpa/1800 return false; } // jpa/0j5f if (EntityState.P_DELETING.ordinal() <= state.ordinal()) { return false; } return true; } /** * Callback when the user transaction begins */ public void begin(Transaction xa) { try { xa.registerSynchronization(this); _isInTransaction = true; _isXA = true; } catch (Exception e) { log.log(Level.WARNING, e.toString(), e); } } /** * Starts a transaction. */ public void beginTransaction() throws SQLException { _isInTransaction = true; if (_conn != null && _isAutoCommit) { _isAutoCommit = false; _conn.setAutoCommit(false); } // _xid = _factory.getXid(); } /** * Sets XA. */ public void setXA(boolean isXA) { _isXA = isXA; _isInTransaction = isXA; if (isXA && ! _isRegistered) register(); } /** * Commits a transaction. */ public void commit() throws SQLException { if (log.isLoggable(Level.FINER)) log.log(Level.FINER, "AmberConnection.commit"); try { flushInternal(); _xid = 0; if (_conn != null) { _conn.commit(); } } catch (RuntimeException e) { throw e; } catch (SQLException e) { throw new IllegalStateException(e); } catch (Exception e) { throw new EJBExceptionWrapper(e); } finally { if (! _isXA) _isInTransaction = false; for (int i = 0; i < _txEntitiesTop; i++) { Entity entity = _txEntities[i]; entity.__caucho_afterCommit(); } if (log.isLoggable(Level.FINER)) log.log(Level.FINER, "cleaning up txEntities"); _txEntitiesTop = 0; } } /** * Callback before a utrans commit. */ public void beforeCompletion() { if (log.isLoggable(Level.FINER)) log.log(Level.FINER, this + " beforeCompletion"); try { beforeCommit(); // XXX: need to figure out how to throw JPA exceptions at commit() time. // } catch (SQLException e) { // throw e; } catch (RuntimeException e) { // jpa/0ga5 throw e; } catch (Exception e) { log.log(Level.WARNING, e.toString(), e); } } /** * Callback after a utrans commit. */ public void afterCompletion(int status) { if (log.isLoggable(Level.FINER)) { if (status == Status.STATUS_COMMITTED) log.finer(this + " afterCompletion(commit)"); else log.finer(this + " afterCompletion(rollback)"); } afterCommit(status == Status.STATUS_COMMITTED); _isXA = false; _isInTransaction = false; _isRegistered = false; // ejb/0d19 } /** * Called before the commit phase */ public void beforeCommit() throws SQLException { if (log.isLoggable(Level.FINER)) log.finer(this + " beforeCommit"); try { flushInternal(); } catch (SQLException e) { throw e; } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new RuntimeException(e); } /* // jpa/0gh0 for (int i = _txEntities.size() - 1; i >= 0; i--) { Entity entity = _txEntities.get(i); // jpa/1500 if (entity.__caucho_getEntityState() == EntityState.P_DELETED) { EntityType entityType = entity.__caucho_getEntityType(); Object key = entity.__caucho_getPrimaryKey(); EntityItem item = _persistenceUnit.getEntity(entityType, key); if (item == null) { // jpa/0ga8: entity has been removed and DELETE SQL was already flushed. continue; } } entity.__caucho_flush(); } */ } /** * Commits a transaction. */ public void afterCommit(boolean isCommit) { try { if (log.isLoggable(Level.FINER)) log.log(Level.FINER, "AmberConnection.afterCommit: " + isCommit); if (! _isXA) _isInTransaction = false; if (isCommit) { if (_completionList.size() > 0) { _persistenceUnit.complete(_completionList); } } // jpa/0k20: clears the completion list in the // finally block so callbacks do not add a completion // which has been just removed. // // _completionList.clear(); for (int i = 0; i < _txEntitiesTop; i++) { Entity entity = _txEntities[i]; try { if (isCommit) entity.__caucho_afterCommit(); else entity.__caucho_afterRollback(); } catch (Exception e) { log.log(Level.WARNING, e.toString(), e); } } if (log.isLoggable(Level.FINER)) log.log(Level.FINER, "cleaning up txEntities"); _txEntitiesTop = 0; // jpa/0s2k Entity []entities = _entities; for (int i = _entitiesTop - 1; i >= 0; i--) { // XXX: needs to check EXTENDED type. // jpa/0h07: persistence context TRANSACTION type. entities[i].__caucho_detach(); } // jpa/0h60 _entitiesTop = 0; // if (! isCommit) { // jpa/0j5c /* XXX: jpa/0k11 - avoids double rollback() Rollback is done from com.caucho.transaction.TransactionImpl to the pool item com.caucho.jca.PoolItem try { if (_conn != null) _conn.rollback(); } catch (SQLException e) { throw new IllegalStateException(e); } */ // } } finally { _completionList.clear(); } } public PersistenceException rollback(Exception e) { try { rollback(); } catch (Exception e1) { log.log(Level.FINE, e1.toString(), e1); } return new PersistenceException(e); } /** * Rollbacks a transaction. */ public void rollback() throws SQLException { if (log.isLoggable(Level.FINER)) log.log(Level.FINER, "AmberConnection.rollback"); try { flushInternal(); _xid = 0; if (_conn != null) { _conn.rollback(); } } catch (RuntimeException e) { throw e; } catch (SQLException e) { throw new IllegalStateException(e); } catch (Exception e) { throw new EJBExceptionWrapper(e); } finally { if (! _isXA) _isInTransaction = false; _completionList.clear(); for (int i = 0; i < _txEntitiesTop; i++) { Entity entity = _txEntities[i]; entity.__caucho_afterRollback(); } _txEntitiesTop = 0; } } /** * Flushes managed entities. */ public void flush() { try { checkTransactionRequired("flush"); flushInternal(); } catch (RuntimeException e) { throw e; } catch (SQLException e) { throw new IllegalStateException(e); } catch (Exception e) { throw new EJBExceptionWrapper(e); } } /** * Flushes managed entities. */ public void flushNoChecks() { try { flushInternal(); } catch (RuntimeException e) { throw e; } catch (SQLException e) { throw new IllegalStateException(e); } catch (Exception e) { throw new EJBExceptionWrapper(e); } } /** * Expires the entities */ public void expire() throws SQLException { Entity []entities = _entities; for (int i = _entitiesTop - 1; i >= 0; i--) { Entity entity = entities[i]; // jpa/0j5e if (! entity.__caucho_getEntityState().isPersist()) entity.__caucho_expire(); } } /** * Returns the connection. */ public Connection getConnection() throws SQLException { DataSource readDataSource = _persistenceUnit.getReadDataSource(); if (! _isXA && ! _isInTransaction && readDataSource != null) { if (_readConn == null) { _readConn = readDataSource.getConnection(); } else if (_readConn.isClosed()) { closeConnectionImpl(); _readConn = _persistenceUnit.getDataSource().getConnection(); } return _readConn; } if (_conn == null) { _conn = _persistenceUnit.getDataSource().getConnection(); _isAutoCommit = true; } else if (_conn.isClosed()) { closeConnectionImpl(); _conn = _persistenceUnit.getDataSource().getConnection(); _isAutoCommit = true; } if (_isXA) { } else if (_isInTransaction && _isAutoCommit) { _isAutoCommit = false; _conn.setAutoCommit(false); } else if (! _isInTransaction && ! _isAutoCommit) { _isAutoCommit = true; _conn.setAutoCommit(true); } return _conn; } /** * Prepares a statement. */ public PreparedStatement prepareStatement(String sql) throws SQLException { try { PreparedStatement pstmt = _preparedStatementMap.get(sql); if (pstmt == null) { Connection conn = getConnection(); // XXX: avoids locking issues. if (_statements.size() > 0) { conn = _statements.get(0).getConnection(); } // XXX: avoids locking issues. // See com.caucho.sql.UserConnection pstmt = conn.prepareStatement(sql, ResultSet.TYPE_FORWARD_ONLY, ResultSet.CONCUR_READ_ONLY); _statements.add(pstmt); _preparedStatementMap.put(sql, pstmt); } return pstmt; } catch (SQLException e) { closeConnectionImpl(); throw e; } } /** * Closes a statement. */ public void closeStatement(String sql) throws SQLException { PreparedStatement pstmt = _preparedStatementMap.remove(sql); if (pstmt != null) { _statements.remove(pstmt); pstmt.close(); } } public static void close(ResultSet rs) { try { if (rs != null) rs.close(); } catch (SQLException e) { throw new AmberRuntimeException(e); } } /** * Prepares an insert statement. */ public PreparedStatement prepareInsertStatement(String sql, boolean isGeneratedId) throws SQLException { PreparedStatement pstmt = null; try { pstmt = _preparedStatementMap.get(sql); if (pstmt != null) return pstmt; Connection conn = getConnection(); // XXX: avoids locking issues. if (_statements.size() > 0) { conn = _statements.get(0).getConnection(); } if (isGeneratedId && _persistenceUnit.hasReturnGeneratedKeys()) pstmt = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS); else { // XXX: avoids locking issues. // See com.caucho.sql.UserConnection pstmt = conn.prepareStatement(sql); } _statements.add(pstmt); _preparedStatementMap.put(sql, pstmt); return pstmt; } catch (SQLException e) { closeStatement(sql); throw e; } } /** * Updates the database with the values in object. If the object does * not exist, throws an exception. * * @param obj the object to update */ public void update(Object obj) { } /** * Saves the object. * * @param obj the object to create */ public void create(Object obj) throws SQLException { // ejb/0g22 exception handling try { createInternal(obj); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new EJBExceptionWrapper(e); } } /** * Saves the object. * * @param obj the object to create */ public void create(String homeName, Object obj) throws SQLException { // ejb/0g22 exception handling try { createInternal(homeName, obj); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new EJBExceptionWrapper(e); } } /** * Saves the object. * * @param obj the object to create */ public void create(AmberEntityHome home, Object obj) throws SQLException { // ejb/0g22 exception handling try { createInternal(home, obj); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new EJBExceptionWrapper(e); } } /** * Updates the object. */ public void update(Entity entity) { if (entity == null) return; // jpa/0g0i if (entity.__caucho_getEntityType() == null) return; // XXX: also needs to check PersistenceContextType.TRANSACTION/EXTENDED. // jpa/0k10 if (! isActiveTransaction()) return; AmberTable table = entity.__caucho_getEntityType().getTable(); Object key = entity.__caucho_getPrimaryKey(); addCompletion(new RowInvalidateCompletion(table.getName(), key)); // jpa/0ga8, jpa/0s2d if (! _txEntities.contains(entity)) { Entity oldEntity = getTransactionEntity(entity.getClass(), key); if (oldEntity == null) { addTxEntity(entity); } else { // XXX: /* // jpa/0s2d Entity oldEntity = _txEntities.get(index); _txEntities.set(index, entity); */ } } /** * Deletes the object. * * @param entity the object to delete */ public void delete(Entity entity) throws SQLException { Entity oldEntity = getEntity(entity.getClass(), entity.__caucho_getPrimaryKey()); if (oldEntity == null) { throw new IllegalStateException(L.l("AmberEntity[{0}:{1}] cannot be deleted since it is not managed", entity.getClass().getName(), entity.__caucho_getPrimaryKey())); /* EntityType entityType = entity.__caucho_getEntityType(); if (entityType == null) return; // throw new AmberException(L.l("entity has no entityType")); AmberEntityHome entityHome = entityType.getHome(); //entityHome = _persistenceUnit.getEntityHome(entity.getClass().getName()); if (entityHome == null) throw new AmberException(L.l("entity has no matching home")); // XXX: this makes no sense entityHome.makePersistent(entity, this, true); addEntity(entity); */ } else { // XXX: jpa/0k12 oldEntity.__caucho_setConnection(this); entity = oldEntity; } entity.__caucho_delete(); } /** * Creates a query object from a query string. * * @param queryString a Hibernate query */ public AmberQuery prepareQuery(String queryString) throws AmberException { return prepareQuery(queryString, false); } /** * Creates a query object from a query string. * * @param queryString a Hibernate query */ public AmberQuery prepareLazyQuery(String queryString) throws AmberException { return prepareQuery(queryString, true); } /** * Creates a query object from a query string. * * @param queryString a Hibernate query */ public AmberQuery prepareUpdate(String queryString) throws AmberException { return prepareQuery(queryString, true); } /** * Creates a query object from a query string. * * @param queryString a Hibernate query */ private AmberQuery prepareQuery(String queryString, boolean isLazy) throws AmberException { AbstractQuery queryProgram = parseQuery(queryString, isLazy); UserQuery query = new UserQuery(queryProgram); query.setSession(this); return query; } /** * Creates a query object from a query string. * * @param sql a Hibernate query */ public AbstractQuery parseQuery(String sql, boolean isLazy) throws AmberException { try { _persistenceUnit.initEntityHomes(); } catch (Exception e) { throw AmberRuntimeException.create(e); } AbstractQuery query = _persistenceUnit.getQueryParseCache(sql); if (query == null) { QueryParser parser = new QueryParser(sql); parser.setPersistenceUnit(_persistenceUnit); parser.setLazyResult(isLazy); query = parser.parse(); _persistenceUnit.putQueryParseCache(sql, query); } return query; } /** * Select a list of objects with a Hibernate query. * * @param hsql the hibernate query * * @return the query results. */ public ResultSet query(String hsql) throws SQLException { AmberQuery query = prepareQuery(hsql); return query.executeQuery(); } /** * Returns the cache chunk. * * @param sql the SQL for the cache chunk * @param args the filled parameters for the cache chunk * @param startRow the starting row for the cache chunk */ public ResultSetCacheChunk getQueryCacheChunk(String sql, Object []args, int startRow) { _queryKey.init(sql, args, startRow); return _persistenceUnit.getQueryChunk(_queryKey); } /** * Returns the result set meta data from cache. */ public ResultSetMetaData getQueryMetaData() { return _persistenceUnit.getQueryMetaData(_queryKey); } /** * Sets the cache chunk. * * @param sql the SQL for the cache chunk * @param args the filled parameters for the cache chunk * @param startRow the starting row for the cache chunk * @param cacheChunk the new value of the cache chunk */ public void putQueryCacheChunk(String sql, Object []args, int startRow, ResultSetCacheChunk cacheChunk, ResultSetMetaData cacheMetaData) { QueryCacheKey key = new QueryCacheKey(); Object []newArgs = new Object[args.length]; System.arraycopy(args, 0, newArgs, 0, args.length); key.init(sql, newArgs, startRow); _persistenceUnit.putQueryChunk(key, cacheChunk); _persistenceUnit.putQueryMetaData(key, cacheMetaData); } /** * Updates the database with a query * * @param hsql the hibernate query * * @return the query results. */ public int update(String hsql) throws SQLException { AmberQuery query = prepareUpdate(hsql); return query.executeUpdate(); } /** * Select a list of objects with a Hibernate query. * * @param hsql the hibernate query * * @return the query results. */ public List find(String hsql) throws SQLException { AmberQuery query = prepareQuery(hsql); return query.list(); } /** * Cleans up the connection. */ public void cleanup() { if (log.isLoggable(Level.FINER)) log.log(Level.FINER, "AmberConnection.cleanup"); try { // XXX: also needs to check PersistenceContextType.TRANSACTION/EXTENDED. // jpa/0g04 if (isActiveTransaction()) { flushInternal(); } } catch (RuntimeException e) { throw e; } catch (SQLException e) { throw new IllegalStateException(e); } catch (Exception e) { throw new EJBExceptionWrapper(e); } finally { _depth = 0; for (int i = _entitiesTop - 1; i >= 0; i--) { _entities[i].__caucho_detach(); } _entitiesTop = 0; _txEntitiesTop = 0; _completionList.clear(); freeConnection(); } } /** * Pushes the depth. */ public void pushDepth() { // these aren't necessary because the AmberConnection is added as // a close callback to the UserTransaction } /** * Pops the depth. */ public void popDepth() { } /** * Frees the connection. */ public void freeConnection() { closeConnectionImpl(); } /** * Frees the connection. */ private void closeConnectionImpl() { Connection conn = _conn; _conn = null; Connection readConn = _readConn; _readConn = null; boolean isAutoCommit = _isAutoCommit; _isAutoCommit = true; try { if (conn != null && ! isAutoCommit) conn.setAutoCommit(true); } catch (SQLException e) { } for (Statement stmt : _statements) { try { stmt.close(); } catch (Exception e) { log.log(Level.WARNING, e.toString(), e); } } try { _preparedStatementMap.clear(); _statements.clear(); if (conn != null) conn.close(); if (readConn != null) readConn.close(); } catch (Exception e) { log.log(Level.WARNING, e.toString(), e); } } @Override public String toString() { if (_persistenceUnit != null) return "AmberConnection[" + _persistenceUnit.getName() + "]"; else return "AmberConnection[closed]"; } /** * Finalizer. */ @Override public void finalize() { cleanup(); } /** * Returns true when cache items can be used. */ public boolean shouldRetrieveFromCache() { // ejb/0d01 return (! isActiveTransaction()); } public void setTransactionalState(Entity entity) { if (isActiveTransaction()) { // jpa/0ga8 entity.__caucho_setConnection(this); // jpa/0j5f EntityState state = entity.__caucho_getEntityState(); //if (state.ordinal() < EntityState.P_DELETING.ordinal()) if (state == EntityState.P_NON_TRANSACTIONAL) entity.__caucho_setEntityState(EntityState.P_TRANSACTIONAL); } } public boolean isCacheEntity(Entity entity) { return entity == getCacheEntity(entity, true); } public Entity getCacheEntity(Entity entity) { return getCacheEntity(entity, false); } public Entity getCacheEntity(Entity entity, boolean isDebug) { // jpa/0h0a if (entity == null) return null; // XXX: jpa/0h20, the cache entity is only available after commit. Entity cacheEntity = entity.__caucho_getCacheEntity(); if (cacheEntity != null) return cacheEntity; return getCacheEntity(entity.getClass(), entity.__caucho_getPrimaryKey(), isDebug); } public Entity getCacheEntity(Class cl, Object pk) { return getCacheEntity(cl, pk, false); } // jpa/0h20 public Entity getCacheEntity(Class cl, Object pk, boolean isDebug) { if (pk == null) return null; String className = cl.getName(); AmberEntityHome entityHome = _persistenceUnit.getEntityHome(className); if (entityHome == null) { if (log.isLoggable(Level.FINER)) log.log(Level.FINER, L.l("Home not found for entity (class: '{0}' PK: '{1}')", className, pk)); return null; } EntityType rootType = entityHome.getRootType(); EntityItem item = _persistenceUnit.getEntity(rootType, pk); if (item == null) return null; // jpa/0o0b if (isDebug) return item.getEntity(); // XXX: jpa/0h31, expires the child cache entity. if (isActiveTransaction()) { Entity txEntity = getTransactionEntity(cl, pk); if (txEntity != null) txEntity.__caucho_getEntityState(); else // jpa/0o0b || ! state.isManaged()) { item.getEntity().__caucho_expire(); return null; } return item.getEntity(); } // // private // // throws Exception (for jpa) // // ejb/0g22 (cmp) expects exception handling in // the public methods. See public void create(Object) above. /** * Adds an entity to the context, assuming it has not been added yet. * Also, if there is a transaction, adds the entity to the list of * transactional entities. */ private void addInternalEntity(Entity entity) { if (log.isLoggable(Level.FINEST)) { log.log(Level.FINEST, L.l("amber {0}[{1}] addInternalEntity", entity.getClass().getName(), entity.__caucho_getPrimaryKey())); } addEntity(entity); // jpa/0g06 if (isActiveTransaction()) { addTxEntity(entity); // jpa/0s2d: merge() setTransactionalState(entity); } } /** * Saves the object. * * @param obj the object to create */ private void createInternal(Object obj) throws Exception { AmberEntityHome home = null; Class cl = obj.getClass(); for (; home == null && cl != null; cl = cl.getSuperclass()) { home = _persistenceUnit.getHome(cl); } if (home == null) throw new AmberException(L.l("'{0}' is not a known entity class.", obj.getClass().getName())); createInternal(home, obj); } /** * Saves the object. * * @param obj the object to create */ private void createInternal(String homeName, Object obj) throws Exception { AmberEntityHome home = _persistenceUnit.getEntityHome(homeName); if (home == null) throw new AmberException(L.l("'{0}' is not a known entity class.", obj.getClass().getName())); createInternal(home, obj); } /** * Saves the object. * * @param obj the object to create */ private void createInternal(AmberEntityHome home, Object obj) throws Exception { // XXX: flushing things like delete might be useful? // XXX: the issue is a flush can break FK constraints and // fail prematurely (jpa/0h26). // commented out: flushInternal(); if (contains(obj)) return; Entity entity = (Entity) obj; // jpa/0g0k: cannot call home.save because of jpa exception handling. if (_persistenceUnit.isJPA()) { // See persistInternal(): entity.__caucho_cascadePrePersist(this); addEntity(entity); // jpa/0ga2 entity.__caucho_lazy_create(this, home.getEntityType()); // See persistInternal(): entity.__caucho_cascadePostPersist(this); } else home.save(this, entity); // jpa/0h25 // XXX: not correct, since we need to keep the P_PERSIST state around // and P_PERSIST is a transactional state // setTransactionalState(entity); // jpa/0g0i AmberTable table = home.getEntityType().getTable(); addCompletion(new RowInsertCompletion(table.getName())); } private void checkTransactionRequired(String operation) throws TransactionRequiredException, SQLException { // XXX: also needs to check PersistenceContextType.TRANSACTION/EXTENDED. if (! (_isXA || isActiveTransaction())) throw new TransactionRequiredException(L.l("{0}() operation can only be executed in the scope of a transaction or with an extended persistence context.", operation)); } private Entity checkEntityType(Object entity, String operation) { if (! (entity instanceof Entity)) throw new IllegalArgumentException(L.l("{0}() operation can only be applied to an entity instance. If the argument is an entity, the corresponding class must be specified in the scope of a persistence unit.", operation)); if (_persistenceUnit.isJPA()) { String className = entity.getClass().getName(); EntityType entityType = (EntityType) _persistenceUnit.getEntityType(className); // jpa/0m08 if (entityType == null) { throw new IllegalArgumentException(L.l("{0}() operation can only be applied to an entity instance. If the argument is an entity, the class '{1}' must be specified in the orm.xml or annotated with @Entity and must be in the scope of a persistence unit.", operation, className)); } } return (Entity) entity; } /** * Detach after non-xa. */ public void detach() { if (_isXA || _isInTransaction) throw new IllegalStateException(L.l("detach cannot be called within transaction")); _completionList.clear(); _txEntitiesTop = 0; // jpa/1700 for (int i = _entitiesTop - 1; i >= 0; i--) { _entities[i].__caucho_detach(); } // jpa/0o0d _entitiesTop = 0; } /** * Flush managed entities. */ private void flushInternal() throws Exception { // Do not flush within merge() or while loading an entity. if (! _isFlushAllowed) return; /* XXX: moved into __caucho_flush for (int i = _txEntities.size() - 1; i >= 0; i--) { Entity entity = _txEntities.get(i); EntityState state = entity.__caucho_getEntityState(); // jpa/0i60 // jpa/0h27: for all entities Y referenced by a *managed* // entity X, where the relationship has been annotated // with cascade=PERSIST/ALL, the persist operation is // applied to Y. It is a lazy cascade as the relationship // is not always initialized at the time persist(X) was // called but must be at flush time. if (state == EntityState.P_PERSIST) { entity.__caucho_cascadePrePersist(this); entity.__caucho_cascadePostPersist(this); } } */ // We avoid breaking FK constraints: // // 1. Assume _txEntities has the following order: A <- B <- C // XXX: Make sure priorities are handled based on owning sides // even when there are cycles in a graph. // // 2. Persist is done in ascending order: A(0) <- B(1) <- C(2) // // 3. Delete is done in descending order: C(2) -> B(1) -> A(0) // Persists in ascending order. for (int i = 0; i < _txEntitiesTop; i++) { Entity entity = _txEntities[i]; if (entity.__caucho_getEntityState().isPersist()) { try { entity.__caucho_flush(); } catch (SQLException e) { throwPersistException(e, entity); } } } // jpa/0h25 // Deletes in descending order. for (int i = _txEntitiesTop - 1; i >= 0; i--) { Entity entity = _txEntities[i]; if (! entity.__caucho_getEntityState().isPersist()) { entity.__caucho_flush(); } } if (! isInTransaction()) { if (_completionList.size() > 0) { _persistenceUnit.complete(_completionList); } _completionList.clear(); for (int i = 0; i < _txEntitiesTop; i++) { Entity entity = _txEntities[i]; entity.__caucho_afterCommit(); } _txEntitiesTop = 0; } } private void throwPersistException(SQLException e, Entity entity) throws SQLException { log.log(Level.FINER, e.toString(), e); String sqlState = e.getSQLState(); JdbcMetaData metaData = _persistenceUnit.getMetaData(); if (metaData.isUniqueConstraintSQLState(sqlState)) { // jpa/0ga5 throw new EntityExistsException(L.l("Trying to persist an entity '{0}[{1}]' that already exists. Entity state '{2}'", entity.getClass().getName(), entity.__caucho_getPrimaryKey(), entity.__caucho_getEntityState())); } else if (metaData.isForeignKeyViolationSQLState(sqlState)) { // jpa/0o42 throw new IllegalStateException(L.l("Trying to persist an entity of class '{0}' with PK '{1}' would break a foreign key constraint. The entity state is '{2}'. Please make sure there are associated entities for all required relationships. If you are merging an entity make sure the association fields are annotated with cascade=MERGE or cascade=ALL.", entity.getClass().getName(), entity.__caucho_getPrimaryKey(), entity.__caucho_getEntityState())); } throw e; } /** * Persists the entity. */ private Entity persistInternal(Entity entity) throws Exception { EntityState state = entity.__caucho_getEntityState(); if (state == null) state = EntityState.TRANSIENT; switch (state) { case TRANSIENT: { Entity contextEntity = getEntity(entity.getClass(), entity.__caucho_getPrimaryKey()); // jpa/0ga3 if (contextEntity == null) { } else if (contextEntity.__caucho_getEntityState().isDeleting()) { // jpa/0ga3 contextEntity.__caucho_flush(); } else if (entity != contextEntity) { return contextEntity; /* // jpa/0ga1: trying to persist a detached entity that already exists. throw new EntityExistsException(L.l("Trying to persist a detached entity of class '{0}' with PK '{1}' that already exists. Entity state '{2}'", entity.getClass().getName(), entity.__caucho_getPrimaryKey(), state)); */ } // jpa/0h24 // Pre-persist child entities. entity.__caucho_cascadePrePersist(this); createInternal(entity); } break; case P_DELETING: case P_DELETED: { // jpa/0i60, jpa/1510, jpa/0h25 // jpa/0h26 entity.__caucho_cascadePrePersist(this); // removed entity instance, reset state and persist. entity.__caucho_makePersistent(null, (EntityType) null); createInternal(entity); } break; case P_PERSISTING: case P_PERSISTED: { // jpa/0h26 // Pre-persist child entities. entity.__caucho_cascadePrePersist(this); } break; default: if (entity.__caucho_getConnection() == this) return entity; else { // jpa/0ga5 (tck): // See entitytest.persist.basic.persistBasicTest4 vs. // callback.inheritance.preUpdateTest throw new EntityExistsException(L.l("Trying to persist an entity that is detached or already exists. Entity state '{0}'", state)); } } // jpa/0j5e updateFlushPriority(entity); // jpa/0h27, jpa/0i5c, jpa/0j5g // Post-persist child entities. entity.__caucho_cascadePostPersist(this); return entity; } /** * Updates flush priorities. */ private void updateFlushPriority(Entity updateEntity) { if (! isActiveTransaction()) return; removeTxEntity(updateEntity); int updatePriority = updateEntity.__caucho_getEntityType().getFlushPriority(); for (int i = _txEntitiesTop - 1; i >= 0; i--) { Entity entity = _txEntities[i]; int currentPriority = entity.__caucho_getEntityType().getFlushPriority(); if (currentPriority < updatePriority) { addTxEntity(i + 1, updateEntity); return; } } addTxEntity(0, updateEntity); } /** * Recursively merges the state of the entity into the current context. */ public <T> T recursiveMerge(T entityT) { // jpa/0ga3, jpa/0h08, jpa/0i5g, jpa/0s2k try { if (entityT == null) return null; Entity entity = checkEntityType(entityT, "merge"); if (log.isLoggable(Level.FINER)) { String className = entity.getClass().getName(); Object pk = entity.__caucho_getPrimaryKey(); EntityState state = entity.__caucho_getEntityState(); log.finer(L.l("recursiveMerge({0}[{1}] state: '{2}'", className, pk, state)); } if (containsMergingEntity(entity)) return (T) entity; else return (T) mergeDetachedEntity(entity); } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new EJBExceptionWrapper(e); } } public Entity mergeDetachedEntity(Entity newEntity) { try { if (newEntity == null) return newEntity; Class entityClass = newEntity.getClass(); String className = newEntity.getClass().getName(); EntityState state = newEntity.__caucho_getEntityState(); Object pk = newEntity.__caucho_getPrimaryKey(); if (log.isLoggable(Level.FINER)) log.finer(L.l("{0}[{1}] amber merge state='{2}'", entityClass.getSimpleName(), pk, state)); if (state.isDeleting()) { // removed entity instance throw new IllegalArgumentException(L.l("{0}: merge operation cannot be applied to a removed entity instance", entityClass)); } // XXX: jpa/0o42 try { Entity existingEntity = null; try { existingEntity = (Entity) load(entityClass, pk, true); } catch (AmberObjectNotFoundException e) { if (log.isLoggable(Level.FINER)) log.log(Level.FINER, e.toString(), e); // JPA: should not throw at all, returns null only. } if (existingEntity != null) { if (containsMergingEntity(existingEntity)) return existingEntity; _mergingEntities.add(existingEntity); existingEntity.__caucho_mergeFrom(this, newEntity); return existingEntity; } // XXX: the original entity should remain detached jpa/0s2k // setTransactionalState(entity); // new entity instance persist(newEntity); return newEntity; } catch (RuntimeException e) { throw e; } catch (Exception e) { throw new EJBExceptionWrapper(e); } } /** * Creates an instance of the named query */ private Query createInternalNativeQuery(String sql) { try { QueryImpl query = new QueryImpl(this); query.setNativeSql(sql); return query; } catch (RuntimeException e) { throw new IllegalArgumentException(e); } catch (Exception e) { throw new EJBExceptionWrapper(e); } } /** * Creates an instance of the native query. */ private Query createInternalNativeQuery(String sql, SqlResultSetMappingConfig map) { Query query = createInternalNativeQuery(sql); QueryImpl queryImpl = (QueryImpl) query; queryImpl.setSqlResultSetMapping(map); return query; } private boolean containsMergingEntity(Entity entity) { if (_mergingEntities.contains(entity)) return true; // jpa/0o42 int index = getEntityMatch(_mergingEntities, entity.getClass(), entity.__caucho_getPrimaryKey()); return (index >= 0); } public void addEntity(Entity entity) { Entity []entities = _entities; if (_entitiesTop == entities.length) { entities = new Entity[_entities.length + 32]; System.arraycopy(_entities, 0, entities, 0, _entities.length); _entities = entities; } entities[_entitiesTop++] = entity; } private void removeEntityImpl(Entity entity) { Entity []entities = _entities; for (int i = _entitiesTop - 1; i >= 0; i--) { if (entities[i] == entity) { System.arraycopy(entities, i + 1, entities, i, _entitiesTop - i - 1); _entitiesTop -= 1; return; } } } private void addTxEntity(Entity entity) { Entity []entities = _txEntities; if (_txEntitiesTop == entities.length) { entities = new Entity[entities.length + 32]; System.arraycopy(_txEntities, 0, entities, 0, _txEntities.length); _txEntities = entities; } entities[_txEntitiesTop++] = entity; } private void removeTxEntity(Entity entity) { Entity []entities = _txEntities; for (int i = _txEntitiesTop - 1; i >= 0; i--) { if (entities[i] == entity) { System.arraycopy(entities, i + 1, entities, i, _txEntitiesTop - i - 1); _txEntitiesTop -= 1; return; } } } private void addTxEntity(int index, Entity entity) { Entity []entities = _txEntities; if (_txEntitiesTop == entities.length) { entities = new Entity[_txEntities.length + 32]; System.arraycopy(_txEntities, 0, entities, 0, _txEntities.length); _txEntities = entities; } if (index < _txEntitiesTop) System.arraycopy(entities, index, entities, index + 1, _txEntitiesTop - index); entities[index] = entity; _txEntitiesTop += 1; } private static int getEntityMatch(ArrayList<Entity> list, Class cl, Object key) { // See also: getEntity() and getTransactionEntity(). // jpa/0o42 for (int i = list.size() - 1; i >= 0; i--) { Entity entity = list.get(i); if (entity.__caucho_match(cl, key)) { return i; } } return -1; } private class EntityTransactionImpl implements EntityTransaction { private boolean _rollbackOnly; /** * Starts a resource transaction. */ public void begin() { // jpa/1522 if (isActiveTransaction()) throw new IllegalStateException("begin() cannot be called when the entity transaction is already active."); _rollbackOnly = false; try { AmberConnection.this.beginTransaction(); } catch (SQLException e) { throw new PersistenceException(e); } } /** * Commits a resource transaction. */ public void commit() { // jpa/1523 if (! isActiveTransaction()) throw new IllegalStateException("commit() cannot be called when the entity transaction is not active."); // jpa/1525 if (getRollbackOnly()) throw new RollbackException("commit() cannot be called when the entity transaction is marked for rollback only."); try { // jpa/11a7 AmberConnection.this.beforeCommit(); _isInTransaction = false; // jpa/11a7 AmberConnection.this.commit(); if (AmberConnection.this._conn != null) { AmberConnection.this._conn.commit(); } // XXX: missing finally issues if _conn.commit fails // jpa/11a7 AmberConnection.this.afterCommit(true); if (AmberConnection.this._conn != null) { closeConnectionImpl(); } } catch (SQLException e) { throw new PersistenceException(e); } } /** * Rolls the current transaction back. */ public void rollback() { // jpa/1524 if (! isActiveTransaction()) throw new IllegalStateException("rollback() cannot be called when the entity transaction is not active."); setRollbackOnly(); PersistenceException exn = null; try { AmberConnection.this.rollback(); } catch (Exception e) { exn = new PersistenceException(e); } finally { try { // jpa/1501 AmberConnection.this.afterCommit(false); } catch (PersistenceException e) { exn = e; } catch (Exception e) { exn = new PersistenceException(e); } finally { // jpa/1525 if (AmberConnection.this._conn != null) { closeConnectionImpl(); } } } if (exn != null) throw exn; } /** * Marks the current transaction for rollback only. */ public void setRollbackOnly() { // jpa/1521 if (! isActiveTransaction()) throw new IllegalStateException("setRollbackOnly() cannot be called when the entity transaction is not active."); _rollbackOnly = true; } /** * Returns true if the transaction is for rollback only. */ public boolean getRollbackOnly() { // jpa/1520 if (! isActiveTransaction()) throw new IllegalStateException("getRollbackOnly() cannot be called when the entity transaction is not active."); return _rollbackOnly; } /** * Test if a transaction is in progress. */ public boolean isActive() { return _isInTransaction; } } /* (non-Javadoc) * @see javax.persistence.EntityManager#createNamedQuery(java.lang.String, java.lang.Class) */ @Override public <T> TypedQuery<T> createNamedQuery(String name, Class<T> resultClass) { // TODO Auto-generated method stub return null; } /* (non-Javadoc) * @see javax.persistence.EntityManager#createQuery(javax.persistence.criteria.CriteriaQuery) */ @Override public <T> TypedQuery<T> createQuery(CriteriaQuery<T> criteriaQuery) { // TODO Auto-generated method stub return null; } @Override public <T> TypedQuery<T> createQuery(String qlString, Class<T> resultClass) { // TODO Auto-generated method stub return null; } /* (non-Javadoc) * @see javax.persistence.EntityManager#detach(java.lang.Object) */ @Override public void detach(Object entity) { // TODO Auto-generated method stub } /* (non-Javadoc) * @see javax.persistence.EntityManager#find(java.lang.Class, java.lang.Object, java.util.Map) */ @Override public <T> T find(Class<T> entityCLass, Object primaryKey, Map<String, Object> properties) { // TODO Auto-generated method stub return null; } /* (non-Javadoc) * @see javax.persistence.EntityManager#find(java.lang.Class, java.lang.Object, javax.persistence.LockModeType) */ @Override public <T> T find(Class<T> entityCLass, Object primaryKey, LockModeType lockMode) { // TODO Auto-generated method stub return null; } /* (non-Javadoc) * @see javax.persistence.EntityManager#find(java.lang.Class, java.lang.Object, javax.persistence.LockModeType, java.util.Map) */ @Override public <T> T find(Class<T> entityCLass, Object primaryKey, LockModeType lockMode, Map<String, Object> properties) { // TODO Auto-generated method stub return null; } /* (non-Javadoc) * @see javax.persistence.EntityManager#getCriteriaBuilder() */ @Override public CriteriaBuilder getCriteriaBuilder() { // TODO Auto-generated method stub return null; } /* (non-Javadoc) * @see javax.persistence.EntityManager#getEntityManagerFactory() */ @Override public EntityManagerFactory getEntityManagerFactory() { // TODO Auto-generated method stub return null; } /* (non-Javadoc) * @see javax.persistence.EntityManager#getLockMode(java.lang.Object) */ @Override public LockModeType getLockMode(Object entity) { // TODO Auto-generated method stub return null; } /* (non-Javadoc) * @see javax.persistence.EntityManager#getMetamodel() */ @Override public Metamodel getMetamodel() { // TODO Auto-generated method stub return null; } /* (non-Javadoc) * @see javax.persistence.EntityManager#getProperties() */ @Override public Map<String, Object> getProperties() { // TODO Auto-generated method stub return null; } /* (non-Javadoc) * @see javax.persistence.EntityManager#lock(java.lang.Object, javax.persistence.LockModeType, java.util.Map) */ @Override public void lock(Object entity, LockModeType lockMode, Map<String, Object> properties) { // TODO Auto-generated method stub } /* (non-Javadoc) * @see javax.persistence.EntityManager#refresh(java.lang.Object, java.util.Map) */ @Override public void refresh(Object entity, Map<String, Object> properties) { // TODO Auto-generated method stub } /* (non-Javadoc) * @see javax.persistence.EntityManager#refresh(java.lang.Object, javax.persistence.LockModeType) */ @Override public void refresh(Object entity, LockModeType lockMode) { // TODO Auto-generated method stub } /* (non-Javadoc) * @see javax.persistence.EntityManager#refresh(java.lang.Object, javax.persistence.LockModeType, java.util.Map) */ @Override public void refresh(Object entity, LockModeType lockMode, Map<String, Object> properties) { // TODO Auto-generated method stub } /* (non-Javadoc) * @see javax.persistence.EntityManager#setProperty(java.lang.String, java.lang.Object) */ @Override public void setProperty(String propertyName, Object value) { // TODO Auto-generated method stub } /* (non-Javadoc) * @see javax.persistence.EntityManager#unwrap(java.lang.Class) */ @Override public <T> T unwrap(Class<T> cls) { // TODO Auto-generated method stub return null; } }