0001: /*
0002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
0003: *
0004: * This file is part of Resin(R) Open Source
0005: *
0006: * Each copy or derived work must preserve the copyright notice and this
0007: * notice unmodified.
0008: *
0009: * Resin Open Source is free software; you can redistribute it and/or modify
0010: * it under the terms of the GNU General Public License as published by
0011: * the Free Software Foundation; either version 2 of the License, or
0012: * (at your option) any later version.
0013: *
0014: * Resin Open Source is distributed in the hope that it will be useful,
0015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
0017: * of NON-INFRINGEMENT. See the GNU General Public License for more
0018: * details.
0019: *
0020: * You should have received a copy of the GNU General Public License
0021: * along with Resin Open Source; if not, write to the
0022: *
0023: * Free Software Foundation, Inc.
0024: * 59 Temple Place, Suite 330
0025: * Boston, MA 02111-1307 USA
0026: *
0027: * @author Scott Ferguson
0028: */
0029:
0030: package com.caucho.amber.manager;
0031:
0032: import com.caucho.amber.AmberException;
0033: import com.caucho.amber.AmberObjectNotFoundException;
0034: import com.caucho.amber.AmberQuery;
0035: import com.caucho.amber.AmberRuntimeException;
0036: import com.caucho.amber.cfg.EntityResultConfig;
0037: import com.caucho.amber.cfg.NamedNativeQueryConfig;
0038: import com.caucho.amber.cfg.SqlResultSetMappingConfig;
0039: import com.caucho.amber.collection.AmberCollection;
0040: import com.caucho.amber.entity.*;
0041: import com.caucho.amber.query.AbstractQuery;
0042: import com.caucho.amber.query.QueryCacheKey;
0043: import com.caucho.amber.query.QueryParser;
0044: import com.caucho.amber.query.ResultSetCacheChunk;
0045: import com.caucho.amber.query.UserQuery;
0046: import com.caucho.amber.table.Table;
0047: import com.caucho.amber.type.EntityType;
0048: import com.caucho.config.ConfigException;
0049: import com.caucho.ejb.EJBExceptionWrapper;
0050: import com.caucho.jca.BeginResource;
0051: import com.caucho.jca.CloseResource;
0052: import com.caucho.jca.UserTransactionProxy;
0053: import com.caucho.jdbc.JdbcMetaData;
0054: import com.caucho.util.L10N;
0055: import com.caucho.util.LruCache;
0056:
0057: import javax.persistence.EntityExistsException;
0058: import javax.persistence.EntityNotFoundException;
0059: import javax.persistence.EntityTransaction;
0060: import javax.persistence.FlushModeType;
0061: import javax.persistence.LockModeType;
0062: import javax.persistence.Query;
0063: import javax.persistence.PersistenceException;
0064: import javax.persistence.RollbackException;
0065: import javax.persistence.TransactionRequiredException;
0066: import javax.sql.DataSource;
0067: import javax.transaction.Status;
0068: import javax.transaction.Synchronization;
0069: import javax.transaction.Transaction;
0070: import java.lang.reflect.Method;
0071: import java.sql.Connection;
0072: import java.sql.PreparedStatement;
0073: import java.sql.ResultSet;
0074: import java.sql.ResultSetMetaData;
0075: import java.sql.SQLException;
0076: import java.sql.Statement;
0077: import java.util.ArrayList;
0078: import java.util.List;
0079: import java.util.Map;
0080: import java.util.logging.Level;
0081: import java.util.logging.Logger;
0082:
0083: /**
0084: * The entity manager from a entity manager proxy.
0085: */
0086: public class AmberConnection implements BeginResource, CloseResource,
0087: Synchronization {
0088: private static final L10N L = new L10N(AmberConnection.class);
0089: private static final Logger log = Logger
0090: .getLogger(AmberConnection.class.getName());
0091:
0092: private static final Entity[] NULL_ENTITIES = new Entity[0];
0093:
0094: private AmberPersistenceUnit _persistenceUnit;
0095:
0096: private boolean _isRegistered;
0097: private boolean _isThreadConnection;
0098:
0099: private Entity[] _entities = new Entity[32];
0100: private int _entitiesTop;
0101:
0102: private Entity[] _txEntities = NULL_ENTITIES;
0103: private int _txEntitiesTop;
0104:
0105: private ArrayList<AmberCompletion> _completionList = new ArrayList<AmberCompletion>();
0106:
0107: private ArrayList<AmberCollection> _queries = new ArrayList<AmberCollection>();
0108:
0109: private EntityTransaction _trans;
0110:
0111: private long _xid;
0112: private boolean _isInTransaction;
0113: private boolean _isXA;
0114:
0115: private boolean _isExtended;
0116:
0117: private boolean _isAppManaged;
0118:
0119: private Connection _conn;
0120: private Connection _readConn;
0121:
0122: private boolean _isAutoCommit = true;
0123:
0124: private int _depth;
0125:
0126: private LruCache<String, PreparedStatement> _preparedStatementMap = new LruCache<String, PreparedStatement>(
0127: 32);
0128:
0129: private ArrayList<Statement> _statements = new ArrayList<Statement>();
0130:
0131: private EntityKey _entityKey = new EntityKey();
0132: private QueryCacheKey _queryKey = new QueryCacheKey();
0133:
0134: private ArrayList<Entity> _mergingEntities = new ArrayList<Entity>();
0135:
0136: private boolean _isFlushAllowed = true;
0137:
0138: /**
0139: * Creates a manager instance.
0140: */
0141: AmberConnection(AmberPersistenceUnit persistenceUnit,
0142: boolean isExtended, boolean isAppManaged) {
0143: _persistenceUnit = persistenceUnit;
0144: _isExtended = isExtended;
0145: _isAppManaged = isAppManaged;
0146: }
0147:
0148: /**
0149: * Creates a manager instance.
0150: */
0151: AmberConnection(AmberPersistenceUnit persistenceUnit,
0152: boolean isExtended) {
0153: this (persistenceUnit, isExtended, false);
0154: }
0155:
0156: /**
0157: * Returns the persistence unit.
0158: */
0159: public AmberPersistenceUnit getPersistenceUnit() {
0160: return _persistenceUnit;
0161: }
0162:
0163: /**
0164: * Returns true for JPA.
0165: */
0166: public boolean isJPA() {
0167: return _persistenceUnit.isJPA();
0168: }
0169:
0170: /**
0171: * Set true for a threaded connection.
0172: */
0173: public void initThreadConnection() {
0174: _isThreadConnection = true;
0175:
0176: initJta();
0177: }
0178:
0179: public void initJta() {
0180: // non-jta connections do not register with the local transaction
0181: if (_persistenceUnit.isJta())
0182: register();
0183: }
0184:
0185: /**
0186: * Makes the instance managed.
0187: */
0188: public void persist(Object entityObject) {
0189: RuntimeException exn = null;
0190:
0191: try {
0192: if (entityObject == null)
0193: return;
0194:
0195: Entity entity = checkEntityType(entityObject, "persist");
0196:
0197: checkTransactionRequired("persist");
0198:
0199: persistInternal(entity);
0200:
0201: // XXX: check spec. for JTA vs. non-JTA behavior and add QA.
0202: // ejb30/persistence/ee/packaging/ejb/resource_local/test14
0203: if (!_persistenceUnit.isJta())
0204: flushInternal();
0205: } catch (RuntimeException e) {
0206: exn = e;
0207: } catch (SQLException e) {
0208: exn = new IllegalStateException(e);
0209: } catch (Exception e) {
0210: exn = new EJBExceptionWrapper(e);
0211: }
0212:
0213: if (exn != null) {
0214: if (!_persistenceUnit.isJta()) {
0215: if (_trans != null)
0216: _trans.setRollbackOnly();
0217: }
0218:
0219: throw exn;
0220: }
0221: }
0222:
0223: /**
0224: * Makes the instance managed called
0225: * from cascading operations.
0226: */
0227: public void persistFromCascade(Object o) {
0228: // jpa/0h25, jpa/0i5e
0229:
0230: try {
0231: if (o == null)
0232: return;
0233:
0234: Entity entity = (Entity) o;
0235:
0236: // jpa/0h25
0237:
0238: persistInternal(entity);
0239:
0240: } catch (EntityExistsException e) {
0241: log.log(Level.FINER, e.toString(), e);
0242: // This is not an issue. It is the cascading
0243: // operation trying to persist the source
0244: // entity from the destination end.
0245: } catch (RuntimeException e) {
0246: throw e;
0247: } catch (SQLException e) {
0248: throw new IllegalStateException(e);
0249: } catch (Exception e) {
0250: throw new EJBExceptionWrapper(e);
0251: }
0252: }
0253:
0254: /**
0255: * Merges the state of the entity into the current context.
0256: */
0257: public <T> T merge(T entityT) {
0258: RuntimeException exn = null;
0259:
0260: try {
0261: flushInternal();
0262:
0263: // Cannot flush before the merge is complete.
0264: _isFlushAllowed = false;
0265:
0266: entityT = recursiveMerge(entityT);
0267:
0268: } catch (RuntimeException e) {
0269: exn = e;
0270: } catch (Exception e) {
0271: exn = new EJBExceptionWrapper(e);
0272: } finally {
0273: _isFlushAllowed = true;
0274:
0275: try {
0276: flushInternal();
0277: } catch (RuntimeException e) {
0278: if (exn == null)
0279: exn = e;
0280: } catch (Exception e) {
0281: if (exn == null)
0282: exn = new EJBExceptionWrapper(e);
0283: } finally {
0284: _mergingEntities.clear();
0285: }
0286: }
0287:
0288: // jpa/0o42, jpa/0o44
0289: if (exn != null)
0290: throw exn;
0291:
0292: return entityT;
0293: }
0294:
0295: /**
0296: * Remove the instance.
0297: */
0298: public void remove(Object entity) {
0299: try {
0300: if (entity == null)
0301: return;
0302:
0303: Entity instance = checkEntityType(entity, "remove");
0304:
0305: checkTransactionRequired("remove");
0306:
0307: if (log.isLoggable(Level.FINER))
0308: log.log(Level.FINER, L.l("removing entity class "
0309: + instance.getClass().getName() + " PK: "
0310: + instance.__caucho_getPrimaryKey()
0311: + " state: "
0312: + instance.__caucho_getEntityState()));
0313:
0314: EntityState state = instance.__caucho_getEntityState();
0315:
0316: if (EntityState.P_DELETING.ordinal() <= state.ordinal()) {
0317: if (log.isLoggable(Level.FINER))
0318: log.log(Level.FINER, L
0319: .l("remove is ignoring entity in state "
0320: + state));
0321:
0322: return;
0323: }
0324:
0325: // jpa/0k12
0326: if (instance.__caucho_getConnection() == null) {
0327: if (instance.__caucho_getEntityType() == null) {
0328: if (log.isLoggable(Level.FINER))
0329: log
0330: .log(
0331: Level.FINER,
0332: L
0333: .l("remove is ignoring entity; performing only cascade post-remove"));
0334:
0335: // Ignore this entity; only post-remove child entities.
0336: instance.__caucho_cascadePostRemove(this );
0337:
0338: // jpa/0ga7
0339: return;
0340: } else
0341: throw new IllegalArgumentException(
0342: L
0343: .l(
0344: "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.",
0345: instance.getClass()
0346: .getName(),
0347: instance
0348: .__caucho_getPrimaryKey()));
0349: }
0350:
0351: // jpa/0h25, jpa/0i5e
0352: // Do not flush dependent objects for cascading persistence
0353: // when this entity is being removed.
0354: instance.__caucho_setEntityState(EntityState.P_DELETING);
0355:
0356: if (log.isLoggable(Level.FINER))
0357: log
0358: .log(
0359: Level.FINER,
0360: L
0361: .l("remove is flushing any lazy cascading operation"));
0362:
0363: // jpa/1620
0364: // In particular, required for cascading persistence, since the cascade
0365: // is lazy until flush
0366: // jpa/0h25 flushInternal();
0367: // Cannot flush since the delete is lazy until flush, i.e.:
0368: // remove(A); // (*) __caucho_flush()
0369: // remove(B);
0370: // (*) would break a FK constraint if B has a reference to A.
0371:
0372: // jpa/0h26
0373: updateFlushPriority(instance);
0374:
0375: // jpa/0h25, jpa/0i5e
0376: // Restores original state.
0377: instance.__caucho_setEntityState(state);
0378:
0379: Object oldEntity;
0380:
0381: oldEntity = getEntity(instance.getClass().getName(),
0382: instance.__caucho_getPrimaryKey());
0383:
0384: // jpa/0ga4
0385: if (oldEntity == null)
0386: throw new IllegalArgumentException(
0387: L
0388: .l("remove() operation can only be applied to a managed entity instance."));
0389:
0390: if (log.isLoggable(Level.FINER))
0391: log.log(Level.FINER, L
0392: .l("remove is performing cascade pre-remove"));
0393:
0394: // Pre-remove child entities.
0395: instance.__caucho_cascadePreRemove(this );
0396:
0397: if (log.isLoggable(Level.FINER))
0398: log
0399: .log(
0400: Level.FINER,
0401: L
0402: .l("remove is performing delete on the target entity"));
0403:
0404: delete(instance);
0405:
0406: // jpa/0o30: flushes the owning side delete.
0407: // XXX: Cannot flush since the delete is lazy until flush.
0408: // jpa/0h25
0409: // instance.__caucho_flush();
0410:
0411: if (log.isLoggable(Level.FINER))
0412: log.log(Level.FINER, L
0413: .l("remove is performing cascade post-remove"));
0414:
0415: // jpa/0o30
0416: // Post-remove child entities.
0417: instance.__caucho_cascadePostRemove(this );
0418:
0419: if (log.isLoggable(Level.FINER))
0420: log.log(Level.FINER, L
0421: .l("DONE successful remove for entity class "
0422: + instance.getClass().getName()
0423: + " PK: "
0424: + instance.__caucho_getPrimaryKey()));
0425:
0426: } catch (RuntimeException e) {
0427: throw e;
0428: } catch (Exception e) {
0429: throw new EJBExceptionWrapper(e);
0430: }
0431: }
0432:
0433: /**
0434: * Find by the primary key.
0435: */
0436: public <T> T find(Class<T> entityClass, Object primaryKey) {
0437: // Do not flush while an entity is being loaded or merged.
0438: boolean oldIsFlushAllowed = _isFlushAllowed;
0439:
0440: try {
0441: // Do not flush while loading an entity.
0442: // Broken relationships would not pass the flush validation.
0443: _isFlushAllowed = false;
0444:
0445: T entity = (T) load(entityClass, primaryKey, true);
0446:
0447: if (!isActiveTransaction()) {
0448: // jpa/0o00
0449: detach();
0450: }
0451:
0452: return entity;
0453: } catch (AmberObjectNotFoundException e) {
0454: if (_persistenceUnit.isJPA()) {
0455: // JPA: should not throw at all, returns null only.
0456: // log.log(Level.FINER, e.toString(), e);
0457: return null;
0458: }
0459:
0460: // ejb/0604
0461: throw e;
0462: } catch (RuntimeException e) {
0463: throw e;
0464: } catch (Exception e) {
0465: throw new EJBExceptionWrapper(e);
0466: } finally {
0467: _isFlushAllowed = oldIsFlushAllowed;
0468: }
0469: }
0470:
0471: /**
0472: * Find by the primary key.
0473: */
0474: public <T> T getReference(Class<T> entityClass, Object primaryKey)
0475: throws EntityNotFoundException, IllegalArgumentException {
0476: T reference = null;
0477:
0478: try {
0479: // XXX: only needs to get a reference.
0480:
0481: reference = (T) load(entityClass, primaryKey, false);
0482:
0483: if (reference == null)
0484: throw new EntityNotFoundException(
0485: L
0486: .l(
0487: "entity with primary key {0} not found in getReference()",
0488: primaryKey));
0489:
0490: /*
0491: if (! (entityClass.isAssignableFrom(Entity.class)))
0492: throw new IllegalArgumentException(L.l("getReference() operation can only be applied to an entity class"));
0493: */
0494:
0495: return reference;
0496:
0497: } catch (EntityNotFoundException e) {
0498: throw e;
0499: } catch (RuntimeException e) {
0500: throw new IllegalArgumentException(e);
0501: } catch (Exception e) {
0502: throw new EJBExceptionWrapper(e);
0503: }
0504: }
0505:
0506: /**
0507: * Clears the connection
0508: */
0509: public void clear() {
0510: _entitiesTop = 0;
0511: _txEntitiesTop = 0;
0512: }
0513:
0514: /**
0515: * Creates a query.
0516: */
0517: public Query createQuery(String sql) {
0518: try {
0519: AbstractQuery queryProgram = parseQuery(sql, false);
0520:
0521: return new QueryImpl(queryProgram, this );
0522: } catch (RuntimeException e) {
0523: throw new IllegalArgumentException(e);
0524: } catch (Exception e) {
0525: throw new EJBExceptionWrapper(e);
0526: }
0527: }
0528:
0529: /**
0530: * Creates an instance of the named query
0531: */
0532: public Query createNamedQuery(String name) {
0533: String sql = _persistenceUnit.getNamedQuery(name);
0534:
0535: if (sql != null)
0536: return createQuery(sql);
0537:
0538: NamedNativeQueryConfig nativeQuery = _persistenceUnit
0539: .getNamedNativeQuery(name);
0540:
0541: sql = nativeQuery.getQuery();
0542:
0543: String resultSetMapping = nativeQuery.getResultSetMapping();
0544:
0545: if (!((resultSetMapping == null) || "".equals(resultSetMapping)))
0546: return createNativeQuery(sql, resultSetMapping);
0547:
0548: String resultClass = nativeQuery.getResultClass();
0549:
0550: AmberEntityHome entityHome = _persistenceUnit
0551: .getEntityHome(resultClass);
0552:
0553: EntityType entityType = entityHome.getEntityType();
0554:
0555: try {
0556: return createNativeQuery(sql, entityType.getInstanceClass());
0557: } catch (Exception e) {
0558: throw new IllegalArgumentException(e);
0559: }
0560: }
0561:
0562: /**
0563: * Creates an instance of the named query
0564: */
0565: public Query createNativeQuery(String sql) {
0566: sql = sql.trim();
0567:
0568: char ch = sql.charAt(0);
0569:
0570: if (ch == 'S' || ch == 's')
0571: throw new UnsupportedOperationException(
0572: L
0573: .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."));
0574:
0575: return createInternalNativeQuery(sql);
0576: }
0577:
0578: /**
0579: * Creates an instance of the named query
0580: */
0581: public Query createNativeQuery(String sql, String map) {
0582: // jpa/0y1-
0583:
0584: SqlResultSetMappingConfig resultSet;
0585:
0586: resultSet = _persistenceUnit.getSqlResultSetMapping(map);
0587:
0588: if (resultSet == null)
0589: throw new IllegalArgumentException(
0590: L
0591: .l(
0592: "createNativeQuery() cannot create a native query for a result set named '{0}'",
0593: map));
0594:
0595: return createInternalNativeQuery(sql, resultSet);
0596: }
0597:
0598: /**
0599: * Creates an instance of the native query
0600: */
0601: public Query createNativeQuery(String sql, Class type) {
0602: SqlResultSetMappingConfig resultSet = new SqlResultSetMappingConfig();
0603:
0604: EntityResultConfig entityResult = new EntityResultConfig();
0605:
0606: entityResult.setEntityClass(type.getName());
0607:
0608: resultSet.addEntityResult(entityResult);
0609:
0610: return createInternalNativeQuery(sql, resultSet);
0611: }
0612:
0613: /**
0614: * Refresh the state of the instance from the database.
0615: */
0616: public void refresh(Object entity) {
0617: try {
0618: if (entity == null)
0619: return;
0620:
0621: if (!(entity instanceof Entity))
0622: throw new IllegalArgumentException(
0623: L
0624: .l(
0625: "refresh() operation can only be applied to an entity instance. This object is of class '{0}'",
0626: entity.getClass().getName()));
0627:
0628: checkTransactionRequired("refresh");
0629:
0630: Entity instance = (Entity) entity;
0631:
0632: String className = instance.getClass().getName();
0633: Object pk = instance.__caucho_getPrimaryKey();
0634:
0635: Entity oldEntity = getEntity(className, pk);
0636:
0637: if (oldEntity != null) {
0638: EntityState state = instance.__caucho_getEntityState();
0639:
0640: if (state.ordinal() <= EntityState.TRANSIENT.ordinal()
0641: || EntityState.P_DELETING.ordinal() <= state
0642: .ordinal()) {
0643: throw new IllegalArgumentException(
0644: L
0645: .l(
0646: "refresh() operation can only be applied to a managed entity instance. The entity state is '{0}' for object of class '{0}' with PK '{1}'",
0647: className,
0648: pk,
0649: state == EntityState.TRANSIENT ? "TRANSIENT"
0650: : "DELETING or DELETED"));
0651: }
0652: } else
0653: throw new IllegalArgumentException(
0654: L
0655: .l(
0656: "refresh() operation can only be applied to a managed entity instance. There was no managed instance of class '{0}' with PK '{1}'",
0657: className, pk));
0658:
0659: // Reset and refresh state.
0660: instance.__caucho_expire();
0661: instance.__caucho_makePersistent(this , (EntityType) null);
0662: instance.__caucho_retrieve_eager(this );
0663: } catch (SQLException e) {
0664: throw new AmberRuntimeException(e);
0665: }
0666: }
0667:
0668: /**
0669: * Returns the flush mode.
0670: */
0671: public FlushModeType getFlushMode() {
0672: return FlushModeType.AUTO;
0673: }
0674:
0675: /**
0676: * Sets the extended type.
0677: */
0678: public void setExtended(boolean isExtended) {
0679: _isExtended = isExtended;
0680: }
0681:
0682: /**
0683: * Returns the flush mode.
0684: */
0685: public void setFlushMode(FlushModeType mode) {
0686: throw new UnsupportedOperationException();
0687: }
0688:
0689: /**
0690: * Locks the object.
0691: */
0692: public void lock(Object entity, LockModeType lockMode) {
0693: throw new UnsupportedOperationException();
0694: }
0695:
0696: /**
0697: * Returns the transaction.
0698: */
0699: public EntityTransaction getTransaction() {
0700: if (_isXA)
0701: throw new IllegalStateException(
0702: L
0703: .l("Cannot call EntityManager.getTransaction() inside a distributed transaction."));
0704:
0705: if (_trans == null)
0706: _trans = new EntityTransactionImpl();
0707:
0708: return _trans;
0709: }
0710:
0711: /**
0712: * Returns true if open.
0713: */
0714: public boolean isOpen() {
0715: return _persistenceUnit != null;
0716: }
0717:
0718: /**
0719: * Registers with the local transaction.
0720: */
0721: void register() {
0722: if (!_isRegistered) {
0723: if (!_isAppManaged)
0724: UserTransactionProxy.getInstance().enlistCloseResource(
0725: this );
0726:
0727: UserTransactionProxy.getInstance()
0728: .enlistBeginResource(this );
0729: }
0730:
0731: _isRegistered = true;
0732: }
0733:
0734: /**
0735: * Joins the transaction.
0736: */
0737: public void joinTransaction() {
0738: // XXX: jpa/0s46, jpa/0s47
0739:
0740: _isInTransaction = true;
0741: }
0742:
0743: /**
0744: * Gets the delegate.
0745: */
0746: public Object getDelegate() {
0747: throw new UnsupportedOperationException();
0748: }
0749:
0750: /**
0751: * Closes the context.
0752: */
0753: public void close() {
0754: if (_persistenceUnit == null) {
0755: // jpa/0s45
0756: throw new IllegalStateException(
0757: "Entity manager is already closed.");
0758: }
0759:
0760: try {
0761: if (_isThreadConnection)
0762: _persistenceUnit.removeThreadConnection();
0763:
0764: _isRegistered = false;
0765:
0766: cleanup();
0767: } catch (Exception e) {
0768: log.log(Level.FINER, e.toString(), e);
0769: } finally {
0770: _persistenceUnit = null;
0771: }
0772: }
0773:
0774: /**
0775: * Returns the amber manager.
0776: */
0777: public AmberPersistenceUnit getAmberManager() {
0778: return _persistenceUnit;
0779: }
0780:
0781: /**
0782: * Registers a collection.
0783: */
0784: public void register(AmberCollection query) {
0785: _queries.add(query);
0786: }
0787:
0788: /**
0789: * Adds a completion
0790: */
0791: public void addCompletion(AmberCompletion completion) {
0792: if (!_completionList.contains(completion))
0793: _completionList.add(completion);
0794: }
0795:
0796: /**
0797: * Returns true if a transaction is active or
0798: * this persistence context is extended.
0799: */
0800: public boolean isActiveTransaction() {
0801: return _isInTransaction || _isExtended;
0802: }
0803:
0804: /**
0805: * Returns true if a transaction is active.
0806: */
0807: public boolean isInTransaction() {
0808: return _isInTransaction;
0809: }
0810:
0811: /**
0812: * Returns the cache chunk size.
0813: */
0814: public int getCacheChunkSize() {
0815: return 25;
0816: }
0817:
0818: public Object load(Class cl, Object key, boolean isEager)
0819: throws AmberException {
0820: if (_persistenceUnit == null)
0821: throw new IllegalStateException(L
0822: .l("AmberConnection is closed"));
0823:
0824: if (log.isLoggable(Level.FINER))
0825: log.log(Level.FINER, L.l("loading entity class "
0826: + cl.getName() + " PK: " + key));
0827:
0828: Entity entity = null;
0829:
0830: if (key == null)
0831: return null;
0832:
0833: // ejb/0d01, jpa/0gh0, jpa/0g0k, jpa/0j5f
0834: // if (shouldRetrieveFromCache())
0835: entity = getEntity(cl.getName(), key);
0836:
0837: if (entity != null) {
0838: // jpa/0s2d: if it contains such entity and we have
0839: // PersistenceContextType.TRANSACTION, the entity is
0840: // managed and we can just return it (otherwise it would
0841: // be detached and not be found in _entities).
0842: // XXX: for PersistenceContextType.EXTENDED???
0843:
0844: return entity;
0845: }
0846:
0847: _entityKey.init(cl, key);
0848:
0849: boolean isXALoaded = false;
0850:
0851: EntityItem cacheItem = loadCacheItem(cl, key, null);
0852:
0853: if (cacheItem == null)
0854: return null;
0855:
0856: AmberEntityHome entityHome = cacheItem.getEntityHome();
0857:
0858: /*
0859: boolean isLoad = true;
0860:
0861: // jpa/0h13 as a negative test.
0862: if (isActiveTransaction())
0863: isLoad = isEager;
0864: */
0865: // jpa/0o03
0866: boolean isLoad = isEager;
0867:
0868: try {
0869: entity = cacheItem.createEntity(this , key);
0870:
0871: if (entity == null)
0872: return null;
0873:
0874: // The entity is added for eager loading
0875: addInternalEntity(entity);
0876:
0877: boolean isXA = isActiveTransaction();
0878:
0879: // jpa/0l48: inheritance loading optimization.
0880: // jpa/0h20: no transaction, copy from the existing cache item.
0881: // jpa/0l42: loading optimization.
0882:
0883: if (isLoad) {
0884: entity.__caucho_retrieve_eager(this );
0885: } else if (isXA) {
0886: // jpa/0v33: within a transaction, cannot copy from cache.
0887: entity.__caucho_retrieve_self(this );
0888: }
0889: } catch (SQLException e) {
0890: if (_persistenceUnit.isJPA()) {
0891: log.log(Level.FINER, e.toString(), e);
0892:
0893: return null;
0894: }
0895:
0896: throw new AmberObjectNotFoundException(L.l(
0897: "{0}[{1}] is an unknown amber object",
0898: cl.getName(), key), e);
0899: } catch (AmberObjectNotFoundException e) {
0900: // 0g0q: if the entity is not found, removes it from context.
0901: if (entity != null)
0902: removeEntity(entity);
0903:
0904: if (_persistenceUnit.isJPA())
0905: return null;
0906:
0907: throw e;
0908: }
0909:
0910: Entity txEntity = getTransactionEntity(entity.getClass()
0911: .getName(), entity.__caucho_getPrimaryKey());
0912:
0913: // XXX: jpa/0v33
0914: if (txEntity != null)
0915: setTransactionalState(txEntity);
0916:
0917: return entity;
0918: }
0919:
0920: public EntityItem loadCacheItem(Class cl, Object key,
0921: AmberEntityHome entityHome) throws AmberException {
0922: _entityKey.init(cl, key);
0923:
0924: EntityItem cacheItem = _persistenceUnit.getEntity(_entityKey);
0925:
0926: if (cacheItem != null)
0927: return cacheItem;
0928:
0929: if (entityHome == null)
0930: entityHome = _persistenceUnit.getEntityHome(cl.getName());
0931:
0932: if (entityHome == null) {
0933: throw new IllegalArgumentException(
0934: L
0935: .l(
0936: "'{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.",
0937: cl.getName(), _persistenceUnit
0938: .getName()));
0939: }
0940:
0941: cacheItem = entityHome.findEntityItem(this , key);
0942:
0943: if (cacheItem == null) {
0944: if (_persistenceUnit.isJPA())
0945: return null;
0946:
0947: // ejb/0604
0948: throw new AmberObjectNotFoundException(
0949: "amber find: no matching object " + cl.getName()
0950: + "[" + key + "]");
0951: }
0952:
0953: if (!isActiveTransaction())
0954: cacheItem = _persistenceUnit.putEntity(cl, key, cacheItem);
0955:
0956: return cacheItem;
0957: }
0958:
0959: /**
0960: * Loads the object based on the class and primary key.
0961: */
0962: public Object load(String entityName, Object key)
0963: throws AmberException {
0964: AmberEntityHome entityHome = _persistenceUnit
0965: .getEntityHome(entityName);
0966:
0967: if (entityHome == null)
0968: return null;
0969:
0970: Entity entity = null;
0971:
0972: // XXX: ejb/0d01
0973: // jpa/0y14 if (shouldRetrieveFromCache())
0974: entity = getEntity(entityName, key);
0975:
0976: if (entity != null)
0977: return entity;
0978:
0979: try {
0980: entityHome.init();
0981: } catch (ConfigException e) {
0982: throw new AmberException(e);
0983: }
0984:
0985: entity = (Entity) find(entityHome.getEntityType()
0986: .getInstanceClass(), key);
0987:
0988: // addEntity(entity);
0989:
0990: return entity;
0991: }
0992:
0993: /**
0994: * Returns the entity for the connection.
0995: */
0996: public Entity getEntity(EntityItem item) {
0997: Entity itemEntity = item.getEntity();
0998:
0999: Class cl = itemEntity.getClass();
1000: Object pk = itemEntity.__caucho_getPrimaryKey();
1001:
1002: Entity entity = getEntity(cl.getName(), pk);
1003:
1004: if (entity != null) {
1005: if (entity.__caucho_getEntityState().isManaged())
1006: return entity;
1007: // else
1008: // jpa/0g40: the copy object was created at some point in
1009: // findEntityItem, but it is still not loaded.
1010: } else {
1011: try {
1012: entity = item.createEntity(this , pk);
1013: } catch (SQLException e) {
1014: throw new AmberRuntimeException(e);
1015: }
1016:
1017: /*
1018: // Create a new entity for the given class and primary key.
1019: try {
1020: entity = (Entity) cl.newInstance();
1021: } catch (Exception e) {
1022: throw new AmberRuntimeException(e);
1023: }
1024: */
1025:
1026: // entity.__caucho_setEntityState(EntityState.P_NON_TRANSACTIONAL);
1027: // entity.__caucho_setPrimaryKey(pk);
1028: // jpa/1000: avoids extra allocations.
1029: addInternalEntity(entity);
1030: }
1031:
1032: // jpa/0l43
1033: //_persistenceUnit.copyFromCacheItem(this, entity, item);
1034: // jpa/0l4a
1035: entity.__caucho_retrieve_eager(this );
1036:
1037: return entity;
1038: }
1039:
1040: /**
1041: * Returns the entity for the connection.
1042: */
1043: public Entity getEntityLazy(EntityItem item) {
1044: Entity itemEntity = item.getEntity();
1045:
1046: Class cl = itemEntity.getClass();
1047: Object pk = itemEntity.__caucho_getPrimaryKey();
1048:
1049: Entity entity = getEntity(cl.getName(), pk);
1050:
1051: if (entity != null) {
1052: if (entity.__caucho_getEntityState().isManaged())
1053: return entity;
1054: // else
1055: // jpa/0g40: the copy object was created at some point in
1056: // findEntityItem, but it is still not loaded.
1057: } else {
1058: try {
1059: entity = item.createEntity(this , pk);
1060: } catch (SQLException e) {
1061: throw new AmberRuntimeException(e);
1062: }
1063:
1064: /*
1065: // Create a new entity for the given class and primary key.
1066: try {
1067: entity = (Entity) cl.newInstance();
1068: } catch (Exception e) {
1069: throw new AmberRuntimeException(e);
1070: }
1071: */
1072:
1073: // entity.__caucho_setEntityState(EntityState.P_NON_TRANSACTIONAL);
1074: // entity.__caucho_setPrimaryKey(pk);
1075: // jpa/1000: avoids extra allocations.
1076: addInternalEntity(entity);
1077: }
1078:
1079: return entity;
1080: }
1081:
1082: /**
1083: * Loads the object based on itself.
1084: */
1085: public Object makePersistent(Object obj) throws SQLException {
1086: Entity entity = (Entity) obj;
1087:
1088: // check to see if exists
1089:
1090: if (entity == null)
1091: throw new NullPointerException();
1092:
1093: Class cl = entity.getClass();
1094:
1095: // Entity oldEntity = getEntity(cl, entity.__caucho_getPrimaryKey());
1096:
1097: AmberEntityHome entityHome;
1098: entityHome = _persistenceUnit.getEntityHome(entity.getClass()
1099: .getName());
1100:
1101: if (entityHome == null)
1102: throw new AmberException(L.l("entity has no matching home"));
1103:
1104: entityHome.makePersistent(entity, this , false);
1105:
1106: return entity;
1107: }
1108:
1109: /**
1110: * Loads the object with the given class.
1111: */
1112: public Entity loadLazy(Class cl, String name, Object key) {
1113: return loadLazy(cl.getName(), name, key);
1114: }
1115:
1116: /**
1117: * Loads the object with the given class.
1118: */
1119: public Entity loadLazy(String className, String name, Object key) {
1120: if (key == null)
1121: return null;
1122:
1123: try {
1124: AmberEntityHome home = _persistenceUnit.getEntityHome(name);
1125:
1126: if (home == null)
1127: throw new RuntimeException(L.l(
1128: "no matching home for {0}", className));
1129:
1130: home.init();
1131:
1132: Object obj = load(home.getEntityType().getInstanceClass(),
1133: key, false);
1134:
1135: Entity entity = (Entity) obj;
1136:
1137: return entity;
1138: } catch (SQLException e) {
1139: log.log(Level.WARNING, e.toString(), e);
1140:
1141: return null;
1142: } catch (ConfigException e) {
1143: throw new AmberRuntimeException(e);
1144: }
1145: }
1146:
1147: /**
1148: * Loads the object with the given class.
1149: */
1150: public EntityItem findEntityItem(String name, Object key) {
1151: try {
1152: AmberEntityHome home = _persistenceUnit.getEntityHome(name);
1153:
1154: if (home == null)
1155: throw new RuntimeException(L.l(
1156: "no matching home for {0}", name));
1157:
1158: home.init();
1159:
1160: return loadCacheItem(home.getJavaClass(), key, home);
1161: } catch (RuntimeException e) {
1162: throw e;
1163: } catch (Exception e) {
1164: throw new AmberRuntimeException(e);
1165: }
1166: }
1167:
1168: /**
1169: * Loads the object with the given class.
1170: */
1171: public EntityItem setEntityItem(String name, Object key,
1172: EntityItem item) {
1173: try {
1174: AmberEntityHome home = _persistenceUnit.getEntityHome(name);
1175:
1176: if (home == null)
1177: throw new RuntimeException(L.l(
1178: "no matching home for {0}", name));
1179:
1180: home.init();
1181:
1182: return home.setEntityItem(key, item);
1183: } catch (RuntimeException e) {
1184: throw e;
1185: } catch (Exception e) {
1186: throw new AmberRuntimeException(e);
1187: }
1188: }
1189:
1190: /**
1191: * Loads the object with the given class.
1192: *
1193: * @param name the class name.
1194: * @param key the key.
1195: * @param notExpiringLoadMask the load mask bit that will not be reset
1196: * when the entity is expiring and reloaded to a new transaction.
1197: * Normally, the bit is only set in bidirectional one-to-one
1198: * relationships where we already know the other side has already
1199: * been loaded in the second or new transactions.
1200: * @param notExpiringGroup the corresponding load group.
1201: */
1202: public Entity loadFromHome(String name, Object key) {
1203: try {
1204: AmberEntityHome home = _persistenceUnit.getEntityHome(name);
1205:
1206: if (home == null)
1207: throw new RuntimeException(L.l(
1208: "no matching home for {0}", name));
1209:
1210: home.init();
1211:
1212: // jpa/0ge4, jpa/0o04, jpa/0o0b, jpa/0o0c: bidirectional optimization.
1213: return (Entity) load(home.getEntityType()
1214: .getInstanceClass(), key, true);
1215: } catch (AmberObjectNotFoundException e) {
1216: if (_persistenceUnit.isJPA()) {
1217: if (log.isLoggable(Level.FINER))
1218: log.log(Level.FINER, e.toString(), e);
1219:
1220: // jpa/0h29
1221: return null;
1222: }
1223:
1224: throw e;
1225: } catch (RuntimeException e) {
1226: throw e;
1227: } catch (Exception e) {
1228: throw new AmberRuntimeException(e);
1229: }
1230: }
1231:
1232: /**
1233: * Loads the object with the given class.
1234: */
1235: public Object loadProxy(String name, Object key) {
1236: if (key == null)
1237: return null;
1238:
1239: AmberEntityHome home = _persistenceUnit.getEntityHome(name);
1240:
1241: if (home == null)
1242: throw new RuntimeException(L.l("no matching home for {0}",
1243: name));
1244:
1245: return loadProxy(home.getEntityType(), key);
1246: }
1247:
1248: /**
1249: * Loads the object with the given class.
1250: */
1251: public Object loadProxy(EntityType type, Object key) {
1252: if (key == null)
1253: return null;
1254:
1255: Entity entity = getEntity(type.getInstanceClass().getName(),
1256: key);
1257:
1258: if (entity != null) {
1259: // jpa/0m30
1260: return entity;
1261: }
1262:
1263: try {
1264: AmberEntityHome home = type.getHome();
1265:
1266: EntityItem item = home.findEntityItem(this , key);
1267:
1268: if (item == null)
1269: return null;
1270:
1271: EntityFactory factory = home.getEntityFactory();
1272:
1273: Object newEntity = factory.getEntity(this , item);
1274:
1275: if (_persistenceUnit.isJPA()) {
1276: // jpa/0h29: eager loading.
1277: Entity instance = (Entity) newEntity;
1278: setTransactionalState(instance);
1279: }
1280:
1281: return newEntity;
1282: } catch (SQLException e) {
1283: log.log(Level.WARNING, e.toString(), e);
1284:
1285: return null;
1286: }
1287: }
1288:
1289: /**
1290: * Loads the CMP 2.1 object for the given entityItem
1291: */
1292: public Object loadProxy(EntityItem entityItem) {
1293: Entity itemEntity = entityItem.getEntity();
1294:
1295: /*
1296: Class cl = itemEntity.getClass();
1297: Object pk = itemEntity.__caucho_getPrimaryKey();
1298:
1299: Entity entity = getEntity(cl.getName(), pk);
1300:
1301: if (entity != null) {
1302: // jpa/0m30
1303: return entity;
1304: }
1305: */
1306:
1307: AmberEntityHome home = entityItem.getEntityHome();
1308:
1309: EntityFactory factory = home.getEntityFactory();
1310:
1311: Object newEntity = factory.getEntity(this , entityItem);
1312:
1313: return newEntity;
1314: }
1315:
1316: /**
1317: * Loads the object based on the class and primary key.
1318: */
1319: public Object load(Class cl, long intKey) throws AmberException {
1320: AmberEntityHome entityHome = _persistenceUnit.getEntityHome(cl
1321: .getName());
1322:
1323: if (entityHome == null)
1324: return null;
1325:
1326: Object key = entityHome.toObjectKey(intKey);
1327:
1328: return load(cl, key, true);
1329: }
1330:
1331: /**
1332: * Loads the object based on the class and primary key.
1333: */
1334: public Object loadLazy(Class cl, long intKey) throws AmberException {
1335: AmberEntityHome entityHome = _persistenceUnit.getEntityHome(cl
1336: .getName());
1337:
1338: if (entityHome == null)
1339: return null;
1340:
1341: Object key = entityHome.toObjectKey(intKey);
1342:
1343: return loadLazy(cl, cl.getName(), key);
1344: }
1345:
1346: /**
1347: * Matches the entity.
1348: */
1349: public Entity getEntity(String className, Object key) {
1350: Entity[] entities = _entities;
1351:
1352: for (int i = _entitiesTop - 1; i >= 0; i--) {
1353: Entity entity = entities[i];
1354:
1355: if (entity.__caucho_match(className, key)) {
1356: return entity;
1357: }
1358: }
1359:
1360: return null;
1361: }
1362:
1363: public Entity getEntity(int index) {
1364: return _entities[index];
1365: }
1366:
1367: public Entity getEntity(Class cl, Object key) {
1368: return getEntity(cl.getName(), key);
1369: }
1370:
1371: /**
1372: * Returns the context entity that corresponds to the
1373: * entity passed in. The entity passed in is normally a
1374: * cache entity but might be the context entity itself
1375: * when we want to make sure the reference is to an
1376: * entity in the persistence context.
1377: */
1378: public Entity getEntity(Entity entity) {
1379: if (entity == null)
1380: return null;
1381:
1382: return getEntity(entity.getClass().getName(), entity
1383: .__caucho_getPrimaryKey());
1384: }
1385:
1386: public Entity getSubEntity(Class cl, Object key) {
1387: Entity[] entities = _entities;
1388:
1389: // jpa/0l43
1390: for (int i = _entitiesTop - 1; i >= 0; i--) {
1391: Entity entity = entities[i];
1392:
1393: if (entity.__caucho_getPrimaryKey().equals(key)) {
1394: if (cl.isAssignableFrom(entity.getClass()))
1395: return entity;
1396: }
1397: }
1398:
1399: return null;
1400: }
1401:
1402: /**
1403: * Gets the cache item referenced by __caucho_item
1404: * from an entity of class/subclass cl.
1405: */
1406: public EntityItem getSubEntityCacheItem(Class cl, Object key) {
1407: Entity[] entities = _entities;
1408:
1409: // jpa/0l4a
1410: for (int i = _entitiesTop - 1; i >= 0; i--) {
1411: Entity entity = _entities[i];
1412:
1413: if (entity.__caucho_getPrimaryKey().equals(key)) {
1414: if (cl.isAssignableFrom(entity.getClass()))
1415: return entity.__caucho_getCacheItem();
1416: }
1417: }
1418:
1419: return null;
1420: }
1421:
1422: public Entity getTransactionEntity(String className, Object key) {
1423: Entity[] entities = _txEntities;
1424:
1425: for (int i = _txEntitiesTop - 1; i >= 0; i--) {
1426: Entity entity = entities[i];
1427:
1428: if (entity.__caucho_match(className, key)) {
1429: return entity;
1430: }
1431: }
1432:
1433: return null;
1434: }
1435:
1436: public Entity getTransactionEntity(int index) {
1437: return _txEntities[index];
1438: }
1439:
1440: /**
1441: * Adds an entity.
1442: */
1443: /*
1444: private boolean addEntity(Entity entity)
1445: {
1446: boolean added = false;
1447:
1448: Object pk = entity.__caucho_getPrimaryKey();
1449: String className = entity.getClass().getName();
1450:
1451: Entity oldEntity = getEntity(className, pk);
1452:
1453: // jpa/0s2d: if (! _entities.contains(entity)) {
1454: if (oldEntity == null) {
1455: addInternalEntity(entity);
1456: added = true;
1457: }
1458: else if (isActiveTransaction()) { // jpa/0g06
1459: int index = getTransactionEntity(className, pk);
1460:
1461: // jpa/0s2d: if (! _txEntities.contains(entity)) {
1462: if (index < 0) {
1463: addTxEntity(entity);
1464: added = true;
1465: }
1466: }
1467:
1468: // jpa/0s2d: merge()
1469: setTransactionalState(entity);
1470:
1471: return added;
1472: }
1473: */
1474:
1475: /**
1476: * Adds a new entity for the given class name and key.
1477: * The new entity object is supposed to be used as a
1478: * copy from cache. This avoids the cache entity to
1479: * be added to the context.
1480: *
1481: * @return null - if the entity is already in the context.
1482: * otherwise, it returns the new entity added to
1483: * the context.
1484: */
1485: public Entity addNewEntity(Class cl, Object key)
1486: throws InstantiationException, IllegalAccessException {
1487: // jpa/0l43
1488: Entity entity = getSubEntity(cl, key);
1489:
1490: // If the entity is already in the context, it returns null.
1491: if (entity != null)
1492: return null;
1493:
1494: if (_persistenceUnit.isJPA()) {
1495: // XXX: needs to create based on the discriminator with inheritance.
1496: // Create a new entity for the given class and primary key.
1497: entity = (Entity) cl.newInstance();
1498:
1499: // jpa/0s2d
1500: entity
1501: .__caucho_setEntityState(EntityState.P_NON_TRANSACTIONAL);
1502: } else {
1503: // HelperBean__Amber -> HelperBean
1504: String className = cl.getSuperclass().getName();
1505:
1506: AmberEntityHome entityHome = _persistenceUnit
1507: .getEntityHome(className);
1508:
1509: if (entityHome == null) {
1510: if (log.isLoggable(Level.FINER))
1511: log
1512: .log(
1513: Level.FINER,
1514: L
1515: .l(
1516: "Amber.addNewEntity: home not found for entity (class: '{0}' PK: '{1}')",
1517: className, key));
1518: return null;
1519: }
1520:
1521: EntityFactory factory = entityHome.getEntityFactory();
1522:
1523: // TestBean__EJB
1524: Object value = factory.getEntity(key);
1525:
1526: Method cauchoGetBeanMethod = entityHome
1527: .getCauchoGetBeanMethod();
1528: if (cauchoGetBeanMethod != null) {
1529: try {
1530: // Bean
1531: entity = (Entity) cauchoGetBeanMethod.invoke(value,
1532: new Object[0]);
1533: // entity.__caucho_makePersistent(aConn, item);
1534: } catch (Exception e) {
1535: log.log(Level.FINER, e.toString(), e);
1536: }
1537: }
1538:
1539: if (entity == null) {
1540: throw new IllegalStateException(
1541: L
1542: .l("AmberConnection.addNewEntity unable to instantiate new entity with cauchoGetBeanMethod"));
1543: }
1544: }
1545:
1546: entity.__caucho_setPrimaryKey(key);
1547:
1548: addInternalEntity(entity);
1549:
1550: return entity;
1551: }
1552:
1553: /**
1554: * Adds a new entity for the given class name and key.
1555: */
1556: public Entity loadEntity(Class cl, Object key, boolean isEager) {
1557: if (key == null)
1558: return null;
1559:
1560: // jpa/0l43
1561: Entity entity = getSubEntity(cl, key);
1562:
1563: // If the entity is already in the context, return it
1564: if (entity != null)
1565: return entity;
1566:
1567: if (_persistenceUnit.isJPA()) {
1568: // XXX: needs to create based on the discriminator with inheritance.
1569: // Create a new entity for the given class and primary key.
1570: try {
1571: entity = (Entity) load(cl, key, isEager);
1572: } catch (AmberException e) {
1573: throw new AmberRuntimeException(e);
1574: }
1575: } else {
1576: // HelperBean__Amber -> HelperBean
1577: String className = cl.getSuperclass().getName();
1578:
1579: AmberEntityHome entityHome = _persistenceUnit
1580: .getEntityHome(className);
1581:
1582: if (entityHome == null) {
1583: if (log.isLoggable(Level.FINER))
1584: log
1585: .log(
1586: Level.FINER,
1587: L
1588: .l(
1589: "Amber.addNewEntity: home not found for entity (class: '{0}' PK: '{1}')",
1590: className, key));
1591: return null;
1592: }
1593:
1594: EntityFactory factory = entityHome.getEntityFactory();
1595:
1596: // TestBean__EJB
1597: Object value = factory.getEntity(key);
1598:
1599: Method cauchoGetBeanMethod = entityHome
1600: .getCauchoGetBeanMethod();
1601: if (cauchoGetBeanMethod != null) {
1602: try {
1603: // Bean
1604: entity = (Entity) cauchoGetBeanMethod.invoke(value,
1605: new Object[0]);
1606: // entity.__caucho_makePersistent(aConn, item);
1607: } catch (Exception e) {
1608: log.log(Level.FINER, e.toString(), e);
1609: }
1610: }
1611:
1612: if (entity == null) {
1613: throw new IllegalStateException(
1614: L
1615: .l("AmberConnection.addNewEntity unable to instantiate new entity with cauchoGetBeanMethod"));
1616: }
1617:
1618: entity.__caucho_setPrimaryKey(key);
1619:
1620: addInternalEntity(entity);
1621: }
1622:
1623: return entity;
1624: }
1625:
1626: /**
1627: * Removes an entity.
1628: */
1629: public boolean removeEntity(Entity entity) {
1630: removeEntityImpl(entity);
1631:
1632: if (isActiveTransaction())
1633: removeTxEntity(entity);
1634:
1635: return true;
1636: }
1637:
1638: /**
1639: * Loads the object based on itself.
1640: */
1641: public boolean contains(Object obj) {
1642: if (obj == null)
1643: return false;
1644:
1645: if (!(obj instanceof Entity))
1646: throw new IllegalArgumentException(
1647: L
1648: .l("contains() operation can only be applied to an entity instance."));
1649:
1650: Entity entity = (Entity) obj;
1651:
1652: // jpa/11a8
1653: if (entity.__caucho_getConnection() != this )
1654: return false;
1655:
1656: EntityState state = entity.__caucho_getEntityState();
1657: if (isInTransaction() && !state.isTransactional()) {
1658: // jpa/11a6, jpa/1800
1659: return false;
1660: }
1661:
1662: // jpa/0j5f
1663: if (EntityState.P_DELETING.ordinal() <= state.ordinal())
1664: return false;
1665:
1666: return true;
1667: }
1668:
1669: /**
1670: * Callback when the user transaction begins
1671: */
1672: public void begin(Transaction xa) {
1673: try {
1674: xa.registerSynchronization(this );
1675:
1676: _isInTransaction = true;
1677: _isXA = true;
1678: } catch (Exception e) {
1679: log.log(Level.WARNING, e.toString(), e);
1680: }
1681: }
1682:
1683: /**
1684: * Starts a transaction.
1685: */
1686: public void beginTransaction() throws SQLException {
1687: _isInTransaction = true;
1688:
1689: if (_conn != null && _isAutoCommit) {
1690: _isAutoCommit = false;
1691: _conn.setAutoCommit(false);
1692: }
1693:
1694: // _xid = _factory.getXid();
1695: }
1696:
1697: /**
1698: * Sets XA.
1699: */
1700: public void setXA(boolean isXA) {
1701: _isXA = isXA;
1702: _isInTransaction = isXA;
1703:
1704: if (isXA && !_isRegistered)
1705: register();
1706: }
1707:
1708: /**
1709: * Commits a transaction.
1710: */
1711: public void commit() throws SQLException {
1712: if (log.isLoggable(Level.FINER))
1713: log.log(Level.FINER, "AmberConnection.commit");
1714:
1715: try {
1716: flushInternal();
1717:
1718: _xid = 0;
1719: if (_conn != null) {
1720: _conn.commit();
1721: }
1722: } catch (RuntimeException e) {
1723: throw e;
1724: } catch (SQLException e) {
1725: throw new IllegalStateException(e);
1726: } catch (Exception e) {
1727: throw new EJBExceptionWrapper(e);
1728: } finally {
1729: if (!_isXA)
1730: _isInTransaction = false;
1731:
1732: for (int i = 0; i < _txEntitiesTop; i++) {
1733: Entity entity = _txEntities[i];
1734:
1735: entity.__caucho_afterCommit();
1736: }
1737:
1738: if (log.isLoggable(Level.FINER))
1739: log.log(Level.FINER, "cleaning up txEntities");
1740:
1741: _txEntitiesTop = 0;
1742: }
1743: }
1744:
1745: /**
1746: * Callback before a utrans commit.
1747: */
1748: public void beforeCompletion() {
1749: if (log.isLoggable(Level.FINER))
1750: log.log(Level.FINER, "AmberConnection.beforeCompletion");
1751:
1752: try {
1753: beforeCommit();
1754: // XXX: need to figure out how to throw JPA exceptions at commit() time.
1755: // } catch (SQLException e) {
1756: // throw e;
1757: } catch (RuntimeException e) {
1758: // jpa/0ga5
1759: throw e;
1760: } catch (Exception e) {
1761: log.log(Level.WARNING, e.toString(), e);
1762: }
1763: }
1764:
1765: /**
1766: * Callback after a utrans commit.
1767: */
1768: public void afterCompletion(int status) {
1769: if (log.isLoggable(Level.FINER))
1770: log.log(Level.FINER, "AmberConnection.afterCompletion");
1771:
1772: afterCommit(status == Status.STATUS_COMMITTED);
1773: _isXA = false;
1774: _isInTransaction = false;
1775: _isRegistered = false; // ejb/0d19
1776: }
1777:
1778: /**
1779: * Called before the commit phase
1780: */
1781: public void beforeCommit() throws SQLException {
1782: if (log.isLoggable(Level.FINER))
1783: log.log(Level.FINER, "AmberConnection.beforeCommit");
1784:
1785: try {
1786: flushInternal();
1787: } catch (SQLException e) {
1788: throw e;
1789: } catch (RuntimeException e) {
1790: throw e;
1791: } catch (Exception e) {
1792: throw new RuntimeException(e);
1793: }
1794:
1795: /*
1796: // jpa/0gh0
1797: for (int i = _txEntities.size() - 1; i >= 0; i--) {
1798: Entity entity = _txEntities.get(i);
1799:
1800: // jpa/1500
1801: if (entity.__caucho_getEntityState() == EntityState.P_DELETED) {
1802: EntityType entityType = entity.__caucho_getEntityType();
1803: Object key = entity.__caucho_getPrimaryKey();
1804: EntityItem item = _persistenceUnit.getEntity(entityType, key);
1805:
1806: if (item == null) {
1807: // jpa/0ga8: entity has been removed and DELETE SQL was already flushed.
1808: continue;
1809: }
1810: }
1811:
1812: entity.__caucho_flush();
1813: }
1814: */
1815: }
1816:
1817: /**
1818: * Commits a transaction.
1819: */
1820: public void afterCommit(boolean isCommit) {
1821: try {
1822: if (log.isLoggable(Level.FINER))
1823: log.log(Level.FINER, "AmberConnection.afterCommit: "
1824: + isCommit);
1825:
1826: if (!_isXA)
1827: _isInTransaction = false;
1828:
1829: if (isCommit) {
1830: if (_completionList.size() > 0) {
1831: _persistenceUnit.complete(_completionList);
1832: }
1833: }
1834:
1835: // jpa/0k20: clears the completion list in the
1836: // finally block so callbacks do not add a completion
1837: // which has been just removed.
1838: //
1839: // _completionList.clear();
1840:
1841: for (int i = 0; i < _txEntitiesTop; i++) {
1842: Entity entity = _txEntities[i];
1843:
1844: try {
1845: if (isCommit)
1846: entity.__caucho_afterCommit();
1847: else
1848: entity.__caucho_afterRollback();
1849: } catch (Exception e) {
1850: log.log(Level.WARNING, e.toString(), e);
1851: }
1852: }
1853:
1854: if (log.isLoggable(Level.FINER))
1855: log.log(Level.FINER, "cleaning up txEntities");
1856:
1857: _txEntitiesTop = 0;
1858:
1859: // jpa/0s2k
1860: Entity[] entities = _entities;
1861: for (int i = _entitiesTop - 1; i >= 0; i--) {
1862: // XXX: needs to check EXTENDED type.
1863: // jpa/0h07: persistence context TRANSACTION type.
1864: entities[i].__caucho_detach();
1865: }
1866:
1867: // jpa/0h60
1868: _entitiesTop = 0;
1869:
1870: // if (! isCommit) {
1871: // jpa/0j5c
1872:
1873: /* XXX: jpa/0k11 - avoids double rollback()
1874: Rollback is done from com.caucho.transaction.TransactionImpl
1875: to the pool item com.caucho.jca.PoolItem
1876: try {
1877: if (_conn != null)
1878: _conn.rollback();
1879: } catch (SQLException e) {
1880: throw new IllegalStateException(e);
1881: }
1882: */
1883: // }
1884: } finally {
1885: _completionList.clear();
1886: }
1887: }
1888:
1889: /**
1890: * Rollbacks a transaction.
1891: */
1892: public void rollback() throws SQLException {
1893: if (log.isLoggable(Level.FINER))
1894: log.log(Level.FINER, "AmberConnection.rollback");
1895:
1896: try {
1897: flushInternal();
1898:
1899: _xid = 0;
1900: if (_conn != null) {
1901: _conn.rollback();
1902: }
1903: } catch (RuntimeException e) {
1904: throw e;
1905: } catch (SQLException e) {
1906: throw new IllegalStateException(e);
1907: } catch (Exception e) {
1908: throw new EJBExceptionWrapper(e);
1909: } finally {
1910: if (!_isXA)
1911: _isInTransaction = false;
1912:
1913: _completionList.clear();
1914:
1915: for (int i = 0; i < _txEntitiesTop; i++) {
1916: Entity entity = _txEntities[i];
1917:
1918: entity.__caucho_afterRollback();
1919: }
1920:
1921: _txEntitiesTop = 0;
1922: }
1923: }
1924:
1925: /**
1926: * Flushes managed entities.
1927: */
1928: public void flush() {
1929: try {
1930: checkTransactionRequired("flush");
1931:
1932: flushInternal();
1933: } catch (RuntimeException e) {
1934: throw e;
1935: } catch (SQLException e) {
1936: throw new IllegalStateException(e);
1937: } catch (Exception e) {
1938: throw new EJBExceptionWrapper(e);
1939: }
1940: }
1941:
1942: /**
1943: * Flushes managed entities.
1944: */
1945: public void flushNoChecks() {
1946: try {
1947: flushInternal();
1948: } catch (RuntimeException e) {
1949: throw e;
1950: } catch (SQLException e) {
1951: throw new IllegalStateException(e);
1952: } catch (Exception e) {
1953: throw new EJBExceptionWrapper(e);
1954: }
1955: }
1956:
1957: /**
1958: * Expires the entities
1959: */
1960: public void expire() throws SQLException {
1961: Entity[] entities = _entities;
1962: for (int i = _entitiesTop - 1; i >= 0; i--) {
1963: Entity entity = entities[i];
1964:
1965: // jpa/0j5e
1966: if (!entity.__caucho_getEntityState().isPersist())
1967: entity.__caucho_expire();
1968: }
1969: }
1970:
1971: /**
1972: * Returns the connection.
1973: */
1974: public Connection getConnection() throws SQLException {
1975: DataSource readDataSource = _persistenceUnit
1976: .getReadDataSource();
1977:
1978: if (!_isXA && !_isInTransaction && readDataSource != null) {
1979: if (_readConn == null) {
1980: _readConn = readDataSource.getConnection();
1981: } else if (_readConn.isClosed()) {
1982: closeConnectionImpl();
1983: _readConn = _persistenceUnit.getDataSource()
1984: .getConnection();
1985: }
1986:
1987: return _readConn;
1988: }
1989:
1990: if (_conn == null) {
1991: _conn = _persistenceUnit.getDataSource().getConnection();
1992: _isAutoCommit = true;
1993: } else if (_conn.isClosed()) {
1994: closeConnectionImpl();
1995: _conn = _persistenceUnit.getDataSource().getConnection();
1996: _isAutoCommit = true;
1997: }
1998:
1999: if (_isXA) {
2000: } else if (_isInTransaction && _isAutoCommit) {
2001: _isAutoCommit = false;
2002: _conn.setAutoCommit(false);
2003: } else if (!_isInTransaction && !_isAutoCommit) {
2004: _isAutoCommit = true;
2005: _conn.setAutoCommit(true);
2006: }
2007:
2008: return _conn;
2009: }
2010:
2011: /**
2012: * Prepares a statement.
2013: */
2014: public PreparedStatement prepareStatement(String sql)
2015: throws SQLException {
2016: try {
2017: PreparedStatement pstmt = _preparedStatementMap.get(sql);
2018:
2019: if (pstmt == null) {
2020: Connection conn = getConnection();
2021:
2022: // XXX: avoids locking issues.
2023: if (_statements.size() > 0) {
2024: conn = _statements.get(0).getConnection();
2025: }
2026:
2027: // XXX: avoids locking issues.
2028: // See com.caucho.sql.UserConnection
2029: pstmt = conn.prepareStatement(sql,
2030: ResultSet.TYPE_FORWARD_ONLY,
2031: ResultSet.CONCUR_READ_ONLY);
2032:
2033: _statements.add(pstmt);
2034:
2035: _preparedStatementMap.put(sql, pstmt);
2036: }
2037:
2038: return pstmt;
2039: } catch (SQLException e) {
2040: closeConnectionImpl();
2041:
2042: throw e;
2043: }
2044: }
2045:
2046: /**
2047: * Closes a statement.
2048: */
2049: public void closeStatement(String sql) throws SQLException {
2050: PreparedStatement pstmt = _preparedStatementMap.remove(sql);
2051:
2052: if (pstmt != null) {
2053: _statements.remove(pstmt);
2054:
2055: pstmt.close();
2056: }
2057: }
2058:
2059: public static void close(ResultSet rs) {
2060: try {
2061: if (rs != null)
2062: rs.close();
2063: } catch (SQLException e) {
2064: throw new AmberRuntimeException(e);
2065: }
2066: }
2067:
2068: /**
2069: * Prepares an insert statement.
2070: */
2071: public PreparedStatement prepareInsertStatement(String sql)
2072: throws SQLException {
2073: PreparedStatement pstmt = null;
2074:
2075: try {
2076: pstmt = _preparedStatementMap.get(sql);
2077:
2078: if (pstmt == null) {
2079: Connection conn = getConnection();
2080:
2081: // XXX: avoids locking issues.
2082: if (_statements.size() > 0) {
2083: conn = _statements.get(0).getConnection();
2084: }
2085:
2086: if (_persistenceUnit.hasReturnGeneratedKeys())
2087: pstmt = conn.prepareStatement(sql,
2088: Statement.RETURN_GENERATED_KEYS);
2089: else {
2090: // XXX: avoids locking issues.
2091: // See com.caucho.sql.UserConnection
2092: pstmt = conn.prepareStatement(sql,
2093: ResultSet.TYPE_FORWARD_ONLY,
2094: ResultSet.CONCUR_READ_ONLY);
2095: }
2096:
2097: _statements.add(pstmt);
2098:
2099: _preparedStatementMap.put(sql, pstmt);
2100: }
2101: } catch (SQLException e) {
2102: closeStatement(sql);
2103: }
2104:
2105: return pstmt;
2106: }
2107:
2108: /**
2109: * Makes the object transactional.
2110: *
2111: * @param obj the object to save
2112: *
2113: * @return the proxy for the saved object
2114: */
2115: public void makeTransactional(Entity entity) {
2116: // ejb/0600
2117: if (!_persistenceUnit.isJPA())
2118: return;
2119:
2120: /* XXX: jpa/0l43
2121: EntityState state = entity.__caucho_getEntityState();
2122:
2123: if (EntityState.TRANSIENT.ordinal() < state.ordinal()
2124: && state.ordinal() < EntityState.P_DELETING.ordinal()) {
2125: // jpa/0g06
2126: addEntity(entity);
2127: }
2128: */
2129:
2130: /*
2131: if (! isInTransaction())
2132: throw new AmberRuntimeException(L.l("makePersistent must be called from within a transaction."));
2133:
2134: if (! (obj instanceof Entity)) {
2135: throw new AmberRuntimeException(L.l("`{0}' is not a known entity class.",
2136: obj.getClass().getName()));
2137: }
2138: */
2139: }
2140:
2141: /**
2142: * Updates the database with the values in object. If the object does
2143: * not exist, throws an exception.
2144: *
2145: * @param obj the object to update
2146: */
2147: public void update(Object obj) {
2148: /*
2149: for (int i = _entities.size() - 1; i >= 0; i--) {
2150: Entity entity = _entities.get(i);
2151:
2152: if (entity.__caucho_match(obj)) {
2153: entity.__caucho_load(obj);
2154:
2155: return entity;
2156: }
2157: }
2158: */
2159:
2160: /*
2161: Class cl = obj.getClass();
2162:
2163: EntityHome home = _factory.getHome(cl);
2164:
2165: if (home == null)
2166: throw new AmberException(L.l("no matching home for {0}", cl.getName()));
2167:
2168: Object key = home.getKeyFromEntity(obj);
2169:
2170: Entity entity = getEntity(cl, key);
2171:
2172: if (entity == null) {
2173: entity = home.load(this, key);
2174:
2175: addEntity(entity);
2176: }
2177:
2178: entity.__caucho_loadFromObject(obj);
2179:
2180: return entity;
2181: */
2182: }
2183:
2184: /**
2185: * Saves the object.
2186: *
2187: * @param obj the object to create
2188: */
2189: public void create(Object obj) throws SQLException {
2190: // ejb/0g22 exception handling
2191: try {
2192:
2193: createInternal(obj);
2194:
2195: } catch (RuntimeException e) {
2196: throw e;
2197: } catch (Exception e) {
2198: throw new EJBExceptionWrapper(e);
2199: }
2200: }
2201:
2202: /**
2203: * Saves the object.
2204: *
2205: * @param obj the object to create
2206: */
2207: public void create(String homeName, Object obj) throws SQLException {
2208: // ejb/0g22 exception handling
2209: try {
2210:
2211: createInternal(homeName, obj);
2212:
2213: } catch (RuntimeException e) {
2214: throw e;
2215: } catch (Exception e) {
2216: throw new EJBExceptionWrapper(e);
2217: }
2218: }
2219:
2220: /**
2221: * Saves the object.
2222: *
2223: * @param obj the object to create
2224: */
2225: public void create(AmberEntityHome home, Object obj)
2226: throws SQLException {
2227: // ejb/0g22 exception handling
2228: try {
2229:
2230: createInternal(home, obj);
2231:
2232: } catch (RuntimeException e) {
2233: throw e;
2234: } catch (Exception e) {
2235: throw new EJBExceptionWrapper(e);
2236: }
2237: }
2238:
2239: /**
2240: * Updates the object.
2241: */
2242: public void update(Entity entity) {
2243: if (entity == null)
2244: return;
2245:
2246: // jpa/0g0i
2247: if (entity.__caucho_getEntityType() == null)
2248: return;
2249:
2250: // XXX: also needs to check PersistenceContextType.TRANSACTION/EXTENDED.
2251: // jpa/0k10
2252: if (!isActiveTransaction())
2253: return;
2254:
2255: Table table = entity.__caucho_getEntityType().getTable();
2256:
2257: Object key = entity.__caucho_getPrimaryKey();
2258:
2259: addCompletion(new RowInvalidateCompletion(table.getName(), key));
2260:
2261: // jpa/0ga8, jpa/0s2d if (! _txEntities.contains(entity)) {
2262: Entity oldEntity = getTransactionEntity(entity.getClass()
2263: .getName(), key);
2264:
2265: if (oldEntity == null) {
2266: addTxEntity(entity);
2267: } else {
2268: // XXX:
2269: /*
2270: // jpa/0s2d
2271: Entity oldEntity = _txEntities.get(index);
2272: _txEntities.set(index, entity);
2273: */
2274: }
2275: }
2276:
2277: /**
2278: * Deletes the object.
2279: *
2280: * @param obj the object to delete
2281: */
2282: public void delete(Entity entity) throws SQLException {
2283: Entity oldEntity = getEntity(entity.getClass().getName(),
2284: entity.__caucho_getPrimaryKey());
2285:
2286: if (oldEntity == null) {
2287: throw new IllegalStateException(
2288: L
2289: .l(
2290: "AmberEntity[{0}:{1}] cannot be deleted since it is not managed",
2291: entity.getClass().getName(), entity
2292: .__caucho_getPrimaryKey()));
2293: /*
2294: EntityType entityType = entity.__caucho_getEntityType();
2295:
2296: if (entityType == null)
2297: return;
2298: // throw new AmberException(L.l("entity has no entityType"));
2299:
2300: AmberEntityHome entityHome = entityType.getHome();
2301: //entityHome = _persistenceUnit.getEntityHome(entity.getClass().getName());
2302:
2303: if (entityHome == null)
2304: throw new AmberException(L.l("entity has no matching home"));
2305:
2306: // XXX: this makes no sense
2307: entityHome.makePersistent(entity, this, true);
2308:
2309: addEntity(entity);
2310: */
2311: } else {
2312: // XXX: jpa/0k12
2313: oldEntity.__caucho_setConnection(this );
2314:
2315: entity = oldEntity;
2316: }
2317:
2318: entity.__caucho_delete();
2319: }
2320:
2321: /**
2322: * Creates a query object from a query string.
2323: *
2324: * @param query a Hibernate query
2325: */
2326: public AmberQuery prepareQuery(String queryString)
2327: throws AmberException {
2328: return prepareQuery(queryString, false);
2329: }
2330:
2331: /**
2332: * Creates a query object from a query string.
2333: *
2334: * @param query a Hibernate query
2335: */
2336: public AmberQuery prepareLazyQuery(String queryString)
2337: throws AmberException {
2338: return prepareQuery(queryString, true);
2339: }
2340:
2341: /**
2342: * Creates a query object from a query string.
2343: *
2344: * @param query a Hibernate query
2345: */
2346: public AmberQuery prepareUpdate(String queryString)
2347: throws AmberException {
2348: return prepareQuery(queryString, true);
2349: }
2350:
2351: /**
2352: * Creates a query object from a query string.
2353: *
2354: * @param query a Hibernate query
2355: */
2356: private AmberQuery prepareQuery(String queryString, boolean isLazy)
2357: throws AmberException {
2358: AbstractQuery queryProgram = parseQuery(queryString, isLazy);
2359:
2360: UserQuery query = new UserQuery(queryProgram);
2361:
2362: query.setSession(this );
2363:
2364: return query;
2365: }
2366:
2367: /**
2368: * Creates a query object from a query string.
2369: *
2370: * @param query a Hibernate query
2371: */
2372: public AbstractQuery parseQuery(String sql, boolean isLazy)
2373: throws AmberException {
2374: try {
2375: _persistenceUnit.initEntityHomes();
2376: } catch (Exception e) {
2377: throw AmberRuntimeException.create(e);
2378: }
2379:
2380: AbstractQuery query = _persistenceUnit.getQueryParseCache(sql);
2381:
2382: if (query == null) {
2383: QueryParser parser = new QueryParser(sql);
2384:
2385: parser.setPersistenceUnit(_persistenceUnit);
2386: parser.setLazyResult(isLazy);
2387:
2388: query = parser.parse();
2389:
2390: _persistenceUnit.putQueryParseCache(sql, query);
2391: }
2392:
2393: return query;
2394: }
2395:
2396: /**
2397: * Select a list of objects with a Hibernate query.
2398: *
2399: * @param query the hibernate query
2400: *
2401: * @return the query results.
2402: */
2403: public ResultSet query(String hsql) throws SQLException {
2404: AmberQuery query = prepareQuery(hsql);
2405:
2406: return query.executeQuery();
2407: }
2408:
2409: /**
2410: * Returns the cache chunk.
2411: *
2412: * @param sql the SQL for the cache chunk
2413: * @param args the filled parameters for the cache chunk
2414: * @param startRow the starting row for the cache chunk
2415: */
2416: public ResultSetCacheChunk getQueryCacheChunk(String sql,
2417: Object[] args, int startRow) {
2418: _queryKey.init(sql, args, startRow);
2419:
2420: return _persistenceUnit.getQueryChunk(_queryKey);
2421: }
2422:
2423: /**
2424: * Returns the result set meta data from cache.
2425: */
2426: public ResultSetMetaData getQueryMetaData() {
2427: return _persistenceUnit.getQueryMetaData(_queryKey);
2428: }
2429:
2430: /**
2431: * Sets the cache chunk.
2432: *
2433: * @param sql the SQL for the cache chunk
2434: * @param args the filled parameters for the cache chunk
2435: * @param startRow the starting row for the cache chunk
2436: * @param cacheChunk the new value of the cache chunk
2437: */
2438: public void putQueryCacheChunk(String sql, Object[] args,
2439: int startRow, ResultSetCacheChunk cacheChunk,
2440: ResultSetMetaData cacheMetaData) {
2441: QueryCacheKey key = new QueryCacheKey();
2442: Object[] newArgs = new Object[args.length];
2443:
2444: System.arraycopy(args, 0, newArgs, 0, args.length);
2445:
2446: key.init(sql, newArgs, startRow);
2447:
2448: _persistenceUnit.putQueryChunk(key, cacheChunk);
2449: _persistenceUnit.putQueryMetaData(key, cacheMetaData);
2450: }
2451:
2452: /**
2453: * Updates the database with a query
2454: *
2455: * @param query the hibernate query
2456: *
2457: * @return the query results.
2458: */
2459: public int update(String hsql) throws SQLException {
2460: AmberQuery query = prepareUpdate(hsql);
2461:
2462: return query.executeUpdate();
2463: }
2464:
2465: /**
2466: * Select a list of objects with a Hibernate query.
2467: *
2468: * @param query the hibernate query
2469: *
2470: * @return the query results.
2471: */
2472: public List find(String hsql) throws SQLException {
2473: AmberQuery query = prepareQuery(hsql);
2474:
2475: return query.list();
2476: }
2477:
2478: /**
2479: * Cleans up the connection.
2480: */
2481: public void cleanup() {
2482: if (log.isLoggable(Level.FINER))
2483: log.log(Level.FINER, "AmberConnection.cleanup");
2484:
2485: try {
2486: // XXX: also needs to check PersistenceContextType.TRANSACTION/EXTENDED.
2487: // jpa/0g04
2488: if (isActiveTransaction()) {
2489: flushInternal();
2490: }
2491: } catch (RuntimeException e) {
2492: throw e;
2493: } catch (SQLException e) {
2494: throw new IllegalStateException(e);
2495: } catch (Exception e) {
2496: throw new EJBExceptionWrapper(e);
2497: } finally {
2498: _depth = 0;
2499:
2500: for (int i = _entitiesTop - 1; i >= 0; i--) {
2501: _entities[i].__caucho_detach();
2502: }
2503:
2504: _entitiesTop = 0;
2505: _txEntitiesTop = 0;
2506: _completionList.clear();
2507:
2508: freeConnection();
2509: }
2510: }
2511:
2512: /**
2513: * Pushes the depth.
2514: */
2515: public void pushDepth() {
2516: // these aren't necessary because the AmberConnection is added as
2517: // a close callback to the UserTransaction
2518: }
2519:
2520: /**
2521: * Pops the depth.
2522: */
2523: public void popDepth() {
2524: }
2525:
2526: /**
2527: * Frees the connection.
2528: */
2529: public void freeConnection() {
2530: closeConnectionImpl();
2531: }
2532:
2533: /**
2534: * Frees the connection.
2535: */
2536: private void closeConnectionImpl() {
2537: Connection conn = _conn;
2538: _conn = null;
2539:
2540: Connection readConn = _readConn;
2541: _readConn = null;
2542:
2543: boolean isAutoCommit = _isAutoCommit;
2544: _isAutoCommit = true;
2545:
2546: try {
2547: if (conn != null && !isAutoCommit)
2548: conn.setAutoCommit(true);
2549: } catch (SQLException e) {
2550: }
2551:
2552: for (Statement stmt : _statements) {
2553: try {
2554: stmt.close();
2555: } catch (Exception e) {
2556: log.log(Level.WARNING, e.toString(), e);
2557: }
2558: }
2559:
2560: try {
2561: _preparedStatementMap.clear();
2562: _statements.clear();
2563:
2564: if (conn != null)
2565: conn.close();
2566:
2567: if (readConn != null)
2568: readConn.close();
2569: } catch (Exception e) {
2570: log.log(Level.WARNING, e.toString(), e);
2571: }
2572: }
2573:
2574: public String toString() {
2575: if (_persistenceUnit != null)
2576: return "AmberConnection[" + _persistenceUnit.getName()
2577: + "]";
2578: else
2579: return "AmberConnection[closed]";
2580: }
2581:
2582: /**
2583: * Finalizer.
2584: */
2585: public void finalize() {
2586: cleanup();
2587: }
2588:
2589: /**
2590: * Returns true when cache items can be used.
2591: */
2592: public boolean shouldRetrieveFromCache() {
2593: // ejb/0d01
2594: return (!isActiveTransaction());
2595: }
2596:
2597: public void setTransactionalState(Entity entity) {
2598: if (isActiveTransaction()) {
2599: // jpa/0ga8
2600: entity.__caucho_setConnection(this );
2601:
2602: // jpa/0j5f
2603: EntityState state = entity.__caucho_getEntityState();
2604:
2605: //if (state.ordinal() < EntityState.P_DELETING.ordinal())
2606: if (state == EntityState.P_NON_TRANSACTIONAL)
2607: entity
2608: .__caucho_setEntityState(EntityState.P_TRANSACTIONAL);
2609: }
2610: }
2611:
2612: public boolean isCacheEntity(Entity entity) {
2613: return entity == getCacheEntity(entity, true);
2614: }
2615:
2616: public Entity getCacheEntity(Entity entity) {
2617: return getCacheEntity(entity, false);
2618: }
2619:
2620: public Entity getCacheEntity(Entity entity, boolean isDebug) {
2621: // jpa/0h0a
2622:
2623: if (entity == null)
2624: return null;
2625:
2626: // XXX: jpa/0h20, the cache entity is only available after commit.
2627: Entity cacheEntity = entity.__caucho_getCacheEntity();
2628:
2629: if (cacheEntity != null)
2630: return cacheEntity;
2631:
2632: return getCacheEntity(entity.getClass(), entity
2633: .__caucho_getPrimaryKey(), isDebug);
2634: }
2635:
2636: public Entity getCacheEntity(Class cl, Object pk) {
2637: return getCacheEntity(cl, pk, false);
2638: }
2639:
2640: // jpa/0h20
2641: public Entity getCacheEntity(Class cl, Object pk, boolean isDebug) {
2642: if (pk == null)
2643: return null;
2644:
2645: String className = cl.getName();
2646:
2647: AmberEntityHome entityHome = _persistenceUnit
2648: .getEntityHome(className);
2649:
2650: if (entityHome == null) {
2651: if (log.isLoggable(Level.FINER))
2652: log
2653: .log(
2654: Level.FINER,
2655: L
2656: .l(
2657: "Home not found for entity (class: '{0}' PK: '{1}')",
2658: className, pk));
2659: return null;
2660: }
2661:
2662: EntityType rootType = entityHome.getRootType();
2663:
2664: EntityItem item = _persistenceUnit.getEntity(rootType, pk);
2665:
2666: if (item == null)
2667: return null;
2668:
2669: // jpa/0o0b
2670: if (isDebug)
2671: return item.getEntity();
2672:
2673: // XXX: jpa/0h31, expires the child cache entity.
2674: if (isActiveTransaction()) {
2675: Entity txEntity = getTransactionEntity(className, pk);
2676:
2677: EntityState state = null;
2678:
2679: if (txEntity != null)
2680: state = txEntity.__caucho_getEntityState();
2681: else
2682: // jpa/0o0b || ! state.isManaged()) {
2683: item.getEntity().__caucho_expire();
2684:
2685: return null;
2686: }
2687:
2688: return item.getEntity();
2689: }
2690:
2691: //
2692: // private
2693: //
2694: // throws Exception (for jpa)
2695: //
2696: // ejb/0g22 (cmp) expects exception handling in
2697: // the public methods. See public void create(Object) above.
2698:
2699: /**
2700: * Adds an entity to the context, assuming it has not been added yet.
2701: * Also, if there is a transaction, adds the entity to the list of
2702: * transactional entities.
2703: */
2704: private void addInternalEntity(Entity entity) {
2705: if (log.isLoggable(Level.FINEST)) {
2706: log.log(Level.FINEST, L.l(
2707: "amber {0}[{1}] addInternalEntity", entity
2708: .getClass().getName(), entity
2709: .__caucho_getPrimaryKey()));
2710: }
2711:
2712: addEntity(entity);
2713:
2714: // jpa/0g06
2715: if (isActiveTransaction()) {
2716: addTxEntity(entity);
2717:
2718: // jpa/0s2d: merge()
2719: setTransactionalState(entity);
2720: }
2721: }
2722:
2723: /**
2724: * Saves the object.
2725: *
2726: * @param obj the object to create
2727: */
2728: private void createInternal(Object obj) throws Exception {
2729: AmberEntityHome home = null;
2730:
2731: Class cl = obj.getClass();
2732:
2733: for (; home == null && cl != null; cl = cl.getSuperclass()) {
2734: home = _persistenceUnit.getHome(cl);
2735: }
2736:
2737: if (home == null)
2738: throw new AmberException(L.l(
2739: "`{0}' is not a known entity class.", obj
2740: .getClass().getName()));
2741:
2742: createInternal(home, obj);
2743: }
2744:
2745: /**
2746: * Saves the object.
2747: *
2748: * @param obj the object to create
2749: */
2750: private void createInternal(String homeName, Object obj)
2751: throws Exception {
2752: AmberEntityHome home = _persistenceUnit.getEntityHome(homeName);
2753:
2754: if (home == null)
2755: throw new AmberException(L.l(
2756: "`{0}' is not a known entity class.", obj
2757: .getClass().getName()));
2758:
2759: createInternal(home, obj);
2760: }
2761:
2762: /**
2763: * Saves the object.
2764: *
2765: * @param obj the object to create
2766: */
2767: private void createInternal(AmberEntityHome home, Object obj)
2768: throws Exception {
2769: // XXX: flushing things like delete might be useful?
2770: // XXX: the issue is a flush can break FK constraints and
2771: // fail prematurely (jpa/0h26).
2772: // commented out: flushInternal();
2773:
2774: if (contains(obj))
2775: return;
2776:
2777: Entity entity = (Entity) obj;
2778:
2779: // jpa/0g0k: cannot call home.save because of jpa exception handling.
2780: if (_persistenceUnit.isJPA()) {
2781: // See persistInternal(): entity.__caucho_cascadePrePersist(this);
2782:
2783: addEntity(entity);
2784:
2785: // jpa/0ga2
2786: entity.__caucho_lazy_create(this , home.getEntityType());
2787:
2788: // See persistInternal(): entity.__caucho_cascadePostPersist(this);
2789: } else
2790: home.save(this , entity);
2791:
2792: // jpa/0h25
2793: // XXX: not correct, since we need to keep the P_PERSIST state around
2794: // and P_PERSIST is a transactional state
2795: // setTransactionalState(entity);
2796:
2797: // jpa/0g0i
2798: Table table = home.getEntityType().getTable();
2799: addCompletion(new RowInsertCompletion(table.getName()));
2800: }
2801:
2802: private void checkTransactionRequired(String operation)
2803: throws TransactionRequiredException, SQLException {
2804: // XXX: also needs to check PersistenceContextType.TRANSACTION/EXTENDED.
2805:
2806: if (!(_isXA || isActiveTransaction()))
2807: throw new TransactionRequiredException(
2808: L
2809: .l(
2810: "{0}() operation can only be executed in the scope of a transaction or with an extended persistence context.",
2811: operation));
2812: }
2813:
2814: private Entity checkEntityType(Object entity, String operation) {
2815: if (!(entity instanceof Entity))
2816: throw new IllegalArgumentException(
2817: L
2818: .l(
2819: "{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.",
2820: operation));
2821:
2822: if (_persistenceUnit.isJPA()) {
2823: String className = entity.getClass().getName();
2824:
2825: EntityType entityType = (EntityType) _persistenceUnit
2826: .getEntityType(className);
2827:
2828: // jpa/0m08
2829: if (entityType == null) {
2830: throw new IllegalArgumentException(
2831: L
2832: .l(
2833: "{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.",
2834: operation, className));
2835: }
2836: }
2837:
2838: return (Entity) entity;
2839: }
2840:
2841: /**
2842: * Detach after non-xa.
2843: */
2844: public void detach() {
2845: if (_isXA || _isInTransaction)
2846: throw new IllegalStateException(L
2847: .l("detach cannot be called within transaction"));
2848:
2849: _completionList.clear();
2850:
2851: _txEntitiesTop = 0;
2852:
2853: // jpa/1700
2854: for (int i = _entitiesTop - 1; i >= 0; i--) {
2855: _entities[i].__caucho_detach();
2856: }
2857:
2858: // jpa/0o0d
2859: _entitiesTop = 0;
2860: }
2861:
2862: /**
2863: * Flush managed entities.
2864: */
2865: private void flushInternal() throws Exception {
2866: // Do not flush within merge() or while loading an entity.
2867: if (!_isFlushAllowed)
2868: return;
2869:
2870: /* XXX: moved into __caucho_flush
2871: for (int i = _txEntities.size() - 1; i >= 0; i--) {
2872: Entity entity = _txEntities.get(i);
2873:
2874: EntityState state = entity.__caucho_getEntityState();
2875:
2876: // jpa/0i60
2877: // jpa/0h27: for all entities Y referenced by a *managed*
2878: // entity X, where the relationship has been annotated
2879: // with cascade=PERSIST/ALL, the persist operation is
2880: // applied to Y. It is a lazy cascade as the relationship
2881: // is not always initialized at the time persist(X) was
2882: // called but must be at flush time.
2883:
2884: if (state == EntityState.P_PERSIST) {
2885: entity.__caucho_cascadePrePersist(this);
2886: entity.__caucho_cascadePostPersist(this);
2887: }
2888: }
2889: */
2890:
2891: // We avoid breaking FK constraints:
2892: //
2893: // 1. Assume _txEntities has the following order: A <- B <- C
2894: // XXX: Make sure priorities are handled based on owning sides
2895: // even when there are cycles in a graph.
2896: //
2897: // 2. Persist is done in ascending order: A(0) <- B(1) <- C(2)
2898: //
2899: // 3. Delete is done in descending order: C(2) -> B(1) -> A(0)
2900: // Persists in ascending order.
2901: for (int i = 0; i < _txEntitiesTop; i++) {
2902: Entity entity = _txEntities[i];
2903:
2904: if (entity.__caucho_getEntityState().isPersist()) {
2905: try {
2906: entity.__caucho_flush();
2907: } catch (SQLException e) {
2908: log.log(Level.FINER, e.toString(), e);
2909:
2910: String sqlState = e.getSQLState();
2911:
2912: JdbcMetaData metaData = _persistenceUnit
2913: .getMetaData();
2914:
2915: if (metaData.isUniqueConstraintSQLState(sqlState)) {
2916: // jpa/0ga5
2917: throw new EntityExistsException(
2918: L
2919: .l(
2920: "Trying to persist an entity of class '{0}' with PK '{1}' that already exists. Entity state '{2}'",
2921: entity.getClass()
2922: .getName(),
2923: entity
2924: .__caucho_getPrimaryKey(),
2925: entity
2926: .__caucho_getEntityState()));
2927: } else if (metaData
2928: .isForeignKeyViolationSQLState(sqlState)) {
2929: // jpa/0o42
2930: throw new IllegalStateException(
2931: L
2932: .l(
2933: "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.",
2934: entity.getClass()
2935: .getName(),
2936: entity
2937: .__caucho_getPrimaryKey(),
2938: entity
2939: .__caucho_getEntityState()));
2940: }
2941:
2942: throw e;
2943: }
2944: }
2945: }
2946:
2947: // jpa/0h25
2948: // Deletes in descending order.
2949: for (int i = _txEntitiesTop - 1; i >= 0; i--) {
2950: Entity entity = _txEntities[i];
2951:
2952: if (!entity.__caucho_getEntityState().isPersist())
2953: entity.__caucho_flush();
2954: }
2955:
2956: if (!isInTransaction()) {
2957: if (_completionList.size() > 0) {
2958: _persistenceUnit.complete(_completionList);
2959: }
2960: _completionList.clear();
2961:
2962: for (int i = 0; i < _txEntitiesTop; i++) {
2963: Entity entity = _txEntities[i];
2964:
2965: entity.__caucho_afterCommit();
2966: }
2967:
2968: _txEntitiesTop = 0;
2969: }
2970: }
2971:
2972: /**
2973: * Persists the entity.
2974: */
2975: private void persistInternal(Entity entity) throws Exception {
2976: EntityState state = entity.__caucho_getEntityState();
2977:
2978: switch (state) {
2979: case TRANSIENT: {
2980: Entity contextEntity = getEntity(entity.getClass()
2981: .getName(), entity.__caucho_getPrimaryKey());
2982:
2983: // jpa/0ga3
2984: if (contextEntity != null) {
2985: if (contextEntity.__caucho_getEntityState().ordinal() == EntityState.P_DELETED
2986: .ordinal()) {
2987: // jpa/0ga3
2988: contextEntity.__caucho_flush();
2989: } else if (entity != contextEntity) {
2990: // jpa/0ga1: trying to persist a detached entity that already exists.
2991: throw new EntityExistsException(
2992: L
2993: .l(
2994: "Trying to persist a detached entity of class '{0}' with PK '{1}' that already exists. Entity state '{2}'",
2995: entity.getClass().getName(),
2996: entity
2997: .__caucho_getPrimaryKey(),
2998: state));
2999: }
3000: }
3001:
3002: // jpa/0h24
3003: // Pre-persist child entities.
3004: entity.__caucho_cascadePrePersist(this );
3005:
3006: createInternal(entity);
3007: }
3008: break;
3009:
3010: case P_DELETING:
3011: case P_DELETED: {
3012: // jpa/0i60, jpa/1510, jpa/0h25
3013: // jpa/0h26
3014: entity.__caucho_cascadePrePersist(this );
3015:
3016: // removed entity instance, reset state and persist.
3017: entity.__caucho_makePersistent(null, (EntityType) null);
3018: createInternal(entity);
3019: }
3020: break;
3021:
3022: case P_PERSISTING:
3023: case P_PERSISTED: {
3024: // jpa/0h26
3025: // Pre-persist child entities.
3026: entity.__caucho_cascadePrePersist(this );
3027: }
3028: break;
3029:
3030: default:
3031: if (entity.__caucho_getConnection() == this )
3032: return;
3033: else {
3034: // jpa/0ga5 (tck):
3035: // See entitytest.persist.basic.persistBasicTest4 vs.
3036: // callback.inheritance.preUpdateTest
3037: throw new EntityExistsException(
3038: L
3039: .l(
3040: "Trying to persist an entity that is detached or already exists. Entity state '{0}'",
3041: state));
3042: }
3043: }
3044:
3045: // jpa/0j5e
3046: updateFlushPriority(entity);
3047:
3048: // jpa/0h27, jpa/0i5c, jpa/0j5g
3049: // Post-persist child entities.
3050: entity.__caucho_cascadePostPersist(this );
3051: }
3052:
3053: /**
3054: * Updates flush priorities.
3055: */
3056: private void updateFlushPriority(Entity updateEntity) {
3057: if (!isActiveTransaction())
3058: return;
3059:
3060: removeTxEntity(updateEntity);
3061:
3062: int updatePriority = updateEntity.__caucho_getEntityType()
3063: .getFlushPriority();
3064:
3065: for (int i = _txEntitiesTop - 1; i >= 0; i--) {
3066: Entity entity = _txEntities[i];
3067:
3068: int currentPriority = entity.__caucho_getEntityType()
3069: .getFlushPriority();
3070:
3071: if (currentPriority < updatePriority) {
3072: addTxEntity(i + 1, updateEntity);
3073: return;
3074: }
3075: }
3076:
3077: addTxEntity(0, updateEntity);
3078: }
3079:
3080: /**
3081: * Recursively merges the state of the entity into the current context.
3082: */
3083: public <T> T recursiveMerge(T entityT) {
3084: // jpa/0ga3, jpa/0h08, jpa/0i5g, jpa/0s2k
3085:
3086: try {
3087: if (entityT == null)
3088: return null;
3089:
3090: Entity entity = checkEntityType(entityT, "merge");
3091:
3092: if (log.isLoggable(Level.FINER)) {
3093: String className = entity.getClass().getName();
3094: Object pk = entity.__caucho_getPrimaryKey();
3095: EntityState state = entity.__caucho_getEntityState();
3096:
3097: log
3098: .log(
3099: Level.FINER,
3100: L
3101: .l(
3102: "recursiveMerge(class: '{0}' PK: '{1}' state: '{2}')",
3103: className, pk, state));
3104: }
3105:
3106: Entity managedEntity = null;
3107:
3108: if (containsMergingEntity(entity))
3109: managedEntity = entity;
3110: else
3111: managedEntity = mergeDetachedEntity(entity, true);
3112:
3113: return (T) managedEntity;
3114:
3115: } catch (RuntimeException e) {
3116: throw e;
3117: } catch (Exception e) {
3118: throw new EJBExceptionWrapper(e);
3119: }
3120: }
3121:
3122: public Entity mergeDetachedEntity(Entity entity, boolean isFullMerge) {
3123: try {
3124: if (entity == null)
3125: return entity;
3126:
3127: String className = entity.getClass().getName();
3128: EntityState state = entity.__caucho_getEntityState();
3129:
3130: Object pk = entity.__caucho_getPrimaryKey();
3131:
3132: if (log.isLoggable(Level.FINER))
3133: log.log(Level.FINER, L.l(
3134: "merge(class: '{0}' PK: '{1}' state: '{2}')",
3135: className, pk, state));
3136:
3137: if (EntityState.P_DELETING.ordinal() <= state.ordinal()) {
3138: // removed entity instance
3139: throw new IllegalArgumentException(
3140: L
3141: .l("Merge operation cannot be applied to a removed entity instance"));
3142: }
3143:
3144: Entity managedEntity = null;
3145:
3146: // XXX: jpa/0o42 try {
3147:
3148: Entity existingEntity = null;
3149:
3150: try {
3151: boolean isManaged = entity.__caucho_getEntityState()
3152: .isManaged();
3153:
3154: existingEntity = (Entity) load(entity.getClass(), pk,
3155: true);
3156: } catch (AmberObjectNotFoundException e) {
3157: if (log.isLoggable(Level.FINER))
3158: log.log(Level.FINER, e.toString(), e);
3159: // JPA: should not throw at all, returns null only.
3160: }
3161:
3162: // XXX: the original entity should remain detached jpa/0s2k
3163: // setTransactionalState(entity);
3164:
3165: if (existingEntity == null) {
3166: // new entity instance
3167: managedEntity = addNewEntity(entity.getClass(), pk);
3168:
3169: if (managedEntity == null)
3170: throw new IllegalStateException(
3171: L
3172: .l(
3173: "Unable to add a new managed entity when merging object class: {0} PK: {1}.",
3174: className, pk));
3175: } else {
3176: // jpa/0h08
3177: managedEntity = existingEntity;
3178: }
3179:
3180: int index = getEntityMatch(_mergingEntities, className, pk);
3181:
3182: if (index >= 0) {
3183: // jpa/0o42
3184: managedEntity = _mergingEntities.get(index);
3185: } else {
3186: _mergingEntities.add(managedEntity);
3187:
3188: if (isFullMerge) {
3189: // jpa/0o42: avoids premature loading.
3190: managedEntity.__caucho_setConnection(null);
3191:
3192: entity.__caucho_copyTo(managedEntity, this , true);
3193:
3194: // jpa/0o42
3195: managedEntity.__caucho_setConnection(this );
3196: }
3197:
3198: if (existingEntity == null) {
3199: // jpa/0o42
3200:
3201: // cascade children
3202: // XXX: called from persist()
3203: // entity.__caucho_cascadePrePersist(this);
3204:
3205: // jpa/0o42
3206: managedEntity.__caucho_setConnection(null);
3207: managedEntity
3208: .__caucho_setEntityState(EntityState.TRANSIENT);
3209:
3210: persist(managedEntity);
3211:
3212: // jpa/0i5g
3213: // XXX: called from persist()
3214: // entity.__caucho_cascadePostPersist(this);
3215:
3216: if (log.isLoggable(Level.FINER))
3217: log
3218: .log(
3219: Level.FINER,
3220: L
3221: .l("merged to a new entity (persisted)"));
3222: } else {
3223: // Handled in addInternalEntity()
3224: // setTransactionalState(managedEntity);
3225:
3226: if (log.isLoggable(Level.FINER))
3227: log.log(Level.FINER, L
3228: .l("merged to an existing entity"));
3229: }
3230:
3231: // jpa/0o42: avoids premature loading.
3232: managedEntity.__caucho_setConnection(null);
3233:
3234: // jpa/0ga3, jpa/0h08, jpa/0o4-
3235: entity.__caucho_merge(managedEntity, this , false);
3236:
3237: // jpa/0o42
3238: managedEntity.__caucho_setConnection(this );
3239:
3240: // jpa/0h08
3241: entity.__caucho_copyDirtyMaskFrom(managedEntity);
3242: entity.__caucho_copyLoadMaskFrom(managedEntity);
3243: }
3244:
3245: /* XXX: jpa/0o42
3246: } catch (Exception e) {
3247: if (e.getCause() instanceof SQLException) {
3248: // OK: entity exists in the database
3249: log.log(Level.FINER, e.toString(), e);
3250: }
3251: else {
3252: throw e;
3253: }
3254: }
3255: */
3256:
3257: return managedEntity;
3258:
3259: } catch (RuntimeException e) {
3260: throw e;
3261: } catch (Exception e) {
3262: throw new EJBExceptionWrapper(e);
3263: }
3264: }
3265:
3266: /**
3267: * Creates an instance of the named query
3268: */
3269: private Query createInternalNativeQuery(String sql) {
3270: try {
3271: QueryImpl query = new QueryImpl(this );
3272:
3273: query.setNativeSql(sql);
3274:
3275: return query;
3276: } catch (RuntimeException e) {
3277: throw new IllegalArgumentException(e);
3278: } catch (Exception e) {
3279: throw new EJBExceptionWrapper(e);
3280: }
3281: }
3282:
3283: /**
3284: * Creates an instance of the native query.
3285: */
3286: private Query createInternalNativeQuery(String sql,
3287: SqlResultSetMappingConfig map) {
3288: Query query = createInternalNativeQuery(sql);
3289:
3290: QueryImpl queryImpl = (QueryImpl) query;
3291:
3292: queryImpl.setSqlResultSetMapping(map);
3293:
3294: return query;
3295: }
3296:
3297: private boolean containsMergingEntity(Entity entity) {
3298: if (_mergingEntities.contains(entity))
3299: return true;
3300:
3301: // jpa/0o42
3302: int index = getEntityMatch(_mergingEntities, entity.getClass()
3303: .getName(), entity.__caucho_getPrimaryKey());
3304:
3305: return (index >= 0);
3306: }
3307:
3308: public void addEntity(Entity entity) {
3309: Entity[] entities = _entities;
3310:
3311: if (_entitiesTop == entities.length) {
3312: entities = new Entity[_entities.length + 32];
3313: System.arraycopy(_entities, 0, entities, 0,
3314: _entities.length);
3315: _entities = entities;
3316: }
3317:
3318: entities[_entitiesTop++] = entity;
3319: }
3320:
3321: private void removeEntityImpl(Entity entity) {
3322: Entity[] entities = _entities;
3323:
3324: for (int i = _entitiesTop - 1; i >= 0; i--) {
3325: if (entities[i] == entity) {
3326: System.arraycopy(entities, i + 1, entities, i,
3327: _entitiesTop - i - 1);
3328:
3329: _entitiesTop -= 1;
3330:
3331: return;
3332: }
3333: }
3334: }
3335:
3336: private void addTxEntity(Entity entity) {
3337: Entity[] entities = _txEntities;
3338:
3339: if (_txEntitiesTop == entities.length) {
3340: entities = new Entity[entities.length + 32];
3341: System.arraycopy(_txEntities, 0, entities, 0,
3342: _txEntities.length);
3343: _txEntities = entities;
3344: }
3345:
3346: entities[_txEntitiesTop++] = entity;
3347: }
3348:
3349: private void removeTxEntity(Entity entity) {
3350: Entity[] entities = _txEntities;
3351:
3352: for (int i = _txEntitiesTop - 1; i >= 0; i--) {
3353: if (entities[i] == entity) {
3354: System.arraycopy(entities, i + 1, entities, i,
3355: _txEntitiesTop - i - 1);
3356:
3357: _txEntitiesTop -= 1;
3358:
3359: return;
3360: }
3361: }
3362: }
3363:
3364: private void addTxEntity(int index, Entity entity) {
3365: Entity[] entities = _txEntities;
3366:
3367: if (_txEntitiesTop == entities.length) {
3368: entities = new Entity[_txEntities.length + 32];
3369: System.arraycopy(_txEntities, 0, entities, 0,
3370: _txEntities.length);
3371: _txEntities = entities;
3372: }
3373:
3374: if (index < _txEntitiesTop)
3375: System.arraycopy(entities, index, entities, index + 1,
3376: _txEntitiesTop - index);
3377:
3378: entities[index] = entity;
3379:
3380: _txEntitiesTop += 1;
3381: }
3382:
3383: private static int getEntityMatch(ArrayList<Entity> list,
3384: String className, Object key) {
3385: // See also: getEntity() and getTransactionEntity().
3386:
3387: // jpa/0o42
3388: for (int i = list.size() - 1; i >= 0; i--) {
3389: Entity entity = list.get(i);
3390:
3391: if (entity.__caucho_match(className, key)) {
3392: return i;
3393: }
3394: }
3395:
3396: return -1;
3397: }
3398:
3399: private class EntityTransactionImpl implements EntityTransaction {
3400: private boolean _rollbackOnly;
3401:
3402: /**
3403: * Starts a resource transaction.
3404: */
3405: public void begin() {
3406: // jpa/1522
3407: if (isActiveTransaction())
3408: throw new IllegalStateException(
3409: "begin() cannot be called when the entity transaction is already active.");
3410:
3411: _rollbackOnly = false;
3412:
3413: try {
3414: AmberConnection.this .beginTransaction();
3415: } catch (SQLException e) {
3416: throw new PersistenceException(e);
3417: }
3418: }
3419:
3420: /**
3421: * Commits a resource transaction.
3422: */
3423: public void commit() {
3424: // jpa/1523
3425: if (!isActiveTransaction())
3426: throw new IllegalStateException(
3427: "commit() cannot be called when the entity transaction is not active.");
3428:
3429: // jpa/1525
3430: if (getRollbackOnly())
3431: throw new RollbackException(
3432: "commit() cannot be called when the entity transaction is marked for rollback only.");
3433:
3434: try {
3435: // jpa/11a7
3436: AmberConnection.this .beforeCommit();
3437:
3438: _isInTransaction = false;
3439:
3440: // jpa/11a7 AmberConnection.this.commit();
3441: if (AmberConnection.this ._conn != null) {
3442: AmberConnection.this ._conn.commit();
3443: }
3444:
3445: // XXX: missing finally issues if _conn.commit fails
3446:
3447: // jpa/11a7
3448: AmberConnection.this .afterCommit(true);
3449:
3450: if (AmberConnection.this ._conn != null) {
3451: closeConnectionImpl();
3452: }
3453: } catch (SQLException e) {
3454: throw new PersistenceException(e);
3455: }
3456: }
3457:
3458: /**
3459: * Rolls the current transaction back.
3460: */
3461: public void rollback() {
3462: // jpa/1524
3463: if (!isActiveTransaction())
3464: throw new IllegalStateException(
3465: "rollback() cannot be called when the entity transaction is not active.");
3466:
3467: setRollbackOnly();
3468:
3469: PersistenceException exn = null;
3470:
3471: try {
3472: AmberConnection.this .rollback();
3473: } catch (Exception e) {
3474: exn = new PersistenceException(e);
3475: } finally {
3476: try {
3477: // jpa/1501
3478: AmberConnection.this .afterCommit(false);
3479: } catch (PersistenceException e) {
3480: exn = e;
3481: } catch (Exception e) {
3482: exn = new PersistenceException(e);
3483: } finally {
3484: // jpa/1525
3485: if (AmberConnection.this ._conn != null) {
3486: closeConnectionImpl();
3487: }
3488: }
3489: }
3490:
3491: if (exn != null)
3492: throw exn;
3493: }
3494:
3495: /**
3496: * Marks the current transaction for rollback only.
3497: */
3498: public void setRollbackOnly() {
3499: // jpa/1521
3500: if (!isActiveTransaction())
3501: throw new IllegalStateException(
3502: "setRollbackOnly() cannot be called when the entity transaction is not active.");
3503:
3504: _rollbackOnly = true;
3505: }
3506:
3507: /**
3508: * Returns true if the transaction is for rollback only.
3509: */
3510: public boolean getRollbackOnly() {
3511: // jpa/1520
3512: if (!isActiveTransaction())
3513: throw new IllegalStateException(
3514: "getRollbackOnly() cannot be called when the entity transaction is not active.");
3515:
3516: return _rollbackOnly;
3517: }
3518:
3519: /**
3520: * Test if a transaction is in progress.
3521: */
3522: public boolean isActive() {
3523: return _isInTransaction;
3524: }
3525: }
3526: }
|