Save This Page
Home » hibernate-entitymanager-sources » org.hibernate » ejb » [javadoc | source]
    1   // $Id: AbstractEntityManagerImpl.java 14812 2008-06-25 10:07:04Z hardy.ferentschik $
    2   /*
    3    * JBoss, the OpenSource EJB server Distributable under LGPL license. See terms of license at
    4    * gnu.org.
    5    */
    6   package org.hibernate.ejb;
    7   
    8   import java.io.IOException;
    9   import java.io.ObjectInputStream;
   10   import java.io.ObjectOutputStream;
   11   import java.io.Serializable;
   12   import java.util.Map;
   13   
   14   import javax.persistence.EntityNotFoundException;
   15   import javax.persistence.EntityTransaction;
   16   import javax.persistence.FlushModeType;
   17   import javax.persistence.LockModeType;
   18   import javax.persistence.NoResultException;
   19   import javax.persistence.NonUniqueResultException;
   20   import javax.persistence.OptimisticLockException;
   21   import javax.persistence.PersistenceContextType;
   22   import javax.persistence.PersistenceException;
   23   import javax.persistence.Query;
   24   import javax.persistence.TransactionRequiredException;
   25   import javax.persistence.spi.PersistenceUnitTransactionType;
   26   import javax.transaction.Status;
   27   import javax.transaction.Synchronization;
   28   import javax.transaction.SystemException;
   29   import javax.transaction.TransactionManager;
   30   
   31   import org.hibernate.AssertionFailure;
   32   import org.hibernate.FlushMode;
   33   import org.hibernate.HibernateException;
   34   import org.hibernate.LockMode;
   35   import org.hibernate.MappingException;
   36   import org.hibernate.ObjectDeletedException;
   37   import org.hibernate.ObjectNotFoundException;
   38   import org.hibernate.QueryException;
   39   import org.hibernate.SQLQuery;
   40   import org.hibernate.Session;
   41   import org.hibernate.StaleObjectStateException;
   42   import org.hibernate.StaleStateException;
   43   import org.hibernate.Transaction;
   44   import org.hibernate.TransientObjectException;
   45   import org.hibernate.TypeMismatchException;
   46   import org.hibernate.UnresolvableObjectException;
   47   import org.hibernate.cfg.Environment;
   48   import org.hibernate.ejb.transaction.JoinableCMTTransaction;
   49   import org.hibernate.ejb.util.ConfigurationHelper;
   50   import org.hibernate.engine.SessionFactoryImplementor;
   51   import org.hibernate.engine.SessionImplementor;
   52   import org.hibernate.proxy.HibernateProxy;
   53   import org.hibernate.transaction.TransactionFactory;
   54   import org.hibernate.util.CollectionHelper;
   55   import org.hibernate.util.JTAHelper;
   56   import org.slf4j.Logger;
   57   import org.slf4j.LoggerFactory;
   58   
   59   /**
   60    * @author <a href="mailto:gavin@hibernate.org">Gavin King</a>
   61    * @author Emmanuel Bernard
   62    */
   63   @SuppressWarnings("unchecked")
   64   public abstract class AbstractEntityManagerImpl implements HibernateEntityManagerImplementor, Serializable {
   65   	private static final Logger log = LoggerFactory.getLogger( AbstractEntityManagerImpl.class );
   66   
   67   	protected transient TransactionImpl tx = new TransactionImpl( this );
   68   	protected PersistenceContextType persistenceContextType;
   69   	private FlushModeType flushModeType = FlushModeType.AUTO;
   70   	private PersistenceUnitTransactionType transactionType;
   71   	private Map properties;
   72   
   73   	protected AbstractEntityManagerImpl(
   74   			PersistenceContextType type, PersistenceUnitTransactionType transactionType, Map properties) {
   75   		this.persistenceContextType = type;
   76   		this.transactionType = transactionType;
   77   		this.properties = properties != null ? properties : CollectionHelper.EMPTY_MAP;
   78   	}
   79   
   80   	protected void postInit() {
   81   		//register in Sync if needed
   82   		if ( PersistenceUnitTransactionType.JTA.equals( transactionType ) ) joinTransaction( true );
   83   		Object flushMode = properties.get( "org.hibernate.flushMode" );
   84   		if (flushMode != null) {
   85   			getSession().setFlushMode( ConfigurationHelper.getFlushMode( flushMode ) );
   86   		}
   87   		this.properties = null;
   88   	}
   89   
   90   	public Query createQuery(String ejbqlString) {
   91   		//adjustFlushMode();
   92   		try {
   93   			return new QueryImpl( getSession().createQuery( ejbqlString ), this );
   94   		}
   95   		catch (HibernateException he) {
   96   			throwPersistenceException( he );
   97   			return null;
   98   		}
   99   	}
  100   
  101   	public Query createNamedQuery(String name) {
  102   		//adjustFlushMode();
  103   		org.hibernate.Query namedQuery;
  104   		try {
  105   			namedQuery = getSession().getNamedQuery( name );
  106   		}
  107   		catch (MappingException e) {
  108   			throw new IllegalArgumentException("Named query not found: " + name);
  109   		}
  110   		try {
  111   			return new QueryImpl( namedQuery, this );
  112   		}
  113   		catch (HibernateException he) {
  114   			throwPersistenceException( he );
  115   			return null;
  116   		}
  117   	}
  118   
  119   	public Query createNativeQuery(String sqlString) {
  120   		//adjustFlushMode();
  121   		try {
  122   			SQLQuery q = getSession().createSQLQuery( sqlString );
  123   			return new QueryImpl( q, this );
  124   		}
  125   		catch (HibernateException he) {
  126   			throwPersistenceException( he );
  127   			return null;
  128   		}
  129   	}
  130   
  131   	public Query createNativeQuery(String sqlString, Class resultClass) {
  132   		//adjustFlushMode();
  133   		try {
  134   			SQLQuery q = getSession().createSQLQuery( sqlString );
  135   			q.addEntity( "alias1", resultClass.getName(), LockMode.READ );
  136   			return new QueryImpl( q, this );
  137   		}
  138   		catch (HibernateException he) {
  139   			throwPersistenceException( he );
  140   			return null;
  141   		}
  142   	}
  143   
  144   	public Query createNativeQuery(String sqlString, String resultSetMapping) {
  145   		//adjustFlushMode();
  146   		try {
  147   			SQLQuery q = getSession().createSQLQuery( sqlString );
  148   			q.setResultSetMapping( resultSetMapping );
  149   			return new QueryImpl( q, this );
  150   		}
  151   		catch (HibernateException he) {
  152   			throwPersistenceException( he );
  153   			return null;
  154   		}
  155   	}
  156   
  157   	@SuppressWarnings("unchecked")
  158   	public <T> T getReference(Class<T> entityClass, Object primaryKey) {
  159   		//adjustFlushMode();
  160   		try {
  161   			return (T) getSession().load( entityClass, (Serializable) primaryKey );
  162   		}
  163   		catch (MappingException e) {
  164   			throw new IllegalArgumentException( e.getMessage(), e );
  165   		}
  166   		catch (TypeMismatchException e ) {
  167   			throw new IllegalArgumentException( e.getMessage(), e );
  168   		}
  169   		catch (ClassCastException e) {
  170   			throw new IllegalArgumentException( e.getMessage(), e );
  171   		}
  172   		catch (HibernateException he) {
  173   			throwPersistenceException( he );
  174   			return null;
  175   		}
  176   	}
  177   
  178   	@SuppressWarnings("unchecked")
  179   	public <A> A find(Class<A> entityClass, Object primaryKey) {
  180   		//adjustFlushMode();
  181   		try {
  182   			return (A) getSession().get( entityClass, (Serializable) primaryKey );
  183   		}
  184   		catch (ObjectDeletedException e) {
  185   			//the spec is silent about people doing remove() find() on the same PC
  186   			return null;
  187   		}
  188   		catch (ObjectNotFoundException e) {
  189   			//should not happen on the entity itself with get
  190   			throw new IllegalArgumentException( e.getMessage(), e );
  191   		}
  192   		catch (MappingException e) {
  193   			throw new IllegalArgumentException( e.getMessage(), e );
  194   		}
  195   		catch (TypeMismatchException e ) {
  196   			throw new IllegalArgumentException( e.getMessage(), e );
  197   		}
  198   		catch (ClassCastException e) {
  199   			throw new IllegalArgumentException( e.getMessage(), e );
  200   		}
  201   		catch (HibernateException he) {
  202   			throwPersistenceException( he );
  203   			return null;
  204   		}
  205   	}
  206   
  207   	private void checkTransactionNeeded() {
  208   		if ( persistenceContextType == PersistenceContextType.TRANSACTION && ! isTransactionInProgress() ) {
  209   			//no need to mark as rollback, no tx in progress
  210   			throw new TransactionRequiredException(
  211   					"no transaction is in progress for a TRANSACTION type persistence context"
  212   			);
  213   		}
  214   	}
  215   
  216   	public void persist(Object entity) {
  217   		checkTransactionNeeded();
  218   		//adjustFlushMode();
  219   		try {
  220   			getSession().persist( entity );
  221   		}
  222   		catch (MappingException e) {
  223   			throw new IllegalArgumentException( e.getMessage() );
  224   		}
  225   		catch (HibernateException he) {
  226   			throwPersistenceException( he );
  227   		}
  228   	}
  229   
  230   	@SuppressWarnings("unchecked")
  231   	public <A> A merge(A entity) {
  232   		checkTransactionNeeded();
  233   		//adjustFlushMode();
  234   		try {
  235   			return (A) getSession().merge( entity );
  236   		}
  237   		catch (ObjectDeletedException sse) {
  238   			throw new IllegalArgumentException( sse );
  239   		}
  240   		catch (MappingException e) {
  241   			throw new IllegalArgumentException( e.getMessage(), e );
  242   		}
  243   		catch (HibernateException he) {
  244   			throwPersistenceException( he );
  245   			return null;
  246   		}
  247   	}
  248   
  249   	public void remove(Object entity) {
  250   		checkTransactionNeeded();
  251   		//adjustFlushMode();
  252   		try {
  253   			getSession().delete( entity );
  254   		}
  255   		catch (MappingException e) {
  256   			throw new IllegalArgumentException( e.getMessage(), e );
  257   		}
  258   		catch (HibernateException he) {
  259   			throwPersistenceException( he );
  260   		}
  261   	}
  262   
  263   	public void refresh(Object entity) {
  264   		checkTransactionNeeded();
  265   		//adjustFlushMode();
  266   		try {
  267   			if ( ! getSession().contains( entity ) ) {
  268   				throw new IllegalArgumentException( "Entity not managed" );
  269   			}
  270   			getSession().refresh( entity );
  271   		}
  272   		catch (MappingException e) {
  273   			throw new IllegalArgumentException( e.getMessage(), e );
  274   		}
  275   		catch (HibernateException he) {
  276   			throwPersistenceException( he );
  277   		}
  278   	}
  279   
  280   	public boolean contains(Object entity) {
  281   		try {
  282   			if ( entity != null
  283   					&& ! ( entity instanceof HibernateProxy )
  284   					&& getSession().getSessionFactory().getClassMetadata( entity.getClass() ) == null ) {
  285   				throw new IllegalArgumentException( "Not an entity:" + entity.getClass() );
  286   			}
  287   			return getSession().contains( entity );
  288   		}
  289   		catch (MappingException e) {
  290   			throw new IllegalArgumentException( e.getMessage(), e );
  291   		}
  292   		catch (HibernateException he) {
  293   			throwPersistenceException( he );
  294   			return false;
  295   		}
  296   	}
  297   
  298   	public void flush() {
  299   		try {
  300   			if ( ! isTransactionInProgress() ) {
  301   				throw new TransactionRequiredException( "no transaction is in progress" );
  302   			}
  303   			//adjustFlushMode();
  304   			getSession().flush();
  305   		}
  306   		catch (HibernateException he) {
  307   			throwPersistenceException( he );
  308   		}
  309   	}
  310   
  311   	/**
  312   	 * return a Session
  313   	 * @throws IllegalStateException if the entity manager is closed
  314   	 */
  315   	public abstract Session getSession();
  316   
  317   	/**
  318   	 * Return a Session (even if the entity manager is closed
  319   	 */
  320   	protected abstract Session getRawSession();
  321   
  322   	public EntityTransaction getTransaction() {
  323   		if ( transactionType == PersistenceUnitTransactionType.JTA ) {
  324   			throw new IllegalStateException( "A JTA EntityManager cannot use getTransaction()" );
  325   		}
  326   		return tx;
  327   	}
  328   
  329   	public void setFlushMode(FlushModeType flushModeType) {
  330   		this.flushModeType = flushModeType;
  331   		if ( flushModeType == FlushModeType.AUTO ) {
  332   			getSession().setFlushMode( FlushMode.AUTO );
  333   		}
  334   		else if ( flushModeType == FlushModeType.COMMIT ) {
  335   			getSession().setFlushMode( FlushMode.COMMIT );
  336   		}
  337   		else {
  338   			throw new AssertionFailure( "Unknown FlushModeType: " + flushModeType );
  339   		}
  340   	}
  341   
  342   	public void clear() {
  343   		//adjustFlushMode();
  344   		try {
  345   			getSession().clear();
  346   		}
  347   		catch (HibernateException he) {
  348   			throwPersistenceException( he );
  349   		}
  350   	}
  351   
  352   	public FlushModeType getFlushMode() {
  353   		FlushMode mode = getSession().getFlushMode();
  354   		if ( mode == FlushMode.AUTO ) {
  355   			this.flushModeType = FlushModeType.AUTO;
  356   		}
  357   		else if ( mode == FlushMode.COMMIT ) {
  358   			this.flushModeType = FlushModeType.COMMIT;
  359   		}
  360   //		else if ( mode == FlushMode.NEVER ) {
  361   //			if ( PersistenceContextType.EXTENDED == persistenceContextType && !isTransactionInProgress() ) {
  362   //				//we are in flushMode none for EXTENDED
  363   //				return flushMode;
  364   //			}
  365   //			else {
  366   //				return null; //TODO exception?
  367   //			}
  368   //		}
  369   		else {
  370   			return null; //TODO exception?
  371   		}
  372   		//otherwise this is an unknown mode for EJB3
  373   		return flushModeType;
  374   	}
  375   
  376   	public void lock(Object entity, LockModeType lockMode) {
  377   		try {
  378   			if ( ! isTransactionInProgress() ) {
  379   				throw new TransactionRequiredException( "no transaction is in progress" );
  380   			}
  381   			//adjustFlushMode();
  382   			if ( !contains( entity ) ) throw new IllegalArgumentException( "entity not in the persistence context" );
  383   			getSession().lock( entity, getLockMode( lockMode ) );
  384   		}
  385   		catch (HibernateException he) {
  386   			throwPersistenceException( he );
  387   		}
  388   	}
  389   
  390   	private LockMode getLockMode(LockModeType lockMode) {
  391   		switch ( lockMode ) {
  392   			case READ:
  393   				return LockMode.UPGRADE; //assuming we are on read-commited and we need to prevent non repeteable read
  394   			case WRITE:
  395   				return LockMode.FORCE;
  396   			default:
  397   				throw new AssertionFailure( "Unknown LockModeType: " + lockMode );
  398   		}
  399   	}
  400   
  401   	public boolean isTransactionInProgress() {
  402   		return ( (SessionImplementor) getRawSession() ).isTransactionInProgress();
  403   	}
  404   
  405   	protected void markAsRollback() {
  406   		log.debug( "mark transaction for rollback");
  407   		if ( tx.isActive() ) {
  408   			tx.setRollbackOnly();
  409   		}
  410   		else {
  411   			//no explicit use of the tx. boudaries methods
  412   			if ( PersistenceUnitTransactionType.JTA == transactionType ) {
  413   				TransactionManager transactionManager =
  414   						( (SessionFactoryImplementor) getRawSession().getSessionFactory() ).getTransactionManager();
  415   				if ( transactionManager == null ) {
  416   					throw new PersistenceException(
  417   							"Using a JTA persistence context wo setting hibernate.transaction.manager_lookup_class"
  418   					);
  419   				}
  420   				try {
  421   					transactionManager.setRollbackOnly();
  422   				}
  423   				catch (SystemException e) {
  424   					throw new PersistenceException( "Unable to set the JTA transaction as RollbackOnly", e );
  425   				}
  426   			}
  427   		}
  428   	}
  429   
  430   	public void joinTransaction() {
  431   		joinTransaction( false );
  432   	}
  433   
  434   	private void joinTransaction(boolean ignoreNotJoining) {
  435   		//set the joined status
  436   		getSession().isOpen(); //for sync
  437   		if ( transactionType == PersistenceUnitTransactionType.JTA ) {
  438   			try {
  439   				log.debug( "Looking for a JTA transaction to join" );
  440   				final Session session = getSession();
  441   				final Transaction transaction = session.getTransaction();
  442   				if ( transaction != null && transaction instanceof JoinableCMTTransaction ) {
  443   					//can't handle it if not a joinnable transaction
  444   					final JoinableCMTTransaction joinableCMTTransaction = (JoinableCMTTransaction) transaction;
  445   
  446   					if ( joinableCMTTransaction.getStatus() == JoinableCMTTransaction.JoinStatus.JOINED ) {
  447   						log.debug( "Transaction already joined" );
  448   						return; //no-op
  449   					}
  450   					joinableCMTTransaction.markForJoined();
  451   					session.isOpen(); //register to the Tx
  452   					if ( joinableCMTTransaction.getStatus() == JoinableCMTTransaction.JoinStatus.NOT_JOINED ) {
  453   						if ( ignoreNotJoining ) {
  454   							log.debug( "No JTA transaction found" );
  455   							return;
  456   						}
  457   						else {
  458   							throw new TransactionRequiredException(
  459   									"No active JTA transaction on joinTransaction call"
  460   							);
  461   						}
  462   					}
  463   					else
  464   					if ( joinableCMTTransaction.getStatus() == JoinableCMTTransaction.JoinStatus.MARKED_FOR_JOINED ) {
  465   						throw new AssertionFailure( "Transaction MARKED_FOR_JOINED after isOpen() call" );
  466   					}
  467   					//flush before completion and
  468   					//register clear on rollback
  469   					log.trace( "Adding flush() and close() synchronization" );
  470   					joinableCMTTransaction.registerSynchronization(
  471   							new Synchronization() {
  472   								public void beforeCompletion() {
  473   									boolean flush = false;
  474   									TransactionFactory.Context ctx = null;
  475   									try {
  476   										ctx = (TransactionFactory.Context) session;
  477   										JoinableCMTTransaction joinable = (JoinableCMTTransaction) session.getTransaction();
  478   										javax.transaction.Transaction transaction = joinable.getTransaction();
  479   										if ( transaction == null )
  480   											log.warn( "Transaction not available on beforeCompletionPhase: assuming valid" );
  481   										flush = !ctx.isFlushModeNever() &&
  482   												//ctx.isFlushBeforeCompletionEnabled() &&
  483   												//TODO probably make it ! isFlushBeforecompletion()
  484   												( transaction == null || !JTAHelper.isRollback( transaction.getStatus() ) );
  485   												//transaction == null workaround a JBoss TMBug
  486   									}
  487   									catch (SystemException se) {
  488   										log.error( "could not determine transaction status", se );
  489   										//throwPersistenceException will mark the transaction as rollbacked
  490   										throwPersistenceException(
  491   												new PersistenceException(
  492   														"could not determine transaction status in beforeCompletion()",
  493   														se
  494   												)
  495   										);
  496   									}
  497   									catch (HibernateException he) {
  498   										throwPersistenceException( he );
  499   									}
  500   
  501   									try {
  502   										if ( flush ) {
  503   											log.trace( "automatically flushing session" );
  504   											ctx.managedFlush();
  505   										}
  506   										else {
  507   											log.trace( "skipping managed flushing" );
  508   										}
  509   									}
  510   									catch (RuntimeException re) {
  511   										//throwPersistenceException will mark the transaction as rollbacked
  512   										if ( re instanceof HibernateException ) {
  513   											throwPersistenceException( (HibernateException) re );
  514   										}
  515   										else {
  516   											throwPersistenceException( new PersistenceException( re ) );
  517   										}
  518   									}
  519   								}
  520   
  521   								public void afterCompletion(int status) {
  522   									try {
  523   										if ( Status.STATUS_ROLLEDBACK == status
  524   												&& transactionType == PersistenceUnitTransactionType.JTA ) {
  525   											if ( session.isOpen() ) {
  526   												session.clear();
  527   											}
  528   										}
  529   										if ( session.isOpen() ) {
  530   											//only reset if the session is opened since you can't get the Transaction otherwise
  531   											JoinableCMTTransaction joinable = (JoinableCMTTransaction) session.getTransaction();
  532   											joinable.resetStatus();
  533   										}
  534   									}
  535   									catch (HibernateException e) {
  536   										throwPersistenceException( e );
  537   									}
  538   								}
  539   							}
  540   					);
  541   				}
  542   				else {
  543   					log.warn( "Cannot join transaction: do not override {}", Environment.TRANSACTION_STRATEGY );
  544   				}
  545   			}
  546   			catch (HibernateException he) {
  547   				throwPersistenceException( he );
  548   			}
  549   		}
  550   		else {
  551   			if ( !ignoreNotJoining ) log.warn( "Calling joinTransaction() on a non JTA EntityManager" );
  552   		}
  553   	}
  554   
  555   	/**
  556   	 * returns the underlying session
  557   	 */
  558   	public Object getDelegate() {
  559   		return getSession();
  560   	}
  561   
  562   	;
  563   
  564   	private void writeObject(ObjectOutputStream oos) throws IOException {
  565   		oos.defaultWriteObject();
  566   	}
  567   
  568   	private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
  569   		ois.defaultReadObject();
  570   		tx = new TransactionImpl( this );
  571   	}
  572   
  573   	public void throwPersistenceException(PersistenceException e) {
  574   		if ( ! ( e instanceof NoResultException || e instanceof NonUniqueResultException ) ) {
  575   			try {
  576   				markAsRollback();
  577   			}
  578   			catch (Exception ne) {
  579   				//we do not want the subsequent exception to swallow the original one
  580   				log.error( "Unable to mark for rollback on PersistenceException: ", ne);
  581   			}
  582   		}
  583   		throw e;
  584   	}
  585   
  586   	public void throwPersistenceException(HibernateException e) {
  587   		if ( e instanceof StaleStateException ) {
  588   			PersistenceException pe = wrapStaleStateException( (StaleStateException) e );
  589   			throwPersistenceException( pe );
  590   		}
  591   		else if ( e instanceof ObjectNotFoundException ) {
  592   			throwPersistenceException( new EntityNotFoundException( e.getMessage() ) );
  593   		}
  594   		else if ( e instanceof org.hibernate.NonUniqueResultException ) {
  595   			throwPersistenceException( new NonUniqueResultException( e.getMessage() ) );
  596   		}
  597   		else if ( e instanceof UnresolvableObjectException ) {
  598   			throwPersistenceException( new EntityNotFoundException( e.getMessage() ) );
  599   		}
  600   		else if ( e instanceof QueryException ) {
  601   			throw new IllegalArgumentException( e );
  602   		}
  603   		else if ( e instanceof TransientObjectException ) {
  604   			try {
  605   				markAsRollback();
  606   			}
  607   			catch (Exception ne) {
  608   				//we do not want the subsequent exception to swallow the original one
  609   				log.error( "Unable to mark for rollback on TransientObjectException: ", ne);
  610   			}
  611   			throw new IllegalStateException( e ); //Spec 3.2.3 Synchronization rules
  612   		}
  613   		else {
  614   			throwPersistenceException( new PersistenceException( e ) );
  615   		}
  616   	}
  617   
  618   	public PersistenceException wrapStaleStateException(StaleStateException e) {
  619   		PersistenceException pe;
  620   		if ( e instanceof StaleObjectStateException ) {
  621   			StaleObjectStateException sose = (StaleObjectStateException) e;
  622   			Serializable identifier = sose.getIdentifier();
  623   			if (identifier != null) {
  624   				Object entity = getRawSession().load( sose.getEntityName(), identifier );
  625   				if ( entity instanceof Serializable ) {
  626   					//avoid some user errors regarding boundary crossing
  627   					pe = new OptimisticLockException( null, e, entity );
  628   				}
  629   				else {
  630   					pe = new OptimisticLockException( e );
  631   				}
  632   			}
  633   			else {
  634   				pe = new OptimisticLockException( e );
  635   			}
  636   		}
  637   		else {
  638   			pe = new OptimisticLockException( e );
  639   		}
  640   		return pe;
  641   	}
  642   }

Save This Page
Home » hibernate-entitymanager-sources » org.hibernate » ejb » [javadoc | source]