SVN layout migration for core/branches/Branch_3_2
git-svn-id: https://svn.jboss.org/repos/hibernate/core/branches/Branch_3_2@11765 1b8cb986-b30d-0410-93ca-fae66ebed9b2
This commit is contained in:
parent
975d485a74
commit
c654277e62
|
@ -0,0 +1,36 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
import org.hibernate.exception.NestableRuntimeException;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Indicates failure of an assertion: a possible bug in Hibernate.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
|
||||
public class AssertionFailure extends NestableRuntimeException {
|
||||
|
||||
private static final Log log = LogFactory.getLog(AssertionFailure.class);
|
||||
|
||||
private static final String MESSAGE = "an assertion failure occured (this may indicate a bug in Hibernate, but is more likely due to unsafe use of the session)";
|
||||
|
||||
public AssertionFailure(String s) {
|
||||
super(s);
|
||||
log.error(MESSAGE, this);
|
||||
}
|
||||
|
||||
public AssertionFailure(String s, Throwable t) {
|
||||
super(s, t);
|
||||
log.error(MESSAGE, t);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Controls how the session interacts with the second-level
|
||||
* cache and query cache.
|
||||
*
|
||||
* @see Session#setCacheMode(CacheMode)
|
||||
* @author Gavin King
|
||||
*/
|
||||
public final class CacheMode implements Serializable {
|
||||
private final String name;
|
||||
private final boolean isPutEnabled;
|
||||
private final boolean isGetEnabled;
|
||||
private static final Map INSTANCES = new HashMap();
|
||||
|
||||
private CacheMode(String name, boolean isPutEnabled, boolean isGetEnabled) {
|
||||
this.name=name;
|
||||
this.isPutEnabled = isPutEnabled;
|
||||
this.isGetEnabled = isGetEnabled;
|
||||
}
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
public boolean isPutEnabled() {
|
||||
return isPutEnabled;
|
||||
}
|
||||
public boolean isGetEnabled() {
|
||||
return isGetEnabled;
|
||||
}
|
||||
/**
|
||||
* The session may read items from the cache, and add items to the cache
|
||||
*/
|
||||
public static final CacheMode NORMAL = new CacheMode("NORMAL", true, true);
|
||||
/**
|
||||
* The session will never interact with the cache, except to invalidate
|
||||
* cache items when updates occur
|
||||
*/
|
||||
public static final CacheMode IGNORE = new CacheMode("IGNORE", false, false);
|
||||
/**
|
||||
* The session may read items from the cache, but will not add items,
|
||||
* except to invalidate items when updates occur
|
||||
*/
|
||||
public static final CacheMode GET = new CacheMode("GET", false, true);
|
||||
/**
|
||||
* The session will never read items from the cache, but will add items
|
||||
* to the cache as it reads them from the database.
|
||||
*/
|
||||
public static final CacheMode PUT = new CacheMode("PUT", true, false);
|
||||
|
||||
/**
|
||||
* The session will never read items from the cache, but will add items
|
||||
* to the cache as it reads them from the database. In this mode, the
|
||||
* effect of <tt>hibernate.cache.use_minimal_puts</tt> is bypassed, in
|
||||
* order to <em>force</em> a cache refresh
|
||||
*/
|
||||
public static final CacheMode REFRESH = new CacheMode("REFRESH", true, false);
|
||||
|
||||
static {
|
||||
INSTANCES.put( NORMAL.name, NORMAL );
|
||||
INSTANCES.put( IGNORE.name, IGNORE );
|
||||
INSTANCES.put( GET.name, GET );
|
||||
INSTANCES.put( PUT.name, PUT );
|
||||
INSTANCES.put( REFRESH.name, REFRESH );
|
||||
}
|
||||
|
||||
private Object readResolve() {
|
||||
return INSTANCES.get( name );
|
||||
}
|
||||
|
||||
public static CacheMode parse(String name) {
|
||||
return ( CacheMode ) INSTANCES.get( name );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
|
||||
/**
|
||||
* Should be thrown by persistent objects from <tt>Lifecycle</tt>
|
||||
* or <tt>Interceptor</tt> callbacks.
|
||||
*
|
||||
* @see Lifecycle
|
||||
* @see Interceptor
|
||||
* @author Gavin King
|
||||
*/
|
||||
|
||||
public class CallbackException extends HibernateException {
|
||||
|
||||
public CallbackException(Exception root) {
|
||||
super("An exception occurred in a callback", root);
|
||||
}
|
||||
|
||||
public CallbackException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CallbackException(String message, Exception e) {
|
||||
super(message, e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
// $Id$
|
||||
package org.hibernate;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Defines the various policies by which Hibernate might release its underlying
|
||||
* JDBC connection.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class ConnectionReleaseMode implements Serializable {
|
||||
|
||||
/**
|
||||
* Indicates that JDBC connection should be aggressively released after each
|
||||
* SQL statement is executed. In this mode, the application <em>must</em>
|
||||
* explicitly close all iterators and scrollable results. This mode may
|
||||
* only be used with a JTA datasource.
|
||||
*/
|
||||
public static final ConnectionReleaseMode AFTER_STATEMENT = new ConnectionReleaseMode( "after_statement" );
|
||||
|
||||
/**
|
||||
* Indicates that JDBC connections should be released after each transaction
|
||||
* ends (works with both JTA-registered synch and HibernateTransaction API).
|
||||
* This mode may not be used with an application server JTA datasource.
|
||||
* <p/>
|
||||
* This is the default mode starting in 3.1; was previously {@link #ON_CLOSE}.
|
||||
*/
|
||||
public static final ConnectionReleaseMode AFTER_TRANSACTION = new ConnectionReleaseMode( "after_transaction" );
|
||||
|
||||
/**
|
||||
* Indicates that connections should only be released when the Session is explicitly closed
|
||||
* or disconnected; this is the legacy (Hibernate2 and pre-3.1) behavior.
|
||||
*/
|
||||
public static final ConnectionReleaseMode ON_CLOSE = new ConnectionReleaseMode( "on_close" );
|
||||
|
||||
|
||||
private String name;
|
||||
|
||||
private ConnectionReleaseMode(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Override of Object.toString(). Returns the release mode name.
|
||||
*
|
||||
* @return The release mode name.
|
||||
*/
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine the correct ConnectionReleaseMode instance based on the given
|
||||
* name.
|
||||
*
|
||||
* @param modeName The release mode name.
|
||||
* @return The appropriate ConnectionReleaseMode instance
|
||||
* @throws HibernateException Indicates the modeName param did not match any known modes.
|
||||
*/
|
||||
public static ConnectionReleaseMode parse(String modeName) throws HibernateException {
|
||||
if ( AFTER_STATEMENT.name.equals( modeName ) ) {
|
||||
return AFTER_STATEMENT;
|
||||
}
|
||||
else if ( AFTER_TRANSACTION.name.equals( modeName ) ) {
|
||||
return AFTER_TRANSACTION;
|
||||
}
|
||||
else if ( ON_CLOSE.name.equals( modeName ) ) {
|
||||
return ON_CLOSE;
|
||||
}
|
||||
throw new HibernateException( "could not determine appropriate connection release mode [" + modeName + "]" );
|
||||
}
|
||||
|
||||
private Object readResolve() {
|
||||
return parse( name );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,338 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.hibernate.criterion.CriteriaSpecification;
|
||||
import org.hibernate.criterion.Criterion;
|
||||
import org.hibernate.criterion.Order;
|
||||
import org.hibernate.criterion.Projection;
|
||||
import org.hibernate.transform.ResultTransformer;
|
||||
|
||||
/**
|
||||
* <tt>Criteria</tt> is a simplified API for retrieving entities
|
||||
* by composing <tt>Criterion</tt> objects. This is a very
|
||||
* convenient approach for functionality like "search" screens
|
||||
* where there is a variable number of conditions to be placed
|
||||
* upon the result set.<br>
|
||||
* <br>
|
||||
* The <tt>Session</tt> is a factory for <tt>Criteria</tt>.
|
||||
* <tt>Criterion</tt> instances are usually obtained via
|
||||
* the factory methods on <tt>Restrictions</tt>. eg.
|
||||
* <pre>
|
||||
* List cats = session.createCriteria(Cat.class)
|
||||
* .add( Restrictions.like("name", "Iz%") )
|
||||
* .add( Restrictions.gt( "weight", new Float(minWeight) ) )
|
||||
* .addOrder( Order.asc("age") )
|
||||
* .list();
|
||||
* </pre>
|
||||
* You may navigate associations using <tt>createAlias()</tt> or
|
||||
* <tt>createCriteria()</tt>.
|
||||
* <pre>
|
||||
* List cats = session.createCriteria(Cat.class)
|
||||
* .createCriteria("kittens")
|
||||
* .add( Restrictions.like("name", "Iz%") )
|
||||
* .list();
|
||||
* </pre>
|
||||
* <pre>
|
||||
* List cats = session.createCriteria(Cat.class)
|
||||
* .createAlias("kittens", "kit")
|
||||
* .add( Restrictions.like("kit.name", "Iz%") )
|
||||
* .list();
|
||||
* </pre>
|
||||
* You may specify projection and aggregation using <tt>Projection</tt>
|
||||
* instances obtained via the factory methods on <tt>Projections</tt>.
|
||||
* <pre>
|
||||
* List cats = session.createCriteria(Cat.class)
|
||||
* .setProjection( Projections.projectionList()
|
||||
* .add( Projections.rowCount() )
|
||||
* .add( Projections.avg("weight") )
|
||||
* .add( Projections.max("weight") )
|
||||
* .add( Projections.min("weight") )
|
||||
* .add( Projections.groupProperty("color") )
|
||||
* )
|
||||
* .addOrder( Order.asc("color") )
|
||||
* .list();
|
||||
* </pre>
|
||||
*
|
||||
* @see Session#createCriteria(java.lang.Class)
|
||||
* @see org.hibernate.criterion.Restrictions
|
||||
* @see org.hibernate.criterion.Projections
|
||||
* @see org.hibernate.criterion.Order
|
||||
* @see org.hibernate.criterion.Criterion
|
||||
* @see org.hibernate.criterion.Projection
|
||||
* @see org.hibernate.criterion.DetachedCriteria a disconnected version of this API
|
||||
* @author Gavin King
|
||||
*/
|
||||
public interface Criteria extends CriteriaSpecification {
|
||||
|
||||
/**
|
||||
* Get the alias of the entity encapsulated by this criteria instance.
|
||||
*
|
||||
* @return The alias for the encapsulated entity.
|
||||
*/
|
||||
public String getAlias();
|
||||
|
||||
/**
|
||||
* Used to specify that the query results will be a projection (scalar in
|
||||
* nature). Implicitly specifies the {@link #PROJECTION} result transformer.
|
||||
* <p/>
|
||||
* The individual components contained within the given
|
||||
* {@link Projection projection} determines the overall "shape" of the
|
||||
* query result.
|
||||
*
|
||||
* @param projection The projection representing the overall "shape" of the
|
||||
* query results.
|
||||
* @return this (for method chaining)
|
||||
*/
|
||||
public Criteria setProjection(Projection projection);
|
||||
|
||||
/**
|
||||
* Add a {@link Criterion restriction} to constrain the results to be
|
||||
* retrieved.
|
||||
*
|
||||
* @param criterion The {@link Criterion criterion} object representing the
|
||||
* restriction to be applied.
|
||||
* @return this (for method chaining)
|
||||
*/
|
||||
public Criteria add(Criterion criterion);
|
||||
|
||||
/**
|
||||
* Add an {@link Order ordering} to the result set.
|
||||
*
|
||||
* @param order The {@link Order order} object representing an ordering
|
||||
* to be applied to the results.
|
||||
* @return this (for method chaining)
|
||||
*/
|
||||
public Criteria addOrder(Order order);
|
||||
|
||||
/**
|
||||
* Specify an association fetching strategy for an association or a
|
||||
* collection of values.
|
||||
*
|
||||
* @param associationPath a dot seperated property path
|
||||
* @param mode The fetch mode for the referenced association
|
||||
* @return this (for method chaining)
|
||||
*/
|
||||
public Criteria setFetchMode(String associationPath, FetchMode mode) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Set the lock mode of the current entity
|
||||
*
|
||||
* @param lockMode The lock mode to be applied
|
||||
* @return this (for method chaining)
|
||||
*/
|
||||
public Criteria setLockMode(LockMode lockMode);
|
||||
|
||||
/**
|
||||
* Set the lock mode of the aliased entity
|
||||
*
|
||||
* @param alias The previously assigned alias representing the entity to
|
||||
* which the given lock mode should apply.
|
||||
* @param lockMode The lock mode to be applied
|
||||
* @return this (for method chaining)
|
||||
*/
|
||||
public Criteria setLockMode(String alias, LockMode lockMode);
|
||||
|
||||
/**
|
||||
* Join an association, assigning an alias to the joined association.
|
||||
* <p/>
|
||||
* Functionally equivalent to {@link #createAlias(String, String, int)} using
|
||||
* {@link #INNER_JOIN} for the joinType.
|
||||
*
|
||||
* @param associationPath A dot-seperated property path
|
||||
* @param alias The alias to assign to the joined association (for later reference).
|
||||
* @return this (for method chaining)
|
||||
*/
|
||||
public Criteria createAlias(String associationPath, String alias) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Join an association using the specified join-type, assigning an alias
|
||||
* to the joined association.
|
||||
* <p/>
|
||||
* The joinType is expected to be one of {@link #INNER_JOIN} (the default),
|
||||
* {@link #FULL_JOIN}, or {@link #LEFT_JOIN}.
|
||||
*
|
||||
* @param associationPath A dot-seperated property path
|
||||
* @param alias The alias to assign to the joined association (for later reference).
|
||||
* @param joinType The type of join to use.
|
||||
* @return this (for method chaining)
|
||||
*/
|
||||
public Criteria createAlias(String associationPath, String alias, int joinType) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Create a new <tt>Criteria</tt>, "rooted" at the associated entity.
|
||||
* <p/>
|
||||
* Functionally equivalent to {@link #createCriteria(String, int)} using
|
||||
* {@link #INNER_JOIN} for the joinType.
|
||||
*
|
||||
* @param associationPath A dot-seperated property path
|
||||
* @return the created "sub criteria"
|
||||
*/
|
||||
public Criteria createCriteria(String associationPath) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Create a new <tt>Criteria</tt>, "rooted" at the associated entity, using the
|
||||
* specified join type.
|
||||
*
|
||||
* @param associationPath A dot-seperated property path
|
||||
* @param joinType The type of join to use.
|
||||
* @return the created "sub criteria"
|
||||
*/
|
||||
public Criteria createCriteria(String associationPath, int joinType) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Create a new <tt>Criteria</tt>, "rooted" at the associated entity,
|
||||
* assigning the given alias.
|
||||
* <p/>
|
||||
* Functionally equivalent to {@link #createCriteria(String, String, int)} using
|
||||
* {@link #INNER_JOIN} for the joinType.
|
||||
*
|
||||
* @param associationPath A dot-seperated property path
|
||||
* @param alias The alias to assign to the joined association (for later reference).
|
||||
* @return the created "sub criteria"
|
||||
*/
|
||||
public Criteria createCriteria(String associationPath, String alias) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Create a new <tt>Criteria</tt>, "rooted" at the associated entity,
|
||||
* assigning the given alias and using the specified join type.
|
||||
*
|
||||
* @param associationPath A dot-seperated property path
|
||||
* @param alias The alias to assign to the joined association (for later reference).
|
||||
* @param joinType The type of join to use.
|
||||
* @return the created "sub criteria"
|
||||
*/
|
||||
public Criteria createCriteria(String associationPath, String alias, int joinType) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Set a strategy for handling the query results. This determines the
|
||||
* "shape" of the query result.
|
||||
*
|
||||
* @param resultTransformer The transformer to apply
|
||||
* @return this (for method chaining)
|
||||
*
|
||||
* @see #ROOT_ENTITY
|
||||
* @see #DISTINCT_ROOT_ENTITY
|
||||
* @see #ALIAS_TO_ENTITY_MAP
|
||||
* @see #PROJECTION
|
||||
*/
|
||||
public Criteria setResultTransformer(ResultTransformer resultTransformer);
|
||||
|
||||
/**
|
||||
* Set a limit upon the number of objects to be retrieved.
|
||||
*
|
||||
* @param maxResults the maximum number of results
|
||||
* @return this (for method chaining)
|
||||
*/
|
||||
public Criteria setMaxResults(int maxResults);
|
||||
|
||||
/**
|
||||
* Set the first result to be retrieved.
|
||||
*
|
||||
* @param firstResult the first result to retrieve, numbered from <tt>0</tt>
|
||||
* @return this (for method chaining)
|
||||
*/
|
||||
public Criteria setFirstResult(int firstResult);
|
||||
|
||||
/**
|
||||
* Set a fetch size for the underlying JDBC query.
|
||||
*
|
||||
* @param fetchSize the fetch size
|
||||
* @return this (for method chaining)
|
||||
*
|
||||
* @see java.sql.Statement#setFetchSize
|
||||
*/
|
||||
public Criteria setFetchSize(int fetchSize);
|
||||
|
||||
/**
|
||||
* Set a timeout for the underlying JDBC query.
|
||||
*
|
||||
* @param timeout The timeout value to apply.
|
||||
* @return this (for method chaining)
|
||||
*
|
||||
* @see java.sql.Statement#setQueryTimeout
|
||||
*/
|
||||
public Criteria setTimeout(int timeout);
|
||||
|
||||
/**
|
||||
* Enable caching of this query result, provided query caching is enabled
|
||||
* for the underlying session factory.
|
||||
*
|
||||
* @param cacheable Should the result be considered cacheable; default is
|
||||
* to not cache (false).
|
||||
* @return this (for method chaining)
|
||||
*/
|
||||
public Criteria setCacheable(boolean cacheable);
|
||||
|
||||
/**
|
||||
* Set the name of the cache region to use for query result caching.
|
||||
*
|
||||
* @param cacheRegion the name of a query cache region, or <tt>null</tt>
|
||||
* for the default query cache
|
||||
* @return this (for method chaining)
|
||||
*
|
||||
* @see #setCacheable
|
||||
*/
|
||||
public Criteria setCacheRegion(String cacheRegion);
|
||||
|
||||
/**
|
||||
* Add a comment to the generated SQL.
|
||||
*
|
||||
* @param comment a human-readable string
|
||||
* @return this (for method chaining)
|
||||
*/
|
||||
public Criteria setComment(String comment);
|
||||
|
||||
/**
|
||||
* Override the flush mode for this particular query.
|
||||
*
|
||||
* @param flushMode The flush mode to use.
|
||||
* @return this (for method chaining)
|
||||
*/
|
||||
public Criteria setFlushMode(FlushMode flushMode);
|
||||
|
||||
/**
|
||||
* Override the cache mode for this particular query.
|
||||
*
|
||||
* @param cacheMode The cache mode to use.
|
||||
* @return this (for method chaining)
|
||||
*/
|
||||
public Criteria setCacheMode(CacheMode cacheMode);
|
||||
|
||||
/**
|
||||
* Get the results.
|
||||
*
|
||||
* @return The list of matched query results.
|
||||
*/
|
||||
public List list() throws HibernateException;
|
||||
|
||||
/**
|
||||
* Get the results as an instance of {@link ScrollableResults}
|
||||
*
|
||||
* @return The {@link ScrollableResults} representing the matched
|
||||
* query results.
|
||||
*/
|
||||
public ScrollableResults scroll() throws HibernateException;
|
||||
|
||||
/**
|
||||
* Get the results as an instance of {@link ScrollableResults} based on the
|
||||
* given scroll mode.
|
||||
*
|
||||
* @param scrollMode Indicates the type of underlying database cursor to
|
||||
* request.
|
||||
* @return The {@link ScrollableResults} representing the matched
|
||||
* query results.
|
||||
*/
|
||||
public ScrollableResults scroll(ScrollMode scrollMode) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Convenience method to return a single instance that matches
|
||||
* the query, or null if the query returns no results.
|
||||
*
|
||||
* @return the single result or <tt>null</tt>
|
||||
* @throws HibernateException if there is more than one matching result
|
||||
*/
|
||||
public Object uniqueResult() throws HibernateException;
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package org.hibernate;
|
||||
|
||||
/**
|
||||
* Raised whenever a duplicate for a certain type occurs.
|
||||
* Duplicate class, table, property name etc.
|
||||
*
|
||||
* @author Max Rydahl Andersen
|
||||
*
|
||||
*/
|
||||
public class DuplicateMappingException extends MappingException {
|
||||
|
||||
private final String name;
|
||||
private final String type;
|
||||
|
||||
public DuplicateMappingException(String customMessage, String type, String name) {
|
||||
super(customMessage);
|
||||
this.type=type;
|
||||
this.name=name;
|
||||
}
|
||||
|
||||
public DuplicateMappingException(String type, String name) {
|
||||
this("Duplicate " + type + " mapping " + name, type, name);
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
/**
|
||||
* An interceptor that does nothing. May be used as a base class
|
||||
* for application-defined custom interceptors.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class EmptyInterceptor implements Interceptor, Serializable {
|
||||
|
||||
public static final Interceptor INSTANCE = new EmptyInterceptor();
|
||||
|
||||
protected EmptyInterceptor() {}
|
||||
|
||||
public void onDelete(
|
||||
Object entity,
|
||||
Serializable id,
|
||||
Object[] state,
|
||||
String[] propertyNames,
|
||||
Type[] types) {}
|
||||
|
||||
public boolean onFlushDirty(
|
||||
Object entity,
|
||||
Serializable id,
|
||||
Object[] currentState,
|
||||
Object[] previousState,
|
||||
String[] propertyNames,
|
||||
Type[] types) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean onLoad(
|
||||
Object entity,
|
||||
Serializable id,
|
||||
Object[] state,
|
||||
String[] propertyNames,
|
||||
Type[] types) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean onSave(
|
||||
Object entity,
|
||||
Serializable id,
|
||||
Object[] state,
|
||||
String[] propertyNames,
|
||||
Type[] types) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void postFlush(Iterator entities) {}
|
||||
public void preFlush(Iterator entities) {}
|
||||
|
||||
public Boolean isTransient(Object entity) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object instantiate(String entityName, EntityMode entityMode, Serializable id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public int[] findDirty(Object entity,
|
||||
Serializable id,
|
||||
Object[] currentState,
|
||||
Object[] previousState,
|
||||
String[] propertyNames,
|
||||
Type[] types) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getEntityName(Object object) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public Object getEntity(String entityName, Serializable id) {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void afterTransactionBegin(Transaction tx) {}
|
||||
public void afterTransactionCompletion(Transaction tx) {}
|
||||
public void beforeTransactionCompletion(Transaction tx) {}
|
||||
|
||||
public String onPrepareStatement(String sql) {
|
||||
return sql;
|
||||
}
|
||||
|
||||
public void onCollectionRemove(Object collection, Serializable key) throws CallbackException {}
|
||||
|
||||
public void onCollectionRecreate(Object collection, Serializable key) throws CallbackException {}
|
||||
|
||||
public void onCollectionUpdate(Object collection, Serializable key) throws CallbackException {}
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
// $Id$
|
||||
package org.hibernate;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Defines the representation modes available for entities.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class EntityMode implements Serializable {
|
||||
|
||||
private static final Map INSTANCES = new HashMap();
|
||||
|
||||
public static final EntityMode POJO = new EntityMode( "pojo" );
|
||||
public static final EntityMode DOM4J = new EntityMode( "dom4j" );
|
||||
public static final EntityMode MAP = new EntityMode( "dynamic-map" );
|
||||
|
||||
static {
|
||||
INSTANCES.put( POJO.name, POJO );
|
||||
INSTANCES.put( DOM4J.name, DOM4J );
|
||||
INSTANCES.put( MAP.name, MAP );
|
||||
}
|
||||
|
||||
private final String name;
|
||||
|
||||
public EntityMode(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
private Object readResolve() {
|
||||
return INSTANCES.get( name );
|
||||
}
|
||||
|
||||
public static EntityMode parse(String name) {
|
||||
EntityMode rtn = ( EntityMode ) INSTANCES.get( name );
|
||||
if ( rtn == null ) {
|
||||
// default is POJO
|
||||
rtn = POJO;
|
||||
}
|
||||
return rtn;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Represents an association fetching strategy. This is used
|
||||
* together with the <tt>Criteria</tt> API to specify runtime
|
||||
* fetching strategies.<br>
|
||||
* <br>
|
||||
* For HQL queries, use the <tt>FETCH</tt> keyword instead.
|
||||
*
|
||||
* @see Criteria#setFetchMode(java.lang.String, FetchMode)
|
||||
* @author Gavin King
|
||||
*/
|
||||
public final class FetchMode implements Serializable {
|
||||
private final String name;
|
||||
private static final Map INSTANCES = new HashMap();
|
||||
|
||||
private FetchMode(String name) {
|
||||
this.name=name;
|
||||
}
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
/**
|
||||
* Default to the setting configured in the mapping file.
|
||||
*/
|
||||
public static final FetchMode DEFAULT = new FetchMode("DEFAULT");
|
||||
|
||||
/**
|
||||
* Fetch using an outer join. Equivalent to <tt>fetch="join"</tt>.
|
||||
*/
|
||||
public static final FetchMode JOIN = new FetchMode("JOIN");
|
||||
/**
|
||||
* Fetch eagerly, using a separate select. Equivalent to
|
||||
* <tt>fetch="select"</tt>.
|
||||
*/
|
||||
public static final FetchMode SELECT = new FetchMode("SELECT");
|
||||
|
||||
/**
|
||||
* Fetch lazily. Equivalent to <tt>outer-join="false"</tt>.
|
||||
* @deprecated use <tt>FetchMode.SELECT</tt>
|
||||
*/
|
||||
public static final FetchMode LAZY = SELECT;
|
||||
/**
|
||||
* Fetch eagerly, using an outer join. Equivalent to
|
||||
* <tt>outer-join="true"</tt>.
|
||||
* @deprecated use <tt>FetchMode.JOIN</tt>
|
||||
*/
|
||||
public static final FetchMode EAGER = JOIN;
|
||||
|
||||
static {
|
||||
INSTANCES.put( JOIN.name, JOIN );
|
||||
INSTANCES.put( SELECT.name, SELECT );
|
||||
INSTANCES.put( DEFAULT.name, DEFAULT );
|
||||
}
|
||||
|
||||
private Object readResolve() {
|
||||
return INSTANCES.get(name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
// $Id$
|
||||
package org.hibernate;
|
||||
|
||||
import org.hibernate.engine.FilterDefinition;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* Type definition of Filter. Filter defines the user's view into enabled dynamic filters,
|
||||
* allowing them to set filter parameter values.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface Filter {
|
||||
|
||||
/**
|
||||
* Get the name of this filter.
|
||||
*
|
||||
* @return This filter's name.
|
||||
*/
|
||||
public String getName();
|
||||
|
||||
/**
|
||||
* Get the filter definition containing additional information about the
|
||||
* filter (such as default-condition and expected parameter names/types).
|
||||
*
|
||||
* @return The filter definition
|
||||
*/
|
||||
public FilterDefinition getFilterDefinition();
|
||||
|
||||
|
||||
/**
|
||||
* Set the named parameter's value for this filter.
|
||||
*
|
||||
* @param name The parameter's name.
|
||||
* @param value The value to be applied.
|
||||
* @return This FilterImpl instance (for method chaining).
|
||||
*/
|
||||
public Filter setParameter(String name, Object value);
|
||||
|
||||
/**
|
||||
* Set the named parameter's value list for this filter. Used
|
||||
* in conjunction with IN-style filter criteria.
|
||||
*
|
||||
* @param name The parameter's name.
|
||||
* @param values The values to be expanded into an SQL IN list.
|
||||
* @return This FilterImpl instance (for method chaining).
|
||||
*/
|
||||
public Filter setParameterList(String name, Collection values);
|
||||
|
||||
/**
|
||||
* Set the named parameter's value list for this filter. Used
|
||||
* in conjunction with IN-style filter criteria.
|
||||
*
|
||||
* @param name The parameter's name.
|
||||
* @param values The values to be expanded into an SQL IN list.
|
||||
* @return This FilterImpl instance (for method chaining).
|
||||
*/
|
||||
public Filter setParameterList(String name, Object[] values);
|
||||
|
||||
/**
|
||||
* Perform validation of the filter state. This is used to verify the
|
||||
* state of the filter after its enablement and before its use.
|
||||
*
|
||||
* @throws HibernateException If the state is not currently valid.
|
||||
*/
|
||||
public void validate() throws HibernateException;
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Represents a flushing strategy. The flush process synchronizes
|
||||
* database state with session state by detecting state changes
|
||||
* and executing SQL statements.
|
||||
*
|
||||
* @see Session#setFlushMode(FlushMode)
|
||||
* @see Query#setFlushMode(FlushMode)
|
||||
* @see Criteria#setFlushMode(FlushMode)
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public final class FlushMode implements Serializable {
|
||||
private static final Map INSTANCES = new HashMap();
|
||||
|
||||
private final int level;
|
||||
private final String name;
|
||||
|
||||
private FlushMode(int level, String name) {
|
||||
this.level = level;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* The {@link Session} is never flushed unless {@link Session#flush}
|
||||
* is explicitly called by the application. This mode is very
|
||||
* efficient for read only transactions.
|
||||
*
|
||||
* @deprecated use {@link #MANUAL} instead.
|
||||
*/
|
||||
public static final FlushMode NEVER = new FlushMode( 0, "NEVER" );
|
||||
|
||||
/**
|
||||
* The {@link Session} is only ever flushed when {@link Session#flush}
|
||||
* is explicitly called by the application. This mode is very
|
||||
* efficient for read only transactions.
|
||||
*/
|
||||
public static final FlushMode MANUAL = new FlushMode( 0, "MANUAL" );
|
||||
|
||||
/**
|
||||
* The {@link Session} is flushed when {@link Transaction#commit}
|
||||
* is called.
|
||||
*/
|
||||
public static final FlushMode COMMIT = new FlushMode(5, "COMMIT");
|
||||
|
||||
/**
|
||||
* The {@link Session} is sometimes flushed before query execution
|
||||
* in order to ensure that queries never return stale state. This
|
||||
* is the default flush mode.
|
||||
*/
|
||||
public static final FlushMode AUTO = new FlushMode(10, "AUTO");
|
||||
|
||||
/**
|
||||
* The {@link Session} is flushed before every query. This is
|
||||
* almost always unnecessary and inefficient.
|
||||
*/
|
||||
public static final FlushMode ALWAYS = new FlushMode(20, "ALWAYS");
|
||||
|
||||
public boolean lessThan(FlushMode other) {
|
||||
return this.level<other.level;
|
||||
}
|
||||
|
||||
static {
|
||||
INSTANCES.put( NEVER.name, NEVER );
|
||||
INSTANCES.put( MANUAL.name, MANUAL );
|
||||
INSTANCES.put( AUTO.name, AUTO );
|
||||
INSTANCES.put( ALWAYS.name, ALWAYS );
|
||||
INSTANCES.put( COMMIT.name, COMMIT );
|
||||
}
|
||||
|
||||
public static boolean isManualFlushMode(FlushMode mode) {
|
||||
return MANUAL.level == mode.level;
|
||||
}
|
||||
|
||||
private Object readResolve() {
|
||||
return INSTANCES.get( name );
|
||||
}
|
||||
|
||||
public static FlushMode parse(String name) {
|
||||
return ( FlushMode ) INSTANCES.get( name );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,454 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.Serializable;
|
||||
import java.sql.Blob;
|
||||
import java.sql.Clob;
|
||||
import java.util.Iterator;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.hibernate.collection.PersistentCollection;
|
||||
import org.hibernate.engine.HibernateIterator;
|
||||
import org.hibernate.intercept.FieldInterceptionHelper;
|
||||
import org.hibernate.intercept.FieldInterceptor;
|
||||
import org.hibernate.lob.BlobImpl;
|
||||
import org.hibernate.lob.ClobImpl;
|
||||
import org.hibernate.lob.SerializableBlob;
|
||||
import org.hibernate.lob.SerializableClob;
|
||||
import org.hibernate.proxy.HibernateProxy;
|
||||
import org.hibernate.proxy.LazyInitializer;
|
||||
import org.hibernate.type.AnyType;
|
||||
import org.hibernate.type.BigDecimalType;
|
||||
import org.hibernate.type.BigIntegerType;
|
||||
import org.hibernate.type.BinaryType;
|
||||
import org.hibernate.type.BlobType;
|
||||
import org.hibernate.type.BooleanType;
|
||||
import org.hibernate.type.ByteType;
|
||||
import org.hibernate.type.CalendarDateType;
|
||||
import org.hibernate.type.CalendarType;
|
||||
import org.hibernate.type.CharacterType;
|
||||
import org.hibernate.type.ClassType;
|
||||
import org.hibernate.type.ClobType;
|
||||
import org.hibernate.type.CompositeCustomType;
|
||||
import org.hibernate.type.CurrencyType;
|
||||
import org.hibernate.type.CustomType;
|
||||
import org.hibernate.type.DateType;
|
||||
import org.hibernate.type.DoubleType;
|
||||
import org.hibernate.type.FloatType;
|
||||
import org.hibernate.type.IntegerType;
|
||||
import org.hibernate.type.LocaleType;
|
||||
import org.hibernate.type.LongType;
|
||||
import org.hibernate.type.ManyToOneType;
|
||||
import org.hibernate.type.NullableType;
|
||||
import org.hibernate.type.SerializableType;
|
||||
import org.hibernate.type.ShortType;
|
||||
import org.hibernate.type.StringType;
|
||||
import org.hibernate.type.TextType;
|
||||
import org.hibernate.type.TimeType;
|
||||
import org.hibernate.type.TimeZoneType;
|
||||
import org.hibernate.type.TimestampType;
|
||||
import org.hibernate.type.TrueFalseType;
|
||||
import org.hibernate.type.Type;
|
||||
import org.hibernate.type.YesNoType;
|
||||
import org.hibernate.type.CharArrayType;
|
||||
import org.hibernate.type.WrapperBinaryType;
|
||||
import org.hibernate.type.CharacterArrayType;
|
||||
import org.hibernate.usertype.CompositeUserType;
|
||||
|
||||
/**
|
||||
* <ul>
|
||||
* <li>Provides access to the full range of Hibernate built-in types. <tt>Type</tt>
|
||||
* instances may be used to bind values to query parameters.
|
||||
* <li>A factory for new <tt>Blob</tt>s and <tt>Clob</tt>s.
|
||||
* <li>Defines static methods for manipulation of proxies.
|
||||
* </ul>
|
||||
*
|
||||
* @author Gavin King
|
||||
* @see java.sql.Clob
|
||||
* @see java.sql.Blob
|
||||
* @see org.hibernate.type.Type
|
||||
*/
|
||||
|
||||
public final class Hibernate {
|
||||
|
||||
/**
|
||||
* Hibernate <tt>long</tt> type.
|
||||
*/
|
||||
public static final NullableType LONG = new LongType();
|
||||
/**
|
||||
* Hibernate <tt>short</tt> type.
|
||||
*/
|
||||
public static final NullableType SHORT = new ShortType();
|
||||
/**
|
||||
* Hibernate <tt>integer</tt> type.
|
||||
*/
|
||||
public static final NullableType INTEGER = new IntegerType();
|
||||
/**
|
||||
* Hibernate <tt>byte</tt> type.
|
||||
*/
|
||||
public static final NullableType BYTE = new ByteType();
|
||||
/**
|
||||
* Hibernate <tt>float</tt> type.
|
||||
*/
|
||||
public static final NullableType FLOAT = new FloatType();
|
||||
/**
|
||||
* Hibernate <tt>double</tt> type.
|
||||
*/
|
||||
public static final NullableType DOUBLE = new DoubleType();
|
||||
/**
|
||||
* Hibernate <tt>character</tt> type.
|
||||
*/
|
||||
public static final NullableType CHARACTER = new CharacterType();
|
||||
/**
|
||||
* Hibernate <tt>string</tt> type.
|
||||
*/
|
||||
public static final NullableType STRING = new StringType();
|
||||
/**
|
||||
* Hibernate <tt>time</tt> type.
|
||||
*/
|
||||
public static final NullableType TIME = new TimeType();
|
||||
/**
|
||||
* Hibernate <tt>date</tt> type.
|
||||
*/
|
||||
public static final NullableType DATE = new DateType();
|
||||
/**
|
||||
* Hibernate <tt>timestamp</tt> type.
|
||||
*/
|
||||
public static final NullableType TIMESTAMP = new TimestampType();
|
||||
/**
|
||||
* Hibernate <tt>boolean</tt> type.
|
||||
*/
|
||||
public static final NullableType BOOLEAN = new BooleanType();
|
||||
/**
|
||||
* Hibernate <tt>true_false</tt> type.
|
||||
*/
|
||||
public static final NullableType TRUE_FALSE = new TrueFalseType();
|
||||
/**
|
||||
* Hibernate <tt>yes_no</tt> type.
|
||||
*/
|
||||
public static final NullableType YES_NO = new YesNoType();
|
||||
/**
|
||||
* Hibernate <tt>big_decimal</tt> type.
|
||||
*/
|
||||
public static final NullableType BIG_DECIMAL = new BigDecimalType();
|
||||
/**
|
||||
* Hibernate <tt>big_integer</tt> type.
|
||||
*/
|
||||
public static final NullableType BIG_INTEGER = new BigIntegerType();
|
||||
/**
|
||||
* Hibernate <tt>binary</tt> type.
|
||||
*/
|
||||
public static final NullableType BINARY = new BinaryType();
|
||||
/**
|
||||
* Hibernate <tt>wrapper-binary</tt> type.
|
||||
*/
|
||||
public static final NullableType WRAPPER_BINARY = new WrapperBinaryType();
|
||||
/**
|
||||
* Hibernate char[] type.
|
||||
*/
|
||||
public static final NullableType CHAR_ARRAY = new CharArrayType();
|
||||
/**
|
||||
* Hibernate Character[] type.
|
||||
*/
|
||||
public static final NullableType CHARACTER_ARRAY = new CharacterArrayType();
|
||||
/**
|
||||
* Hibernate <tt>text</tt> type.
|
||||
*/
|
||||
public static final NullableType TEXT = new TextType();
|
||||
/**
|
||||
* Hibernate <tt>blob</tt> type.
|
||||
*/
|
||||
public static final Type BLOB = new BlobType();
|
||||
/**
|
||||
* Hibernate <tt>clob</tt> type.
|
||||
*/
|
||||
public static final Type CLOB = new ClobType();
|
||||
/**
|
||||
* Hibernate <tt>calendar</tt> type.
|
||||
*/
|
||||
public static final NullableType CALENDAR = new CalendarType();
|
||||
/**
|
||||
* Hibernate <tt>calendar_date</tt> type.
|
||||
*/
|
||||
public static final NullableType CALENDAR_DATE = new CalendarDateType();
|
||||
/**
|
||||
* Hibernate <tt>locale</tt> type.
|
||||
*/
|
||||
public static final NullableType LOCALE = new LocaleType();
|
||||
/**
|
||||
* Hibernate <tt>currency</tt> type.
|
||||
*/
|
||||
public static final NullableType CURRENCY = new CurrencyType();
|
||||
/**
|
||||
* Hibernate <tt>timezone</tt> type.
|
||||
*/
|
||||
public static final NullableType TIMEZONE = new TimeZoneType();
|
||||
/**
|
||||
* Hibernate <tt>class</tt> type.
|
||||
*/
|
||||
public static final NullableType CLASS = new ClassType();
|
||||
/**
|
||||
* Hibernate <tt>serializable</tt> type.
|
||||
*/
|
||||
public static final NullableType SERIALIZABLE = new SerializableType( Serializable.class );
|
||||
/**
|
||||
* Hibernate <tt>object</tt> type.
|
||||
*/
|
||||
public static final Type OBJECT = new AnyType();
|
||||
|
||||
|
||||
/**
|
||||
* Cannot be instantiated.
|
||||
*/
|
||||
private Hibernate() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
/**
|
||||
* A Hibernate <tt>serializable</tt> type.
|
||||
*/
|
||||
public static Type serializable(Class serializableClass) {
|
||||
return new SerializableType( serializableClass );
|
||||
}
|
||||
|
||||
/**
|
||||
* A Hibernate <tt>any</tt> type.
|
||||
*
|
||||
* @param metaType a type mapping <tt>java.lang.Class</tt> to a single column
|
||||
* @param identifierType the entity identifier type
|
||||
* @return the Type
|
||||
*/
|
||||
public static Type any(Type metaType, Type identifierType) {
|
||||
return new AnyType( metaType, identifierType );
|
||||
}
|
||||
|
||||
/**
|
||||
* A Hibernate persistent object (entity) type.
|
||||
*
|
||||
* @param persistentClass a mapped entity class
|
||||
*/
|
||||
public static Type entity(Class persistentClass) {
|
||||
// not really a many-to-one association *necessarily*
|
||||
return new ManyToOneType( persistentClass.getName() );
|
||||
}
|
||||
|
||||
/**
|
||||
* A Hibernate persistent object (entity) type.
|
||||
*
|
||||
* @param entityName a mapped entity class
|
||||
*/
|
||||
public static Type entity(String entityName) {
|
||||
// not really a many-to-one association *necessarily*
|
||||
return new ManyToOneType( entityName );
|
||||
}
|
||||
|
||||
/**
|
||||
* A Hibernate custom type.
|
||||
*
|
||||
* @param userTypeClass a class that implements <tt>UserType</tt>
|
||||
*/
|
||||
public static Type custom(Class userTypeClass) throws HibernateException {
|
||||
return custom( userTypeClass, null );
|
||||
}
|
||||
|
||||
/**
|
||||
* A Hibernate parameterizable custom type.
|
||||
*
|
||||
* @param userTypeClass a class that implements <tt>UserType and ParameterizableType</tt>
|
||||
* @param parameterNames the names of the parameters passed to the type
|
||||
* @param parameterValues the values of the parameters passed to the type. They must match
|
||||
* up with the order and length of the parameterNames array.
|
||||
*/
|
||||
public static Type custom(Class userTypeClass, String[] parameterNames, String[] parameterValues)
|
||||
throws HibernateException {
|
||||
Properties parameters = new Properties();
|
||||
for ( int i = 0; i < parameterNames.length; i++ ) {
|
||||
parameters.setProperty( parameterNames[i], parameterValues[i] );
|
||||
}
|
||||
return custom( userTypeClass, parameters );
|
||||
}
|
||||
|
||||
/**
|
||||
* A Hibernate parameterizable custom type.
|
||||
*
|
||||
* @param userTypeClass a class that implements <tt>UserType and ParameterizableType</tt>
|
||||
* @param parameters the parameters as a collection of name/value pairs
|
||||
*/
|
||||
public static Type custom(Class userTypeClass, Properties parameters)
|
||||
throws HibernateException {
|
||||
if ( CompositeUserType.class.isAssignableFrom( userTypeClass ) ) {
|
||||
CompositeCustomType type = new CompositeCustomType( userTypeClass, parameters );
|
||||
return type;
|
||||
}
|
||||
else {
|
||||
CustomType type = new CustomType( userTypeClass, parameters );
|
||||
return type;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Force initialization of a proxy or persistent collection.
|
||||
* <p/>
|
||||
* Note: This only ensures intialization of a proxy object or collection;
|
||||
* it is not guaranteed that the elements INSIDE the collection will be initialized/materialized.
|
||||
*
|
||||
* @param proxy a persistable object, proxy, persistent collection or <tt>null</tt>
|
||||
* @throws HibernateException if we can't initialize the proxy at this time, eg. the <tt>Session</tt> was closed
|
||||
*/
|
||||
public static void initialize(Object proxy) throws HibernateException {
|
||||
if ( proxy == null ) {
|
||||
return;
|
||||
}
|
||||
else if ( proxy instanceof HibernateProxy ) {
|
||||
( ( HibernateProxy ) proxy ).getHibernateLazyInitializer().initialize();
|
||||
}
|
||||
else if ( proxy instanceof PersistentCollection ) {
|
||||
( ( PersistentCollection ) proxy ).forceInitialization();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the proxy or persistent collection is initialized.
|
||||
*
|
||||
* @param proxy a persistable object, proxy, persistent collection or <tt>null</tt>
|
||||
* @return true if the argument is already initialized, or is not a proxy or collection
|
||||
*/
|
||||
public static boolean isInitialized(Object proxy) {
|
||||
if ( proxy instanceof HibernateProxy ) {
|
||||
return !( ( HibernateProxy ) proxy ).getHibernateLazyInitializer().isUninitialized();
|
||||
}
|
||||
else if ( proxy instanceof PersistentCollection ) {
|
||||
return ( ( PersistentCollection ) proxy ).wasInitialized();
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the true, underlying class of a proxied persistent class. This operation
|
||||
* will initialize a proxy by side-effect.
|
||||
*
|
||||
* @param proxy a persistable object or proxy
|
||||
* @return the true class of the instance
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public static Class getClass(Object proxy) {
|
||||
if ( proxy instanceof HibernateProxy ) {
|
||||
return ( ( HibernateProxy ) proxy ).getHibernateLazyInitializer()
|
||||
.getImplementation()
|
||||
.getClass();
|
||||
}
|
||||
else {
|
||||
return proxy.getClass();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new <tt>Blob</tt>. The returned object will be initially immutable.
|
||||
*
|
||||
* @param bytes a byte array
|
||||
* @return the Blob
|
||||
*/
|
||||
public static Blob createBlob(byte[] bytes) {
|
||||
return new SerializableBlob( new BlobImpl( bytes ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new <tt>Blob</tt>. The returned object will be initially immutable.
|
||||
*
|
||||
* @param stream a binary stream
|
||||
* @param length the number of bytes in the stream
|
||||
* @return the Blob
|
||||
*/
|
||||
public static Blob createBlob(InputStream stream, int length) {
|
||||
return new SerializableBlob( new BlobImpl( stream, length ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new <tt>Blob</tt>. The returned object will be initially immutable.
|
||||
*
|
||||
* @param stream a binary stream
|
||||
* @return the Blob
|
||||
* @throws IOException
|
||||
*/
|
||||
public static Blob createBlob(InputStream stream) throws IOException {
|
||||
return new SerializableBlob( new BlobImpl( stream, stream.available() ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new <tt>Clob</tt>. The returned object will be initially immutable.
|
||||
*
|
||||
* @param string a <tt>String</tt>
|
||||
*/
|
||||
public static Clob createClob(String string) {
|
||||
return new SerializableClob( new ClobImpl( string ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new <tt>Clob</tt>. The returned object will be initially immutable.
|
||||
*
|
||||
* @param reader a character stream
|
||||
* @param length the number of characters in the stream
|
||||
*/
|
||||
public static Clob createClob(Reader reader, int length) {
|
||||
return new SerializableClob( new ClobImpl( reader, length ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Close an <tt>Iterator</tt> created by <tt>iterate()</tt> immediately,
|
||||
* instead of waiting until the session is closed or disconnected.
|
||||
*
|
||||
* @param iterator an <tt>Iterator</tt> created by <tt>iterate()</tt>
|
||||
* @throws HibernateException
|
||||
* @see org.hibernate.Query#iterate
|
||||
* @see Query#iterate()
|
||||
*/
|
||||
public static void close(Iterator iterator) throws HibernateException {
|
||||
if ( iterator instanceof HibernateIterator ) {
|
||||
( ( HibernateIterator ) iterator ).close();
|
||||
}
|
||||
else {
|
||||
throw new IllegalArgumentException( "not a Hibernate iterator" );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the property is initialized. If the named property does not exist
|
||||
* or is not persistent, this method always returns <tt>true</tt>.
|
||||
*
|
||||
* @param proxy The potential proxy
|
||||
* @param propertyName the name of a persistent attribute of the object
|
||||
* @return true if the named property of the object is not listed as uninitialized
|
||||
* @return false if the object is an uninitialized proxy, or the named property is uninitialized
|
||||
*/
|
||||
public static boolean isPropertyInitialized(Object proxy, String propertyName) {
|
||||
|
||||
Object entity;
|
||||
if ( proxy instanceof HibernateProxy ) {
|
||||
LazyInitializer li = ( ( HibernateProxy ) proxy ).getHibernateLazyInitializer();
|
||||
if ( li.isUninitialized() ) {
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
entity = li.getImplementation();
|
||||
}
|
||||
}
|
||||
else {
|
||||
entity = proxy;
|
||||
}
|
||||
|
||||
if ( FieldInterceptionHelper.isInstrumented( entity ) ) {
|
||||
FieldInterceptor interceptor = FieldInterceptionHelper.extractFieldInterceptor( entity );
|
||||
return interceptor == null || interceptor.isInitialized( propertyName );
|
||||
}
|
||||
else {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
import org.hibernate.exception.NestableRuntimeException;
|
||||
|
||||
/**
|
||||
* Any exception that occurs inside the persistence layer
|
||||
* or JDBC driver. <tt>SQLException</tt>s are always wrapped
|
||||
* by instances of <tt>JDBCException</tt>.
|
||||
*
|
||||
* @see JDBCException
|
||||
* @author Gavin King
|
||||
*/
|
||||
|
||||
public class HibernateException extends NestableRuntimeException {
|
||||
|
||||
public HibernateException(Throwable root) {
|
||||
super(root);
|
||||
}
|
||||
|
||||
public HibernateException(String string, Throwable root) {
|
||||
super(string, root);
|
||||
}
|
||||
|
||||
public HibernateException(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
/**
|
||||
* Thrown if Hibernate can't instantiate an entity or component
|
||||
* class at runtime.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
|
||||
public class InstantiationException extends HibernateException {
|
||||
|
||||
private final Class clazz;
|
||||
|
||||
public InstantiationException(String s, Class clazz, Throwable root) {
|
||||
super(s, root);
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
public InstantiationException(String s, Class clazz) {
|
||||
super(s);
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
public InstantiationException(String s, Class clazz, Exception e) {
|
||||
super(s, e);
|
||||
this.clazz = clazz;
|
||||
}
|
||||
|
||||
public Class getPersistentClass() {
|
||||
return clazz;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return super.getMessage() + clazz.getName();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
/**
|
||||
* Allows user code to inspect and/or change property values.
|
||||
* <br><br>
|
||||
* Inspection occurs before property values are written and after they are read
|
||||
* from the database.<br>
|
||||
* <br>
|
||||
* There might be a single instance of <tt>Interceptor</tt> for a <tt>SessionFactory</tt>, or a new instance
|
||||
* might be specified for each <tt>Session</tt>. Whichever approach is used, the interceptor must be
|
||||
* serializable if the <tt>Session</tt> is to be serializable. This means that <tt>SessionFactory</tt>-scoped
|
||||
* interceptors should implement <tt>readResolve()</tt>.<br>
|
||||
* <br>
|
||||
* The <tt>Session</tt> may not be invoked from a callback (nor may a callback cause a collection or proxy to
|
||||
* be lazily initialized).<br>
|
||||
* <br>
|
||||
* Instead of implementing this interface directly, it is usually better to extend <tt>EmptyInterceptor</tt>
|
||||
* and override only the callback methods of interest.
|
||||
*
|
||||
* @see SessionFactory#openSession(Interceptor)
|
||||
* @see org.hibernate.cfg.Configuration#setInterceptor(Interceptor)
|
||||
* @see EmptyInterceptor
|
||||
* @author Gavin King
|
||||
*/
|
||||
public interface Interceptor {
|
||||
/**
|
||||
* Called just before an object is initialized. The interceptor may change the <tt>state</tt>, which will
|
||||
* be propagated to the persistent object. Note that when this method is called, <tt>entity</tt> will be
|
||||
* an empty uninitialized instance of the class.
|
||||
*
|
||||
* @return <tt>true</tt> if the user modified the <tt>state</tt> in any way.
|
||||
*/
|
||||
public boolean onLoad(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) throws CallbackException;
|
||||
/**
|
||||
* Called when an object is detected to be dirty, during a flush. The interceptor may modify the detected
|
||||
* <tt>currentState</tt>, which will be propagated to both the database and the persistent object.
|
||||
* Note that not all flushes end in actual synchronization with the database, in which case the
|
||||
* new <tt>currentState</tt> will be propagated to the object, but not necessarily (immediately) to
|
||||
* the database. It is strongly recommended that the interceptor <b>not</b> modify the <tt>previousState</tt>.
|
||||
*
|
||||
* @return <tt>true</tt> if the user modified the <tt>currentState</tt> in any way.
|
||||
*/
|
||||
public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) throws CallbackException;
|
||||
/**
|
||||
* Called before an object is saved. The interceptor may modify the <tt>state</tt>, which will be used for
|
||||
* the SQL <tt>INSERT</tt> and propagated to the persistent object.
|
||||
*
|
||||
* @return <tt>true</tt> if the user modified the <tt>state</tt> in any way.
|
||||
*/
|
||||
public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) throws CallbackException;
|
||||
/**
|
||||
* Called before an object is deleted. It is not recommended that the interceptor modify the <tt>state</tt>.
|
||||
*/
|
||||
public void onDelete(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) throws CallbackException;
|
||||
/**
|
||||
* Called before a collection is (re)created.
|
||||
*/
|
||||
public void onCollectionRecreate(Object collection, Serializable key) throws CallbackException;
|
||||
/**
|
||||
* Called before a collection is deleted.
|
||||
*/
|
||||
public void onCollectionRemove(Object collection, Serializable key) throws CallbackException;
|
||||
/**
|
||||
* Called before a collection is updated.
|
||||
*/
|
||||
public void onCollectionUpdate(Object collection, Serializable key) throws CallbackException;
|
||||
/**
|
||||
* Called before a flush
|
||||
*/
|
||||
public void preFlush(Iterator entities) throws CallbackException;
|
||||
/**
|
||||
* Called after a flush that actually ends in execution of the SQL statements required to synchronize
|
||||
* in-memory state with the database.
|
||||
*/
|
||||
public void postFlush(Iterator entities) throws CallbackException;
|
||||
/**
|
||||
* Called to distinguish between transient and detached entities. The return value determines the
|
||||
* state of the entity with respect to the current session.
|
||||
* <ul>
|
||||
* <li><tt>Boolean.TRUE</tt> - the entity is transient
|
||||
* <li><tt>Boolean.FALSE</tt> - the entity is detached
|
||||
* <li><tt>null</tt> - Hibernate uses the <tt>unsaved-value</tt> mapping and other heuristics to
|
||||
* determine if the object is unsaved
|
||||
* </ul>
|
||||
* @param entity a transient or detached entity
|
||||
* @return Boolean or <tt>null</tt> to choose default behaviour
|
||||
*/
|
||||
public Boolean isTransient(Object entity);
|
||||
/**
|
||||
* Called from <tt>flush()</tt>. The return value determines whether the entity is updated
|
||||
* <ul>
|
||||
* <li>an array of property indices - the entity is dirty
|
||||
* <li>an empty array - the entity is not dirty
|
||||
* <li><tt>null</tt> - use Hibernate's default dirty-checking algorithm
|
||||
* </ul>
|
||||
* @param entity a persistent entity
|
||||
* @return array of dirty property indices or <tt>null</tt> to choose default behaviour
|
||||
*/
|
||||
public int[] findDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types);
|
||||
/**
|
||||
* Instantiate the entity class. Return <tt>null</tt> to indicate that Hibernate should use
|
||||
* the default constructor of the class. The identifier property of the returned instance
|
||||
* should be initialized with the given identifier.
|
||||
*
|
||||
* @param entityName the name of the entity
|
||||
* @param entityMode The type of entity instance to be returned.
|
||||
* @param id the identifier of the new instance
|
||||
* @return an instance of the class, or <tt>null</tt> to choose default behaviour
|
||||
*/
|
||||
public Object instantiate(String entityName, EntityMode entityMode, Serializable id) throws CallbackException;
|
||||
|
||||
/**
|
||||
* Get the entity name for a persistent or transient instance
|
||||
* @param object an entity instance
|
||||
* @return the name of the entity
|
||||
*/
|
||||
public String getEntityName(Object object) throws CallbackException;
|
||||
|
||||
/**
|
||||
* Get a fully loaded entity instance that is cached externally
|
||||
* @param entityName the name of the entity
|
||||
* @param id the instance identifier
|
||||
* @return a fully initialized entity
|
||||
* @throws CallbackException
|
||||
*/
|
||||
public Object getEntity(String entityName, Serializable id) throws CallbackException;
|
||||
|
||||
/**
|
||||
* Called when a Hibernate transaction is begun via the Hibernate <tt>Transaction</tt>
|
||||
* API. Will not be called if transactions are being controlled via some other
|
||||
* mechanism (CMT, for example).
|
||||
*/
|
||||
public void afterTransactionBegin(Transaction tx);
|
||||
/**
|
||||
* Called before a transaction is committed (but not before rollback).
|
||||
*/
|
||||
public void beforeTransactionCompletion(Transaction tx);
|
||||
/**
|
||||
* Called after a transaction is committed or rolled back.
|
||||
*/
|
||||
public void afterTransactionCompletion(Transaction tx);
|
||||
|
||||
/**
|
||||
* Called when sql string is being prepared.
|
||||
* @param sql sql to be prepared
|
||||
* @return original or modified sql
|
||||
*/
|
||||
public String onPrepareStatement(String sql);
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
package org.hibernate;
|
||||
|
||||
/**
|
||||
* Thrown when a mapping is found to be invalid.
|
||||
* Similar to MappingException, but this contains more info about the path and type of mapping (e.g. file, resource or url)
|
||||
*
|
||||
* @author Max Rydahl Andersen
|
||||
*
|
||||
*/
|
||||
public class InvalidMappingException extends MappingException {
|
||||
|
||||
private final String path;
|
||||
private final String type;
|
||||
|
||||
public InvalidMappingException(String customMessage, String type, String path, Throwable cause) {
|
||||
super(customMessage, cause);
|
||||
this.type=type;
|
||||
this.path=path;
|
||||
}
|
||||
|
||||
public InvalidMappingException(String customMessage, String type, String path) {
|
||||
super(customMessage);
|
||||
this.type=type;
|
||||
this.path=path;
|
||||
}
|
||||
|
||||
public InvalidMappingException(String type, String path) {
|
||||
this("Could not parse mapping document from " + type + (path==null?"":" " + path), type, path);
|
||||
}
|
||||
|
||||
public InvalidMappingException(String type, String path, Throwable cause) {
|
||||
this("Could not parse mapping document from " + type + (path==null?"":" " + path), type, path, cause);
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
import java.sql.SQLException;
|
||||
|
||||
|
||||
/**
|
||||
* Wraps an <tt>SQLException</tt>. Indicates that an exception
|
||||
* occurred during a JDBC call.
|
||||
*
|
||||
* @see java.sql.SQLException
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class JDBCException extends HibernateException {
|
||||
|
||||
private SQLException sqle;
|
||||
private String sql;
|
||||
|
||||
public JDBCException(String string, SQLException root) {
|
||||
super(string, root);
|
||||
sqle=root;
|
||||
}
|
||||
|
||||
public JDBCException(String string, SQLException root, String sql) {
|
||||
this(string, root);
|
||||
this.sql = sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the SQLState of the underlying <tt>SQLException</tt>.
|
||||
* @see java.sql.SQLException
|
||||
* @return String
|
||||
*/
|
||||
public String getSQLState() {
|
||||
return sqle.getSQLState();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the <tt>errorCode</tt> of the underlying <tt>SQLException</tt>.
|
||||
* @see java.sql.SQLException
|
||||
* @return int the error code
|
||||
*/
|
||||
public int getErrorCode() {
|
||||
return sqle.getErrorCode();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the underlying <tt>SQLException</tt>.
|
||||
* @return SQLException
|
||||
*/
|
||||
public SQLException getSQLException() {
|
||||
return sqle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the actual SQL statement that caused the exception
|
||||
* (may be null)
|
||||
*/
|
||||
public String getSQL() {
|
||||
return sql;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Indicates access to unfetched data outside of a session context.
|
||||
* For example, when an uninitialized proxy or collection is accessed
|
||||
* after the session was closed.
|
||||
*
|
||||
* @see Hibernate#initialize(java.lang.Object)
|
||||
* @see Hibernate#isInitialized(java.lang.Object)
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class LazyInitializationException extends HibernateException {
|
||||
|
||||
public LazyInitializationException(String msg) {
|
||||
super(msg);
|
||||
LogFactory.getLog(LazyInitializationException.class).error(msg, this);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Instances represent a lock mode for a row of a relational
|
||||
* database table. It is not intended that users spend much
|
||||
* time worrying about locking since Hibernate usually
|
||||
* obtains exactly the right lock level automatically.
|
||||
* Some "advanced" users may wish to explicitly specify lock
|
||||
* levels.
|
||||
*
|
||||
* @see Session#lock(Object,LockMode)
|
||||
* @author Gavin King
|
||||
*/
|
||||
public final class LockMode implements Serializable {
|
||||
private final int level;
|
||||
private final String name;
|
||||
private static final Map INSTANCES = new HashMap();
|
||||
|
||||
private LockMode(int level, String name) {
|
||||
this.level=level;
|
||||
this.name=name;
|
||||
}
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
/**
|
||||
* Check if this lock mode is more restrictive than the given lock mode.
|
||||
*
|
||||
* @param mode LockMode to check
|
||||
* @return true if this lock mode is more restrictive than given lock mode
|
||||
*/
|
||||
public boolean greaterThan(LockMode mode) {
|
||||
return level > mode.level;
|
||||
}
|
||||
/**
|
||||
* Check if this lock mode is less restrictive than the given lock mode.
|
||||
*
|
||||
* @param mode LockMode to check
|
||||
* @return true if this lock mode is less restrictive than given lock mode
|
||||
*/
|
||||
public boolean lessThan(LockMode mode) {
|
||||
return level < mode.level;
|
||||
}
|
||||
/**
|
||||
* No lock required. If an object is requested with this lock
|
||||
* mode, a <tt>READ</tt> lock will be obtained if it is
|
||||
* necessary to actually read the state from the database,
|
||||
* rather than pull it from a cache.<br>
|
||||
* <br>
|
||||
* This is the "default" lock mode.
|
||||
*/
|
||||
public static final LockMode NONE = new LockMode(0, "NONE");
|
||||
/**
|
||||
* A shared lock. Objects in this lock mode were read from
|
||||
* the database in the current transaction, rather than being
|
||||
* pulled from a cache.
|
||||
*/
|
||||
public static final LockMode READ = new LockMode(5, "READ");
|
||||
/**
|
||||
* An upgrade lock. Objects loaded in this lock mode are
|
||||
* materialized using an SQL <tt>select ... for update</tt>.
|
||||
*/
|
||||
public static final LockMode UPGRADE = new LockMode(10, "UPGRADE");
|
||||
/**
|
||||
* Attempt to obtain an upgrade lock, using an Oracle-style
|
||||
* <tt>select for update nowait</tt>. The semantics of
|
||||
* this lock mode, once obtained, are the same as
|
||||
* <tt>UPGRADE</tt>.
|
||||
*/
|
||||
public static final LockMode UPGRADE_NOWAIT = new LockMode(10, "UPGRADE_NOWAIT");
|
||||
/**
|
||||
* A <tt>WRITE</tt> lock is obtained when an object is updated
|
||||
* or inserted. This lock mode is for internal use only and is
|
||||
* not a valid mode for <tt>load()</tt> or <tt>lock()</tt> (both
|
||||
* of which throw exceptions if WRITE is specified).
|
||||
*/
|
||||
public static final LockMode WRITE = new LockMode(10, "WRITE");
|
||||
|
||||
/**
|
||||
* Similiar to {@link #UPGRADE} except that, for versioned entities,
|
||||
* it results in a forced version increment.
|
||||
*/
|
||||
public static final LockMode FORCE = new LockMode( 15, "FORCE" );
|
||||
|
||||
static {
|
||||
INSTANCES.put( NONE.name, NONE );
|
||||
INSTANCES.put( READ.name, READ );
|
||||
INSTANCES.put( UPGRADE.name, UPGRADE );
|
||||
INSTANCES.put( UPGRADE_NOWAIT.name, UPGRADE_NOWAIT );
|
||||
INSTANCES.put( WRITE.name, WRITE );
|
||||
INSTANCES.put( FORCE.name, FORCE );
|
||||
}
|
||||
|
||||
private Object readResolve() {
|
||||
return parse( name );
|
||||
}
|
||||
|
||||
public static LockMode parse(String name) {
|
||||
return ( LockMode ) INSTANCES.get(name);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
/**
|
||||
* An exception that usually occurs at configuration time, rather
|
||||
* than runtime, as a result of something screwy in the O-R mappings.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
|
||||
public class MappingException extends HibernateException {
|
||||
|
||||
public MappingException(String msg, Throwable root) {
|
||||
super( msg, root );
|
||||
}
|
||||
|
||||
public MappingException(Throwable root) {
|
||||
super(root);
|
||||
}
|
||||
|
||||
public MappingException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,41 @@
|
|||
package org.hibernate;
|
||||
|
||||
/**
|
||||
* Thrown when a resource for a mapping could not be found.
|
||||
*
|
||||
* @author Max Rydahl Andersen
|
||||
*
|
||||
*/
|
||||
public class MappingNotFoundException extends MappingException {
|
||||
|
||||
private final String path;
|
||||
private final String type;
|
||||
|
||||
public MappingNotFoundException(String customMessage, String type, String path, Throwable cause) {
|
||||
super(customMessage, cause);
|
||||
this.type=type;
|
||||
this.path=path;
|
||||
}
|
||||
|
||||
public MappingNotFoundException(String customMessage, String type, String path) {
|
||||
super(customMessage);
|
||||
this.type=type;
|
||||
this.path=path;
|
||||
}
|
||||
|
||||
public MappingNotFoundException(String type, String path) {
|
||||
this(type + ": " + path + " not found", type, path);
|
||||
}
|
||||
|
||||
public MappingNotFoundException(String type, String path, Throwable cause) {
|
||||
this(type + ": " + path + " not found", type, path, cause);
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
return path;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
|
||||
/**
|
||||
* This exception is thrown when an operation would
|
||||
* break session-scoped identity. This occurs if the
|
||||
* user tries to associate two different instances of
|
||||
* the same Java class with a particular identifier,
|
||||
* in the scope of a single <tt>Session</tt>.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class NonUniqueObjectException extends HibernateException {
|
||||
private final Serializable identifier;
|
||||
private final String entityName;
|
||||
|
||||
public NonUniqueObjectException(String message, Serializable id, String clazz) {
|
||||
super(message);
|
||||
this.entityName = clazz;
|
||||
this.identifier = id;
|
||||
}
|
||||
|
||||
public NonUniqueObjectException(Serializable id, String clazz) {
|
||||
this("a different object with the same identifier value was already associated with the session", id, clazz);
|
||||
}
|
||||
|
||||
public Serializable getIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return super.getMessage() + ": " +
|
||||
MessageHelper.infoString(entityName, identifier);
|
||||
}
|
||||
|
||||
public String getEntityName() {
|
||||
return entityName;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
/**
|
||||
* Thrown when the application calls <tt>Query.uniqueResult()</tt> and
|
||||
* the query returned more than one result. Unlike all other Hibernate
|
||||
* exceptions, this one is recoverable!
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class NonUniqueResultException extends HibernateException {
|
||||
|
||||
public NonUniqueResultException(int resultCount) {
|
||||
super( "query did not return a unique result: " + resultCount );
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Thrown when the user tries to do something illegal with a deleted
|
||||
* object.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class ObjectDeletedException extends UnresolvableObjectException {
|
||||
|
||||
public ObjectDeletedException(String message, Serializable identifier, String clazz) {
|
||||
super(message, identifier, clazz);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Thrown when <tt>Session.load()</tt> fails to select a row with
|
||||
* the given primary key (identifier value). This exception might not
|
||||
* be thrown when <tt>load()</tt> is called, even if there was no
|
||||
* row on the database, because <tt>load()</tt> returns a proxy if
|
||||
* possible. Applications should use <tt>Session.get()</tt> to test if
|
||||
* a row exists in the database.<br>
|
||||
* <br>
|
||||
* Like all Hibernate exceptions, this exception is considered
|
||||
* unrecoverable.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class ObjectNotFoundException extends UnresolvableObjectException {
|
||||
|
||||
public ObjectNotFoundException(Serializable identifier, String clazz) {
|
||||
super(identifier, clazz);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
/**
|
||||
* Thrown when the user passes a persistent instance to a <tt>Session</tt>
|
||||
* method that expects a transient instance.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class PersistentObjectException extends HibernateException {
|
||||
|
||||
public PersistentObjectException(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
import org.hibernate.util.StringHelper;
|
||||
|
||||
/**
|
||||
* A problem occurred accessing a property of an instance of a
|
||||
* persistent class by reflection, or via CGLIB. There are a
|
||||
* number of possible underlying causes, including
|
||||
* <ul>
|
||||
* <li>failure of a security check
|
||||
* <li>an exception occurring inside the getter or setter method
|
||||
* <li>a nullable database column was mapped to a primitive-type property
|
||||
* <li>the Hibernate type was not castable to the property type (or vice-versa)
|
||||
* </ul>
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class PropertyAccessException extends HibernateException {
|
||||
|
||||
private final Class persistentClass;
|
||||
private final String propertyName;
|
||||
private final boolean wasSetter;
|
||||
|
||||
public PropertyAccessException(Throwable root, String s, boolean wasSetter, Class persistentClass, String propertyName) {
|
||||
super(s, root);
|
||||
this.persistentClass = persistentClass;
|
||||
this.wasSetter = wasSetter;
|
||||
this.propertyName = propertyName;
|
||||
}
|
||||
|
||||
public Class getPersistentClass() {
|
||||
return persistentClass;
|
||||
}
|
||||
|
||||
public String getPropertyName() {
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return super.getMessage() +
|
||||
( wasSetter ? " setter of " : " getter of ") +
|
||||
StringHelper.qualify( persistentClass.getName(), propertyName );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
/**
|
||||
* Indicates that an expected getter or setter method could not be
|
||||
* found on a class.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class PropertyNotFoundException extends MappingException {
|
||||
|
||||
public PropertyNotFoundException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
import org.hibernate.util.StringHelper;
|
||||
|
||||
/**
|
||||
* Thrown when the (illegal) value of a property can not be persisted.
|
||||
* There are two main causes:
|
||||
* <ul>
|
||||
* <li>a property declared <tt>not-null="true"</tt> is null
|
||||
* <li>an association references an unsaved transient instance
|
||||
* </ul>
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class PropertyValueException extends HibernateException {
|
||||
|
||||
private final String entityName;
|
||||
private final String propertyName;
|
||||
|
||||
public PropertyValueException(String s, String entityName, String propertyName) {
|
||||
super(s);
|
||||
this.entityName = entityName;
|
||||
this.propertyName = propertyName;
|
||||
}
|
||||
|
||||
public String getEntityName() {
|
||||
return entityName;
|
||||
}
|
||||
|
||||
public String getPropertyName() {
|
||||
return propertyName;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return super.getMessage() + ": " +
|
||||
StringHelper.qualify(entityName, propertyName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a well formed property path.
|
||||
* Basicaly, it will return parent.child
|
||||
*
|
||||
* @param parent parent in path
|
||||
* @param child child in path
|
||||
* @return parent-child path
|
||||
*/
|
||||
public static String buildPropertyPath(String parent, String child) {
|
||||
return new StringBuffer(parent).append('.').append(child).toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,387 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.transform.ResultTransformer;
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
/**
|
||||
* An object-oriented representation of a Hibernate query. A <tt>Query</tt>
|
||||
* instance is obtained by calling <tt>Session.createQuery()</tt>. This
|
||||
* interface exposes some extra functionality beyond that provided by
|
||||
* <tt>Session.iterate()</tt> and <tt>Session.find()</tt>:
|
||||
* <ul>
|
||||
* <li>a particular page of the result set may be selected by calling <tt>
|
||||
* setMaxResults(), setFirstResult()</tt>
|
||||
* <li>named query parameters may be used
|
||||
* <li>the results may be returned as an instance of <tt>ScrollableResults</tt>
|
||||
* </ul>
|
||||
* <br>
|
||||
* Named query parameters are tokens of the form <tt>:name</tt> in the
|
||||
* query string. A value is bound to the <tt>integer</tt> parameter
|
||||
* <tt>:foo</tt> by calling<br>
|
||||
* <br>
|
||||
* <tt>setParameter("foo", foo, Hibernate.INTEGER);</tt><br>
|
||||
* <br>
|
||||
* for example. A name may appear multiple times in the query string.<br>
|
||||
* <br>
|
||||
* JDBC-style <tt>?</tt> parameters are also supported. To bind a
|
||||
* value to a JDBC-style parameter use a set method that accepts an
|
||||
* <tt>int</tt> positional argument (numbered from zero, contrary
|
||||
* to JDBC).<br>
|
||||
* <br>
|
||||
* You may not mix and match JDBC-style parameters and named parameters
|
||||
* in the same query.<br>
|
||||
* <br>
|
||||
* Queries are executed by calling <tt>list()</tt>, <tt>scroll()</tt> or
|
||||
* <tt>iterate()</tt>. A query may be re-executed by subsequent invocations.
|
||||
* Its lifespan is, however, bounded by the lifespan of the <tt>Session</tt>
|
||||
* that created it.<br>
|
||||
* <br>
|
||||
* Implementors are not intended to be threadsafe.
|
||||
*
|
||||
* @see org.hibernate.Session#createQuery(java.lang.String)
|
||||
* @see org.hibernate.ScrollableResults
|
||||
* @author Gavin King
|
||||
*/
|
||||
public interface Query {
|
||||
/**
|
||||
* Get the query string.
|
||||
*
|
||||
* @return the query string
|
||||
*/
|
||||
public String getQueryString();
|
||||
/**
|
||||
* Return the Hibernate types of the query result set.
|
||||
* @return an array of types
|
||||
*/
|
||||
public Type[] getReturnTypes() throws HibernateException;
|
||||
/**
|
||||
* Return the HQL select clause aliases (if any)
|
||||
* @return an array of aliases as strings
|
||||
*/
|
||||
public String[] getReturnAliases() throws HibernateException;
|
||||
/**
|
||||
* Return the names of all named parameters of the query.
|
||||
* @return the parameter names, in no particular order
|
||||
*/
|
||||
public String[] getNamedParameters() throws HibernateException;
|
||||
/**
|
||||
* Return the query results as an <tt>Iterator</tt>. If the query
|
||||
* contains multiple results pre row, the results are returned in
|
||||
* an instance of <tt>Object[]</tt>.<br>
|
||||
* <br>
|
||||
* Entities returned as results are initialized on demand. The first
|
||||
* SQL query returns identifiers only.<br>
|
||||
*
|
||||
* @return the result iterator
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public Iterator iterate() throws HibernateException;
|
||||
/**
|
||||
* Return the query results as <tt>ScrollableResults</tt>. The
|
||||
* scrollability of the returned results depends upon JDBC driver
|
||||
* support for scrollable <tt>ResultSet</tt>s.<br>
|
||||
*
|
||||
* @see ScrollableResults
|
||||
* @return the result iterator
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public ScrollableResults scroll() throws HibernateException;
|
||||
/**
|
||||
* Return the query results as <tt>ScrollableResults</tt>. The
|
||||
* scrollability of the returned results depends upon JDBC driver
|
||||
* support for scrollable <tt>ResultSet</tt>s.<br>
|
||||
*
|
||||
* @see ScrollableResults
|
||||
* @see ScrollMode
|
||||
* @return the result iterator
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public ScrollableResults scroll(ScrollMode scrollMode) throws HibernateException;
|
||||
/**
|
||||
* Return the query results as a <tt>List</tt>. If the query contains
|
||||
* multiple results pre row, the results are returned in an instance
|
||||
* of <tt>Object[]</tt>.
|
||||
*
|
||||
* @return the result list
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public List list() throws HibernateException;
|
||||
/**
|
||||
* Convenience method to return a single instance that matches
|
||||
* the query, or null if the query returns no results.
|
||||
*
|
||||
* @return the single result or <tt>null</tt>
|
||||
* @throws NonUniqueResultException if there is more than one matching result
|
||||
*/
|
||||
public Object uniqueResult() throws HibernateException;
|
||||
|
||||
/**
|
||||
* Execute the update or delete statement.
|
||||
* </p>
|
||||
* The semantics are compliant with the ejb3 Query.executeUpdate()
|
||||
* method.
|
||||
*
|
||||
* @return The number of entities updated or deleted.
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public int executeUpdate() throws HibernateException;
|
||||
|
||||
/**
|
||||
* Set the maximum number of rows to retrieve. If not set,
|
||||
* there is no limit to the number of rows retrieved.
|
||||
* @param maxResults the maximum number of rows
|
||||
*/
|
||||
public Query setMaxResults(int maxResults);
|
||||
/**
|
||||
* Set the first row to retrieve. If not set, rows will be
|
||||
* retrieved beginnning from row <tt>0</tt>.
|
||||
* @param firstResult a row number, numbered from <tt>0</tt>
|
||||
*/
|
||||
public Query setFirstResult(int firstResult);
|
||||
|
||||
/**
|
||||
* Entities retrieved by this query will be loaded in
|
||||
* a read-only mode where Hibernate will never dirty-check
|
||||
* them or make changes persistent.
|
||||
*
|
||||
*/
|
||||
public Query setReadOnly(boolean readOnly);
|
||||
|
||||
/**
|
||||
* Enable caching of this query result set.
|
||||
* @param cacheable Should the query results be cacheable?
|
||||
*/
|
||||
public Query setCacheable(boolean cacheable);
|
||||
|
||||
/**
|
||||
* Set the name of the cache region.
|
||||
* @param cacheRegion the name of a query cache region, or <tt>null</tt>
|
||||
* for the default query cache
|
||||
*/
|
||||
public Query setCacheRegion(String cacheRegion);
|
||||
|
||||
/**
|
||||
* Set a timeout for the underlying JDBC query.
|
||||
* @param timeout the timeout in seconds
|
||||
*/
|
||||
public Query setTimeout(int timeout);
|
||||
/**
|
||||
* Set a fetch size for the underlying JDBC query.
|
||||
* @param fetchSize the fetch size
|
||||
*/
|
||||
public Query setFetchSize(int fetchSize);
|
||||
|
||||
/**
|
||||
* Set the lockmode for the objects idententified by the
|
||||
* given alias that appears in the <tt>FROM</tt> clause.
|
||||
* @param alias a query alias, or <tt>this</tt> for a collection filter
|
||||
*/
|
||||
public Query setLockMode(String alias, LockMode lockMode);
|
||||
|
||||
/**
|
||||
* Add a comment to the generated SQL.
|
||||
* @param comment a human-readable string
|
||||
*/
|
||||
public Query setComment(String comment);
|
||||
|
||||
/**
|
||||
* Override the current session flush mode, just for
|
||||
* this query.
|
||||
* @see org.hibernate.FlushMode
|
||||
*/
|
||||
public Query setFlushMode(FlushMode flushMode);
|
||||
|
||||
/**
|
||||
* Override the current session cache mode, just for
|
||||
* this query.
|
||||
* @see org.hibernate.CacheMode
|
||||
*/
|
||||
public Query setCacheMode(CacheMode cacheMode);
|
||||
|
||||
/**
|
||||
* Bind a value to a JDBC-style query parameter.
|
||||
* @param position the position of the parameter in the query
|
||||
* string, numbered from <tt>0</tt>.
|
||||
* @param val the possibly-null parameter value
|
||||
* @param type the Hibernate type
|
||||
*/
|
||||
public Query setParameter(int position, Object val, Type type);
|
||||
/**
|
||||
* Bind a value to a named query parameter.
|
||||
* @param name the name of the parameter
|
||||
* @param val the possibly-null parameter value
|
||||
* @param type the Hibernate type
|
||||
*/
|
||||
public Query setParameter(String name, Object val, Type type);
|
||||
|
||||
/**
|
||||
* Bind a value to a JDBC-style query parameter. The Hibernate type of the parameter is
|
||||
* first detected via the usage/position in the query and if not sufficient secondly
|
||||
* guessed from the class of the given object.
|
||||
* @param position the position of the parameter in the query
|
||||
* string, numbered from <tt>0</tt>.
|
||||
* @param val the non-null parameter value
|
||||
* @throws org.hibernate.HibernateException if no type could be determined
|
||||
*/
|
||||
public Query setParameter(int position, Object val) throws HibernateException;
|
||||
/**
|
||||
* Bind a value to a named query parameter. The Hibernate type of the parameter is
|
||||
* first detected via the usage/position in the query and if not sufficient secondly
|
||||
* guessed from the class of the given object.
|
||||
* @param name the name of the parameter
|
||||
* @param val the non-null parameter value
|
||||
* @throws org.hibernate.HibernateException if no type could be determined
|
||||
*/
|
||||
public Query setParameter(String name, Object val) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Bind values and types to positional parameters.
|
||||
*/
|
||||
public Query setParameters(Object[] values, Type[] types) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Bind multiple values to a named query parameter. This is useful for binding
|
||||
* a list of values to an expression such as <tt>foo.bar in (:value_list)</tt>.
|
||||
* @param name the name of the parameter
|
||||
* @param vals a collection of values to list
|
||||
* @param type the Hibernate type of the values
|
||||
*/
|
||||
public Query setParameterList(String name, Collection vals, Type type) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Bind multiple values to a named query parameter. The Hibernate type of the parameter is
|
||||
* first detected via the usage/position in the query and if not sufficient secondly
|
||||
* guessed from the class of the first object in the collection. This is useful for binding a list of values
|
||||
* to an expression such as <tt>foo.bar in (:value_list)</tt>.
|
||||
* @param name the name of the parameter
|
||||
* @param vals a collection of values to list
|
||||
*/
|
||||
public Query setParameterList(String name, Collection vals) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Bind multiple values to a named query parameter. This is useful for binding
|
||||
* a list of values to an expression such as <tt>foo.bar in (:value_list)</tt>.
|
||||
* @param name the name of the parameter
|
||||
* @param vals a collection of values to list
|
||||
* @param type the Hibernate type of the values
|
||||
*/
|
||||
public Query setParameterList(String name, Object[] vals, Type type) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Bind multiple values to a named query parameter. The Hibernate type of the parameter is
|
||||
* first detected via the usage/position in the query and if not sufficient secondly
|
||||
* guessed from the class of the first object in the array. This is useful for binding a list of values
|
||||
* to an expression such as <tt>foo.bar in (:value_list)</tt>.
|
||||
* @param name the name of the parameter
|
||||
* @param vals a collection of values to list
|
||||
*/
|
||||
public Query setParameterList(String name, Object[] vals) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Bind the property values of the given bean to named parameters of the query,
|
||||
* matching property names with parameter names and mapping property types to
|
||||
* Hibernate types using hueristics.
|
||||
* @param bean any JavaBean or POJO
|
||||
*/
|
||||
public Query setProperties(Object bean) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Bind the values of the given Map for each named parameters of the query,
|
||||
* matching key names with parameter names and mapping value types to
|
||||
* Hibernate types using hueristics.
|
||||
* @param bean a java.util.Map
|
||||
*/
|
||||
public Query setProperties(Map bean) throws HibernateException;
|
||||
|
||||
|
||||
public Query setString(int position, String val);
|
||||
public Query setCharacter(int position, char val);
|
||||
public Query setBoolean(int position, boolean val);
|
||||
public Query setByte(int position, byte val);
|
||||
public Query setShort(int position, short val);
|
||||
public Query setInteger(int position, int val);
|
||||
public Query setLong(int position, long val);
|
||||
public Query setFloat(int position, float val);
|
||||
public Query setDouble(int position, double val);
|
||||
public Query setBinary(int position, byte[] val);
|
||||
public Query setText(int position, String val);
|
||||
public Query setSerializable(int position, Serializable val);
|
||||
public Query setLocale(int position, Locale locale);
|
||||
public Query setBigDecimal(int position, BigDecimal number);
|
||||
public Query setBigInteger(int position, BigInteger number);
|
||||
|
||||
public Query setDate(int position, Date date);
|
||||
public Query setTime(int position, Date date);
|
||||
public Query setTimestamp(int position, Date date);
|
||||
|
||||
public Query setCalendar(int position, Calendar calendar);
|
||||
public Query setCalendarDate(int position, Calendar calendar);
|
||||
|
||||
public Query setString(String name, String val);
|
||||
public Query setCharacter(String name, char val);
|
||||
public Query setBoolean(String name, boolean val);
|
||||
public Query setByte(String name, byte val);
|
||||
public Query setShort(String name, short val);
|
||||
public Query setInteger(String name, int val);
|
||||
public Query setLong(String name, long val);
|
||||
public Query setFloat(String name, float val);
|
||||
public Query setDouble(String name, double val);
|
||||
public Query setBinary(String name, byte[] val);
|
||||
public Query setText(String name, String val);
|
||||
public Query setSerializable(String name, Serializable val);
|
||||
public Query setLocale(String name, Locale locale);
|
||||
public Query setBigDecimal(String name, BigDecimal number);
|
||||
public Query setBigInteger(String name, BigInteger number);
|
||||
|
||||
public Query setDate(String name, Date date);
|
||||
public Query setTime(String name, Date date);
|
||||
public Query setTimestamp(String name, Date date);
|
||||
|
||||
public Query setCalendar(String name, Calendar calendar);
|
||||
public Query setCalendarDate(String name, Calendar calendar);
|
||||
|
||||
/**
|
||||
* Bind an instance of a mapped persistent class to a JDBC-style query parameter.
|
||||
* @param position the position of the parameter in the query
|
||||
* string, numbered from <tt>0</tt>.
|
||||
* @param val a non-null instance of a persistent class
|
||||
*/
|
||||
public Query setEntity(int position, Object val); // use setParameter for null values
|
||||
|
||||
/**
|
||||
* Bind an instance of a mapped persistent class to a named query parameter.
|
||||
* @param name the name of the parameter
|
||||
* @param val a non-null instance of a persistent class
|
||||
*/
|
||||
public Query setEntity(String name, Object val); // use setParameter for null values
|
||||
|
||||
|
||||
/**
|
||||
* Set a strategy for handling the query results. This can be used to change
|
||||
* "shape" of the query result.
|
||||
*
|
||||
* @param transformer The transformer to apply
|
||||
* @return this (for method chaining)
|
||||
*/
|
||||
public Query setResultTransformer(ResultTransformer transformer);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
/**
|
||||
* A problem occurred translating a Hibernate query to SQL
|
||||
* due to invalid query syntax, etc.
|
||||
*/
|
||||
public class QueryException extends HibernateException {
|
||||
|
||||
private String queryString;
|
||||
|
||||
public QueryException(String message) {
|
||||
super(message);
|
||||
}
|
||||
public QueryException(String message, Throwable e) {
|
||||
super(message, e);
|
||||
}
|
||||
|
||||
public QueryException(String message, String queryString) {
|
||||
super(message);
|
||||
this.queryString = queryString;
|
||||
}
|
||||
|
||||
public QueryException(Exception e) {
|
||||
super(e);
|
||||
}
|
||||
public String getQueryString() {
|
||||
return queryString;
|
||||
}
|
||||
|
||||
public void setQueryString(String queryString) {
|
||||
this.queryString = queryString;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
String msg = super.getMessage();
|
||||
if ( queryString!=null ) msg += " [" + queryString + ']';
|
||||
return msg;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
//$Id: $
|
||||
package org.hibernate;
|
||||
|
||||
/**
|
||||
* Parameter invalid or not found in the query
|
||||
*
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
public class QueryParameterException extends QueryException {
|
||||
|
||||
public QueryParameterException(Exception e) {
|
||||
super( e );
|
||||
}
|
||||
|
||||
public QueryParameterException(String message) {
|
||||
super( message );
|
||||
}
|
||||
|
||||
public QueryParameterException(String message, Throwable e) {
|
||||
super( message, e );
|
||||
}
|
||||
|
||||
public QueryParameterException(String message, String queryString) {
|
||||
super( message, queryString );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.hibernate.type.VersionType;
|
||||
|
||||
/**
|
||||
* Represents a replication strategy.
|
||||
*
|
||||
* @see Session#replicate(Object, ReplicationMode)
|
||||
* @author Gavin King
|
||||
*/
|
||||
public abstract class ReplicationMode implements Serializable {
|
||||
private final String name;
|
||||
private static final Map INSTANCES = new HashMap();
|
||||
|
||||
public ReplicationMode(String name) {
|
||||
this.name=name;
|
||||
}
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
public abstract boolean shouldOverwriteCurrentVersion(Object entity, Object currentVersion, Object newVersion, VersionType versionType);
|
||||
/**
|
||||
* Throw an exception when a row already exists.
|
||||
*/
|
||||
public static final ReplicationMode EXCEPTION = new ReplicationMode("EXCEPTION") {
|
||||
public boolean shouldOverwriteCurrentVersion(Object entity, Object currentVersion, Object newVersion, VersionType versionType) {
|
||||
throw new AssertionFailure("should not be called");
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Ignore replicated entities when a row already exists.
|
||||
*/
|
||||
public static final ReplicationMode IGNORE = new ReplicationMode("IGNORE") {
|
||||
public boolean shouldOverwriteCurrentVersion(Object entity, Object currentVersion, Object newVersion, VersionType versionType) {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* Overwrite existing rows when a row already exists.
|
||||
*/
|
||||
public static final ReplicationMode OVERWRITE = new ReplicationMode("OVERWRITE") {
|
||||
public boolean shouldOverwriteCurrentVersion(Object entity, Object currentVersion, Object newVersion, VersionType versionType) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* When a row already exists, choose the latest version.
|
||||
*/
|
||||
public static final ReplicationMode LATEST_VERSION = new ReplicationMode("LATEST_VERSION") {
|
||||
public boolean shouldOverwriteCurrentVersion(Object entity, Object currentVersion, Object newVersion, VersionType versionType) {
|
||||
if (versionType==null) return true; //always overwrite nonversioned data
|
||||
return versionType.getComparator().compare(currentVersion, newVersion) <= 0;
|
||||
}
|
||||
};
|
||||
|
||||
static {
|
||||
INSTANCES.put( LATEST_VERSION.name, LATEST_VERSION );
|
||||
INSTANCES.put( IGNORE.name, IGNORE );
|
||||
INSTANCES.put( OVERWRITE.name, OVERWRITE );
|
||||
INSTANCES.put( EXCEPTION.name, EXCEPTION );
|
||||
}
|
||||
|
||||
private Object readResolve() {
|
||||
return INSTANCES.get(name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
/**
|
||||
* Allows the user to declare the types and select list injection
|
||||
* points of all entities returned by the query. Also allows
|
||||
* declaration of the type and column alias of any scalar results
|
||||
* of the query.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public interface SQLQuery extends Query {
|
||||
/**
|
||||
* Declare a "root" entity, without specifying an alias
|
||||
*/
|
||||
public SQLQuery addEntity(String entityName);
|
||||
/**
|
||||
* Declare a "root" entity
|
||||
*/
|
||||
public SQLQuery addEntity(String alias, String entityName);
|
||||
/**
|
||||
* Declare a "root" entity, specifying a lock mode
|
||||
*/
|
||||
public SQLQuery addEntity(String alias, String entityName, LockMode lockMode);
|
||||
/**
|
||||
* Declare a "root" entity, without specifying an alias
|
||||
*/
|
||||
public SQLQuery addEntity(Class entityClass);
|
||||
/**
|
||||
* Declare a "root" entity
|
||||
*/
|
||||
public SQLQuery addEntity(String alias, Class entityClass);
|
||||
/**
|
||||
* Declare a "root" entity, specifying a lock mode
|
||||
*/
|
||||
public SQLQuery addEntity(String alias, Class entityClass, LockMode lockMode);
|
||||
|
||||
/**
|
||||
* Declare a "joined" entity
|
||||
*/
|
||||
public SQLQuery addJoin(String alias, String path);
|
||||
/**
|
||||
* Declare a "joined" entity, specifying a lock mode
|
||||
*/
|
||||
public SQLQuery addJoin(String alias, String path, LockMode lockMode);
|
||||
|
||||
/**
|
||||
* Declare a scalar query result
|
||||
*/
|
||||
public SQLQuery addScalar(String columnAlias, Type type);
|
||||
|
||||
/**
|
||||
* Declare a scalar query. Hibernate will attempt to automatically detect the underlying type.
|
||||
*/
|
||||
public SQLQuery addScalar(String columnAlias);
|
||||
|
||||
/**
|
||||
* Use a predefined named ResultSetMapping
|
||||
*/
|
||||
public SQLQuery setResultSetMapping(String name);
|
||||
|
||||
/**
|
||||
* Adds a query space for auto-flush synchronization.
|
||||
*
|
||||
* @param querySpace The query space to be auto-flushed for this query.
|
||||
* @return this, for method chaning
|
||||
*/
|
||||
public SQLQuery addSynchronizedQuerySpace(String querySpace);
|
||||
|
||||
/**
|
||||
* Adds an entity name or auto-flush synchronization.
|
||||
*
|
||||
* @param entityName The name of the entity upon whose defined
|
||||
* query spaces we should additionally synchronize.
|
||||
* @return this, for method chaning
|
||||
* @throws MappingException Indicates the given entity name could not be
|
||||
* resolved.
|
||||
*/
|
||||
public SQLQuery addSynchronizedEntityName(String entityName) throws MappingException;
|
||||
|
||||
/**
|
||||
* Adds an entity name or auto-flush synchronization.
|
||||
*
|
||||
* @param entityClass The class of the entity upon whose defined
|
||||
* query spaces we should additionally synchronize.
|
||||
* @return this, for method chaning
|
||||
* @throws MappingException Indicates the given entity class could not be
|
||||
* resolved.
|
||||
*/
|
||||
public SQLQuery addSynchronizedEntityClass(Class entityClass) throws MappingException;
|
||||
}
|
|
@ -0,0 +1,74 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.sql.ResultSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Specifies the type of JDBC scrollable result set to use
|
||||
* underneath a <tt>ScrollableResults</tt>
|
||||
*
|
||||
* @see Query#scroll(ScrollMode)
|
||||
* @see ScrollableResults
|
||||
* @author Gavin King
|
||||
*/
|
||||
public final class ScrollMode implements Serializable {
|
||||
private final int resultSetType;
|
||||
private final String name;
|
||||
private static final Map INSTANCES = new HashMap();
|
||||
|
||||
private ScrollMode(int level, String name) {
|
||||
this.resultSetType=level;
|
||||
this.name=name;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the JDBC result set type code
|
||||
*/
|
||||
public int toResultSetType() {
|
||||
return resultSetType;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see java.sql.ResultSet.TYPE_FORWARD_ONLY
|
||||
*/
|
||||
public static final ScrollMode FORWARD_ONLY = new ScrollMode(ResultSet.TYPE_FORWARD_ONLY, "FORWARD_ONLY");
|
||||
/**
|
||||
* @see java.sql.ResultSet.TYPE_SCROLL_SENSITIVE
|
||||
*/
|
||||
public static final ScrollMode SCROLL_SENSITIVE = new ScrollMode(ResultSet.TYPE_SCROLL_SENSITIVE, "SCROLL_SENSITIVE");
|
||||
/**
|
||||
* Note that since the Hibernate session acts as a cache, you
|
||||
* might need to expicitly evict objects, if you need to see
|
||||
* changes made by other transactions.
|
||||
* @see java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE
|
||||
*/
|
||||
public static final ScrollMode SCROLL_INSENSITIVE = new ScrollMode(ResultSet.TYPE_SCROLL_INSENSITIVE, "SCROLL_INSENSITIVE");
|
||||
|
||||
public boolean lessThan(ScrollMode other) {
|
||||
return this.resultSetType<other.resultSetType;
|
||||
}
|
||||
|
||||
static {
|
||||
INSTANCES.put( FORWARD_ONLY.name, FORWARD_ONLY );
|
||||
INSTANCES.put( SCROLL_INSENSITIVE.name, SCROLL_INSENSITIVE );
|
||||
INSTANCES.put( SCROLL_SENSITIVE.name, SCROLL_SENSITIVE );
|
||||
}
|
||||
|
||||
private Object readResolve() {
|
||||
return INSTANCES.get(name);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,204 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.BigInteger;
|
||||
import java.sql.Blob;
|
||||
import java.sql.Clob;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.Locale;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.hibernate.type.Type;
|
||||
|
||||
/**
|
||||
* A result iterator that allows moving around within the results
|
||||
* by arbitrary increments. The <tt>Query</tt> / <tt>ScrollableResults</tt>
|
||||
* pattern is very similar to the JDBC <tt>PreparedStatement</tt>/
|
||||
* <tt>ResultSet</tt> pattern and the semantics of methods of this interface
|
||||
* are similar to the similarly named methods on <tt>ResultSet</tt>.<br>
|
||||
* <br>
|
||||
* Contrary to JDBC, columns of results are numbered from zero.
|
||||
*
|
||||
* @see Query#scroll()
|
||||
* @author Gavin King
|
||||
*/
|
||||
public interface ScrollableResults {
|
||||
/**
|
||||
* Advance to the next result
|
||||
* @return <tt>true</tt> if there is another result
|
||||
*/
|
||||
public boolean next() throws HibernateException;
|
||||
/**
|
||||
* Retreat to the previous result
|
||||
* @return <tt>true</tt> if there is a previous result
|
||||
*/
|
||||
public boolean previous() throws HibernateException;
|
||||
/**
|
||||
* Scroll an arbitrary number of locations
|
||||
* @param i a positive (forward) or negative (backward) number of rows
|
||||
* @return <tt>true</tt> if there is a result at the new location
|
||||
*/
|
||||
public boolean scroll(int i) throws HibernateException;
|
||||
/**
|
||||
* Go to the last result
|
||||
* @return <tt>true</tt> if there are any results
|
||||
*/
|
||||
public boolean last() throws HibernateException;
|
||||
/**
|
||||
* Go to the first result
|
||||
* @return <tt>true</tt> if there are any results
|
||||
*/
|
||||
public boolean first() throws HibernateException;
|
||||
/**
|
||||
* Go to a location just before first result (this is the initial location)
|
||||
*/
|
||||
public void beforeFirst() throws HibernateException;
|
||||
/**
|
||||
* Go to a location just after the last result
|
||||
*/
|
||||
public void afterLast() throws HibernateException;
|
||||
/**
|
||||
* Is this the first result?
|
||||
*
|
||||
* @return <tt>true</tt> if this is the first row of results
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public boolean isFirst() throws HibernateException;
|
||||
/**
|
||||
* Is this the last result?
|
||||
*
|
||||
* @return <tt>true</tt> if this is the last row of results
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public boolean isLast() throws HibernateException;
|
||||
/**
|
||||
* Release resources immediately.
|
||||
*/
|
||||
public void close() throws HibernateException;
|
||||
/**
|
||||
* Get the current row of results
|
||||
* @return an object or array
|
||||
*/
|
||||
public Object[] get() throws HibernateException;
|
||||
/**
|
||||
* Get the <tt>i</tt>th object in the current row of results, without
|
||||
* initializing any other results in the row. This method may be used
|
||||
* safely, regardless of the type of the column (ie. even for scalar
|
||||
* results).
|
||||
* @param i the column, numbered from zero
|
||||
* @return an object of any Hibernate type or <tt>null</tt>
|
||||
*/
|
||||
public Object get(int i) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Get the type of the <tt>i</tt>th column of results
|
||||
* @param i the column, numbered from zero
|
||||
* @return the Hibernate type
|
||||
*/
|
||||
public Type getType(int i);
|
||||
|
||||
/**
|
||||
* Convenience method to read an <tt>integer</tt>
|
||||
*/
|
||||
public Integer getInteger(int col) throws HibernateException;
|
||||
/**
|
||||
* Convenience method to read a <tt>long</tt>
|
||||
*/
|
||||
public Long getLong(int col) throws HibernateException;
|
||||
/**
|
||||
* Convenience method to read a <tt>float</tt>
|
||||
*/
|
||||
public Float getFloat(int col) throws HibernateException;
|
||||
/**
|
||||
* Convenience method to read a <tt>boolean</tt>
|
||||
*/
|
||||
public Boolean getBoolean(int col) throws HibernateException;
|
||||
/**
|
||||
* Convenience method to read a <tt>double</tt>
|
||||
*/
|
||||
public Double getDouble(int col) throws HibernateException;
|
||||
/**
|
||||
* Convenience method to read a <tt>short</tt>
|
||||
*/
|
||||
public Short getShort(int col) throws HibernateException;
|
||||
/**
|
||||
* Convenience method to read a <tt>byte</tt>
|
||||
*/
|
||||
public Byte getByte(int col) throws HibernateException;
|
||||
/**
|
||||
* Convenience method to read a <tt>character</tt>
|
||||
*/
|
||||
public Character getCharacter(int col) throws HibernateException;
|
||||
/**
|
||||
* Convenience method to read a <tt>binary</tt>
|
||||
*/
|
||||
public byte[] getBinary(int col) throws HibernateException;
|
||||
/**
|
||||
* Convenience method to read <tt>text</tt>
|
||||
*/
|
||||
public String getText(int col) throws HibernateException;
|
||||
/**
|
||||
* Convenience method to read a <tt>blob</tt>
|
||||
*/
|
||||
public Blob getBlob(int col) throws HibernateException;
|
||||
/**
|
||||
* Convenience method to read a <tt>clob</tt>
|
||||
*/
|
||||
public Clob getClob(int col) throws HibernateException;
|
||||
/**
|
||||
* Convenience method to read a <tt>string</tt>
|
||||
*/
|
||||
public String getString(int col) throws HibernateException;
|
||||
/**
|
||||
* Convenience method to read a <tt>big_decimal</tt>
|
||||
*/
|
||||
public BigDecimal getBigDecimal(int col) throws HibernateException;
|
||||
/**
|
||||
* Convenience method to read a <tt>big_integer</tt>
|
||||
*/
|
||||
public BigInteger getBigInteger(int col) throws HibernateException;
|
||||
/**
|
||||
* Convenience method to read a <tt>date</tt>, <tt>time</tt> or <tt>timestamp</tt>
|
||||
*/
|
||||
public Date getDate(int col) throws HibernateException;
|
||||
/**
|
||||
* Convenience method to read a <tt>locale</tt>
|
||||
*/
|
||||
public Locale getLocale(int col) throws HibernateException;
|
||||
/**
|
||||
* Convenience method to read a <tt>calendar</tt> or <tt>calendar_date</tt>
|
||||
*/
|
||||
public Calendar getCalendar(int col) throws HibernateException;
|
||||
/**
|
||||
* Convenience method to read a <tt>currency</tt>
|
||||
*/
|
||||
//public Currency getCurrency(int col) throws HibernateException;
|
||||
/**
|
||||
* Convenience method to read a <tt>timezone</tt>
|
||||
*/
|
||||
public TimeZone getTimeZone(int col) throws HibernateException;
|
||||
/**
|
||||
* Get the current location in the result set. The first
|
||||
* row is number <tt>0</tt>, contrary to JDBC.
|
||||
* @return the row number, numbered from <tt>0</tt>, or <tt>-1</tt> if
|
||||
* there is no current row
|
||||
*/
|
||||
public int getRowNumber() throws HibernateException;
|
||||
/**
|
||||
* Set the current location in the result set, numbered from either the
|
||||
* first row (row number <tt>0</tt>), or the last row (row
|
||||
* number <tt>-1</tt>).
|
||||
* @param rowNumber the row number, numbered from the last row, in the
|
||||
* case of a negative row number
|
||||
* @return true if there is a row at that row number
|
||||
*/
|
||||
public boolean setRowNumber(int rowNumber) throws HibernateException;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,779 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.sql.Connection;
|
||||
|
||||
import org.hibernate.stat.SessionStatistics;
|
||||
|
||||
/**
|
||||
* The main runtime interface between a Java application and Hibernate. This is the
|
||||
* central API class abstracting the notion of a persistence service.<br>
|
||||
* <br>
|
||||
* The lifecycle of a <tt>Session</tt> is bounded by the beginning and end of a logical
|
||||
* transaction. (Long transactions might span several database transactions.)<br>
|
||||
* <br>
|
||||
* The main function of the <tt>Session</tt> is to offer create, read and delete operations
|
||||
* for instances of mapped entity classes. Instances may exist in one of three states:<br>
|
||||
* <br>
|
||||
* <i>transient:</i> never persistent, not associated with any <tt>Session</tt><br>
|
||||
* <i>persistent:</i> associated with a unique <tt>Session</tt><br>
|
||||
* <i>detached:</i> previously persistent, not associated with any <tt>Session</tt><br>
|
||||
* <br>
|
||||
* Transient instances may be made persistent by calling <tt>save()</tt>,
|
||||
* <tt>persist()</tt> or <tt>saveOrUpdate()</tt>. Persistent instances may be made transient
|
||||
* by calling<tt> delete()</tt>. Any instance returned by a <tt>get()</tt> or
|
||||
* <tt>load()</tt> method is persistent. Detached instances may be made persistent
|
||||
* by calling <tt>update()</tt>, <tt>saveOrUpdate()</tt>, <tt>lock()</tt> or <tt>replicate()</tt>.
|
||||
* The state of a transient or detached instance may also be made persistent as a new
|
||||
* persistent instance by calling <tt>merge()</tt>.<br>
|
||||
* <br>
|
||||
* <tt>save()</tt> and <tt>persist()</tt> result in an SQL <tt>INSERT</tt>, <tt>delete()</tt>
|
||||
* in an SQL <tt>DELETE</tt> and <tt>update()</tt> or <tt>merge()</tt> in an SQL <tt>UPDATE</tt>.
|
||||
* Changes to <i>persistent</i> instances are detected at flush time and also result in an SQL
|
||||
* <tt>UPDATE</tt>. <tt>saveOrUpdate()</tt> and <tt>replicate()</tt> result in either an
|
||||
* <tt>INSERT</tt> or an <tt>UPDATE</tt>.<br>
|
||||
* <br>
|
||||
* It is not intended that implementors be threadsafe. Instead each thread/transaction
|
||||
* should obtain its own instance from a <tt>SessionFactory</tt>.<br>
|
||||
* <br>
|
||||
* A <tt>Session</tt> instance is serializable if its persistent classes are serializable.<br>
|
||||
* <br>
|
||||
* A typical transaction should use the following idiom:
|
||||
* <pre>
|
||||
* Session sess = factory.openSession();
|
||||
* Transaction tx;
|
||||
* try {
|
||||
* tx = sess.beginTransaction();
|
||||
* //do some work
|
||||
* ...
|
||||
* tx.commit();
|
||||
* }
|
||||
* catch (Exception e) {
|
||||
* if (tx!=null) tx.rollback();
|
||||
* throw e;
|
||||
* }
|
||||
* finally {
|
||||
* sess.close();
|
||||
* }
|
||||
* </pre>
|
||||
* <br>
|
||||
* If the <tt>Session</tt> throws an exception, the transaction must be rolled back
|
||||
* and the session discarded. The internal state of the <tt>Session</tt> might not
|
||||
* be consistent with the database after the exception occurs.
|
||||
*
|
||||
* @see SessionFactory
|
||||
* @author Gavin King
|
||||
*/
|
||||
public interface Session extends Serializable {
|
||||
|
||||
/**
|
||||
* Retrieve the entity mode in effect for this session.
|
||||
*
|
||||
* @return The entity mode for this session.
|
||||
*/
|
||||
public EntityMode getEntityMode();
|
||||
|
||||
/**
|
||||
* Starts a new Session with the given entity mode in effect. This secondary
|
||||
* Session inherits the connection, transaction, and other context
|
||||
* information from the primary Session. It doesn't need to be flushed
|
||||
* or closed by the developer.
|
||||
*
|
||||
* @param entityMode The entity mode to use for the new session.
|
||||
* @return The new session
|
||||
*/
|
||||
public Session getSession(EntityMode entityMode);
|
||||
|
||||
/**
|
||||
* Force this session to flush. Must be called at the end of a
|
||||
* unit of work, before commiting the transaction and closing the
|
||||
* session (depending on {@link #setFlushMode flush-mode},
|
||||
* {@link Transaction#commit()} calls this method).
|
||||
* <p/>
|
||||
* <i>Flushing</i> is the process of synchronizing the underlying persistent
|
||||
* store with persistable state held in memory.
|
||||
*
|
||||
* @throws HibernateException Indicates problems flushing the session or
|
||||
* talking to the database.
|
||||
*/
|
||||
public void flush() throws HibernateException;
|
||||
|
||||
/**
|
||||
* Set the flush mode for this session.
|
||||
* <p/>
|
||||
* The flush mode determines the points at which the session is flushed.
|
||||
* <i>Flushing</i> is the process of synchronizing the underlying persistent
|
||||
* store with persistable state held in memory.
|
||||
* <p/>
|
||||
* For a logically "read only" session, it is reasonable to set the session's
|
||||
* flush mode to {@link FlushMode#MANUAL} at the start of the session (in
|
||||
* order to achieve some extra performance).
|
||||
*
|
||||
* @param flushMode the new flush mode
|
||||
* @see FlushMode
|
||||
*/
|
||||
public void setFlushMode(FlushMode flushMode);
|
||||
|
||||
/**
|
||||
* Get the current flush mode for this session.
|
||||
*
|
||||
* @return The flush mode
|
||||
*/
|
||||
public FlushMode getFlushMode();
|
||||
|
||||
/**
|
||||
* Set the cache mode.
|
||||
* <p/>
|
||||
* Cache mode determines the manner in which this session can interact with
|
||||
* the second level cache.
|
||||
*
|
||||
* @param cacheMode The new cache mode.
|
||||
*/
|
||||
public void setCacheMode(CacheMode cacheMode);
|
||||
|
||||
/**
|
||||
* Get the current cache mode.
|
||||
*
|
||||
* @return The current cache mode.
|
||||
*/
|
||||
public CacheMode getCacheMode();
|
||||
|
||||
/**
|
||||
* Get the session factory which created this session.
|
||||
*
|
||||
* @return The session factory.
|
||||
* @see SessionFactory
|
||||
|
||||
*/
|
||||
public SessionFactory getSessionFactory();
|
||||
|
||||
/**
|
||||
* Get the JDBC connection of this Session.<br>
|
||||
* <br>
|
||||
* If the session is using aggressive collection release (as in a
|
||||
* CMT environment), it is the application's responsibility to
|
||||
* close the connection returned by this call. Otherwise, the
|
||||
* application should not close the connection.
|
||||
*
|
||||
* @return the JDBC connection in use by the <tt>Session</tt>
|
||||
* @throws HibernateException if the <tt>Session</tt> is disconnected
|
||||
* @deprecated To be replaced with a SPI for performing work against the connection; scheduled for removal in 4.x
|
||||
*/
|
||||
public Connection connection() throws HibernateException;
|
||||
|
||||
/**
|
||||
* End the session by releasing the JDBC connection and cleaning up. It is
|
||||
* not strictly necessary to close the session but you must at least
|
||||
* {@link #disconnect()} it.
|
||||
*
|
||||
* @return the connection provided by the application or null.
|
||||
* @throws HibernateException Indicates problems cleaning up.
|
||||
*/
|
||||
public Connection close() throws HibernateException;
|
||||
|
||||
/**
|
||||
* Cancel the execution of the current query.
|
||||
* <p/>
|
||||
* This is the sole method on session which may be safely called from
|
||||
* another thread.
|
||||
*
|
||||
* @throws HibernateException There was a problem canceling the query
|
||||
*/
|
||||
public void cancelQuery() throws HibernateException;
|
||||
|
||||
/**
|
||||
* Check if the session is still open.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean isOpen();
|
||||
|
||||
/**
|
||||
* Check if the session is currently connected.
|
||||
*
|
||||
* @return boolean
|
||||
*/
|
||||
public boolean isConnected();
|
||||
|
||||
/**
|
||||
* Does this session contain any changes which must be synchronized with
|
||||
* the database? In other words, would any DML operations be executed if
|
||||
* we flushed this session?
|
||||
*
|
||||
* @return True if the session contains pending changes; false otherwise.
|
||||
* @throws HibernateException could not perform dirtying checking
|
||||
*/
|
||||
public boolean isDirty() throws HibernateException;
|
||||
|
||||
/**
|
||||
* Return the identifier value of the given entity as associated with this
|
||||
* session. An exception is thrown if the given entity instance is transient
|
||||
* or detached in relation to this session.
|
||||
*
|
||||
* @param object a persistent instance
|
||||
* @return the identifier
|
||||
* @throws TransientObjectException if the instance is transient or associated with
|
||||
* a different session
|
||||
*/
|
||||
public Serializable getIdentifier(Object object) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Check if this instance is associated with this <tt>Session</tt>.
|
||||
*
|
||||
* @param object an instance of a persistent class
|
||||
* @return true if the given instance is associated with this <tt>Session</tt>
|
||||
*/
|
||||
public boolean contains(Object object);
|
||||
|
||||
/**
|
||||
* Remove this instance from the session cache. Changes to the instance will
|
||||
* not be synchronized with the database. This operation cascades to associated
|
||||
* instances if the association is mapped with <tt>cascade="evict"</tt>.
|
||||
*
|
||||
* @param object a persistent instance
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public void evict(Object object) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Return the persistent instance of the given entity class with the given identifier,
|
||||
* obtaining the specified lock mode, assuming the instance exists.
|
||||
*
|
||||
* @param theClass a persistent class
|
||||
* @param id a valid identifier of an existing persistent instance of the class
|
||||
* @param lockMode the lock level
|
||||
* @return the persistent instance or proxy
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public Object load(Class theClass, Serializable id, LockMode lockMode) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Return the persistent instance of the given entity class with the given identifier,
|
||||
* obtaining the specified lock mode, assuming the instance exists.
|
||||
*
|
||||
* @param entityName a persistent class
|
||||
* @param id a valid identifier of an existing persistent instance of the class
|
||||
* @param lockMode the lock level
|
||||
* @return the persistent instance or proxy
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public Object load(String entityName, Serializable id, LockMode lockMode) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Return the persistent instance of the given entity class with the given identifier,
|
||||
* assuming that the instance exists.
|
||||
* <br><br>
|
||||
* You should not use this method to determine if an instance exists (use <tt>get()</tt>
|
||||
* instead). Use this only to retrieve an instance that you assume exists, where non-existence
|
||||
* would be an actual error.
|
||||
*
|
||||
* @param theClass a persistent class
|
||||
* @param id a valid identifier of an existing persistent instance of the class
|
||||
* @return the persistent instance or proxy
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public Object load(Class theClass, Serializable id) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Return the persistent instance of the given entity class with the given identifier,
|
||||
* assuming that the instance exists.
|
||||
* <br><br>
|
||||
* You should not use this method to determine if an instance exists (use <tt>get()</tt>
|
||||
* instead). Use this only to retrieve an instance that you assume exists, where non-existence
|
||||
* would be an actual error.
|
||||
*
|
||||
* @param entityName a persistent class
|
||||
* @param id a valid identifier of an existing persistent instance of the class
|
||||
* @return the persistent instance or proxy
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public Object load(String entityName, Serializable id) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Read the persistent state associated with the given identifier into the given transient
|
||||
* instance.
|
||||
*
|
||||
* @param object an "empty" instance of the persistent class
|
||||
* @param id a valid identifier of an existing persistent instance of the class
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public void load(Object object, Serializable id) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Persist the state of the given detached instance, reusing the current
|
||||
* identifier value. This operation cascades to associated instances if
|
||||
* the association is mapped with <tt>cascade="replicate"</tt>.
|
||||
*
|
||||
* @param object a detached instance of a persistent class
|
||||
*/
|
||||
public void replicate(Object object, ReplicationMode replicationMode) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Persist the state of the given detached instance, reusing the current
|
||||
* identifier value. This operation cascades to associated instances if
|
||||
* the association is mapped with <tt>cascade="replicate"</tt>.
|
||||
*
|
||||
* @param object a detached instance of a persistent class
|
||||
*/
|
||||
public void replicate(String entityName, Object object, ReplicationMode replicationMode) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Persist the given transient instance, first assigning a generated identifier. (Or
|
||||
* using the current value of the identifier property if the <tt>assigned</tt>
|
||||
* generator is used.) This operation cascades to associated instances if the
|
||||
* association is mapped with <tt>cascade="save-update"</tt>.
|
||||
*
|
||||
* @param object a transient instance of a persistent class
|
||||
* @return the generated identifier
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public Serializable save(Object object) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Persist the given transient instance, first assigning a generated identifier. (Or
|
||||
* using the current value of the identifier property if the <tt>assigned</tt>
|
||||
* generator is used.) This operation cascades to associated instances if the
|
||||
* association is mapped with <tt>cascade="save-update"</tt>.
|
||||
*
|
||||
* @param object a transient instance of a persistent class
|
||||
* @return the generated identifier
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public Serializable save(String entityName, Object object) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Either {@link #save(Object)} or {@link #update(Object)} the given
|
||||
* instance, depending upon resolution of the unsaved-value checks (see the
|
||||
* manual for discussion of unsaved-value checking).
|
||||
* <p/>
|
||||
* This operation cascades to associated instances if the association is mapped
|
||||
* with <tt>cascade="save-update"</tt>.
|
||||
*
|
||||
* @see Session#save(Object)
|
||||
* @see Session#update(Object)
|
||||
* @param object a transient or detached instance containing new or updated state
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public void saveOrUpdate(Object object) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Either {@link #save(String, Object)} or {@link #update(String, Object)}
|
||||
* the given instance, depending upon resolution of the unsaved-value checks
|
||||
* (see the manual for discussion of unsaved-value checking).
|
||||
* <p/>
|
||||
* This operation cascades to associated instances if the association is mapped
|
||||
* with <tt>cascade="save-update"</tt>.
|
||||
*
|
||||
* @see Session#save(String,Object)
|
||||
* @see Session#update(String,Object)
|
||||
* @param entityName The name of the entity
|
||||
* @param object a transient or detached instance containing new or updated state
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public void saveOrUpdate(String entityName, Object object) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Update the persistent instance with the identifier of the given detached
|
||||
* instance. If there is a persistent instance with the same identifier,
|
||||
* an exception is thrown. This operation cascades to associated instances
|
||||
* if the association is mapped with <tt>cascade="save-update"</tt>.
|
||||
*
|
||||
* @param object a detached instance containing updated state
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public void update(Object object) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Update the persistent instance with the identifier of the given detached
|
||||
* instance. If there is a persistent instance with the same identifier,
|
||||
* an exception is thrown. This operation cascades to associated instances
|
||||
* if the association is mapped with <tt>cascade="save-update"</tt>.
|
||||
*
|
||||
* @param object a detached instance containing updated state
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public void update(String entityName, Object object) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Copy the state of the given object onto the persistent object with the same
|
||||
* identifier. If there is no persistent instance currently associated with
|
||||
* the session, it will be loaded. Return the persistent instance. If the
|
||||
* given instance is unsaved, save a copy of and return it as a newly persistent
|
||||
* instance. The given instance does not become associated with the session.
|
||||
* This operation cascades to associated instances if the association is mapped
|
||||
* with <tt>cascade="merge"</tt>.<br>
|
||||
* <br>
|
||||
* The semantics of this method are defined by JSR-220.
|
||||
*
|
||||
* @param object a detached instance with state to be copied
|
||||
* @return an updated persistent instance
|
||||
*/
|
||||
public Object merge(Object object) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Copy the state of the given object onto the persistent object with the same
|
||||
* identifier. If there is no persistent instance currently associated with
|
||||
* the session, it will be loaded. Return the persistent instance. If the
|
||||
* given instance is unsaved, save a copy of and return it as a newly persistent
|
||||
* instance. The given instance does not become associated with the session.
|
||||
* This operation cascades to associated instances if the association is mapped
|
||||
* with <tt>cascade="merge"</tt>.<br>
|
||||
* <br>
|
||||
* The semantics of this method are defined by JSR-220.
|
||||
*
|
||||
* @param object a detached instance with state to be copied
|
||||
* @return an updated persistent instance
|
||||
*/
|
||||
public Object merge(String entityName, Object object) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Make a transient instance persistent. This operation cascades to associated
|
||||
* instances if the association is mapped with <tt>cascade="persist"</tt>.<br>
|
||||
* <br>
|
||||
* The semantics of this method are defined by JSR-220.
|
||||
*
|
||||
* @param object a transient instance to be made persistent
|
||||
*/
|
||||
public void persist(Object object) throws HibernateException;
|
||||
/**
|
||||
* Make a transient instance persistent. This operation cascades to associated
|
||||
* instances if the association is mapped with <tt>cascade="persist"</tt>.<br>
|
||||
* <br>
|
||||
* The semantics of this method are defined by JSR-220.
|
||||
*
|
||||
* @param object a transient instance to be made persistent
|
||||
*/
|
||||
public void persist(String entityName, Object object) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Remove a persistent instance from the datastore. The argument may be
|
||||
* an instance associated with the receiving <tt>Session</tt> or a transient
|
||||
* instance with an identifier associated with existing persistent state.
|
||||
* This operation cascades to associated instances if the association is mapped
|
||||
* with <tt>cascade="delete"</tt>.
|
||||
*
|
||||
* @param object the instance to be removed
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public void delete(Object object) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Remove a persistent instance from the datastore. The <b>object</b> argument may be
|
||||
* an instance associated with the receiving <tt>Session</tt> or a transient
|
||||
* instance with an identifier associated with existing persistent state.
|
||||
* This operation cascades to associated instances if the association is mapped
|
||||
* with <tt>cascade="delete"</tt>.
|
||||
*
|
||||
* @param entityName The entity name for the instance to be removed.
|
||||
* @param object the instance to be removed
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public void delete(String entityName, Object object) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Obtain the specified lock level upon the given object. This may be used to
|
||||
* perform a version check (<tt>LockMode.READ</tt>), to upgrade to a pessimistic
|
||||
* lock (<tt>LockMode.UPGRADE</tt>), or to simply reassociate a transient instance
|
||||
* with a session (<tt>LockMode.NONE</tt>). This operation cascades to associated
|
||||
* instances if the association is mapped with <tt>cascade="lock"</tt>.
|
||||
*
|
||||
* @param object a persistent or transient instance
|
||||
* @param lockMode the lock level
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public void lock(Object object, LockMode lockMode) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Obtain the specified lock level upon the given object. This may be used to
|
||||
* perform a version check (<tt>LockMode.READ</tt>), to upgrade to a pessimistic
|
||||
* lock (<tt>LockMode.UPGRADE</tt>), or to simply reassociate a transient instance
|
||||
* with a session (<tt>LockMode.NONE</tt>). This operation cascades to associated
|
||||
* instances if the association is mapped with <tt>cascade="lock"</tt>.
|
||||
*
|
||||
* @param object a persistent or transient instance
|
||||
* @param lockMode the lock level
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public void lock(String entityName, Object object, LockMode lockMode) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Re-read the state of the given instance from the underlying database. It is
|
||||
* inadvisable to use this to implement long-running sessions that span many
|
||||
* business tasks. This method is, however, useful in certain special circumstances.
|
||||
* For example
|
||||
* <ul>
|
||||
* <li>where a database trigger alters the object state upon insert or update
|
||||
* <li>after executing direct SQL (eg. a mass update) in the same session
|
||||
* <li>after inserting a <tt>Blob</tt> or <tt>Clob</tt>
|
||||
* </ul>
|
||||
*
|
||||
* @param object a persistent or detached instance
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public void refresh(Object object) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Re-read the state of the given instance from the underlying database, with
|
||||
* the given <tt>LockMode</tt>. It is inadvisable to use this to implement
|
||||
* long-running sessions that span many business tasks. This method is, however,
|
||||
* useful in certain special circumstances.
|
||||
*
|
||||
* @param object a persistent or detached instance
|
||||
* @param lockMode the lock mode to use
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public void refresh(Object object, LockMode lockMode) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Determine the current lock mode of the given object.
|
||||
*
|
||||
* @param object a persistent instance
|
||||
* @return the current lock mode
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public LockMode getCurrentLockMode(Object object) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Begin a unit of work and return the associated <tt>Transaction</tt> object.
|
||||
* If a new underlying transaction is required, begin the transaction. Otherwise
|
||||
* continue the new work in the context of the existing underlying transaction.
|
||||
* The class of the returned <tt>Transaction</tt> object is determined by the
|
||||
* property <tt>hibernate.transaction_factory</tt>.
|
||||
*
|
||||
* @return a Transaction instance
|
||||
* @throws HibernateException
|
||||
* @see Transaction
|
||||
*/
|
||||
public Transaction beginTransaction() throws HibernateException;
|
||||
|
||||
/**
|
||||
* Get the <tt>Transaction</tt> instance associated with this session.
|
||||
* The class of the returned <tt>Transaction</tt> object is determined by the
|
||||
* property <tt>hibernate.transaction_factory</tt>.
|
||||
*
|
||||
* @return a Transaction instance
|
||||
* @throws HibernateException
|
||||
* @see Transaction
|
||||
*/
|
||||
public Transaction getTransaction();
|
||||
|
||||
/**
|
||||
* Create a new <tt>Criteria</tt> instance, for the given entity class,
|
||||
* or a superclass of an entity class.
|
||||
*
|
||||
* @param persistentClass a class, which is persistent, or has persistent subclasses
|
||||
* @return Criteria
|
||||
*/
|
||||
public Criteria createCriteria(Class persistentClass);
|
||||
|
||||
/**
|
||||
* Create a new <tt>Criteria</tt> instance, for the given entity class,
|
||||
* or a superclass of an entity class, with the given alias.
|
||||
*
|
||||
* @param persistentClass a class, which is persistent, or has persistent subclasses
|
||||
* @return Criteria
|
||||
*/
|
||||
public Criteria createCriteria(Class persistentClass, String alias);
|
||||
|
||||
/**
|
||||
* Create a new <tt>Criteria</tt> instance, for the given entity name.
|
||||
*
|
||||
* @param entityName
|
||||
* @return Criteria
|
||||
*/
|
||||
public Criteria createCriteria(String entityName);
|
||||
|
||||
/**
|
||||
* Create a new <tt>Criteria</tt> instance, for the given entity name,
|
||||
* with the given alias.
|
||||
*
|
||||
* @param entityName
|
||||
* @return Criteria
|
||||
*/
|
||||
public Criteria createCriteria(String entityName, String alias);
|
||||
|
||||
/**
|
||||
* Create a new instance of <tt>Query</tt> for the given HQL query string.
|
||||
*
|
||||
* @param queryString a HQL query
|
||||
* @return Query
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public Query createQuery(String queryString) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Create a new instance of <tt>SQLQuery</tt> for the given SQL query string.
|
||||
*
|
||||
* @param queryString a SQL query
|
||||
* @return SQLQuery
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public SQLQuery createSQLQuery(String queryString) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Create a new instance of <tt>Query</tt> for the given collection and filter string.
|
||||
*
|
||||
* @param collection a persistent collection
|
||||
* @param queryString a Hibernate query
|
||||
* @return Query
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public Query createFilter(Object collection, String queryString) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Obtain an instance of <tt>Query</tt> for a named query string defined in the
|
||||
* mapping file.
|
||||
*
|
||||
* @param queryName the name of a query defined externally
|
||||
* @return Query
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public Query getNamedQuery(String queryName) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Completely clear the session. Evict all loaded instances and cancel all pending
|
||||
* saves, updates and deletions. Do not close open iterators or instances of
|
||||
* <tt>ScrollableResults</tt>.
|
||||
*/
|
||||
public void clear();
|
||||
|
||||
/**
|
||||
* Return the persistent instance of the given entity class with the given identifier,
|
||||
* or null if there is no such persistent instance. (If the instance, or a proxy for the
|
||||
* instance, is already associated with the session, return that instance or proxy.)
|
||||
*
|
||||
* @param clazz a persistent class
|
||||
* @param id an identifier
|
||||
* @return a persistent instance or null
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public Object get(Class clazz, Serializable id) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Return the persistent instance of the given entity class with the given identifier,
|
||||
* or null if there is no such persistent instance. Obtain the specified lock mode
|
||||
* if the instance exists.
|
||||
*
|
||||
* @param clazz a persistent class
|
||||
* @param id an identifier
|
||||
* @param lockMode the lock mode
|
||||
* @return a persistent instance or null
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public Object get(Class clazz, Serializable id, LockMode lockMode) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Return the persistent instance of the given named entity with the given identifier,
|
||||
* or null if there is no such persistent instance. (If the instance, or a proxy for the
|
||||
* instance, is already associated with the session, return that instance or proxy.)
|
||||
*
|
||||
* @param entityName the entity name
|
||||
* @param id an identifier
|
||||
* @return a persistent instance or null
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public Object get(String entityName, Serializable id) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Return the persistent instance of the given entity class with the given identifier,
|
||||
* or null if there is no such persistent instance. Obtain the specified lock mode
|
||||
* if the instance exists.
|
||||
*
|
||||
* @param entityName the entity name
|
||||
* @param id an identifier
|
||||
* @param lockMode the lock mode
|
||||
* @return a persistent instance or null
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public Object get(String entityName, Serializable id, LockMode lockMode) throws HibernateException;
|
||||
|
||||
|
||||
/**
|
||||
* Return the entity name for a persistent entity
|
||||
*
|
||||
* @param object a persistent entity
|
||||
* @return the entity name
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public String getEntityName(Object object) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Enable the named filter for this current session.
|
||||
*
|
||||
* @param filterName The name of the filter to be enabled.
|
||||
* @return The Filter instance representing the enabled fiter.
|
||||
*/
|
||||
public Filter enableFilter(String filterName);
|
||||
|
||||
/**
|
||||
* Retrieve a currently enabled filter by name.
|
||||
*
|
||||
* @param filterName The name of the filter to be retrieved.
|
||||
* @return The Filter instance representing the enabled fiter.
|
||||
*/
|
||||
public Filter getEnabledFilter(String filterName);
|
||||
|
||||
/**
|
||||
* Disable the named filter for the current session.
|
||||
*
|
||||
* @param filterName The name of the filter to be disabled.
|
||||
*/
|
||||
public void disableFilter(String filterName);
|
||||
|
||||
/**
|
||||
* Get the statistics for this session.
|
||||
*/
|
||||
public SessionStatistics getStatistics();
|
||||
|
||||
/**
|
||||
* Set an unmodified persistent object to read only mode, or a read only
|
||||
* object to modifiable mode. In read only mode, no snapshot is maintained
|
||||
* and the instance is never dirty checked.
|
||||
*
|
||||
* @see Query#setReadOnly(boolean)
|
||||
*/
|
||||
public void setReadOnly(Object entity, boolean readOnly);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Disconnect the <tt>Session</tt> from the current JDBC connection. If
|
||||
* the connection was obtained by Hibernate close it and return it to
|
||||
* the connection pool; otherwise, return it to the application.
|
||||
* <p/>
|
||||
* This is used by applications which supply JDBC connections to Hibernate
|
||||
* and which require long-sessions (or long-conversations)
|
||||
* <p/>
|
||||
* Note that disconnect() called on a session where the connection was
|
||||
* retrieved by Hibernate through its configured
|
||||
* {@link org.hibernate.connection.ConnectionProvider} has no effect,
|
||||
* provided {@link ConnectionReleaseMode#ON_CLOSE} is not in effect.
|
||||
*
|
||||
* @return the application-supplied connection or <tt>null</tt>
|
||||
* @see #reconnect(Connection)
|
||||
* @see #reconnect()
|
||||
*/
|
||||
Connection disconnect() throws HibernateException;
|
||||
|
||||
/**
|
||||
* Obtain a new JDBC connection. This is used by applications which
|
||||
* require long transactions and do not supply connections to the
|
||||
* session.
|
||||
*
|
||||
* @see #disconnect()
|
||||
* @deprecated Manual reconnection is only needed in the case of
|
||||
* application-supplied connections, in which case the
|
||||
* {@link #reconnect(java.sql.Connection)} for should be used.
|
||||
*/
|
||||
void reconnect() throws HibernateException;
|
||||
|
||||
/**
|
||||
* Reconnect to the given JDBC connection. This is used by applications
|
||||
* which require long transactions and use application-supplied connections.
|
||||
*
|
||||
* @param connection a JDBC connection
|
||||
* @see #disconnect()
|
||||
*/
|
||||
void reconnect(Connection connection) throws HibernateException;
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
/**
|
||||
* Thrown when the user calls a method of a {@link Session} that is in an
|
||||
* inappropropriate state for the given call (for example, the the session
|
||||
* is closed or disconnected).
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class SessionException extends HibernateException {
|
||||
|
||||
/**
|
||||
* Constructs a new SessionException with the given message.
|
||||
*
|
||||
* @param message The message indicating the specific problem.
|
||||
*/
|
||||
public SessionException(String message) {
|
||||
super( message );
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,224 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.sql.Connection;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.naming.Referenceable;
|
||||
|
||||
import org.hibernate.metadata.ClassMetadata;
|
||||
import org.hibernate.metadata.CollectionMetadata;
|
||||
import org.hibernate.stat.Statistics;
|
||||
import org.hibernate.engine.FilterDefinition;
|
||||
|
||||
/**
|
||||
* Creates <tt>Session</tt>s. Usually an application has a single <tt>SessionFactory</tt>.
|
||||
* Threads servicing client requests obtain <tt>Session</tt>s from the factory.<br>
|
||||
* <br>
|
||||
* Implementors must be threadsafe.<br>
|
||||
* <br>
|
||||
* <tt>SessionFactory</tt>s are immutable. The behaviour of a <tt>SessionFactory</tt> is
|
||||
* controlled by properties supplied at configuration time. These properties are defined
|
||||
* on <tt>Environment</tt>.
|
||||
*
|
||||
* @see Session
|
||||
* @see org.hibernate.cfg.Environment
|
||||
* @see org.hibernate.cfg.Configuration
|
||||
* @see org.hibernate.connection.ConnectionProvider
|
||||
* @see org.hibernate.transaction.TransactionFactory
|
||||
* @author Gavin King
|
||||
*/
|
||||
public interface SessionFactory extends Referenceable, Serializable {
|
||||
|
||||
/**
|
||||
* Open a <tt>Session</tt> on the given connection.
|
||||
* <p>
|
||||
* Note that the second-level cache will be disabled if you
|
||||
* supply a JDBC connection. Hibernate will not be able to track
|
||||
* any statements you might have executed in the same transaction.
|
||||
* Consider implementing your own <tt>ConnectionProvider</tt>.
|
||||
*
|
||||
* @param connection a connection provided by the application.
|
||||
* @return Session
|
||||
*/
|
||||
public org.hibernate.classic.Session openSession(Connection connection);
|
||||
|
||||
/**
|
||||
* Create database connection and open a <tt>Session</tt> on it, specifying an
|
||||
* interceptor.
|
||||
*
|
||||
* @param interceptor a session-scoped interceptor
|
||||
* @return Session
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public org.hibernate.classic.Session openSession(Interceptor interceptor) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Open a <tt>Session</tt> on the given connection, specifying an interceptor.
|
||||
* <p>
|
||||
* Note that the second-level cache will be disabled if you
|
||||
* supply a JDBC connection. Hibernate will not be able to track
|
||||
* any statements you might have executed in the same transaction.
|
||||
* Consider implementing your own <tt>ConnectionProvider</tt>.
|
||||
*
|
||||
* @param connection a connection provided by the application.
|
||||
* @param interceptor a session-scoped interceptor
|
||||
* @return Session
|
||||
*/
|
||||
public org.hibernate.classic.Session openSession(Connection connection, Interceptor interceptor);
|
||||
|
||||
/**
|
||||
* Create database connection and open a <tt>Session</tt> on it.
|
||||
*
|
||||
* @return Session
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public org.hibernate.classic.Session openSession() throws HibernateException;
|
||||
|
||||
/**
|
||||
* Obtains the current session. The definition of what exactly "current"
|
||||
* means controlled by the {@link org.hibernate.context.CurrentSessionContext} impl configured
|
||||
* for use.
|
||||
* <p/>
|
||||
* Note that for backwards compatibility, if a {@link org.hibernate.context.CurrentSessionContext}
|
||||
* is not configured but a JTA {@link org.hibernate.transaction.TransactionManagerLookup}
|
||||
* is configured this will default to the {@link org.hibernate.context.JTASessionContext}
|
||||
* impl.
|
||||
*
|
||||
* @return The current session.
|
||||
* @throws HibernateException Indicates an issue locating a suitable current session.
|
||||
*/
|
||||
public org.hibernate.classic.Session getCurrentSession() throws HibernateException;
|
||||
|
||||
/**
|
||||
* Get the <tt>ClassMetadata</tt> associated with the given entity class
|
||||
*
|
||||
* @see org.hibernate.metadata.ClassMetadata
|
||||
*/
|
||||
public ClassMetadata getClassMetadata(Class persistentClass) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Get the <tt>ClassMetadata</tt> associated with the given entity name
|
||||
*
|
||||
* @see org.hibernate.metadata.ClassMetadata
|
||||
* @since 3.0
|
||||
*/
|
||||
public ClassMetadata getClassMetadata(String entityName) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Get the <tt>CollectionMetadata</tt> associated with the named collection role
|
||||
*
|
||||
* @see org.hibernate.metadata.CollectionMetadata
|
||||
*/
|
||||
public CollectionMetadata getCollectionMetadata(String roleName) throws HibernateException;
|
||||
|
||||
|
||||
/**
|
||||
* Get all <tt>ClassMetadata</tt> as a <tt>Map</tt> from entityname <tt>String</tt>
|
||||
* to metadata object
|
||||
*
|
||||
* @see org.hibernate.metadata.ClassMetadata
|
||||
* @return a map from <tt>String</tt> an entity name to <tt>ClassMetaData</tt>
|
||||
* @since 3.0 changed key from <tt>Class</tt> to <tt>String</tt>
|
||||
*/
|
||||
public Map getAllClassMetadata() throws HibernateException;
|
||||
|
||||
/**
|
||||
* Get all <tt>CollectionMetadata</tt> as a <tt>Map</tt> from role name
|
||||
* to metadata object
|
||||
*
|
||||
* @see org.hibernate.metadata.CollectionMetadata
|
||||
* @return a map from <tt>String</tt> to <tt>CollectionMetadata</tt>
|
||||
*/
|
||||
public Map getAllCollectionMetadata() throws HibernateException;
|
||||
|
||||
/**
|
||||
* Get the statistics for this session factory
|
||||
*/
|
||||
public Statistics getStatistics();
|
||||
|
||||
/**
|
||||
* Destroy this <tt>SessionFactory</tt> and release all resources (caches,
|
||||
* connection pools, etc). It is the responsibility of the application
|
||||
* to ensure that there are no open <tt>Session</tt>s before calling
|
||||
* <tt>close()</tt>.
|
||||
*/
|
||||
public void close() throws HibernateException;
|
||||
|
||||
/**
|
||||
* Was this <tt>SessionFactory</tt> already closed?
|
||||
*/
|
||||
public boolean isClosed();
|
||||
|
||||
/**
|
||||
* Evict all entries from the second-level cache. This method occurs outside
|
||||
* of any transaction; it performs an immediate "hard" remove, so does not respect
|
||||
* any transaction isolation semantics of the usage strategy. Use with care.
|
||||
*/
|
||||
public void evict(Class persistentClass) throws HibernateException;
|
||||
/**
|
||||
* Evict an entry from the second-level cache. This method occurs outside
|
||||
* of any transaction; it performs an immediate "hard" remove, so does not respect
|
||||
* any transaction isolation semantics of the usage strategy. Use with care.
|
||||
*/
|
||||
public void evict(Class persistentClass, Serializable id) throws HibernateException;
|
||||
/**
|
||||
* Evict all entries from the second-level cache. This method occurs outside
|
||||
* of any transaction; it performs an immediate "hard" remove, so does not respect
|
||||
* any transaction isolation semantics of the usage strategy. Use with care.
|
||||
*/
|
||||
public void evictEntity(String entityName) throws HibernateException;
|
||||
/**
|
||||
* Evict an entry from the second-level cache. This method occurs outside
|
||||
* of any transaction; it performs an immediate "hard" remove, so does not respect
|
||||
* any transaction isolation semantics of the usage strategy. Use with care.
|
||||
*/
|
||||
public void evictEntity(String entityName, Serializable id) throws HibernateException;
|
||||
/**
|
||||
* Evict all entries from the second-level cache. This method occurs outside
|
||||
* of any transaction; it performs an immediate "hard" remove, so does not respect
|
||||
* any transaction isolation semantics of the usage strategy. Use with care.
|
||||
*/
|
||||
public void evictCollection(String roleName) throws HibernateException;
|
||||
/**
|
||||
* Evict an entry from the second-level cache. This method occurs outside
|
||||
* of any transaction; it performs an immediate "hard" remove, so does not respect
|
||||
* any transaction isolation semantics of the usage strategy. Use with care.
|
||||
*/
|
||||
public void evictCollection(String roleName, Serializable id) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Evict any query result sets cached in the default query cache region.
|
||||
*/
|
||||
public void evictQueries() throws HibernateException;
|
||||
/**
|
||||
* Evict any query result sets cached in the named query cache region.
|
||||
*/
|
||||
public void evictQueries(String cacheRegion) throws HibernateException;
|
||||
/**
|
||||
* Get a new stateless session.
|
||||
*/
|
||||
public StatelessSession openStatelessSession();
|
||||
/**
|
||||
* Get a new stateless session for the given JDBC connection.
|
||||
*/
|
||||
public StatelessSession openStatelessSession(Connection connection);
|
||||
|
||||
/**
|
||||
* Obtain a set of the names of all filters defined on this SessionFactory.
|
||||
*
|
||||
* @return The set of filter names.
|
||||
*/
|
||||
public Set getDefinedFilterNames();
|
||||
|
||||
/**
|
||||
* Obtain the definition of a filter by name.
|
||||
*
|
||||
* @param filterName The name of the filter for which to obtain the definition.
|
||||
* @return The filter definition.
|
||||
* @throws HibernateException If no filter defined with the given name.
|
||||
*/
|
||||
public FilterDefinition getFilterDefinition(String filterName) throws HibernateException;
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
|
||||
/**
|
||||
* A <tt>StaleStateException</tt> that carries information
|
||||
* about a particular entity instance that was the source
|
||||
* of the failure.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class StaleObjectStateException extends StaleStateException {
|
||||
private final String entityName;
|
||||
private final Serializable identifier;
|
||||
|
||||
public StaleObjectStateException(String persistentClass, Serializable identifier) {
|
||||
super("Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect)");
|
||||
this.entityName = persistentClass;
|
||||
this.identifier = identifier;
|
||||
}
|
||||
|
||||
public String getEntityName() {
|
||||
return entityName;
|
||||
}
|
||||
|
||||
public Serializable getIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return super.getMessage() + ": " +
|
||||
MessageHelper.infoString(entityName, identifier);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
/**
|
||||
* Thrown when a version number or timestamp check failed, indicating that the
|
||||
* <tt>Session</tt> contained stale data (when using long transactions
|
||||
* with versioning). Also occurs if we try delete or update a row that does
|
||||
* not exist.<br>
|
||||
* <br>
|
||||
* Note that this exception often indicates that the user failed to specify the
|
||||
* correct <tt>unsaved-value</tt> strategy for a class!
|
||||
*
|
||||
* @see StaleObjectStateException
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class StaleStateException extends HibernateException {
|
||||
|
||||
public StaleStateException(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,217 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.sql.Connection;
|
||||
|
||||
/**
|
||||
* A command-oriented API for performing bulk operations
|
||||
* against a database.<br>
|
||||
* <br>
|
||||
* A stateless session does not implement a first-level cache nor
|
||||
* interact with any second-level cache, nor does it implement
|
||||
* transactional write-behind or automatic dirty checking, nor do
|
||||
* operations cascade to associated instances. Collections are
|
||||
* ignored by a stateless session. Operations performed via a
|
||||
* stateless session bypass Hibernate's event model and
|
||||
* interceptors. Stateless sessions are vulnerable to data
|
||||
* aliasing effects, due to the lack of a first-level cache.<br>
|
||||
* <br>
|
||||
* For certain kinds of transactions, a stateless session may
|
||||
* perform slightly faster than a stateful session.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public interface StatelessSession extends Serializable {
|
||||
/**
|
||||
* Close the stateless session and release the JDBC connection.
|
||||
*/
|
||||
public void close();
|
||||
|
||||
/**
|
||||
* Insert a row.
|
||||
*
|
||||
* @param entity a new transient instance
|
||||
*/
|
||||
public Serializable insert(Object entity);
|
||||
|
||||
/**
|
||||
* Insert a row.
|
||||
*
|
||||
* @param entityName The entityName for the entity to be inserted
|
||||
* @param entity a new transient instance
|
||||
* @return the identifier of the instance
|
||||
*/
|
||||
public Serializable insert(String entityName, Object entity);
|
||||
|
||||
/**
|
||||
* Update a row.
|
||||
*
|
||||
* @param entity a detached entity instance
|
||||
*/
|
||||
public void update(Object entity);
|
||||
|
||||
/**
|
||||
* Update a row.
|
||||
*
|
||||
* @param entityName The entityName for the entity to be updated
|
||||
* @param entity a detached entity instance
|
||||
*/
|
||||
public void update(String entityName, Object entity);
|
||||
|
||||
/**
|
||||
* Delete a row.
|
||||
*
|
||||
* @param entity a detached entity instance
|
||||
*/
|
||||
public void delete(Object entity);
|
||||
|
||||
/**
|
||||
* Delete a row.
|
||||
*
|
||||
* @param entityName The entityName for the entity to be deleted
|
||||
* @param entity a detached entity instance
|
||||
*/
|
||||
public void delete(String entityName, Object entity);
|
||||
|
||||
/**
|
||||
* Retrieve a row.
|
||||
*
|
||||
* @return a detached entity instance
|
||||
*/
|
||||
public Object get(String entityName, Serializable id);
|
||||
|
||||
/**
|
||||
* Retrieve a row.
|
||||
*
|
||||
* @return a detached entity instance
|
||||
*/
|
||||
public Object get(Class entityClass, Serializable id);
|
||||
|
||||
/**
|
||||
* Retrieve a row, obtaining the specified lock mode.
|
||||
*
|
||||
* @return a detached entity instance
|
||||
*/
|
||||
public Object get(String entityName, Serializable id, LockMode lockMode);
|
||||
|
||||
/**
|
||||
* Retrieve a row, obtaining the specified lock mode.
|
||||
*
|
||||
* @return a detached entity instance
|
||||
*/
|
||||
public Object get(Class entityClass, Serializable id, LockMode lockMode);
|
||||
|
||||
/**
|
||||
* Refresh the entity instance state from the database.
|
||||
*
|
||||
* @param entity The entity to be refreshed.
|
||||
*/
|
||||
public void refresh(Object entity);
|
||||
|
||||
/**
|
||||
* Refresh the entity instance state from the database.
|
||||
*
|
||||
* @param entityName The entityName for the entity to be refreshed.
|
||||
* @param entity The entity to be refreshed.
|
||||
*/
|
||||
public void refresh(String entityName, Object entity);
|
||||
|
||||
/**
|
||||
* Refresh the entity instance state from the database.
|
||||
*
|
||||
* @param entity The entity to be refreshed.
|
||||
* @param lockMode The LockMode to be applied.
|
||||
*/
|
||||
public void refresh(Object entity, LockMode lockMode);
|
||||
|
||||
/**
|
||||
* Refresh the entity instance state from the database.
|
||||
*
|
||||
* @param entityName The entityName for the entity to be refreshed.
|
||||
* @param entity The entity to be refreshed.
|
||||
* @param lockMode The LockMode to be applied.
|
||||
*/
|
||||
public void refresh(String entityName, Object entity, LockMode lockMode);
|
||||
|
||||
/**
|
||||
* Create a new instance of <tt>Query</tt> for the given HQL query string.
|
||||
* Entities returned by the query are detached.
|
||||
*/
|
||||
public Query createQuery(String queryString);
|
||||
|
||||
/**
|
||||
* Obtain an instance of <tt>Query</tt> for a named query string defined in
|
||||
* the mapping file. Entities returned by the query are detached.
|
||||
*/
|
||||
public Query getNamedQuery(String queryName);
|
||||
|
||||
/**
|
||||
* Create a new <tt>Criteria</tt> instance, for the given entity class,
|
||||
* or a superclass of an entity class. Entities returned by the query are
|
||||
* detached.
|
||||
*
|
||||
* @param persistentClass a class, which is persistent, or has persistent subclasses
|
||||
* @return Criteria
|
||||
*/
|
||||
public Criteria createCriteria(Class persistentClass);
|
||||
|
||||
/**
|
||||
* Create a new <tt>Criteria</tt> instance, for the given entity class,
|
||||
* or a superclass of an entity class, with the given alias.
|
||||
* Entities returned by the query are detached.
|
||||
*
|
||||
* @param persistentClass a class, which is persistent, or has persistent subclasses
|
||||
* @return Criteria
|
||||
*/
|
||||
public Criteria createCriteria(Class persistentClass, String alias);
|
||||
|
||||
/**
|
||||
* Create a new <tt>Criteria</tt> instance, for the given entity name.
|
||||
* Entities returned by the query are detached.
|
||||
*
|
||||
* @param entityName
|
||||
* @return Criteria
|
||||
*/
|
||||
public Criteria createCriteria(String entityName);
|
||||
|
||||
/**
|
||||
* Create a new <tt>Criteria</tt> instance, for the given entity name,
|
||||
* with the given alias. Entities returned by the query are detached.
|
||||
*
|
||||
* @param entityName
|
||||
* @return Criteria
|
||||
*/
|
||||
public Criteria createCriteria(String entityName, String alias);
|
||||
|
||||
/**
|
||||
* Create a new instance of <tt>SQLQuery</tt> for the given SQL query string.
|
||||
* Entities returned by the query are detached.
|
||||
*
|
||||
* @param queryString a SQL query
|
||||
* @return SQLQuery
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public SQLQuery createSQLQuery(String queryString) throws HibernateException;
|
||||
|
||||
/**
|
||||
* Begin a Hibernate transaction.
|
||||
*/
|
||||
public Transaction beginTransaction();
|
||||
|
||||
/**
|
||||
* Get the current Hibernate transaction.
|
||||
*/
|
||||
public Transaction getTransaction();
|
||||
|
||||
/**
|
||||
* Returns the current JDBC connection associated with this
|
||||
* instance.<br>
|
||||
* <br>
|
||||
* If the session is using aggressive connection release (as in a
|
||||
* CMT environment), it is the application's responsibility to
|
||||
* close the connection returned by this call. Otherwise, the
|
||||
* application should not close the connection.
|
||||
*/
|
||||
public Connection connection();
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
import javax.transaction.Synchronization;
|
||||
|
||||
/**
|
||||
* Allows the application to define units of work, while
|
||||
* maintaining abstraction from the underlying transaction
|
||||
* implementation (eg. JTA, JDBC).<br>
|
||||
* <br>
|
||||
* A transaction is associated with a <tt>Session</tt> and is
|
||||
* usually instantiated by a call to <tt>Session.beginTransaction()</tt>.
|
||||
* A single session might span multiple transactions since
|
||||
* the notion of a session (a conversation between the application
|
||||
* and the datastore) is of coarser granularity than the notion of
|
||||
* a transaction. However, it is intended that there be at most one
|
||||
* uncommitted <tt>Transaction</tt> associated with a particular
|
||||
* <tt>Session</tt> at any time.<br>
|
||||
* <br>
|
||||
* Implementors are not intended to be threadsafe.
|
||||
*
|
||||
* @see Session#beginTransaction()
|
||||
* @see org.hibernate.transaction.TransactionFactory
|
||||
* @author Anton van Straaten
|
||||
*/
|
||||
public interface Transaction {
|
||||
|
||||
/**
|
||||
* Begin a new transaction.
|
||||
*/
|
||||
public void begin() throws HibernateException;
|
||||
|
||||
/**
|
||||
* Flush the associated <tt>Session</tt> and end the unit of work (unless
|
||||
* we are in {@link FlushMode#NEVER}.
|
||||
* </p>
|
||||
* This method will commit the underlying transaction if and only
|
||||
* if the underlying transaction was initiated by this object.
|
||||
*
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public void commit() throws HibernateException;
|
||||
|
||||
/**
|
||||
* Force the underlying transaction to roll back.
|
||||
*
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public void rollback() throws HibernateException;
|
||||
|
||||
/**
|
||||
* Was this transaction rolled back or set to rollback only?
|
||||
* <p/>
|
||||
* This only accounts for actions initiated from this local transaction.
|
||||
* If, for example, the underlying transaction is forced to rollback via
|
||||
* some other means, this method still reports false because the rollback
|
||||
* was not initiated from here.
|
||||
*
|
||||
* @return boolean True if the transaction was rolled back via this
|
||||
* local transaction; false otherwise.
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public boolean wasRolledBack() throws HibernateException;
|
||||
|
||||
/**
|
||||
* Check if this transaction was successfully committed.
|
||||
* <p/>
|
||||
* This method could return <tt>false</tt> even after successful invocation
|
||||
* of {@link #commit}. As an example, JTA based strategies no-op on
|
||||
* {@link #commit} calls if they did not start the transaction; in that case,
|
||||
* they also report {@link #wasCommitted} as false.
|
||||
*
|
||||
* @return boolean True if the transaction was (unequivocally) committed
|
||||
* via this local transaction; false otherwise.
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public boolean wasCommitted() throws HibernateException;
|
||||
|
||||
/**
|
||||
* Is this transaction still active?
|
||||
* <p/>
|
||||
* Again, this only returns information in relation to the
|
||||
* local transaction, not the actual underlying transaction.
|
||||
*
|
||||
* @return boolean Treu if this local transaction is still active.
|
||||
*/
|
||||
public boolean isActive() throws HibernateException;
|
||||
|
||||
/**
|
||||
* Register a user synchronization callback for this transaction.
|
||||
*
|
||||
* @param synchronization The Synchronization callback to register.
|
||||
* @throws HibernateException
|
||||
*/
|
||||
public void registerSynchronization(Synchronization synchronization)
|
||||
throws HibernateException;
|
||||
|
||||
/**
|
||||
* Set the transaction timeout for any transaction started by
|
||||
* a subsequent call to <tt>begin()</tt> on this instance.
|
||||
*
|
||||
* @param seconds The number of seconds before a timeout.
|
||||
*/
|
||||
public void setTimeout(int seconds);
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
/**
|
||||
* Indicates that a transaction could not be begun, committed
|
||||
* or rolled back.
|
||||
*
|
||||
* @see Transaction
|
||||
* @author Anton van Straaten
|
||||
*/
|
||||
|
||||
public class TransactionException extends HibernateException {
|
||||
|
||||
public TransactionException(String message, Exception root) {
|
||||
super(message,root);
|
||||
}
|
||||
|
||||
public TransactionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
/**
|
||||
* Thrown when the user passes a transient instance to a <tt>Session</tt>
|
||||
* method that expects a persistent instance.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
|
||||
public class TransientObjectException extends HibernateException {
|
||||
|
||||
public TransientObjectException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
//$Id: $
|
||||
package org.hibernate;
|
||||
|
||||
/**
|
||||
* Used when a user provided type does not match the expected one
|
||||
*
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
public class TypeMismatchException extends HibernateException {
|
||||
public TypeMismatchException(Throwable root) {
|
||||
super( root );
|
||||
}
|
||||
|
||||
public TypeMismatchException(String s) {
|
||||
super( s );
|
||||
}
|
||||
|
||||
public TypeMismatchException(String string, Throwable root) {
|
||||
super( string, root );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
|
||||
/**
|
||||
* Thrown when Hibernate could not resolve an object by id, especially when
|
||||
* loading an association.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class UnresolvableObjectException extends HibernateException {
|
||||
|
||||
private final Serializable identifier;
|
||||
private final String entityName;
|
||||
|
||||
public UnresolvableObjectException(Serializable identifier, String clazz) {
|
||||
this("No row with the given identifier exists", identifier, clazz);
|
||||
}
|
||||
UnresolvableObjectException(String message, Serializable identifier, String clazz) {
|
||||
super(message);
|
||||
this.identifier = identifier;
|
||||
this.entityName = clazz;
|
||||
}
|
||||
public Serializable getIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return super.getMessage() + ": " +
|
||||
MessageHelper.infoString(entityName, identifier);
|
||||
}
|
||||
|
||||
public String getEntityName() {
|
||||
return entityName;
|
||||
}
|
||||
|
||||
public static void throwIfNull(Object o, Serializable id, String clazz)
|
||||
throws UnresolvableObjectException {
|
||||
if (o==null) throw new UnresolvableObjectException(id, clazz);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
//$Id$
|
||||
package org.hibernate;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Thrown when <tt>Session.load()</tt> selects a row with
|
||||
* the given primary key (identifier value) but the row's
|
||||
* discriminator value specifies a subclass that is not
|
||||
* assignable to the class requested by the user.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public class WrongClassException extends HibernateException {
|
||||
|
||||
private final Serializable identifier;
|
||||
private final String entityName;
|
||||
|
||||
public WrongClassException(String msg, Serializable identifier, String clazz) {
|
||||
super(msg);
|
||||
this.identifier = identifier;
|
||||
this.entityName = clazz;
|
||||
}
|
||||
public Serializable getIdentifier() {
|
||||
return identifier;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return "Object with id: " +
|
||||
identifier +
|
||||
" was not of the specified subclass: " +
|
||||
entityName +
|
||||
" (" + super.getMessage() + ")" ;
|
||||
}
|
||||
|
||||
public String getEntityName() {
|
||||
return entityName;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,151 @@
|
|||
// $Id$
|
||||
package org.hibernate.action;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.persister.entity.Queryable;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Iterator;
|
||||
import java.util.HashSet;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Implementation of BulkOperationCleanupAction.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class BulkOperationCleanupAction implements Executable, Serializable {
|
||||
|
||||
private final SessionImplementor session;
|
||||
|
||||
private final Set affectedEntityNames = new HashSet();
|
||||
private final Set affectedCollectionRoles = new HashSet();
|
||||
private final Serializable[] spaces;
|
||||
|
||||
public BulkOperationCleanupAction(SessionImplementor session, Queryable[] affectedQueryables) {
|
||||
this.session = session;
|
||||
// TODO : probably better to calculate these and pass them in, as it'll be more performant
|
||||
ArrayList tmpSpaces = new ArrayList();
|
||||
for ( int i = 0; i < affectedQueryables.length; i++ ) {
|
||||
if ( affectedQueryables[i].hasCache() ) {
|
||||
affectedEntityNames.add( affectedQueryables[i].getEntityName() );
|
||||
}
|
||||
Set roles = session.getFactory().getCollectionRolesByEntityParticipant( affectedQueryables[i].getEntityName() );
|
||||
if ( roles != null ) {
|
||||
affectedCollectionRoles.addAll( roles );
|
||||
}
|
||||
for ( int y = 0; y < affectedQueryables[i].getQuerySpaces().length; y++ ) {
|
||||
tmpSpaces.add( affectedQueryables[i].getQuerySpaces()[y] );
|
||||
}
|
||||
}
|
||||
this.spaces = new Serializable[ tmpSpaces.size() ];
|
||||
for ( int i = 0; i < tmpSpaces.size(); i++ ) {
|
||||
this.spaces[i] = ( Serializable ) tmpSpaces.get( i );
|
||||
}
|
||||
}
|
||||
|
||||
/** Create an action that will evict collection and entity regions based on queryspaces (table names).
|
||||
* TODO: cache the autodetected information and pass it in instead.
|
||||
**/
|
||||
public BulkOperationCleanupAction(SessionImplementor session, Set querySpaces) {
|
||||
this.session = session;
|
||||
|
||||
Set tmpSpaces = new HashSet(querySpaces);
|
||||
SessionFactoryImplementor factory = session.getFactory();
|
||||
Iterator iterator = factory.getAllClassMetadata().entrySet().iterator();
|
||||
while ( iterator.hasNext() ) {
|
||||
Map.Entry entry = (Map.Entry) iterator.next();
|
||||
String entityName = (String) entry.getKey();
|
||||
EntityPersister persister = factory.getEntityPersister( entityName );
|
||||
Serializable[] entitySpaces = persister.getQuerySpaces();
|
||||
|
||||
if (affectedEntity( querySpaces, entitySpaces )) {
|
||||
if ( persister.hasCache() ) {
|
||||
affectedEntityNames.add( persister.getEntityName() );
|
||||
}
|
||||
Set roles = session.getFactory().getCollectionRolesByEntityParticipant( persister.getEntityName() );
|
||||
if ( roles != null ) {
|
||||
affectedCollectionRoles.addAll( roles );
|
||||
}
|
||||
for ( int y = 0; y < entitySpaces.length; y++ ) {
|
||||
tmpSpaces.add( entitySpaces[y] );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
this.spaces = (Serializable[]) tmpSpaces.toArray( new Serializable[tmpSpaces.size()] );
|
||||
}
|
||||
|
||||
|
||||
/** returns true if no queryspaces or if there are a match */
|
||||
private boolean affectedEntity(Set querySpaces, Serializable[] entitySpaces) {
|
||||
if(querySpaces==null || querySpaces.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
for ( int i = 0; i < entitySpaces.length; i++ ) {
|
||||
if ( querySpaces.contains( entitySpaces[i] ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void init() {
|
||||
evictEntityRegions();
|
||||
evictCollectionRegions();
|
||||
}
|
||||
|
||||
public boolean hasAfterTransactionCompletion() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void afterTransactionCompletion(boolean success) throws HibernateException {
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
// HACK ALERT!!!!!
|
||||
if ( session.getFactory().getSettings().getCacheProvider() instanceof org.hibernate.cache.OptimisticTreeCacheProvider
|
||||
|| session.getFactory().getSettings().getCacheProvider() instanceof org.hibernate.cache.TreeCacheProvider ) {
|
||||
return;
|
||||
}
|
||||
///////////////////////////////////////////////////////////////////////
|
||||
evictEntityRegions();
|
||||
evictCollectionRegions();
|
||||
}
|
||||
|
||||
public Serializable[] getPropertySpaces() {
|
||||
return spaces;
|
||||
}
|
||||
|
||||
public void beforeExecutions() throws HibernateException {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
public void execute() throws HibernateException {
|
||||
// nothing to do
|
||||
}
|
||||
|
||||
private void evictEntityRegions() {
|
||||
if ( affectedEntityNames != null ) {
|
||||
Iterator itr = affectedEntityNames.iterator();
|
||||
while ( itr.hasNext() ) {
|
||||
final String entityName = ( String ) itr.next();
|
||||
session.getFactory().evictEntity( entityName );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void evictCollectionRegions() {
|
||||
if ( affectedCollectionRoles != null ) {
|
||||
Iterator itr = affectedCollectionRoles.iterator();
|
||||
while ( itr.hasNext() ) {
|
||||
final String roleName = ( String ) itr.next();
|
||||
session.getFactory().evictCollection( roleName );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
//$Id$
|
||||
package org.hibernate.action;
|
||||
|
||||
import org.hibernate.cache.CacheConcurrencyStrategy.SoftLock;
|
||||
import org.hibernate.cache.CacheException;
|
||||
import org.hibernate.cache.CacheKey;
|
||||
import org.hibernate.collection.PersistentCollection;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
import org.hibernate.util.StringHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Any action relating to insert/update/delete of a collection
|
||||
* @author Gavin King
|
||||
*/
|
||||
public abstract class CollectionAction implements Executable, Serializable, Comparable {
|
||||
|
||||
private transient CollectionPersister persister;
|
||||
private final Serializable key;
|
||||
private Serializable finalKey;
|
||||
private final SessionImplementor session;
|
||||
private SoftLock lock;
|
||||
private final String collectionRole;
|
||||
private final PersistentCollection collection;
|
||||
|
||||
public CollectionAction(
|
||||
final CollectionPersister persister,
|
||||
final PersistentCollection collection,
|
||||
final Serializable key,
|
||||
final SessionImplementor session)
|
||||
throws CacheException {
|
||||
this.persister = persister;
|
||||
this.session = session;
|
||||
this.key = key;
|
||||
this.collectionRole = persister.getRole();
|
||||
this.collection = collection;
|
||||
}
|
||||
|
||||
protected PersistentCollection getCollection() {
|
||||
return collection;
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
|
||||
ois.defaultReadObject();
|
||||
persister = session.getFactory().getCollectionPersister( collectionRole );
|
||||
}
|
||||
|
||||
public void afterTransactionCompletion(boolean success) throws CacheException {
|
||||
if ( persister.hasCache() ) {
|
||||
final CacheKey ck = new CacheKey(
|
||||
key,
|
||||
persister.getKeyType(),
|
||||
persister.getRole(),
|
||||
session.getEntityMode(),
|
||||
session.getFactory()
|
||||
);
|
||||
persister.getCache().release(ck, lock);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasAfterTransactionCompletion() {
|
||||
return persister.hasCache();
|
||||
}
|
||||
|
||||
public Serializable[] getPropertySpaces() {
|
||||
return persister.getCollectionSpaces();
|
||||
}
|
||||
|
||||
protected final CollectionPersister getPersister() {
|
||||
return persister;
|
||||
}
|
||||
|
||||
protected final Serializable getKey() {
|
||||
finalKey = key;
|
||||
if ( key instanceof DelayedPostInsertIdentifier ) {
|
||||
// need to look it up from the persistence-context
|
||||
finalKey = session.getPersistenceContext().getEntry( collection.getOwner() ).getId();
|
||||
if ( finalKey == key ) {
|
||||
// we may be screwed here since the collection action is about to execute
|
||||
// and we do not know the final owner key value
|
||||
}
|
||||
}
|
||||
return finalKey;
|
||||
}
|
||||
|
||||
protected final SessionImplementor getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
public final void beforeExecutions() throws CacheException {
|
||||
// we need to obtain the lock before any actions are
|
||||
// executed, since this may be an inverse="true"
|
||||
// bidirectional association and it is one of the
|
||||
// earlier entity actions which actually updates
|
||||
// the database (this action is resposible for
|
||||
// second-level cache invalidation only)
|
||||
if ( persister.hasCache() ) {
|
||||
final CacheKey ck = new CacheKey(
|
||||
key,
|
||||
persister.getKeyType(),
|
||||
persister.getRole(),
|
||||
session.getEntityMode(),
|
||||
session.getFactory()
|
||||
);
|
||||
lock = persister.getCache().lock(ck, null);
|
||||
}
|
||||
}
|
||||
|
||||
protected final void evict() throws CacheException {
|
||||
if ( persister.hasCache() ) {
|
||||
CacheKey ck = new CacheKey(
|
||||
key,
|
||||
persister.getKeyType(),
|
||||
persister.getRole(),
|
||||
session.getEntityMode(),
|
||||
session.getFactory()
|
||||
);
|
||||
persister.getCache().evict(ck);
|
||||
}
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return StringHelper.unqualify( getClass().getName() ) +
|
||||
MessageHelper.infoString( collectionRole, key );
|
||||
}
|
||||
|
||||
public int compareTo(Object other) {
|
||||
CollectionAction action = ( CollectionAction ) other;
|
||||
//sort first by role name
|
||||
int roleComparison = collectionRole.compareTo( action.collectionRole );
|
||||
if ( roleComparison != 0 ) {
|
||||
return roleComparison;
|
||||
}
|
||||
else {
|
||||
//then by fk
|
||||
return persister.getKeyType()
|
||||
.compare( key, action.key, session.getEntityMode() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
//$Id$
|
||||
package org.hibernate.action;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.cache.CacheException;
|
||||
import org.hibernate.collection.PersistentCollection;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public final class CollectionRecreateAction extends CollectionAction {
|
||||
|
||||
public CollectionRecreateAction(
|
||||
final PersistentCollection collection,
|
||||
final CollectionPersister persister,
|
||||
final Serializable id,
|
||||
final SessionImplementor session)
|
||||
throws CacheException {
|
||||
super( persister, collection, id, session );
|
||||
}
|
||||
|
||||
public void execute() throws HibernateException {
|
||||
final PersistentCollection collection = getCollection();
|
||||
|
||||
getPersister().recreate( collection, getKey(), getSession() );
|
||||
|
||||
getSession().getPersistenceContext()
|
||||
.getCollectionEntry(collection)
|
||||
.afterAction(collection);
|
||||
|
||||
evict();
|
||||
|
||||
if ( getSession().getFactory().getStatistics().isStatisticsEnabled() ) {
|
||||
getSession().getFactory().getStatisticsImplementor()
|
||||
.recreateCollection( getPersister().getRole() );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
//$Id$
|
||||
package org.hibernate.action;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.cache.CacheException;
|
||||
import org.hibernate.collection.PersistentCollection;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public final class CollectionRemoveAction extends CollectionAction {
|
||||
|
||||
private boolean emptySnapshot;
|
||||
|
||||
public CollectionRemoveAction(
|
||||
final PersistentCollection collection,
|
||||
final CollectionPersister persister,
|
||||
final Serializable id,
|
||||
final boolean emptySnapshot,
|
||||
final SessionImplementor session)
|
||||
throws CacheException {
|
||||
super( persister, collection, id, session );
|
||||
this.emptySnapshot = emptySnapshot;
|
||||
}
|
||||
|
||||
public void execute() throws HibernateException {
|
||||
if ( !emptySnapshot ) getPersister().remove( getKey(), getSession() );
|
||||
|
||||
final PersistentCollection collection = getCollection();
|
||||
if (collection!=null) {
|
||||
getSession().getPersistenceContext()
|
||||
.getCollectionEntry(collection)
|
||||
.afterAction(collection);
|
||||
}
|
||||
|
||||
evict();
|
||||
|
||||
if ( getSession().getFactory().getStatistics().isStatisticsEnabled() ) {
|
||||
getSession().getFactory().getStatisticsImplementor()
|
||||
.removeCollection( getPersister().getRole() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,78 @@
|
|||
//$Id$
|
||||
package org.hibernate.action;
|
||||
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.cache.CacheException;
|
||||
import org.hibernate.collection.PersistentCollection;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.persister.collection.CollectionPersister;
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public final class CollectionUpdateAction extends CollectionAction {
|
||||
|
||||
private final boolean emptySnapshot;
|
||||
|
||||
public CollectionUpdateAction(
|
||||
final PersistentCollection collection,
|
||||
final CollectionPersister persister,
|
||||
final Serializable id,
|
||||
final boolean emptySnapshot,
|
||||
final SessionImplementor session)
|
||||
throws CacheException {
|
||||
super( persister, collection, id, session );
|
||||
this.emptySnapshot = emptySnapshot;
|
||||
}
|
||||
|
||||
public void execute() throws HibernateException {
|
||||
final Serializable id = getKey();
|
||||
final SessionImplementor session = getSession();
|
||||
final CollectionPersister persister = getPersister();
|
||||
final PersistentCollection collection = getCollection();
|
||||
boolean affectedByFilters = persister.isAffectedByEnabledFilters(session);
|
||||
|
||||
if ( !collection.wasInitialized() ) {
|
||||
if ( !collection.hasQueuedOperations() ) throw new AssertionFailure( "no queued adds" );
|
||||
//do nothing - we only need to notify the cache...
|
||||
}
|
||||
else if ( !affectedByFilters && collection.empty() ) {
|
||||
if ( !emptySnapshot ) persister.remove( id, session );
|
||||
}
|
||||
else if ( collection.needsRecreate(persister) ) {
|
||||
if (affectedByFilters) {
|
||||
throw new HibernateException(
|
||||
"cannot recreate collection while filter is enabled: " +
|
||||
MessageHelper.collectionInfoString( persister, id, persister.getFactory() )
|
||||
);
|
||||
}
|
||||
if ( !emptySnapshot ) persister.remove( id, session );
|
||||
persister.recreate( collection, id, session );
|
||||
}
|
||||
else {
|
||||
persister.deleteRows( collection, id, session );
|
||||
persister.updateRows( collection, id, session );
|
||||
persister.insertRows( collection, id, session );
|
||||
}
|
||||
|
||||
getSession().getPersistenceContext()
|
||||
.getCollectionEntry(collection)
|
||||
.afterAction(collection);
|
||||
|
||||
evict();
|
||||
|
||||
if ( getSession().getFactory().getStatistics().isStatisticsEnabled() ) {
|
||||
getSession().getFactory().getStatisticsImplementor().
|
||||
updateCollection( getPersister().getRole() );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package org.hibernate.action;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Acts as a stand-in for an entity identifier which is supposed to be
|
||||
* generated on insert (like an IDENTITY column) where the insert needed to
|
||||
* be delayed because we were outside a transaction when the persist
|
||||
* occurred (save currently still performs the insert).
|
||||
* <p/>
|
||||
* The stand-in is only used within the {@link org.hibernate.engine.PersistenceContext}
|
||||
* in order to distinguish one instance from another; it is never injected into
|
||||
* the entity instance or returned to the client...
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class DelayedPostInsertIdentifier implements Serializable {
|
||||
private static long SEQUENCE = 0;
|
||||
private final long sequence;
|
||||
|
||||
public DelayedPostInsertIdentifier() {
|
||||
synchronized( DelayedPostInsertIdentifier.class ) {
|
||||
if ( SEQUENCE == Long.MAX_VALUE ) {
|
||||
SEQUENCE = 0;
|
||||
}
|
||||
this.sequence = SEQUENCE++;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if ( this == o ) {
|
||||
return true;
|
||||
}
|
||||
if ( o == null || getClass() != o.getClass() ) {
|
||||
return false;
|
||||
}
|
||||
final DelayedPostInsertIdentifier that = ( DelayedPostInsertIdentifier ) o;
|
||||
return sequence == that.sequence;
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return ( int ) ( sequence ^ ( sequence >>> 32 ) );
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "<delayed:" + sequence + ">";
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,136 @@
|
|||
//$Id$
|
||||
package org.hibernate.action;
|
||||
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.pretty.MessageHelper;
|
||||
import org.hibernate.util.StringHelper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* Base class for actions relating to insert/update/delete of an entity
|
||||
* instance.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public abstract class EntityAction implements Executable, Serializable, Comparable {
|
||||
|
||||
private final String entityName;
|
||||
private final Serializable id;
|
||||
private final Object instance;
|
||||
private final SessionImplementor session;
|
||||
|
||||
private transient EntityPersister persister;
|
||||
|
||||
/**
|
||||
* Instantiate an action.
|
||||
*
|
||||
* @param session The session from which this action is coming.
|
||||
* @param id The id of the entity
|
||||
* @param instance The entiyt instance
|
||||
* @param persister The entity persister
|
||||
*/
|
||||
protected EntityAction(SessionImplementor session, Serializable id, Object instance, EntityPersister persister) {
|
||||
this.entityName = persister.getEntityName();
|
||||
this.id = id;
|
||||
this.instance = instance;
|
||||
this.session = session;
|
||||
this.persister = persister;
|
||||
}
|
||||
|
||||
protected abstract boolean hasPostCommitEventListeners();
|
||||
|
||||
/**
|
||||
* entity name accessor
|
||||
*
|
||||
* @return The entity name
|
||||
*/
|
||||
public String getEntityName() {
|
||||
return entityName;
|
||||
}
|
||||
|
||||
/**
|
||||
* entity id accessor
|
||||
*
|
||||
* @return The entity id
|
||||
*/
|
||||
public final Serializable getId() {
|
||||
if ( id instanceof DelayedPostInsertIdentifier ) {
|
||||
return session.getPersistenceContext().getEntry( instance ).getId();
|
||||
}
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* entity instance accessor
|
||||
*
|
||||
* @return The entity instance
|
||||
*/
|
||||
public final Object getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* originating session accessor
|
||||
*
|
||||
* @return The session from which this action originated.
|
||||
*/
|
||||
public final SessionImplementor getSession() {
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* entity persister accessor
|
||||
*
|
||||
* @return The entity persister
|
||||
*/
|
||||
public final EntityPersister getPersister() {
|
||||
return persister;
|
||||
}
|
||||
|
||||
public final Serializable[] getPropertySpaces() {
|
||||
return persister.getPropertySpaces();
|
||||
}
|
||||
|
||||
public void beforeExecutions() {
|
||||
throw new AssertionFailure( "beforeExecutions() called for non-collection action" );
|
||||
}
|
||||
|
||||
public boolean hasAfterTransactionCompletion() {
|
||||
return persister.hasCache() || hasPostCommitEventListeners();
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return StringHelper.unqualify( getClass().getName() ) + MessageHelper.infoString( entityName, id );
|
||||
}
|
||||
|
||||
public int compareTo(Object other) {
|
||||
EntityAction action = ( EntityAction ) other;
|
||||
//sort first by entity name
|
||||
int roleComparison = entityName.compareTo( action.entityName );
|
||||
if ( roleComparison != 0 ) {
|
||||
return roleComparison;
|
||||
}
|
||||
else {
|
||||
//then by id
|
||||
return persister.getIdentifierType().compare( id, action.id, session.getEntityMode() );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Serialization...
|
||||
*
|
||||
* @param ois Thed object stream
|
||||
* @throws IOException Problem performing the default stream reading
|
||||
* @throws ClassNotFoundException Problem performing the default stream reading
|
||||
*/
|
||||
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
|
||||
ois.defaultReadObject();
|
||||
persister = session.getFactory().getEntityPersister( entityName );
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,174 @@
|
|||
//$Id$
|
||||
package org.hibernate.action;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.cache.CacheKey;
|
||||
import org.hibernate.cache.CacheConcurrencyStrategy.SoftLock;
|
||||
import org.hibernate.engine.EntityEntry;
|
||||
import org.hibernate.engine.EntityKey;
|
||||
import org.hibernate.engine.PersistenceContext;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.event.PostDeleteEvent;
|
||||
import org.hibernate.event.PostDeleteEventListener;
|
||||
import org.hibernate.event.PreDeleteEvent;
|
||||
import org.hibernate.event.PreDeleteEventListener;
|
||||
import org.hibernate.event.EventSource;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
public final class EntityDeleteAction extends EntityAction {
|
||||
|
||||
private final Object version;
|
||||
private SoftLock lock;
|
||||
private final boolean isCascadeDeleteEnabled;
|
||||
private final Object[] state;
|
||||
|
||||
public EntityDeleteAction(
|
||||
final Serializable id,
|
||||
final Object[] state,
|
||||
final Object version,
|
||||
final Object instance,
|
||||
final EntityPersister persister,
|
||||
final boolean isCascadeDeleteEnabled,
|
||||
final SessionImplementor session) {
|
||||
super( session, id, instance, persister );
|
||||
this.version = version;
|
||||
this.isCascadeDeleteEnabled = isCascadeDeleteEnabled;
|
||||
this.state = state;
|
||||
}
|
||||
|
||||
public void execute() throws HibernateException {
|
||||
Serializable id = getId();
|
||||
EntityPersister persister = getPersister();
|
||||
SessionImplementor session = getSession();
|
||||
Object instance = getInstance();
|
||||
|
||||
boolean veto = preDelete();
|
||||
|
||||
Object version = this.version;
|
||||
if ( persister.isVersionPropertyGenerated() ) {
|
||||
// we need to grab the version value from the entity, otherwise
|
||||
// we have issues with generated-version entities that may have
|
||||
// multiple actions queued during the same flush
|
||||
version = persister.getVersion( instance, session.getEntityMode() );
|
||||
}
|
||||
|
||||
final CacheKey ck;
|
||||
if ( persister.hasCache() ) {
|
||||
ck = new CacheKey(
|
||||
id,
|
||||
persister.getIdentifierType(),
|
||||
persister.getRootEntityName(),
|
||||
session.getEntityMode(),
|
||||
session.getFactory()
|
||||
);
|
||||
lock = persister.getCache().lock(ck, version);
|
||||
}
|
||||
else {
|
||||
ck = null;
|
||||
}
|
||||
|
||||
if ( !isCascadeDeleteEnabled && !veto ) {
|
||||
persister.delete( id, version, instance, session );
|
||||
}
|
||||
|
||||
//postDelete:
|
||||
// After actually deleting a row, record the fact that the instance no longer
|
||||
// exists on the database (needed for identity-column key generation), and
|
||||
// remove it from the session cache
|
||||
final PersistenceContext persistenceContext = session.getPersistenceContext();
|
||||
EntityEntry entry = persistenceContext.removeEntry( instance );
|
||||
if ( entry == null ) {
|
||||
throw new AssertionFailure( "possible nonthreadsafe access to session" );
|
||||
}
|
||||
entry.postDelete();
|
||||
|
||||
EntityKey key = new EntityKey( entry.getId(), entry.getPersister(), session.getEntityMode() );
|
||||
persistenceContext.removeEntity(key);
|
||||
persistenceContext.removeProxy(key);
|
||||
|
||||
if ( persister.hasCache() ) persister.getCache().evict(ck);
|
||||
|
||||
postDelete();
|
||||
|
||||
if ( getSession().getFactory().getStatistics().isStatisticsEnabled() && !veto ) {
|
||||
getSession().getFactory().getStatisticsImplementor()
|
||||
.deleteEntity( getPersister().getEntityName() );
|
||||
}
|
||||
}
|
||||
|
||||
private boolean preDelete() {
|
||||
PreDeleteEventListener[] preListeners = getSession().getListeners()
|
||||
.getPreDeleteEventListeners();
|
||||
boolean veto = false;
|
||||
if (preListeners.length>0) {
|
||||
PreDeleteEvent preEvent = new PreDeleteEvent( getInstance(), getId(), state, getPersister() );
|
||||
for ( int i = 0; i < preListeners.length; i++ ) {
|
||||
veto = preListeners[i].onPreDelete(preEvent) || veto;
|
||||
}
|
||||
}
|
||||
return veto;
|
||||
}
|
||||
|
||||
private void postDelete() {
|
||||
PostDeleteEventListener[] postListeners = getSession().getListeners()
|
||||
.getPostDeleteEventListeners();
|
||||
if (postListeners.length>0) {
|
||||
PostDeleteEvent postEvent = new PostDeleteEvent(
|
||||
getInstance(),
|
||||
getId(),
|
||||
state,
|
||||
getPersister(),
|
||||
(EventSource) getSession()
|
||||
);
|
||||
for ( int i = 0; i < postListeners.length; i++ ) {
|
||||
postListeners[i].onPostDelete(postEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void postCommitDelete() {
|
||||
PostDeleteEventListener[] postListeners = getSession().getListeners()
|
||||
.getPostCommitDeleteEventListeners();
|
||||
if (postListeners.length>0) {
|
||||
PostDeleteEvent postEvent = new PostDeleteEvent(
|
||||
getInstance(),
|
||||
getId(),
|
||||
state,
|
||||
getPersister(),
|
||||
(EventSource) getSession()
|
||||
);
|
||||
for ( int i = 0; i < postListeners.length; i++ ) {
|
||||
postListeners[i].onPostDelete(postEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void afterTransactionCompletion(boolean success) throws HibernateException {
|
||||
if ( getPersister().hasCache() ) {
|
||||
final CacheKey ck = new CacheKey(
|
||||
getId(),
|
||||
getPersister().getIdentifierType(),
|
||||
getPersister().getRootEntityName(),
|
||||
getSession().getEntityMode(),
|
||||
getSession().getFactory()
|
||||
);
|
||||
getPersister().getCache().release(ck, lock);
|
||||
}
|
||||
postCommitDelete();
|
||||
}
|
||||
|
||||
protected boolean hasPostCommitEventListeners() {
|
||||
return getSession().getListeners().getPostCommitDeleteEventListeners().length>0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
//$Id$
|
||||
package org.hibernate.action;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.engine.EntityKey;
|
||||
import org.hibernate.event.PostInsertEvent;
|
||||
import org.hibernate.event.PostInsertEventListener;
|
||||
import org.hibernate.event.PreInsertEvent;
|
||||
import org.hibernate.event.PreInsertEventListener;
|
||||
import org.hibernate.event.EventSource;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
public final class EntityIdentityInsertAction extends EntityAction {
|
||||
private final Object[] state;
|
||||
private final boolean isDelayed;
|
||||
private final EntityKey delayedEntityKey;
|
||||
//private CacheEntry cacheEntry;
|
||||
private Serializable generatedId;
|
||||
|
||||
public EntityIdentityInsertAction(
|
||||
Object[] state,
|
||||
Object instance,
|
||||
EntityPersister persister,
|
||||
SessionImplementor session,
|
||||
boolean isDelayed) throws HibernateException {
|
||||
super( session, null, instance, persister );
|
||||
this.state = state;
|
||||
this.isDelayed = isDelayed;
|
||||
delayedEntityKey = isDelayed ? generateDelayedEntityKey() : null;
|
||||
}
|
||||
|
||||
public void execute() throws HibernateException {
|
||||
|
||||
final EntityPersister persister = getPersister();
|
||||
final SessionImplementor session = getSession();
|
||||
final Object instance = getInstance();
|
||||
|
||||
boolean veto = preInsert();
|
||||
|
||||
// Don't need to lock the cache here, since if someone
|
||||
// else inserted the same pk first, the insert would fail
|
||||
|
||||
if ( !veto ) {
|
||||
generatedId = persister.insert( state, instance, session );
|
||||
if ( persister.hasInsertGeneratedProperties() ) {
|
||||
persister.processInsertGeneratedProperties( generatedId, instance, state, session );
|
||||
}
|
||||
//need to do that here rather than in the save event listener to let
|
||||
//the post insert events to have a id-filled entity when IDENTITY is used (EJB3)
|
||||
persister.setIdentifier( instance, generatedId, session.getEntityMode() );
|
||||
}
|
||||
|
||||
|
||||
//TODO: this bit actually has to be called after all cascades!
|
||||
// but since identity insert is called *synchronously*,
|
||||
// instead of asynchronously as other actions, it isn't
|
||||
/*if ( persister.hasCache() && !persister.isCacheInvalidationRequired() ) {
|
||||
cacheEntry = new CacheEntry(object, persister, session);
|
||||
persister.getCache().insert(generatedId, cacheEntry);
|
||||
}*/
|
||||
|
||||
postInsert();
|
||||
|
||||
if ( session.getFactory().getStatistics().isStatisticsEnabled() && !veto ) {
|
||||
session.getFactory().getStatisticsImplementor()
|
||||
.insertEntity( getPersister().getEntityName() );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void postInsert() {
|
||||
if ( isDelayed ) {
|
||||
getSession().getPersistenceContext().replaceDelayedEntityIdentityInsertKeys( delayedEntityKey, generatedId );
|
||||
}
|
||||
PostInsertEventListener[] postListeners = getSession().getListeners()
|
||||
.getPostInsertEventListeners();
|
||||
if (postListeners.length>0) {
|
||||
PostInsertEvent postEvent = new PostInsertEvent(
|
||||
getInstance(),
|
||||
generatedId,
|
||||
state,
|
||||
getPersister(),
|
||||
(EventSource) getSession()
|
||||
);
|
||||
for ( int i = 0; i < postListeners.length; i++ ) {
|
||||
postListeners[i].onPostInsert(postEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void postCommitInsert() {
|
||||
PostInsertEventListener[] postListeners = getSession().getListeners()
|
||||
.getPostCommitInsertEventListeners();
|
||||
if (postListeners.length>0) {
|
||||
PostInsertEvent postEvent = new PostInsertEvent(
|
||||
getInstance(),
|
||||
generatedId,
|
||||
state,
|
||||
getPersister(),
|
||||
(EventSource) getSession()
|
||||
);
|
||||
for ( int i = 0; i < postListeners.length; i++ ) {
|
||||
postListeners[i].onPostInsert(postEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean preInsert() {
|
||||
PreInsertEventListener[] preListeners = getSession().getListeners()
|
||||
.getPreInsertEventListeners();
|
||||
boolean veto = false;
|
||||
if (preListeners.length>0) {
|
||||
PreInsertEvent preEvent = new PreInsertEvent( getInstance(), null, state, getPersister(), getSession() );
|
||||
for ( int i = 0; i < preListeners.length; i++ ) {
|
||||
veto = preListeners[i].onPreInsert(preEvent) || veto;
|
||||
}
|
||||
}
|
||||
return veto;
|
||||
}
|
||||
|
||||
//Make 100% certain that this is called before any subsequent ScheduledUpdate.afterTransactionCompletion()!!
|
||||
public void afterTransactionCompletion(boolean success) throws HibernateException {
|
||||
//TODO: reenable if we also fix the above todo
|
||||
/*EntityPersister persister = getEntityPersister();
|
||||
if ( success && persister.hasCache() && !persister.isCacheInvalidationRequired() ) {
|
||||
persister.getCache().afterInsert( getGeneratedId(), cacheEntry );
|
||||
}*/
|
||||
postCommitInsert();
|
||||
}
|
||||
|
||||
public boolean hasAfterTransactionCompletion() {
|
||||
//TODO: simply remove this override
|
||||
// if we fix the above todos
|
||||
return hasPostCommitEventListeners();
|
||||
}
|
||||
|
||||
protected boolean hasPostCommitEventListeners() {
|
||||
return getSession().getListeners().getPostCommitInsertEventListeners().length>0;
|
||||
}
|
||||
|
||||
public final Serializable getGeneratedId() {
|
||||
return generatedId;
|
||||
}
|
||||
|
||||
public EntityKey getDelayedEntityKey() {
|
||||
return delayedEntityKey;
|
||||
}
|
||||
|
||||
private synchronized EntityKey generateDelayedEntityKey() {
|
||||
if ( !isDelayed ) {
|
||||
throw new AssertionFailure( "cannot request delayed entity-key for non-delayed post-insert-id generation" );
|
||||
}
|
||||
return new EntityKey( new DelayedPostInsertIdentifier(), getPersister(), getSession().getEntityMode() );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,200 @@
|
|||
//$Id$
|
||||
package org.hibernate.action;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.cache.CacheKey;
|
||||
import org.hibernate.cache.entry.CacheEntry;
|
||||
import org.hibernate.engine.EntityEntry;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.engine.Versioning;
|
||||
import org.hibernate.event.PostInsertEvent;
|
||||
import org.hibernate.event.PostInsertEventListener;
|
||||
import org.hibernate.event.PreInsertEvent;
|
||||
import org.hibernate.event.PreInsertEventListener;
|
||||
import org.hibernate.event.EventSource;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
|
||||
public final class EntityInsertAction extends EntityAction {
|
||||
|
||||
private Object[] state;
|
||||
private Object version;
|
||||
private Object cacheEntry;
|
||||
|
||||
public EntityInsertAction(
|
||||
Serializable id,
|
||||
Object[] state,
|
||||
Object instance,
|
||||
Object version,
|
||||
EntityPersister persister,
|
||||
SessionImplementor session) throws HibernateException {
|
||||
super( session, id, instance, persister );
|
||||
this.state = state;
|
||||
this.version = version;
|
||||
}
|
||||
|
||||
public Object[] getState() {
|
||||
return state;
|
||||
}
|
||||
|
||||
public void execute() throws HibernateException {
|
||||
EntityPersister persister = getPersister();
|
||||
SessionImplementor session = getSession();
|
||||
Object instance = getInstance();
|
||||
Serializable id = getId();
|
||||
|
||||
boolean veto = preInsert();
|
||||
|
||||
// Don't need to lock the cache here, since if someone
|
||||
// else inserted the same pk first, the insert would fail
|
||||
|
||||
if ( !veto ) {
|
||||
|
||||
persister.insert( id, state, instance, session );
|
||||
|
||||
EntityEntry entry = session.getPersistenceContext().getEntry( instance );
|
||||
if ( entry == null ) {
|
||||
throw new AssertionFailure( "possible nonthreadsafe access to session" );
|
||||
}
|
||||
|
||||
entry.postInsert();
|
||||
|
||||
if ( persister.hasInsertGeneratedProperties() ) {
|
||||
persister.processInsertGeneratedProperties( id, instance, state, session );
|
||||
if ( persister.isVersionPropertyGenerated() ) {
|
||||
version = Versioning.getVersion(state, persister);
|
||||
}
|
||||
entry.postUpdate(instance, state, version);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
final SessionFactoryImplementor factory = getSession().getFactory();
|
||||
|
||||
if ( isCachePutEnabled( persister, session ) ) {
|
||||
|
||||
CacheEntry ce = new CacheEntry(
|
||||
state,
|
||||
persister,
|
||||
persister.hasUninitializedLazyProperties( instance, session.getEntityMode() ),
|
||||
version,
|
||||
session,
|
||||
instance
|
||||
);
|
||||
|
||||
cacheEntry = persister.getCacheEntryStructure().structure(ce);
|
||||
final CacheKey ck = new CacheKey(
|
||||
id,
|
||||
persister.getIdentifierType(),
|
||||
persister.getRootEntityName(),
|
||||
session.getEntityMode(),
|
||||
session.getFactory()
|
||||
);
|
||||
// boolean put = persister.getCache().insert(ck, cacheEntry);
|
||||
boolean put = persister.getCache().insert( ck, cacheEntry, version );
|
||||
|
||||
if ( put && factory.getStatistics().isStatisticsEnabled() ) {
|
||||
factory.getStatisticsImplementor()
|
||||
.secondLevelCachePut( getPersister().getCache().getRegionName() );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
postInsert();
|
||||
|
||||
if ( factory.getStatistics().isStatisticsEnabled() && !veto ) {
|
||||
factory.getStatisticsImplementor()
|
||||
.insertEntity( getPersister().getEntityName() );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void postInsert() {
|
||||
PostInsertEventListener[] postListeners = getSession().getListeners()
|
||||
.getPostInsertEventListeners();
|
||||
if ( postListeners.length > 0 ) {
|
||||
PostInsertEvent postEvent = new PostInsertEvent(
|
||||
getInstance(),
|
||||
getId(),
|
||||
state,
|
||||
getPersister(),
|
||||
(EventSource) getSession()
|
||||
);
|
||||
for ( int i = 0; i < postListeners.length; i++ ) {
|
||||
postListeners[i].onPostInsert(postEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void postCommitInsert() {
|
||||
PostInsertEventListener[] postListeners = getSession().getListeners()
|
||||
.getPostCommitInsertEventListeners();
|
||||
if ( postListeners.length > 0 ) {
|
||||
PostInsertEvent postEvent = new PostInsertEvent(
|
||||
getInstance(),
|
||||
getId(),
|
||||
state,
|
||||
getPersister(),
|
||||
(EventSource) getSession()
|
||||
);
|
||||
for ( int i = 0; i < postListeners.length; i++ ) {
|
||||
postListeners[i].onPostInsert(postEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean preInsert() {
|
||||
PreInsertEventListener[] preListeners = getSession().getListeners()
|
||||
.getPreInsertEventListeners();
|
||||
boolean veto = false;
|
||||
if (preListeners.length>0) {
|
||||
PreInsertEvent preEvent = new PreInsertEvent( getInstance(), getId(), state, getPersister(), getSession() );
|
||||
for ( int i = 0; i < preListeners.length; i++ ) {
|
||||
veto = preListeners[i].onPreInsert(preEvent) || veto;
|
||||
}
|
||||
}
|
||||
return veto;
|
||||
}
|
||||
|
||||
//Make 100% certain that this is called before any subsequent ScheduledUpdate.afterTransactionCompletion()!!
|
||||
public void afterTransactionCompletion(boolean success) throws HibernateException {
|
||||
EntityPersister persister = getPersister();
|
||||
if ( success && isCachePutEnabled( persister, getSession() ) ) {
|
||||
final CacheKey ck = new CacheKey(
|
||||
getId(),
|
||||
persister.getIdentifierType(),
|
||||
persister.getRootEntityName(),
|
||||
getSession().getEntityMode(),
|
||||
getSession().getFactory()
|
||||
);
|
||||
boolean put = persister.getCache().afterInsert(ck, cacheEntry, version );
|
||||
|
||||
if ( put && getSession().getFactory().getStatistics().isStatisticsEnabled() ) {
|
||||
getSession().getFactory().getStatisticsImplementor()
|
||||
.secondLevelCachePut( getPersister().getCache().getRegionName() );
|
||||
}
|
||||
}
|
||||
postCommitInsert();
|
||||
}
|
||||
|
||||
protected boolean hasPostCommitEventListeners() {
|
||||
return getSession().getListeners().getPostCommitInsertEventListeners().length>0;
|
||||
}
|
||||
|
||||
private boolean isCachePutEnabled(EntityPersister persister, SessionImplementor session) {
|
||||
return persister.hasCache() &&
|
||||
!persister.isCacheInvalidationRequired() &&
|
||||
session.getCacheMode().isPutEnabled();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,261 @@
|
|||
//$Id$
|
||||
package org.hibernate.action;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.cache.CacheException;
|
||||
import org.hibernate.cache.CacheKey;
|
||||
import org.hibernate.cache.CacheConcurrencyStrategy.SoftLock;
|
||||
import org.hibernate.cache.entry.CacheEntry;
|
||||
import org.hibernate.engine.EntityEntry;
|
||||
import org.hibernate.engine.SessionFactoryImplementor;
|
||||
import org.hibernate.engine.SessionImplementor;
|
||||
import org.hibernate.engine.Status;
|
||||
import org.hibernate.engine.Versioning;
|
||||
import org.hibernate.event.PostUpdateEvent;
|
||||
import org.hibernate.event.PostUpdateEventListener;
|
||||
import org.hibernate.event.PreUpdateEvent;
|
||||
import org.hibernate.event.PreUpdateEventListener;
|
||||
import org.hibernate.event.EventSource;
|
||||
import org.hibernate.persister.entity.EntityPersister;
|
||||
import org.hibernate.type.TypeFactory;
|
||||
|
||||
public final class EntityUpdateAction extends EntityAction {
|
||||
|
||||
private final Object[] state;
|
||||
private final Object[] previousState;
|
||||
private final Object previousVersion;
|
||||
private Object nextVersion;
|
||||
private final int[] dirtyFields;
|
||||
private final boolean hasDirtyCollection;
|
||||
private final Object rowId;
|
||||
private Object cacheEntry;
|
||||
private SoftLock lock;
|
||||
|
||||
public EntityUpdateAction(
|
||||
final Serializable id,
|
||||
final Object[] state,
|
||||
final int[] dirtyProperties,
|
||||
final boolean hasDirtyCollection,
|
||||
final Object[] previousState,
|
||||
final Object previousVersion,
|
||||
final Object nextVersion,
|
||||
final Object instance,
|
||||
final Object rowId,
|
||||
final EntityPersister persister,
|
||||
final SessionImplementor session) throws HibernateException {
|
||||
super( session, id, instance, persister );
|
||||
this.state = state;
|
||||
this.previousState = previousState;
|
||||
this.previousVersion = previousVersion;
|
||||
this.nextVersion = nextVersion;
|
||||
this.dirtyFields = dirtyProperties;
|
||||
this.hasDirtyCollection = hasDirtyCollection;
|
||||
this.rowId = rowId;
|
||||
}
|
||||
|
||||
public void execute() throws HibernateException {
|
||||
Serializable id = getId();
|
||||
EntityPersister persister = getPersister();
|
||||
SessionImplementor session = getSession();
|
||||
Object instance = getInstance();
|
||||
|
||||
boolean veto = preUpdate();
|
||||
|
||||
final SessionFactoryImplementor factory = getSession().getFactory();
|
||||
Object previousVersion = this.previousVersion;
|
||||
if ( persister.isVersionPropertyGenerated() ) {
|
||||
// we need to grab the version value from the entity, otherwise
|
||||
// we have issues with generated-version entities that may have
|
||||
// multiple actions queued during the same flush
|
||||
previousVersion = persister.getVersion( instance, session.getEntityMode() );
|
||||
}
|
||||
|
||||
final CacheKey ck;
|
||||
if ( persister.hasCache() ) {
|
||||
ck = new CacheKey(
|
||||
id,
|
||||
persister.getIdentifierType(),
|
||||
persister.getRootEntityName(),
|
||||
session.getEntityMode(),
|
||||
session.getFactory()
|
||||
);
|
||||
lock = persister.getCache().lock(ck, previousVersion);
|
||||
}
|
||||
else {
|
||||
ck = null;
|
||||
}
|
||||
|
||||
if ( !veto ) {
|
||||
persister.update(
|
||||
id,
|
||||
state,
|
||||
dirtyFields,
|
||||
hasDirtyCollection,
|
||||
previousState,
|
||||
previousVersion,
|
||||
instance,
|
||||
rowId,
|
||||
session
|
||||
);
|
||||
}
|
||||
|
||||
EntityEntry entry = getSession().getPersistenceContext().getEntry( instance );
|
||||
if ( entry == null ) {
|
||||
throw new AssertionFailure( "possible nonthreadsafe access to session" );
|
||||
}
|
||||
|
||||
if ( entry.getStatus()==Status.MANAGED || persister.isVersionPropertyGenerated() ) {
|
||||
// get the updated snapshot of the entity state by cloning current state;
|
||||
// it is safe to copy in place, since by this time no-one else (should have)
|
||||
// has a reference to the array
|
||||
TypeFactory.deepCopy(
|
||||
state,
|
||||
persister.getPropertyTypes(),
|
||||
persister.getPropertyCheckability(),
|
||||
state,
|
||||
session
|
||||
);
|
||||
if ( persister.hasUpdateGeneratedProperties() ) {
|
||||
// this entity defines proeprty generation, so process those generated
|
||||
// values...
|
||||
persister.processUpdateGeneratedProperties( id, instance, state, session );
|
||||
if ( persister.isVersionPropertyGenerated() ) {
|
||||
nextVersion = Versioning.getVersion( state, persister );
|
||||
}
|
||||
}
|
||||
// have the entity entry perform post-update processing, passing it the
|
||||
// update state and the new version (if one).
|
||||
entry.postUpdate( instance, state, nextVersion );
|
||||
}
|
||||
|
||||
if ( persister.hasCache() ) {
|
||||
if ( persister.isCacheInvalidationRequired() || entry.getStatus()!=Status.MANAGED ) {
|
||||
persister.getCache().evict(ck);
|
||||
}
|
||||
else {
|
||||
//TODO: inefficient if that cache is just going to ignore the updated state!
|
||||
CacheEntry ce = new CacheEntry(
|
||||
state,
|
||||
persister,
|
||||
persister.hasUninitializedLazyProperties( instance, session.getEntityMode() ),
|
||||
nextVersion,
|
||||
getSession(),
|
||||
instance
|
||||
);
|
||||
cacheEntry = persister.getCacheEntryStructure().structure(ce);
|
||||
// boolean put = persister.getCache().update(ck, cacheEntry);
|
||||
boolean put = persister.getCache().update( ck, cacheEntry, nextVersion, previousVersion );
|
||||
|
||||
if ( put && factory.getStatistics().isStatisticsEnabled() ) {
|
||||
factory.getStatisticsImplementor()
|
||||
.secondLevelCachePut( getPersister().getCache().getRegionName() );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
postUpdate();
|
||||
|
||||
if ( factory.getStatistics().isStatisticsEnabled() && !veto ) {
|
||||
factory.getStatisticsImplementor()
|
||||
.updateEntity( getPersister().getEntityName() );
|
||||
}
|
||||
}
|
||||
|
||||
private void postUpdate() {
|
||||
PostUpdateEventListener[] postListeners = getSession().getListeners()
|
||||
.getPostUpdateEventListeners();
|
||||
if (postListeners.length>0) {
|
||||
PostUpdateEvent postEvent = new PostUpdateEvent(
|
||||
getInstance(),
|
||||
getId(),
|
||||
state,
|
||||
previousState,
|
||||
getPersister(),
|
||||
(EventSource) getSession()
|
||||
);
|
||||
for ( int i = 0; i < postListeners.length; i++ ) {
|
||||
postListeners[i].onPostUpdate(postEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void postCommitUpdate() {
|
||||
PostUpdateEventListener[] postListeners = getSession().getListeners()
|
||||
.getPostCommitUpdateEventListeners();
|
||||
if (postListeners.length>0) {
|
||||
PostUpdateEvent postEvent = new PostUpdateEvent(
|
||||
getInstance(),
|
||||
getId(),
|
||||
state,
|
||||
previousState,
|
||||
getPersister(),
|
||||
(EventSource) getSession()
|
||||
);
|
||||
for ( int i = 0; i < postListeners.length; i++ ) {
|
||||
postListeners[i].onPostUpdate(postEvent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean preUpdate() {
|
||||
PreUpdateEventListener[] preListeners = getSession().getListeners()
|
||||
.getPreUpdateEventListeners();
|
||||
boolean veto = false;
|
||||
if (preListeners.length>0) {
|
||||
PreUpdateEvent preEvent = new PreUpdateEvent(
|
||||
getInstance(),
|
||||
getId(),
|
||||
state,
|
||||
previousState,
|
||||
getPersister(),
|
||||
getSession()
|
||||
);
|
||||
for ( int i = 0; i < preListeners.length; i++ ) {
|
||||
veto = preListeners[i].onPreUpdate(preEvent) || veto;
|
||||
}
|
||||
}
|
||||
return veto;
|
||||
}
|
||||
|
||||
public void afterTransactionCompletion(boolean success) throws CacheException {
|
||||
EntityPersister persister = getPersister();
|
||||
if ( persister.hasCache() ) {
|
||||
|
||||
final CacheKey ck = new CacheKey(
|
||||
getId(),
|
||||
persister.getIdentifierType(),
|
||||
persister.getRootEntityName(),
|
||||
getSession().getEntityMode(),
|
||||
getSession().getFactory()
|
||||
);
|
||||
|
||||
if ( success && cacheEntry!=null /*!persister.isCacheInvalidationRequired()*/ ) {
|
||||
boolean put = persister.getCache().afterUpdate(ck, cacheEntry, nextVersion, lock );
|
||||
|
||||
if ( put && getSession().getFactory().getStatistics().isStatisticsEnabled() ) {
|
||||
getSession().getFactory().getStatisticsImplementor()
|
||||
.secondLevelCachePut( getPersister().getCache().getRegionName() );
|
||||
}
|
||||
}
|
||||
else {
|
||||
persister.getCache().release(ck, lock );
|
||||
}
|
||||
}
|
||||
postCommitUpdate();
|
||||
}
|
||||
|
||||
protected boolean hasPostCommitEventListeners() {
|
||||
return getSession().getListeners().getPostCommitUpdateEventListeners().length>0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
//$Id$
|
||||
package org.hibernate.action;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* An operation which may be scheduled for later execution.
|
||||
* Usually, the operation is a database insert/update/delete,
|
||||
* together with required second-level cache management.
|
||||
*
|
||||
* @author Gavin King
|
||||
*/
|
||||
public interface Executable {
|
||||
/**
|
||||
* Called before executing any actions
|
||||
*/
|
||||
public void beforeExecutions() throws HibernateException;
|
||||
/**
|
||||
* Execute this action
|
||||
*/
|
||||
public void execute() throws HibernateException;
|
||||
/**
|
||||
* Do we need to retain this instance until after the
|
||||
* transaction completes?
|
||||
* @return false if this class defines a no-op
|
||||
* <tt>hasAfterTransactionCompletion()</tt>
|
||||
*/
|
||||
public boolean hasAfterTransactionCompletion();
|
||||
/**
|
||||
* Called after the transaction completes
|
||||
*/
|
||||
public void afterTransactionCompletion(boolean success) throws HibernateException;
|
||||
/**
|
||||
* What spaces (tables) are affected by this action?
|
||||
*/
|
||||
public Serializable[] getPropertySpaces();
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<html>
|
||||
<head>
|
||||
</head>
|
||||
<body>
|
||||
<p>
|
||||
This package defines "actions" that are scheduled for
|
||||
asycnchronous execution by the event listeners.
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,45 @@
|
|||
//$Id: $
|
||||
package org.hibernate.bytecode;
|
||||
|
||||
import org.hibernate.bytecode.util.ClassFilter;
|
||||
import org.hibernate.bytecode.util.FieldFilter;
|
||||
|
||||
import java.security.ProtectionDomain;
|
||||
|
||||
/**
|
||||
* @author Emmanuel Bernard
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public abstract class AbstractClassTransformerImpl implements ClassTransformer {
|
||||
|
||||
protected final ClassFilter classFilter;
|
||||
protected final FieldFilter fieldFilter;
|
||||
|
||||
protected AbstractClassTransformerImpl(ClassFilter classFilter, FieldFilter fieldFilter) {
|
||||
this.classFilter = classFilter;
|
||||
this.fieldFilter = fieldFilter;
|
||||
}
|
||||
|
||||
public byte[] transform(
|
||||
ClassLoader loader,
|
||||
String className,
|
||||
Class classBeingRedefined,
|
||||
ProtectionDomain protectionDomain,
|
||||
byte[] classfileBuffer) {
|
||||
// to be safe...
|
||||
className = className.replace( '/', '.' );
|
||||
if ( classFilter.shouldInstrumentClass( className ) ) {
|
||||
return doTransform( loader, className, classBeingRedefined, protectionDomain, classfileBuffer );
|
||||
}
|
||||
else {
|
||||
return classfileBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract byte[] doTransform(
|
||||
ClassLoader loader,
|
||||
String className,
|
||||
Class classBeingRedefined,
|
||||
ProtectionDomain protectionDomain,
|
||||
byte[] classfileBuffer);
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package org.hibernate.bytecode;
|
||||
|
||||
/**
|
||||
* A proxy factory for "basic proxy" generation
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface BasicProxyFactory {
|
||||
public Object getProxy();
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
package org.hibernate.bytecode;
|
||||
|
||||
import org.hibernate.bytecode.util.ClassFilter;
|
||||
import org.hibernate.bytecode.util.FieldFilter;
|
||||
|
||||
/**
|
||||
* Contract for providers of bytecode services to Hibernate.
|
||||
* <p/>
|
||||
* Bytecode requirements break down into basically 3 areas<ol>
|
||||
* <li>proxy generation (both for runtime-lazy-loading and basic proxy generation)
|
||||
* {@link #getProxyFactoryFactory()}
|
||||
* <li>bean relection optimization {@link #getReflectionOptimizer}
|
||||
* <li>field-access instumentation {@link #getTransformer}
|
||||
* </ol>
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface BytecodeProvider {
|
||||
/**
|
||||
* Retrieve the specific factory for this provider capable of
|
||||
* generating run-time proxies for lazy-loading purposes.
|
||||
*
|
||||
* @return The provider specifc factory.
|
||||
*/
|
||||
public ProxyFactoryFactory getProxyFactoryFactory();
|
||||
|
||||
/**
|
||||
* Retrieve the ReflectionOptimizer delegate for this provider
|
||||
* capable of generating reflection optimization components.
|
||||
*
|
||||
* @param clazz The class to be reflected upon.
|
||||
* @param getterNames Names of all property getters to be accessed via reflection.
|
||||
* @param setterNames Names of all property setters to be accessed via reflection.
|
||||
* @param types The types of all properties to be accessed.
|
||||
* @return The reflection optimization delegate.
|
||||
*/
|
||||
public ReflectionOptimizer getReflectionOptimizer(Class clazz, String[] getterNames, String[] setterNames, Class[] types);
|
||||
|
||||
/**
|
||||
* Generate a ClassTransformer capable of performing bytecode manipulation.
|
||||
*
|
||||
* @param classFilter filter used to limit which classes are to be instrumented
|
||||
* via this ClassTransformer.
|
||||
* @param fieldFilter filter used to limit which fields are to be instrumented
|
||||
* via this ClassTransformer.
|
||||
* @return The appropriate ClassTransformer.
|
||||
*/
|
||||
public ClassTransformer getTransformer(ClassFilter classFilter, FieldFilter fieldFilter);
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
//$Id: $
|
||||
package org.hibernate.bytecode;
|
||||
|
||||
import java.security.ProtectionDomain;
|
||||
|
||||
/**
|
||||
* A persistence provider provides an instance of this interface
|
||||
* to the PersistenceUnitInfo.addTransformer method.
|
||||
* The supplied transformer instance will get called to transform
|
||||
* entity class files when they are loaded and redefined. The transformation
|
||||
* occurs before the class is defined by the JVM
|
||||
*
|
||||
*
|
||||
* @author <a href="mailto:bill@jboss.org">Bill Burke</a>
|
||||
* @author Emmanuel Bernard
|
||||
*/
|
||||
public interface ClassTransformer
|
||||
{
|
||||
/**
|
||||
* Invoked when a class is being loaded or redefined to add hooks for persistence bytecode manipulation
|
||||
*
|
||||
* @param loader the defining class loaderof the class being transformed. It may be null if using bootstrap loader
|
||||
* @param classname The name of the class being transformed
|
||||
* @param classBeingRedefined If an already loaded class is being redefined, then pass this as a parameter
|
||||
* @param protectionDomain ProtectionDomain of the class being (re)-defined
|
||||
* @param classfileBuffer The input byte buffer in class file format
|
||||
* @return A well-formed class file that can be loaded
|
||||
*/
|
||||
public byte[] transform(ClassLoader loader,
|
||||
String classname,
|
||||
Class classBeingRedefined,
|
||||
ProtectionDomain protectionDomain,
|
||||
byte[] classfileBuffer);
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
package org.hibernate.bytecode;
|
||||
|
||||
import org.hibernate.bytecode.util.ByteCodeHelper;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* A specialized classloader which performs bytecode enhancement on class
|
||||
* definitions as they are loaded into the classloader scope.
|
||||
*
|
||||
* @author Emmanuel Bernard
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class InstrumentedClassLoader extends ClassLoader {
|
||||
|
||||
private ClassTransformer classTransformer;
|
||||
|
||||
public InstrumentedClassLoader(ClassLoader parent, ClassTransformer classTransformer) {
|
||||
super( parent );
|
||||
this.classTransformer = classTransformer;
|
||||
}
|
||||
|
||||
public Class loadClass(String name) throws ClassNotFoundException {
|
||||
if ( name.startsWith( "java." ) || classTransformer == null ) {
|
||||
return getParent().loadClass( name );
|
||||
}
|
||||
|
||||
Class c = findLoadedClass( name );
|
||||
if ( c != null ) {
|
||||
return c;
|
||||
}
|
||||
|
||||
InputStream is = this.getResourceAsStream( name.replace( '.', '/' ) + ".class" );
|
||||
if ( is == null ) {
|
||||
throw new ClassNotFoundException( name + " not found" );
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] originalBytecode = ByteCodeHelper.readByteCode( is );
|
||||
byte[] transformedBytecode = classTransformer.transform( getParent(), name, null, null, originalBytecode );
|
||||
if ( originalBytecode == transformedBytecode ) {
|
||||
// no transformations took place, so handle it as we would a
|
||||
// non-instrumented class
|
||||
return getParent().loadClass( name );
|
||||
}
|
||||
else {
|
||||
return defineClass( name, transformedBytecode, 0, transformedBytecode.length );
|
||||
}
|
||||
}
|
||||
catch( Throwable t ) {
|
||||
throw new ClassNotFoundException( name + " not found", t );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package org.hibernate.bytecode;
|
||||
|
||||
import org.hibernate.proxy.ProxyFactory;
|
||||
|
||||
/**
|
||||
* An interface for factories of {@link ProxyFactory proxy factory} instances.
|
||||
* <p/>
|
||||
* Currently used to abstract from the tupizer whether we are using CGLIB or
|
||||
* Javassist for lazy proxy generation.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface ProxyFactoryFactory {
|
||||
/**
|
||||
* Build a proxy factory specifically for handling runtime
|
||||
* lazy loading.
|
||||
*
|
||||
* @return The lazy-load proxy factory.
|
||||
*/
|
||||
public ProxyFactory buildProxyFactory();
|
||||
|
||||
/**
|
||||
* Build a proxy factory for basic proxy concerns. The return
|
||||
* should be capable of properly handling newInstance() calls.
|
||||
* <p/>
|
||||
* Should build basic proxies essentially equivalent to JDK proxies in
|
||||
* terms of capabilities, but should be able to deal with abstract super
|
||||
* classes in addition to proxy interfaces.
|
||||
* <p/>
|
||||
* Must pass in either superClass or interfaces (or both).
|
||||
*
|
||||
* @param superClass The abstract super class (or null if none).
|
||||
* @param interfaces Interfaces to be proxied (or null if none).
|
||||
* @return The proxy class
|
||||
*/
|
||||
public BasicProxyFactory buildBasicProxyFactory(Class superClass, Class[] interfaces);
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package org.hibernate.bytecode;
|
||||
|
||||
/**
|
||||
* Represents reflection optimization for a particular class.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface ReflectionOptimizer {
|
||||
|
||||
public InstantiationOptimizer getInstantiationOptimizer();
|
||||
public AccessOptimizer getAccessOptimizer();
|
||||
|
||||
/**
|
||||
* Represents optimized entity instantiation.
|
||||
*/
|
||||
public static interface InstantiationOptimizer {
|
||||
/**
|
||||
* Perform instantiation of an instance of the underlying class.
|
||||
*
|
||||
* @return The new instance.
|
||||
*/
|
||||
public Object newInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents optimized entity property access.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface AccessOptimizer {
|
||||
public String[] getPropertyNames();
|
||||
public Object[] getPropertyValues(Object object);
|
||||
public void setPropertyValues(Object object, Object[] values);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
package org.hibernate.bytecode.cglib;
|
||||
|
||||
import org.hibernate.bytecode.ReflectionOptimizer;
|
||||
import org.hibernate.PropertyAccessException;
|
||||
import net.sf.cglib.beans.BulkBean;
|
||||
import net.sf.cglib.beans.BulkBeanException;
|
||||
import net.sf.cglib.reflect.FastClass;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
|
||||
/**
|
||||
* The {@link ReflectionOptimizer.AccessOptimizer} implementation for CGLIB
|
||||
* which simply acts as an adpater to the {@link BulkBean} class.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class AccessOptimizerAdapter implements ReflectionOptimizer.AccessOptimizer, Serializable {
|
||||
|
||||
public static final String PROPERTY_GET_EXCEPTION =
|
||||
"exception getting property value with CGLIB (set hibernate.bytecode.use_reflection_optimizer=false for more info)";
|
||||
|
||||
public static final String PROPERTY_SET_EXCEPTION =
|
||||
"exception setting property value with CGLIB (set hibernate.bytecode.use_reflection_optimizer=false for more info)";
|
||||
|
||||
private Class mappedClass;
|
||||
private BulkBean bulkBean;
|
||||
|
||||
public AccessOptimizerAdapter(BulkBean bulkBean, Class mappedClass) {
|
||||
this.bulkBean = bulkBean;
|
||||
this.mappedClass = mappedClass;
|
||||
}
|
||||
|
||||
public String[] getPropertyNames() {
|
||||
return bulkBean.getGetters();
|
||||
}
|
||||
|
||||
public Object[] getPropertyValues(Object object) {
|
||||
try {
|
||||
return bulkBean.getPropertyValues( object );
|
||||
}
|
||||
catch ( Throwable t ) {
|
||||
throw new PropertyAccessException(
|
||||
t,
|
||||
PROPERTY_GET_EXCEPTION,
|
||||
false,
|
||||
mappedClass,
|
||||
getterName( t, bulkBean )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public void setPropertyValues(Object object, Object[] values) {
|
||||
try {
|
||||
bulkBean.setPropertyValues( object, values );
|
||||
}
|
||||
catch ( Throwable t ) {
|
||||
throw new PropertyAccessException(
|
||||
t,
|
||||
PROPERTY_SET_EXCEPTION,
|
||||
true,
|
||||
mappedClass,
|
||||
setterName( t, bulkBean )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static String setterName(Throwable t, BulkBean optimizer) {
|
||||
if ( t instanceof BulkBeanException ) {
|
||||
return optimizer.getSetters()[( ( BulkBeanException ) t ).getIndex()];
|
||||
}
|
||||
else {
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
private static String getterName(Throwable t, BulkBean optimizer) {
|
||||
if ( t instanceof BulkBeanException ) {
|
||||
return optimizer.getGetters()[( ( BulkBeanException ) t ).getIndex()];
|
||||
}
|
||||
else {
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
private void writeObject(ObjectOutputStream out) throws IOException {
|
||||
out.writeObject( mappedClass );
|
||||
out.writeObject( bulkBean.getGetters() );
|
||||
out.writeObject( bulkBean.getSetters() );
|
||||
out.writeObject( bulkBean.getPropertyTypes() );
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
Class beanClass = ( Class ) in.readObject();
|
||||
String[] getters = ( String[] ) in.readObject();
|
||||
String[] setters = ( String[] ) in.readObject();
|
||||
Class[] types = ( Class[] ) in.readObject();
|
||||
bulkBean = BulkBean.create( beanClass, getters, setters, types );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
package org.hibernate.bytecode.cglib;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import net.sf.cglib.beans.BulkBean;
|
||||
import net.sf.cglib.beans.BulkBeanException;
|
||||
import net.sf.cglib.reflect.FastClass;
|
||||
import net.sf.cglib.transform.ClassFilter;
|
||||
import net.sf.cglib.transform.ClassTransformer;
|
||||
import net.sf.cglib.transform.ClassTransformerFactory;
|
||||
import net.sf.cglib.transform.TransformingClassLoader;
|
||||
import net.sf.cglib.transform.impl.InterceptFieldFilter;
|
||||
import net.sf.cglib.transform.impl.InterceptFieldTransformer;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.hibernate.bytecode.BytecodeProvider;
|
||||
import org.hibernate.bytecode.ProxyFactoryFactory;
|
||||
import org.hibernate.bytecode.ReflectionOptimizer;
|
||||
import org.hibernate.bytecode.util.FieldFilter;
|
||||
import org.hibernate.util.StringHelper;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
/**
|
||||
* Bytecode provider implementation for CGLIB.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class BytecodeProviderImpl implements BytecodeProvider {
|
||||
|
||||
private static final Log log = LogFactory.getLog( BytecodeProviderImpl.class );
|
||||
|
||||
public ProxyFactoryFactory getProxyFactoryFactory() {
|
||||
return new ProxyFactoryFactoryImpl();
|
||||
}
|
||||
|
||||
public ReflectionOptimizer getReflectionOptimizer(
|
||||
Class clazz,
|
||||
String[] getterNames,
|
||||
String[] setterNames,
|
||||
Class[] types) {
|
||||
FastClass fastClass;
|
||||
BulkBean bulkBean;
|
||||
try {
|
||||
fastClass = FastClass.create( clazz );
|
||||
bulkBean = BulkBean.create( clazz, getterNames, setterNames, types );
|
||||
if ( !clazz.isInterface() && !Modifier.isAbstract( clazz.getModifiers() ) ) {
|
||||
if ( fastClass == null ) {
|
||||
bulkBean = null;
|
||||
}
|
||||
else {
|
||||
//test out the optimizer:
|
||||
Object instance = fastClass.newInstance();
|
||||
bulkBean.setPropertyValues( instance, bulkBean.getPropertyValues( instance ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( Throwable t ) {
|
||||
fastClass = null;
|
||||
bulkBean = null;
|
||||
String message = "reflection optimizer disabled for: " +
|
||||
clazz.getName() +
|
||||
" [" +
|
||||
StringHelper.unqualify( t.getClass().getName() ) +
|
||||
": " +
|
||||
t.getMessage();
|
||||
|
||||
if (t instanceof BulkBeanException ) {
|
||||
int index = ( (BulkBeanException) t ).getIndex();
|
||||
if (index >= 0) {
|
||||
message += " (property " + setterNames[index] + ")";
|
||||
}
|
||||
}
|
||||
|
||||
log.debug( message );
|
||||
}
|
||||
|
||||
if ( fastClass != null && bulkBean != null ) {
|
||||
return new ReflectionOptimizerImpl(
|
||||
new InstantiationOptimizerAdapter( fastClass ),
|
||||
new AccessOptimizerAdapter( bulkBean, clazz )
|
||||
);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public org.hibernate.bytecode.ClassTransformer getTransformer(org.hibernate.bytecode.util.ClassFilter classFilter, FieldFilter fieldFilter) {
|
||||
return new CglibClassTransformer( classFilter, fieldFilter );
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
//$Id: $
|
||||
package org.hibernate.bytecode.cglib;
|
||||
|
||||
import java.security.ProtectionDomain;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
import net.sf.cglib.transform.ClassTransformer;
|
||||
import net.sf.cglib.transform.TransformingClassGenerator;
|
||||
import net.sf.cglib.transform.ClassReaderGenerator;
|
||||
import net.sf.cglib.transform.impl.InterceptFieldEnabled;
|
||||
import net.sf.cglib.transform.impl.InterceptFieldFilter;
|
||||
import net.sf.cglib.transform.impl.InterceptFieldTransformer;
|
||||
import net.sf.cglib.core.ClassNameReader;
|
||||
import net.sf.cglib.core.DebuggingClassWriter;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.hibernate.bytecode.AbstractClassTransformerImpl;
|
||||
import org.hibernate.bytecode.util.FieldFilter;
|
||||
import org.hibernate.bytecode.util.ClassFilter;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.objectweb.asm.Attribute;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassWriter;
|
||||
import org.objectweb.asm.attrs.Attributes;
|
||||
|
||||
/**
|
||||
* Enhance the classes allowing them to implements InterceptFieldEnabled
|
||||
* This interface is then used by Hibernate for some optimizations.
|
||||
*
|
||||
* @author Emmanuel Bernard
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class CglibClassTransformer extends AbstractClassTransformerImpl {
|
||||
|
||||
private static Log log = LogFactory.getLog( CglibClassTransformer.class.getName() );
|
||||
|
||||
public CglibClassTransformer(ClassFilter classFilter, FieldFilter fieldFilter) {
|
||||
super( classFilter, fieldFilter );
|
||||
}
|
||||
|
||||
protected byte[] doTransform(
|
||||
ClassLoader loader,
|
||||
String className,
|
||||
Class classBeingRedefined,
|
||||
ProtectionDomain protectionDomain,
|
||||
byte[] classfileBuffer) {
|
||||
ClassReader reader;
|
||||
try {
|
||||
reader = new ClassReader( new ByteArrayInputStream( classfileBuffer ) );
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.error( "Unable to read class", e );
|
||||
throw new HibernateException( "Unable to read class: " + e.getMessage() );
|
||||
}
|
||||
|
||||
String[] names = ClassNameReader.getClassInfo( reader );
|
||||
ClassWriter w = new DebuggingClassWriter( true );
|
||||
ClassTransformer t = getClassTransformer( names );
|
||||
if ( t != null ) {
|
||||
if ( log.isDebugEnabled() ) {
|
||||
log.debug( "Enhancing " + className );
|
||||
}
|
||||
ByteArrayOutputStream out;
|
||||
byte[] result;
|
||||
try {
|
||||
reader = new ClassReader( new ByteArrayInputStream( classfileBuffer ) );
|
||||
new TransformingClassGenerator(
|
||||
new ClassReaderGenerator( reader, attributes(), skipDebug() ), t
|
||||
).generateClass( w );
|
||||
out = new ByteArrayOutputStream();
|
||||
out.write( w.toByteArray() );
|
||||
result = out.toByteArray();
|
||||
out.close();
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error( "Unable to transform class", e );
|
||||
throw new HibernateException( "Unable to transform class: " + e.getMessage() );
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return classfileBuffer;
|
||||
}
|
||||
|
||||
|
||||
private Attribute[] attributes() {
|
||||
return Attributes.getDefaultAttributes();
|
||||
}
|
||||
|
||||
private boolean skipDebug() {
|
||||
return false;
|
||||
}
|
||||
|
||||
private ClassTransformer getClassTransformer(final String[] classInfo) {
|
||||
if ( isAlreadyInstrumented( classInfo ) ) {
|
||||
return null;
|
||||
}
|
||||
return new InterceptFieldTransformer(
|
||||
new InterceptFieldFilter() {
|
||||
public boolean acceptRead(Type owner, String name) {
|
||||
return fieldFilter.shouldTransformFieldAccess( classInfo[0], owner.getClassName(), name );
|
||||
}
|
||||
|
||||
public boolean acceptWrite(Type owner, String name) {
|
||||
return fieldFilter.shouldTransformFieldAccess( classInfo[0], owner.getClassName(), name );
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private boolean isAlreadyInstrumented(String[] classInfo) {
|
||||
for ( int i = 1; i < classInfo.length; i++ ) {
|
||||
if ( InterceptFieldEnabled.class.getName().equals( classInfo[i] ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
package org.hibernate.bytecode.cglib;
|
||||
|
||||
import org.hibernate.bytecode.ReflectionOptimizer;
|
||||
import net.sf.cglib.reflect.FastClass;
|
||||
import org.hibernate.InstantiationException;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
|
||||
/**
|
||||
* The {@link ReflectionOptimizer.InstantiationOptimizer} implementation for CGLIB
|
||||
* which simply acts as an adpater to the {@link FastClass} class.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class InstantiationOptimizerAdapter implements ReflectionOptimizer.InstantiationOptimizer, Serializable {
|
||||
private FastClass fastClass;
|
||||
|
||||
public InstantiationOptimizerAdapter(FastClass fastClass) {
|
||||
this.fastClass = fastClass;
|
||||
}
|
||||
|
||||
public Object newInstance() {
|
||||
try {
|
||||
return fastClass.newInstance();
|
||||
}
|
||||
catch ( Throwable t ) {
|
||||
throw new InstantiationException(
|
||||
"Could not instantiate entity with CGLIB optimizer: ",
|
||||
fastClass.getJavaClass(),
|
||||
t
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void writeObject(ObjectOutputStream out) throws IOException {
|
||||
out.writeObject( fastClass.getJavaClass() );
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||
Class beanClass = ( Class ) in.readObject();
|
||||
fastClass = FastClass.create( beanClass );
|
||||
}
|
||||
}
|
|
@ -0,0 +1,141 @@
|
|||
package org.hibernate.bytecode.cglib;
|
||||
|
||||
import org.hibernate.bytecode.ProxyFactoryFactory;
|
||||
import org.hibernate.bytecode.BasicProxyFactory;
|
||||
import org.hibernate.proxy.ProxyFactory;
|
||||
import org.hibernate.proxy.pojo.cglib.CGLIBProxyFactory;
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.HibernateException;
|
||||
import net.sf.cglib.proxy.Enhancer;
|
||||
import net.sf.cglib.proxy.CallbackFilter;
|
||||
import net.sf.cglib.proxy.MethodInterceptor;
|
||||
import net.sf.cglib.proxy.MethodProxy;
|
||||
import net.sf.cglib.proxy.NoOp;
|
||||
import net.sf.cglib.proxy.Callback;
|
||||
import net.sf.cglib.proxy.Factory;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* A factory for CGLIB-based {@link ProxyFactory} instances.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class ProxyFactoryFactoryImpl implements ProxyFactoryFactory {
|
||||
|
||||
/**
|
||||
* Builds a CGLIB-based proxy factory.
|
||||
*
|
||||
* @return a new CGLIB-based proxy factory.
|
||||
*/
|
||||
public ProxyFactory buildProxyFactory() {
|
||||
return new CGLIBProxyFactory();
|
||||
}
|
||||
|
||||
public BasicProxyFactory buildBasicProxyFactory(Class superClass, Class[] interfaces) {
|
||||
return new BasicProxyFactoryImpl( superClass, interfaces );
|
||||
}
|
||||
|
||||
public static class BasicProxyFactoryImpl implements BasicProxyFactory {
|
||||
private final Class proxyClass;
|
||||
private final Factory factory;
|
||||
|
||||
public BasicProxyFactoryImpl(Class superClass, Class[] interfaces) {
|
||||
if ( superClass == null && ( interfaces == null || interfaces.length < 1 ) ) {
|
||||
throw new AssertionFailure( "attempting to build proxy without any superclass or interfaces" );
|
||||
}
|
||||
|
||||
Enhancer en = new Enhancer();
|
||||
en.setUseCache( false );
|
||||
en.setInterceptDuringConstruction( false );
|
||||
en.setUseFactory( true );
|
||||
en.setCallbackTypes( CALLBACK_TYPES );
|
||||
en.setCallbackFilter( FINALIZE_FILTER );
|
||||
if ( superClass != null ) {
|
||||
en.setSuperclass( superClass );
|
||||
}
|
||||
if ( interfaces != null && interfaces.length > 0 ) {
|
||||
en.setInterfaces( interfaces );
|
||||
}
|
||||
proxyClass = en.createClass();
|
||||
try {
|
||||
factory = ( Factory ) proxyClass.newInstance();
|
||||
}
|
||||
catch ( Throwable t ) {
|
||||
throw new HibernateException( "Unable to build CGLIB Factory instance" );
|
||||
}
|
||||
}
|
||||
|
||||
public Object getProxy() {
|
||||
try {
|
||||
return factory.newInstance(
|
||||
new Callback[] { new PassThroughInterceptor( proxyClass.getName() ), NoOp.INSTANCE }
|
||||
);
|
||||
}
|
||||
catch ( Throwable t ) {
|
||||
throw new HibernateException( "Unable to instantiate proxy instance" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final CallbackFilter FINALIZE_FILTER = new CallbackFilter() {
|
||||
public int accept(Method method) {
|
||||
if ( method.getParameterTypes().length == 0 && method.getName().equals("finalize") ){
|
||||
return 1;
|
||||
}
|
||||
else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private static final Class[] CALLBACK_TYPES = new Class[] { MethodInterceptor.class, NoOp.class };
|
||||
|
||||
private static class PassThroughInterceptor implements MethodInterceptor {
|
||||
private HashMap data = new HashMap();
|
||||
private final String proxiedClassName;
|
||||
|
||||
public PassThroughInterceptor(String proxiedClassName) {
|
||||
this.proxiedClassName = proxiedClassName;
|
||||
}
|
||||
|
||||
public Object intercept(
|
||||
Object obj,
|
||||
Method method,
|
||||
Object[] args,
|
||||
MethodProxy proxy) throws Throwable {
|
||||
String name = method.getName();
|
||||
if ( "toString".equals( name ) ) {
|
||||
return proxiedClassName + "@" + System.identityHashCode( obj );
|
||||
}
|
||||
else if ( "equals".equals( name ) ) {
|
||||
return args[0] instanceof Factory && ( ( Factory ) args[0] ).getCallback( 0 ) == this
|
||||
? Boolean.TRUE
|
||||
: Boolean.FALSE;
|
||||
}
|
||||
else if ( "hashCode".equals( name ) ) {
|
||||
return new Integer( System.identityHashCode( obj ) );
|
||||
}
|
||||
boolean hasGetterSignature = method.getParameterTypes().length == 0 && method.getReturnType() != null;
|
||||
boolean hasSetterSignature = method.getParameterTypes().length == 1 && ( method.getReturnType() == null || method.getReturnType() == void.class );
|
||||
if ( name.startsWith( "get" ) && hasGetterSignature ) {
|
||||
String propName = name.substring( 3 );
|
||||
return data.get( propName );
|
||||
}
|
||||
else if ( name.startsWith( "is" ) && hasGetterSignature ) {
|
||||
String propName = name.substring( 2 );
|
||||
return data.get( propName );
|
||||
}
|
||||
else if ( name.startsWith( "set" ) && hasSetterSignature) {
|
||||
String propName = name.substring( 3 );
|
||||
data.put( propName, args[0] );
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
// todo : what else to do here?
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package org.hibernate.bytecode.cglib;
|
||||
|
||||
import org.hibernate.bytecode.ReflectionOptimizer;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.io.ObjectOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
|
||||
/**
|
||||
* ReflectionOptimizer implementation for CGLIB.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class ReflectionOptimizerImpl implements ReflectionOptimizer, Serializable {
|
||||
private transient InstantiationOptimizerAdapter instantiationOptimizer;
|
||||
private transient AccessOptimizerAdapter accessOptimizer;
|
||||
|
||||
public ReflectionOptimizerImpl(
|
||||
InstantiationOptimizerAdapter instantiationOptimizer,
|
||||
AccessOptimizerAdapter accessOptimizer) {
|
||||
this.instantiationOptimizer = instantiationOptimizer;
|
||||
this.accessOptimizer = accessOptimizer;
|
||||
}
|
||||
|
||||
public InstantiationOptimizer getInstantiationOptimizer() {
|
||||
return instantiationOptimizer;
|
||||
}
|
||||
|
||||
public AccessOptimizer getAccessOptimizer() {
|
||||
return accessOptimizer;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
package org.hibernate.bytecode.javassist;
|
||||
|
||||
import org.hibernate.bytecode.ReflectionOptimizer;
|
||||
import org.hibernate.PropertyAccessException;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* The {@link ReflectionOptimizer.AccessOptimizer} implementation for Javassist
|
||||
* which simply acts as an adpater to the {@link BulkAccessor} class.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class AccessOptimizerAdapter implements ReflectionOptimizer.AccessOptimizer, Serializable {
|
||||
|
||||
public static final String PROPERTY_GET_EXCEPTION =
|
||||
"exception getting property value with Javassist (set hibernate.bytecode.use_reflection_optimizer=false for more info)";
|
||||
|
||||
public static final String PROPERTY_SET_EXCEPTION =
|
||||
"exception setting property value with Javassist (set hibernate.bytecode.use_reflection_optimizer=false for more info)";
|
||||
|
||||
private final BulkAccessor bulkAccessor;
|
||||
private final Class mappedClass;
|
||||
|
||||
public AccessOptimizerAdapter(BulkAccessor bulkAccessor, Class mappedClass) {
|
||||
this.bulkAccessor = bulkAccessor;
|
||||
this.mappedClass = mappedClass;
|
||||
}
|
||||
|
||||
public String[] getPropertyNames() {
|
||||
return bulkAccessor.getGetters();
|
||||
}
|
||||
|
||||
public Object[] getPropertyValues(Object object) {
|
||||
try {
|
||||
return bulkAccessor.getPropertyValues( object );
|
||||
}
|
||||
catch ( Throwable t ) {
|
||||
throw new PropertyAccessException(
|
||||
t,
|
||||
PROPERTY_GET_EXCEPTION,
|
||||
false,
|
||||
mappedClass,
|
||||
getterName( t, bulkAccessor )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public void setPropertyValues(Object object, Object[] values) {
|
||||
try {
|
||||
bulkAccessor.setPropertyValues( object, values );
|
||||
}
|
||||
catch ( Throwable t ) {
|
||||
throw new PropertyAccessException(
|
||||
t,
|
||||
PROPERTY_SET_EXCEPTION,
|
||||
true,
|
||||
mappedClass,
|
||||
setterName( t, bulkAccessor )
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static String setterName(Throwable t, BulkAccessor accessor) {
|
||||
if (t instanceof BulkAccessorException ) {
|
||||
return accessor.getSetters()[ ( (BulkAccessorException) t ).getIndex() ];
|
||||
}
|
||||
else {
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
|
||||
private static String getterName(Throwable t, BulkAccessor accessor) {
|
||||
if (t instanceof BulkAccessorException ) {
|
||||
return accessor.getGetters()[ ( (BulkAccessorException) t ).getIndex() ];
|
||||
}
|
||||
else {
|
||||
return "?";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
package org.hibernate.bytecode.javassist;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
|
||||
/**
|
||||
* A JavaBean accessor.
|
||||
* <p/>
|
||||
* <p>This object provides methods that set/get multiple properties
|
||||
* of a JavaBean at once. This class and its support classes have been
|
||||
* developed for the comaptibility with cglib
|
||||
* (<tt>http://cglib.sourceforge.net/</tt>).
|
||||
*
|
||||
* @author Muga Nishizawa
|
||||
* @author modified by Shigeru Chiba
|
||||
*/
|
||||
public abstract class BulkAccessor implements Serializable {
|
||||
protected Class target;
|
||||
protected String[] getters, setters;
|
||||
protected Class[] types;
|
||||
|
||||
protected BulkAccessor() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtains the values of properties of a given bean.
|
||||
*
|
||||
* @param bean JavaBean.
|
||||
* @param values the obtained values are stored in this array.
|
||||
*/
|
||||
public abstract void getPropertyValues(Object bean, Object[] values);
|
||||
|
||||
/**
|
||||
* Sets properties of a given bean to specified values.
|
||||
*
|
||||
* @param bean JavaBean.
|
||||
* @param values the values assinged to properties.
|
||||
*/
|
||||
public abstract void setPropertyValues(Object bean, Object[] values);
|
||||
|
||||
/**
|
||||
* Returns the values of properties of a given bean.
|
||||
*
|
||||
* @param bean JavaBean.
|
||||
*/
|
||||
public Object[] getPropertyValues(Object bean) {
|
||||
Object[] values = new Object[getters.length];
|
||||
getPropertyValues( bean, values );
|
||||
return values;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the types of properties.
|
||||
*/
|
||||
public Class[] getPropertyTypes() {
|
||||
return ( Class[] ) types.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the setter names of properties.
|
||||
*/
|
||||
public String[] getGetters() {
|
||||
return ( String[] ) getters.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the getter names of the properties.
|
||||
*/
|
||||
public String[] getSetters() {
|
||||
return ( String[] ) setters.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of <code>BulkAccessor</code>.
|
||||
* The created instance provides methods for setting/getting
|
||||
* specified properties at once.
|
||||
*
|
||||
* @param beanClass the class of the JavaBeans accessed
|
||||
* through the created object.
|
||||
* @param getters the names of setter methods for specified properties.
|
||||
* @param setters the names of getter methods for specified properties.
|
||||
* @param types the types of specified properties.
|
||||
*/
|
||||
public static BulkAccessor create(
|
||||
Class beanClass,
|
||||
String[] getters,
|
||||
String[] setters,
|
||||
Class[] types) {
|
||||
BulkAccessorFactory factory = new BulkAccessorFactory( beanClass, getters, setters, types );
|
||||
return factory.create();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
package org.hibernate.bytecode.javassist;
|
||||
|
||||
/**
|
||||
* An exception thrown while generating a bulk accessor.
|
||||
*
|
||||
* @author Muga Nishizawa
|
||||
* @author modified by Shigeru Chiba
|
||||
*/
|
||||
public class BulkAccessorException extends RuntimeException {
|
||||
private Throwable myCause;
|
||||
|
||||
/**
|
||||
* Gets the cause of this throwable.
|
||||
* It is for JDK 1.3 compatibility.
|
||||
*/
|
||||
public Throwable getCause() {
|
||||
return (myCause == this ? null : myCause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the cause of this throwable.
|
||||
* It is for JDK 1.3 compatibility.
|
||||
*/
|
||||
public synchronized Throwable initCause(Throwable cause) {
|
||||
myCause = cause;
|
||||
return this;
|
||||
}
|
||||
|
||||
private int index;
|
||||
|
||||
/**
|
||||
* Constructs an exception.
|
||||
*/
|
||||
public BulkAccessorException(String message) {
|
||||
super(message);
|
||||
index = -1;
|
||||
initCause(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an exception.
|
||||
*
|
||||
* @param index the index of the property that causes an exception.
|
||||
*/
|
||||
public BulkAccessorException(String message, int index) {
|
||||
this(message + ": " + index);
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an exception.
|
||||
*/
|
||||
public BulkAccessorException(String message, Throwable cause) {
|
||||
super(message);
|
||||
index = -1;
|
||||
initCause(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs an exception.
|
||||
*
|
||||
* @param index the index of the property that causes an exception.
|
||||
*/
|
||||
public BulkAccessorException(Throwable cause, int index) {
|
||||
this("Property " + index);
|
||||
this.index = index;
|
||||
initCause(cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index of the property that causes this exception.
|
||||
*
|
||||
* @return -1 if the index is not specified.
|
||||
*/
|
||||
public int getIndex() {
|
||||
return this.index;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,388 @@
|
|||
package org.hibernate.bytecode.javassist;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.security.ProtectionDomain;
|
||||
|
||||
import javassist.CannotCompileException;
|
||||
import javassist.bytecode.AccessFlag;
|
||||
import javassist.bytecode.Bytecode;
|
||||
import javassist.bytecode.ClassFile;
|
||||
import javassist.bytecode.ConstPool;
|
||||
import javassist.bytecode.MethodInfo;
|
||||
import javassist.bytecode.Opcode;
|
||||
import javassist.util.proxy.FactoryHelper;
|
||||
import javassist.util.proxy.RuntimeSupport;
|
||||
|
||||
/**
|
||||
* A factory of bulk accessors.
|
||||
*
|
||||
* @author Muga Nishizawa
|
||||
* @author modified by Shigeru Chiba
|
||||
*/
|
||||
class BulkAccessorFactory {
|
||||
private static final String PACKAGE_NAME_PREFIX = "org.javassist.tmp.";
|
||||
private static final String BULKACESSOR_CLASS_NAME = BulkAccessor.class.getName();
|
||||
private static final String OBJECT_CLASS_NAME = Object.class.getName();
|
||||
private static final String GENERATED_GETTER_NAME = "getPropertyValues";
|
||||
private static final String GENERATED_SETTER_NAME = "setPropertyValues";
|
||||
private static final String GET_SETTER_DESC = "(Ljava/lang/Object;[Ljava/lang/Object;)V";
|
||||
private static final String THROWABLE_CLASS_NAME = Throwable.class.getName();
|
||||
private static final String BULKEXCEPTION_CLASS_NAME = BulkAccessorException.class.getName();
|
||||
private static int counter = 0;
|
||||
|
||||
private Class targetBean;
|
||||
private String[] getterNames;
|
||||
private String[] setterNames;
|
||||
private Class[] types;
|
||||
public String writeDirectory;
|
||||
|
||||
BulkAccessorFactory(
|
||||
Class target,
|
||||
String[] getterNames,
|
||||
String[] setterNames,
|
||||
Class[] types) {
|
||||
this.targetBean = target;
|
||||
this.getterNames = getterNames;
|
||||
this.setterNames = setterNames;
|
||||
this.types = types;
|
||||
this.writeDirectory = null;
|
||||
}
|
||||
|
||||
BulkAccessor create() {
|
||||
Method[] getters = new Method[getterNames.length];
|
||||
Method[] setters = new Method[setterNames.length];
|
||||
findAccessors( targetBean, getterNames, setterNames, types, getters, setters );
|
||||
|
||||
Class beanClass;
|
||||
try {
|
||||
ClassFile classfile = make( getters, setters );
|
||||
ClassLoader loader = this.getClassLoader();
|
||||
if ( writeDirectory != null ) {
|
||||
FactoryHelper.writeFile( classfile, writeDirectory );
|
||||
}
|
||||
|
||||
beanClass = FactoryHelper.toClass( classfile, loader, getDomain() );
|
||||
return ( BulkAccessor ) this.newInstance( beanClass );
|
||||
}
|
||||
catch ( Exception e ) {
|
||||
throw new BulkAccessorException( e.getMessage(), e );
|
||||
}
|
||||
}
|
||||
|
||||
private ProtectionDomain getDomain() {
|
||||
Class cl;
|
||||
if ( this.targetBean != null ) {
|
||||
cl = this.targetBean;
|
||||
}
|
||||
else {
|
||||
cl = this.getClass();
|
||||
}
|
||||
return cl.getProtectionDomain();
|
||||
}
|
||||
|
||||
private ClassFile make(Method[] getters, Method[] setters) throws CannotCompileException {
|
||||
String className = targetBean.getName();
|
||||
// set the name of bulk accessor.
|
||||
className = className + "_$$_bulkaccess_" + counter++;
|
||||
if ( className.startsWith( "java." ) ) {
|
||||
className = "org.javassist.tmp." + className;
|
||||
}
|
||||
|
||||
ClassFile classfile = new ClassFile( false, className, BULKACESSOR_CLASS_NAME );
|
||||
classfile.setAccessFlags( AccessFlag.PUBLIC );
|
||||
addDefaultConstructor( classfile );
|
||||
addGetter( classfile, getters );
|
||||
addSetter( classfile, setters );
|
||||
return classfile;
|
||||
}
|
||||
|
||||
private ClassLoader getClassLoader() {
|
||||
if ( targetBean != null && targetBean.getName().equals( OBJECT_CLASS_NAME ) ) {
|
||||
return targetBean.getClassLoader();
|
||||
}
|
||||
else {
|
||||
return getClass().getClassLoader();
|
||||
}
|
||||
}
|
||||
|
||||
private Object newInstance(Class type) throws Exception {
|
||||
BulkAccessor instance = ( BulkAccessor ) type.newInstance();
|
||||
instance.target = targetBean;
|
||||
int len = getterNames.length;
|
||||
instance.getters = new String[len];
|
||||
instance.setters = new String[len];
|
||||
instance.types = new Class[len];
|
||||
for ( int i = 0; i < len; i++ ) {
|
||||
instance.getters[i] = getterNames[i];
|
||||
instance.setters[i] = setterNames[i];
|
||||
instance.types[i] = types[i];
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Declares a constructor that takes no parameter.
|
||||
*
|
||||
* @param classfile
|
||||
* @throws CannotCompileException
|
||||
*/
|
||||
private void addDefaultConstructor(ClassFile classfile) throws CannotCompileException {
|
||||
ConstPool cp = classfile.getConstPool();
|
||||
String cons_desc = "()V";
|
||||
MethodInfo mi = new MethodInfo( cp, MethodInfo.nameInit, cons_desc );
|
||||
|
||||
Bytecode code = new Bytecode( cp, 0, 1 );
|
||||
// aload_0
|
||||
code.addAload( 0 );
|
||||
// invokespecial
|
||||
code.addInvokespecial( BulkAccessor.class.getName(), MethodInfo.nameInit, cons_desc );
|
||||
// return
|
||||
code.addOpcode( Opcode.RETURN );
|
||||
|
||||
mi.setCodeAttribute( code.toCodeAttribute() );
|
||||
mi.setAccessFlags( AccessFlag.PUBLIC );
|
||||
classfile.addMethod( mi );
|
||||
}
|
||||
|
||||
private void addGetter(ClassFile classfile, final Method[] getters) throws CannotCompileException {
|
||||
ConstPool cp = classfile.getConstPool();
|
||||
int target_type_index = cp.addClassInfo( this.targetBean.getName() );
|
||||
String desc = GET_SETTER_DESC;
|
||||
MethodInfo mi = new MethodInfo( cp, GENERATED_GETTER_NAME, desc );
|
||||
|
||||
Bytecode code = new Bytecode( cp, 6, 4 );
|
||||
/* | this | bean | args | raw bean | */
|
||||
if ( getters.length >= 0 ) {
|
||||
// aload_1 // load bean
|
||||
code.addAload( 1 );
|
||||
// checkcast // cast bean
|
||||
code.addCheckcast( this.targetBean.getName() );
|
||||
// astore_3 // store bean
|
||||
code.addAstore( 3 );
|
||||
for ( int i = 0; i < getters.length; ++i ) {
|
||||
if ( getters[i] != null ) {
|
||||
Method getter = getters[i];
|
||||
// aload_2 // args
|
||||
code.addAload( 2 );
|
||||
// iconst_i // continue to aastore
|
||||
code.addIconst( i ); // growing stack is 1
|
||||
Class returnType = getter.getReturnType();
|
||||
int typeIndex = -1;
|
||||
if ( returnType.isPrimitive() ) {
|
||||
typeIndex = FactoryHelper.typeIndex( returnType );
|
||||
// new
|
||||
code.addNew( FactoryHelper.wrapperTypes[typeIndex] );
|
||||
// dup
|
||||
code.addOpcode( Opcode.DUP );
|
||||
}
|
||||
|
||||
// aload_3 // load the raw bean
|
||||
code.addAload( 3 );
|
||||
String getter_desc = RuntimeSupport.makeDescriptor( getter );
|
||||
String getterName = getter.getName();
|
||||
if ( this.targetBean.isInterface() ) {
|
||||
// invokeinterface
|
||||
code.addInvokeinterface( target_type_index, getterName, getter_desc, 1 );
|
||||
}
|
||||
else {
|
||||
// invokevirtual
|
||||
code.addInvokevirtual( target_type_index, getterName, getter_desc );
|
||||
}
|
||||
|
||||
if ( typeIndex >= 0 ) { // is a primitive type
|
||||
// invokespecial
|
||||
code.addInvokespecial(
|
||||
FactoryHelper.wrapperTypes[typeIndex],
|
||||
MethodInfo.nameInit,
|
||||
FactoryHelper.wrapperDesc[typeIndex]
|
||||
);
|
||||
}
|
||||
|
||||
// aastore // args
|
||||
code.add( Opcode.AASTORE );
|
||||
code.growStack( -3 );
|
||||
}
|
||||
}
|
||||
}
|
||||
// return
|
||||
code.addOpcode( Opcode.RETURN );
|
||||
|
||||
mi.setCodeAttribute( code.toCodeAttribute() );
|
||||
mi.setAccessFlags( AccessFlag.PUBLIC );
|
||||
classfile.addMethod( mi );
|
||||
}
|
||||
|
||||
private void addSetter(ClassFile classfile, final Method[] setters) throws CannotCompileException {
|
||||
ConstPool cp = classfile.getConstPool();
|
||||
int target_type_index = cp.addClassInfo( this.targetBean.getName() );
|
||||
String desc = GET_SETTER_DESC;
|
||||
MethodInfo mi = new MethodInfo( cp, GENERATED_SETTER_NAME, desc );
|
||||
|
||||
Bytecode code = new Bytecode( cp, 4, 6 );
|
||||
/* | this | bean | args | i | raw bean | exception | */
|
||||
if ( setters.length > 0 ) {
|
||||
int start, end; // required to exception table
|
||||
// iconst_0 // i
|
||||
code.addIconst( 0 );
|
||||
// istore_3 // store i
|
||||
code.addIstore( 3 );
|
||||
// aload_1 // load the bean
|
||||
code.addAload( 1 );
|
||||
// checkcast // cast the bean into a raw bean
|
||||
code.addCheckcast( this.targetBean.getName() );
|
||||
// astore 4 // store the raw bean
|
||||
code.addAstore( 4 );
|
||||
/* current stack len = 0 */
|
||||
// start region to handling exception (BulkAccessorException)
|
||||
start = code.currentPc();
|
||||
int lastIndex = 0;
|
||||
for ( int i = 0; i < setters.length; ++i ) {
|
||||
if ( setters[i] != null ) {
|
||||
int diff = i - lastIndex;
|
||||
if ( diff > 0 ) {
|
||||
// iinc 3, 1
|
||||
code.addOpcode( Opcode.IINC );
|
||||
code.add( 3 );
|
||||
code.add( diff );
|
||||
lastIndex = i;
|
||||
}
|
||||
}
|
||||
/* current stack len = 0 */
|
||||
// aload 4 // load the raw bean
|
||||
code.addAload( 4 );
|
||||
// aload_2 // load the args
|
||||
code.addAload( 2 );
|
||||
// iconst_i
|
||||
code.addIconst( i );
|
||||
// aaload
|
||||
code.addOpcode( Opcode.AALOAD );
|
||||
// checkcast
|
||||
Class[] setterParamTypes = setters[i].getParameterTypes();
|
||||
Class setterParamType = setterParamTypes[0];
|
||||
if ( setterParamType.isPrimitive() ) {
|
||||
// checkcast (case of primitive type)
|
||||
// invokevirtual (case of primitive type)
|
||||
this.addUnwrapper( classfile, code, setterParamType );
|
||||
}
|
||||
else {
|
||||
// checkcast (case of reference type)
|
||||
code.addCheckcast( setterParamType.getName() );
|
||||
}
|
||||
/* current stack len = 2 */
|
||||
String rawSetterMethod_desc = RuntimeSupport.makeDescriptor( setters[i] );
|
||||
if ( !this.targetBean.isInterface() ) {
|
||||
// invokevirtual
|
||||
code.addInvokevirtual( target_type_index, setters[i].getName(), rawSetterMethod_desc );
|
||||
}
|
||||
else {
|
||||
// invokeinterface
|
||||
Class[] params = setters[i].getParameterTypes();
|
||||
int size;
|
||||
if ( params[0].equals( Double.TYPE ) || params[0].equals( Long.TYPE ) ) {
|
||||
size = 3;
|
||||
}
|
||||
else {
|
||||
size = 2;
|
||||
}
|
||||
|
||||
code.addInvokeinterface( target_type_index, setters[i].getName(), rawSetterMethod_desc, size );
|
||||
}
|
||||
}
|
||||
|
||||
// end region to handling exception (BulkAccessorException)
|
||||
end = code.currentPc();
|
||||
// return
|
||||
code.addOpcode( Opcode.RETURN );
|
||||
/* current stack len = 0 */
|
||||
// register in exception table
|
||||
int throwableType_index = cp.addClassInfo( THROWABLE_CLASS_NAME );
|
||||
code.addExceptionHandler( start, end, code.currentPc(), throwableType_index );
|
||||
// astore 5 // store exception
|
||||
code.addAstore( 5 );
|
||||
// new // BulkAccessorException
|
||||
code.addNew( BULKEXCEPTION_CLASS_NAME );
|
||||
// dup
|
||||
code.addOpcode( Opcode.DUP );
|
||||
// aload 5 // load exception
|
||||
code.addAload( 5 );
|
||||
// iload_3 // i
|
||||
code.addIload( 3 );
|
||||
// invokespecial // BulkAccessorException.<init>
|
||||
String cons_desc = "(Ljava/lang/Throwable;I)V";
|
||||
code.addInvokespecial( BULKEXCEPTION_CLASS_NAME, MethodInfo.nameInit, cons_desc );
|
||||
// athrow
|
||||
code.addOpcode( Opcode.ATHROW );
|
||||
}
|
||||
else {
|
||||
// return
|
||||
code.addOpcode( Opcode.RETURN );
|
||||
}
|
||||
|
||||
mi.setCodeAttribute( code.toCodeAttribute() );
|
||||
mi.setAccessFlags( AccessFlag.PUBLIC );
|
||||
classfile.addMethod( mi );
|
||||
}
|
||||
|
||||
private void addUnwrapper(
|
||||
ClassFile classfile,
|
||||
Bytecode code,
|
||||
Class type) {
|
||||
int index = FactoryHelper.typeIndex( type );
|
||||
String wrapperType = FactoryHelper.wrapperTypes[index];
|
||||
// checkcast
|
||||
code.addCheckcast( wrapperType );
|
||||
// invokevirtual
|
||||
code.addInvokevirtual( wrapperType, FactoryHelper.unwarpMethods[index], FactoryHelper.unwrapDesc[index] );
|
||||
}
|
||||
|
||||
private static void findAccessors(
|
||||
Class clazz,
|
||||
String[] getterNames,
|
||||
String[] setterNames,
|
||||
Class[] types,
|
||||
Method[] getters,
|
||||
Method[] setters) {
|
||||
int length = types.length;
|
||||
if ( setterNames.length != length || getterNames.length != length ) {
|
||||
throw new BulkAccessorException( "bad number of accessors" );
|
||||
}
|
||||
|
||||
Class[] getParam = new Class[0];
|
||||
Class[] setParam = new Class[1];
|
||||
for ( int i = 0; i < length; i++ ) {
|
||||
if ( getterNames[i] != null ) {
|
||||
Method getter = findAccessor( clazz, getterNames[i], getParam, i );
|
||||
if ( getter.getReturnType() != types[i] ) {
|
||||
throw new BulkAccessorException( "wrong return type: " + getterNames[i], i );
|
||||
}
|
||||
|
||||
getters[i] = getter;
|
||||
}
|
||||
|
||||
if ( setterNames[i] != null ) {
|
||||
setParam[0] = types[i];
|
||||
setters[i] = findAccessor( clazz, setterNames[i], setParam, i );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Method findAccessor(
|
||||
Class clazz,
|
||||
String name,
|
||||
Class[] params,
|
||||
int index) throws BulkAccessorException {
|
||||
try {
|
||||
Method method = clazz.getDeclaredMethod( name, params );
|
||||
if ( Modifier.isPrivate( method.getModifiers() ) ) {
|
||||
throw new BulkAccessorException( "private property", index );
|
||||
}
|
||||
|
||||
return method;
|
||||
}
|
||||
catch ( NoSuchMethodException e ) {
|
||||
throw new BulkAccessorException( "cannot find an accessor", index );
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
package org.hibernate.bytecode.javassist;
|
||||
|
||||
import java.lang.reflect.Modifier;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.hibernate.bytecode.BytecodeProvider;
|
||||
import org.hibernate.bytecode.ClassTransformer;
|
||||
import org.hibernate.bytecode.ProxyFactoryFactory;
|
||||
import org.hibernate.bytecode.ReflectionOptimizer;
|
||||
import org.hibernate.bytecode.util.ClassFilter;
|
||||
import org.hibernate.bytecode.util.FieldFilter;
|
||||
import org.hibernate.util.StringHelper;
|
||||
|
||||
/**
|
||||
* Bytecode provider implementation for Javassist.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class BytecodeProviderImpl implements BytecodeProvider {
|
||||
|
||||
private static final Log log = LogFactory.getLog( BytecodeProviderImpl.class );
|
||||
|
||||
public ProxyFactoryFactory getProxyFactoryFactory() {
|
||||
return new ProxyFactoryFactoryImpl();
|
||||
}
|
||||
|
||||
public ReflectionOptimizer getReflectionOptimizer(
|
||||
Class clazz,
|
||||
String[] getterNames,
|
||||
String[] setterNames,
|
||||
Class[] types) {
|
||||
FastClass fastClass;
|
||||
BulkAccessor bulkAccessor;
|
||||
try {
|
||||
fastClass = FastClass.create( clazz );
|
||||
bulkAccessor = BulkAccessor.create( clazz, getterNames, setterNames, types );
|
||||
if ( !clazz.isInterface() && !Modifier.isAbstract( clazz.getModifiers() ) ) {
|
||||
if ( fastClass == null ) {
|
||||
bulkAccessor = null;
|
||||
}
|
||||
else {
|
||||
//test out the optimizer:
|
||||
Object instance = fastClass.newInstance();
|
||||
bulkAccessor.setPropertyValues( instance, bulkAccessor.getPropertyValues( instance ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
catch ( Throwable t ) {
|
||||
fastClass = null;
|
||||
bulkAccessor = null;
|
||||
String message = "reflection optimizer disabled for: " +
|
||||
clazz.getName() +
|
||||
" [" +
|
||||
StringHelper.unqualify( t.getClass().getName() ) +
|
||||
": " +
|
||||
t.getMessage();
|
||||
|
||||
if ( t instanceof BulkAccessorException ) {
|
||||
int index = ( ( BulkAccessorException ) t ).getIndex();
|
||||
if ( index >= 0 ) {
|
||||
message += " (property " + setterNames[index] + ")";
|
||||
}
|
||||
}
|
||||
|
||||
log.debug( message );
|
||||
}
|
||||
|
||||
if ( fastClass != null && bulkAccessor != null ) {
|
||||
return new ReflectionOptimizerImpl(
|
||||
new InstantiationOptimizerAdapter( fastClass ),
|
||||
new AccessOptimizerAdapter( bulkAccessor, clazz )
|
||||
);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public ClassTransformer getTransformer(ClassFilter classFilter, FieldFilter fieldFilter) {
|
||||
return new JavassistClassTransformer( classFilter, fieldFilter );
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
package org.hibernate.bytecode.javassist;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* @author Muga Nishizawa
|
||||
*/
|
||||
public class FastClass implements Serializable {
|
||||
|
||||
private static final Class[] EMPTY_CLASS_ARRAY = new Class[0];
|
||||
|
||||
private Class type;
|
||||
|
||||
private FastClass() {
|
||||
}
|
||||
|
||||
private FastClass(Class type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public Object invoke(
|
||||
String name,
|
||||
Class[] parameterTypes,
|
||||
Object obj,
|
||||
Object[] args) throws InvocationTargetException {
|
||||
return this.invoke( this.getIndex( name, parameterTypes ), obj, args );
|
||||
}
|
||||
|
||||
public Object invoke(
|
||||
int index,
|
||||
Object obj,
|
||||
Object[] args) throws InvocationTargetException {
|
||||
Method[] methods = this.type.getMethods();
|
||||
try {
|
||||
return methods[index].invoke( obj, args );
|
||||
}
|
||||
catch ( ArrayIndexOutOfBoundsException e ) {
|
||||
throw new IllegalArgumentException(
|
||||
"Cannot find matching method/constructor"
|
||||
);
|
||||
}
|
||||
catch ( IllegalAccessException e ) {
|
||||
throw new InvocationTargetException( e );
|
||||
}
|
||||
}
|
||||
|
||||
public Object newInstance() throws InvocationTargetException {
|
||||
return this.newInstance( this.getIndex( EMPTY_CLASS_ARRAY ), null );
|
||||
}
|
||||
|
||||
public Object newInstance(
|
||||
Class[] parameterTypes,
|
||||
Object[] args) throws InvocationTargetException {
|
||||
return this.newInstance( this.getIndex( parameterTypes ), args );
|
||||
}
|
||||
|
||||
public Object newInstance(
|
||||
int index,
|
||||
Object[] args) throws InvocationTargetException {
|
||||
Constructor[] conss = this.type.getConstructors();
|
||||
try {
|
||||
return conss[index].newInstance( args );
|
||||
}
|
||||
catch ( ArrayIndexOutOfBoundsException e ) {
|
||||
throw new IllegalArgumentException( "Cannot find matching method/constructor" );
|
||||
}
|
||||
catch ( InstantiationException e ) {
|
||||
throw new InvocationTargetException( e );
|
||||
}
|
||||
catch ( IllegalAccessException e ) {
|
||||
throw new InvocationTargetException( e );
|
||||
}
|
||||
}
|
||||
|
||||
public int getIndex(String name, Class[] parameterTypes) {
|
||||
Method[] methods = this.type.getMethods();
|
||||
boolean eq = true;
|
||||
for ( int i = 0; i < methods.length; ++i ) {
|
||||
if ( !Modifier.isPublic( methods[i].getModifiers() ) ) {
|
||||
continue;
|
||||
}
|
||||
if ( !methods[i].getName().equals( name ) ) {
|
||||
continue;
|
||||
}
|
||||
Class[] params = methods[i].getParameterTypes();
|
||||
if ( params.length != parameterTypes.length ) {
|
||||
continue;
|
||||
}
|
||||
eq = true;
|
||||
for ( int j = 0; j < params.length; ++j ) {
|
||||
if ( !params[j].equals( parameterTypes[j] ) ) {
|
||||
eq = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( eq ) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int getIndex(Class[] parameterTypes) {
|
||||
Constructor[] conss = this.type.getConstructors();
|
||||
boolean eq = true;
|
||||
for ( int i = 0; i < conss.length; ++i ) {
|
||||
if ( !Modifier.isPublic( conss[i].getModifiers() ) ) {
|
||||
continue;
|
||||
}
|
||||
Class[] params = conss[i].getParameterTypes();
|
||||
if ( params.length != parameterTypes.length ) {
|
||||
continue;
|
||||
}
|
||||
eq = true;
|
||||
for ( int j = 0; j < params.length; ++j ) {
|
||||
if ( !params[j].equals( parameterTypes[j] ) ) {
|
||||
eq = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( eq ) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public int getMaxIndex() {
|
||||
Method[] methods = this.type.getMethods();
|
||||
int count = 0;
|
||||
for ( int i = 0; i < methods.length; ++i ) {
|
||||
if ( Modifier.isPublic( methods[i].getModifiers() ) ) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return this.type.getName();
|
||||
}
|
||||
|
||||
public Class getJavaClass() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return this.type.toString();
|
||||
}
|
||||
|
||||
public int hashCode() {
|
||||
return this.type.hashCode();
|
||||
}
|
||||
|
||||
public boolean equals(Object o) {
|
||||
if ( !( o instanceof FastClass ) ) {
|
||||
return false;
|
||||
}
|
||||
return this.type.equals( ( ( FastClass ) o ).type );
|
||||
}
|
||||
|
||||
public static FastClass create(Class type) {
|
||||
FastClass fc = new FastClass( type );
|
||||
return fc;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
package org.hibernate.bytecode.javassist;
|
||||
|
||||
/**
|
||||
* Contract for deciding whether fields should be read and/or write intercepted.
|
||||
*
|
||||
* @author Muga Nishizawa
|
||||
*/
|
||||
public interface FieldFilter {
|
||||
/**
|
||||
* Should the given field be read intercepted?
|
||||
*
|
||||
* @param desc
|
||||
* @param name
|
||||
* @return true if the given field should be read intercepted; otherwise
|
||||
* false.
|
||||
*/
|
||||
boolean handleRead(String desc, String name);
|
||||
|
||||
/**
|
||||
* Should the given field be write intercepted?
|
||||
*
|
||||
* @param desc
|
||||
* @param name
|
||||
* @return true if the given field should be write intercepted; otherwise
|
||||
* false.
|
||||
*/
|
||||
boolean handleWrite(String desc, String name);
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
package org.hibernate.bytecode.javassist;
|
||||
|
||||
/**
|
||||
* Interface introduced to the enhanced class in order to be able to
|
||||
* inject a {@link FieldHandler} to define the interception behavior.
|
||||
*
|
||||
* @author Muga Nishizawa
|
||||
*/
|
||||
public interface FieldHandled {
|
||||
/**
|
||||
* Inject the field interception handler to be used.
|
||||
*
|
||||
* @param handler The field interception handler.
|
||||
*/
|
||||
public void setFieldHandler(FieldHandler handler);
|
||||
|
||||
/**
|
||||
* Access to the current field interception handler.
|
||||
*
|
||||
* @return The current field interception handler.
|
||||
*/
|
||||
public FieldHandler getFieldHandler();
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
package org.hibernate.bytecode.javassist;
|
||||
|
||||
/**
|
||||
* The interface defining how interception of a field should be handled.
|
||||
*
|
||||
* @author Muga Nishizawa
|
||||
*/
|
||||
public interface FieldHandler {
|
||||
|
||||
/**
|
||||
* Called to handle writing an int value to a given field.
|
||||
*
|
||||
* @param obj ?
|
||||
* @param name The name of the field being written
|
||||
* @param oldValue The old field value
|
||||
* @param newValue The new field value.
|
||||
* @return ?
|
||||
*/
|
||||
int writeInt(Object obj, String name, int oldValue, int newValue);
|
||||
|
||||
char writeChar(Object obj, String name, char oldValue, char newValue);
|
||||
|
||||
byte writeByte(Object obj, String name, byte oldValue, byte newValue);
|
||||
|
||||
boolean writeBoolean(Object obj, String name, boolean oldValue,
|
||||
boolean newValue);
|
||||
|
||||
short writeShort(Object obj, String name, short oldValue, short newValue);
|
||||
|
||||
float writeFloat(Object obj, String name, float oldValue, float newValue);
|
||||
|
||||
double writeDouble(Object obj, String name, double oldValue, double newValue);
|
||||
|
||||
long writeLong(Object obj, String name, long oldValue, long newValue);
|
||||
|
||||
Object writeObject(Object obj, String name, Object oldValue, Object newValue);
|
||||
|
||||
int readInt(Object obj, String name, int oldValue);
|
||||
|
||||
char readChar(Object obj, String name, char oldValue);
|
||||
|
||||
byte readByte(Object obj, String name, byte oldValue);
|
||||
|
||||
boolean readBoolean(Object obj, String name, boolean oldValue);
|
||||
|
||||
short readShort(Object obj, String name, short oldValue);
|
||||
|
||||
float readFloat(Object obj, String name, float oldValue);
|
||||
|
||||
double readDouble(Object obj, String name, double oldValue);
|
||||
|
||||
long readLong(Object obj, String name, long oldValue);
|
||||
|
||||
Object readObject(Object obj, String name, Object oldValue);
|
||||
|
||||
}
|
|
@ -0,0 +1,592 @@
|
|||
package org.hibernate.bytecode.javassist;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import javassist.CannotCompileException;
|
||||
import javassist.bytecode.AccessFlag;
|
||||
import javassist.bytecode.BadBytecode;
|
||||
import javassist.bytecode.Bytecode;
|
||||
import javassist.bytecode.ClassFile;
|
||||
import javassist.bytecode.CodeAttribute;
|
||||
import javassist.bytecode.CodeIterator;
|
||||
import javassist.bytecode.ConstPool;
|
||||
import javassist.bytecode.Descriptor;
|
||||
import javassist.bytecode.FieldInfo;
|
||||
import javassist.bytecode.MethodInfo;
|
||||
import javassist.bytecode.Opcode;
|
||||
import org.hibernate.bytecode.javassist.FieldFilter;
|
||||
import org.hibernate.bytecode.javassist.FieldHandled;
|
||||
import org.hibernate.bytecode.javassist.FieldHandler;
|
||||
|
||||
/**
|
||||
* The thing that handles actual class enhancement in regards to
|
||||
* intercepting field accesses.
|
||||
*
|
||||
* @author Muga Nishizawa
|
||||
*/
|
||||
public class FieldTransformer {
|
||||
|
||||
private static final String EACH_READ_METHOD_PREFIX = "$javassist_read_";
|
||||
|
||||
private static final String EACH_WRITE_METHOD_PREFIX = "$javassist_write_";
|
||||
|
||||
private static final String FIELD_HANDLED_TYPE_NAME = FieldHandled.class
|
||||
.getName();
|
||||
|
||||
private static final String HANDLER_FIELD_NAME = "$JAVASSIST_READ_WRITE_HANDLER";
|
||||
|
||||
private static final String FIELD_HANDLER_TYPE_NAME = FieldHandler.class
|
||||
.getName();
|
||||
|
||||
private static final String HANDLER_FIELD_DESCRIPTOR = 'L' + FIELD_HANDLER_TYPE_NAME
|
||||
.replace('.', '/') + ';';
|
||||
|
||||
private static final String GETFIELDHANDLER_METHOD_NAME = "getFieldHandler";
|
||||
|
||||
private static final String SETFIELDHANDLER_METHOD_NAME = "setFieldHandler";
|
||||
|
||||
private static final String GETFIELDHANDLER_METHOD_DESCRIPTOR = "()"
|
||||
+ HANDLER_FIELD_DESCRIPTOR;
|
||||
|
||||
private static final String SETFIELDHANDLER_METHOD_DESCRIPTOR = "("
|
||||
+ HANDLER_FIELD_DESCRIPTOR + ")V";
|
||||
|
||||
private FieldFilter filter;
|
||||
|
||||
private HashMap readableFields;
|
||||
|
||||
private HashMap writableFields;
|
||||
|
||||
public FieldTransformer() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public FieldTransformer(FieldFilter f) {
|
||||
filter = f;
|
||||
readableFields = new HashMap();
|
||||
writableFields = new HashMap();
|
||||
}
|
||||
|
||||
public void setFieldFilter(FieldFilter f) {
|
||||
filter = f;
|
||||
}
|
||||
|
||||
public void transform(File file) throws Exception {
|
||||
DataInputStream in = new DataInputStream(new FileInputStream(file));
|
||||
ClassFile classfile = new ClassFile(in);
|
||||
transform(classfile);
|
||||
DataOutputStream out = new DataOutputStream(new FileOutputStream(file));
|
||||
try {
|
||||
classfile.write(out);
|
||||
} finally {
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
public void transform(ClassFile classfile) throws Exception {
|
||||
if (classfile.isInterface()) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
addFieldHandlerField(classfile);
|
||||
addGetFieldHandlerMethod(classfile);
|
||||
addSetFieldHandlerMethod(classfile);
|
||||
addFieldHandledInterface(classfile);
|
||||
addReadWriteMethods(classfile);
|
||||
transformInvokevirtualsIntoPutAndGetfields(classfile);
|
||||
} catch (CannotCompileException e) {
|
||||
throw new RuntimeException(e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
private void addFieldHandlerField(ClassFile classfile)
|
||||
throws CannotCompileException {
|
||||
ConstPool cp = classfile.getConstPool();
|
||||
FieldInfo finfo = new FieldInfo(cp, HANDLER_FIELD_NAME,
|
||||
HANDLER_FIELD_DESCRIPTOR);
|
||||
finfo.setAccessFlags(AccessFlag.PRIVATE | AccessFlag.TRANSIENT);
|
||||
classfile.addField(finfo);
|
||||
}
|
||||
|
||||
private void addGetFieldHandlerMethod(ClassFile classfile)
|
||||
throws CannotCompileException {
|
||||
ConstPool cp = classfile.getConstPool();
|
||||
int this_class_index = cp.getThisClassInfo();
|
||||
MethodInfo minfo = new MethodInfo(cp, GETFIELDHANDLER_METHOD_NAME,
|
||||
GETFIELDHANDLER_METHOD_DESCRIPTOR);
|
||||
/* local variable | this | */
|
||||
Bytecode code = new Bytecode(cp, 2, 1);
|
||||
// aload_0 // load this
|
||||
code.addAload(0);
|
||||
// getfield // get field "$JAVASSIST_CALLBACK" defined already
|
||||
code.addOpcode(Opcode.GETFIELD);
|
||||
int field_index = cp.addFieldrefInfo(this_class_index,
|
||||
HANDLER_FIELD_NAME, HANDLER_FIELD_DESCRIPTOR);
|
||||
code.addIndex(field_index);
|
||||
// areturn // return the value of the field
|
||||
code.addOpcode(Opcode.ARETURN);
|
||||
minfo.setCodeAttribute(code.toCodeAttribute());
|
||||
minfo.setAccessFlags(AccessFlag.PUBLIC);
|
||||
classfile.addMethod(minfo);
|
||||
}
|
||||
|
||||
private void addSetFieldHandlerMethod(ClassFile classfile)
|
||||
throws CannotCompileException {
|
||||
ConstPool cp = classfile.getConstPool();
|
||||
int this_class_index = cp.getThisClassInfo();
|
||||
MethodInfo minfo = new MethodInfo(cp, SETFIELDHANDLER_METHOD_NAME,
|
||||
SETFIELDHANDLER_METHOD_DESCRIPTOR);
|
||||
/* local variables | this | callback | */
|
||||
Bytecode code = new Bytecode(cp, 3, 3);
|
||||
// aload_0 // load this
|
||||
code.addAload(0);
|
||||
// aload_1 // load callback
|
||||
code.addAload(1);
|
||||
// putfield // put field "$JAVASSIST_CALLBACK" defined already
|
||||
code.addOpcode(Opcode.PUTFIELD);
|
||||
int field_index = cp.addFieldrefInfo(this_class_index,
|
||||
HANDLER_FIELD_NAME, HANDLER_FIELD_DESCRIPTOR);
|
||||
code.addIndex(field_index);
|
||||
// return
|
||||
code.addOpcode(Opcode.RETURN);
|
||||
minfo.setCodeAttribute(code.toCodeAttribute());
|
||||
minfo.setAccessFlags(AccessFlag.PUBLIC);
|
||||
classfile.addMethod(minfo);
|
||||
}
|
||||
|
||||
private void addFieldHandledInterface(ClassFile classfile) {
|
||||
String[] interfaceNames = classfile.getInterfaces();
|
||||
String[] newInterfaceNames = new String[interfaceNames.length + 1];
|
||||
System.arraycopy(interfaceNames, 0, newInterfaceNames, 0,
|
||||
interfaceNames.length);
|
||||
newInterfaceNames[newInterfaceNames.length - 1] = FIELD_HANDLED_TYPE_NAME;
|
||||
classfile.setInterfaces(newInterfaceNames);
|
||||
}
|
||||
|
||||
private void addReadWriteMethods(ClassFile classfile)
|
||||
throws CannotCompileException {
|
||||
List fields = classfile.getFields();
|
||||
for (Iterator field_iter = fields.iterator(); field_iter.hasNext();) {
|
||||
FieldInfo finfo = (FieldInfo) field_iter.next();
|
||||
if ((finfo.getAccessFlags() & AccessFlag.STATIC) == 0
|
||||
&& (!finfo.getName().equals(HANDLER_FIELD_NAME))) {
|
||||
// case of non-static field
|
||||
if (filter.handleRead(finfo.getDescriptor(), finfo
|
||||
.getName())) {
|
||||
addReadMethod(classfile, finfo);
|
||||
readableFields.put(finfo.getName(), finfo
|
||||
.getDescriptor());
|
||||
}
|
||||
if (filter.handleWrite(finfo.getDescriptor(), finfo
|
||||
.getName())) {
|
||||
addWriteMethod(classfile, finfo);
|
||||
writableFields.put(finfo.getName(), finfo
|
||||
.getDescriptor());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void addReadMethod(ClassFile classfile, FieldInfo finfo)
|
||||
throws CannotCompileException {
|
||||
ConstPool cp = classfile.getConstPool();
|
||||
int this_class_index = cp.getThisClassInfo();
|
||||
String desc = "()" + finfo.getDescriptor();
|
||||
MethodInfo minfo = new MethodInfo(cp, EACH_READ_METHOD_PREFIX
|
||||
+ finfo.getName(), desc);
|
||||
/* local variables | target obj | each oldvalue | */
|
||||
Bytecode code = new Bytecode(cp, 5, 3);
|
||||
// aload_0
|
||||
code.addAload(0);
|
||||
// getfield // get each field
|
||||
code.addOpcode(Opcode.GETFIELD);
|
||||
int base_field_index = cp.addFieldrefInfo(this_class_index, finfo
|
||||
.getName(), finfo.getDescriptor());
|
||||
code.addIndex(base_field_index);
|
||||
// aload_0
|
||||
code.addAload(0);
|
||||
// invokeinterface // invoke Enabled.getInterceptFieldCallback()
|
||||
int enabled_class_index = cp.addClassInfo(FIELD_HANDLED_TYPE_NAME);
|
||||
code.addInvokeinterface(enabled_class_index,
|
||||
GETFIELDHANDLER_METHOD_NAME, GETFIELDHANDLER_METHOD_DESCRIPTOR,
|
||||
1);
|
||||
// ifnonnull
|
||||
code.addOpcode(Opcode.IFNONNULL);
|
||||
code.addIndex(4);
|
||||
// *return // each type
|
||||
addTypeDependDataReturn(code, finfo.getDescriptor());
|
||||
// *store_1 // each type
|
||||
addTypeDependDataStore(code, finfo.getDescriptor(), 1);
|
||||
// aload_0
|
||||
code.addAload(0);
|
||||
// invokeinterface // invoke Enabled.getInterceptFieldCallback()
|
||||
code.addInvokeinterface(enabled_class_index,
|
||||
GETFIELDHANDLER_METHOD_NAME, GETFIELDHANDLER_METHOD_DESCRIPTOR,
|
||||
1);
|
||||
// aload_0
|
||||
code.addAload(0);
|
||||
// ldc // name of the field
|
||||
code.addLdc(finfo.getName());
|
||||
// *load_1 // each type
|
||||
addTypeDependDataLoad(code, finfo.getDescriptor(), 1);
|
||||
// invokeinterface // invoke Callback.read*() // each type
|
||||
addInvokeFieldHandlerMethod(classfile, code, finfo.getDescriptor(),
|
||||
true);
|
||||
// *return // each type
|
||||
addTypeDependDataReturn(code, finfo.getDescriptor());
|
||||
|
||||
minfo.setCodeAttribute(code.toCodeAttribute());
|
||||
minfo.setAccessFlags(AccessFlag.PUBLIC);
|
||||
classfile.addMethod(minfo);
|
||||
}
|
||||
|
||||
private void addWriteMethod(ClassFile classfile, FieldInfo finfo)
|
||||
throws CannotCompileException {
|
||||
ConstPool cp = classfile.getConstPool();
|
||||
int this_class_index = cp.getThisClassInfo();
|
||||
String desc = "(" + finfo.getDescriptor() + ")V";
|
||||
MethodInfo minfo = new MethodInfo(cp, EACH_WRITE_METHOD_PREFIX
|
||||
+ finfo.getName(), desc);
|
||||
/* local variables | target obj | each oldvalue | */
|
||||
Bytecode code = new Bytecode(cp, 6, 3);
|
||||
// aload_0
|
||||
code.addAload(0);
|
||||
// invokeinterface // enabled.getInterceptFieldCallback()
|
||||
int enabled_class_index = cp.addClassInfo(FIELD_HANDLED_TYPE_NAME);
|
||||
code.addInvokeinterface(enabled_class_index,
|
||||
GETFIELDHANDLER_METHOD_NAME, GETFIELDHANDLER_METHOD_DESCRIPTOR,
|
||||
1);
|
||||
// ifnonnull (label1)
|
||||
code.addOpcode(Opcode.IFNONNULL);
|
||||
code.addIndex(9);
|
||||
// aload_0
|
||||
code.addAload(0);
|
||||
// *load_1
|
||||
addTypeDependDataLoad(code, finfo.getDescriptor(), 1);
|
||||
// putfield
|
||||
code.addOpcode(Opcode.PUTFIELD);
|
||||
int base_field_index = cp.addFieldrefInfo(this_class_index, finfo
|
||||
.getName(), finfo.getDescriptor());
|
||||
code.addIndex(base_field_index);
|
||||
code.growStack(-Descriptor.dataSize(finfo.getDescriptor()));
|
||||
// return ;
|
||||
code.addOpcode(Opcode.RETURN);
|
||||
// aload_0
|
||||
code.addAload(0);
|
||||
// dup
|
||||
code.addOpcode(Opcode.DUP);
|
||||
// invokeinterface // enabled.getInterceptFieldCallback()
|
||||
code.addInvokeinterface(enabled_class_index,
|
||||
GETFIELDHANDLER_METHOD_NAME, GETFIELDHANDLER_METHOD_DESCRIPTOR,
|
||||
1);
|
||||
// aload_0
|
||||
code.addAload(0);
|
||||
// ldc // field name
|
||||
code.addLdc(finfo.getName());
|
||||
// aload_0
|
||||
code.addAload(0);
|
||||
// getfield // old value of the field
|
||||
code.addOpcode(Opcode.GETFIELD);
|
||||
code.addIndex(base_field_index);
|
||||
code.growStack(Descriptor.dataSize(finfo.getDescriptor()) - 1);
|
||||
// *load_1
|
||||
addTypeDependDataLoad(code, finfo.getDescriptor(), 1);
|
||||
// invokeinterface // callback.write*(..)
|
||||
addInvokeFieldHandlerMethod(classfile, code, finfo.getDescriptor(),
|
||||
false);
|
||||
// putfield // new value of the field
|
||||
code.addOpcode(Opcode.PUTFIELD);
|
||||
code.addIndex(base_field_index);
|
||||
code.growStack(-Descriptor.dataSize(finfo.getDescriptor()));
|
||||
// return
|
||||
code.addOpcode(Opcode.RETURN);
|
||||
|
||||
minfo.setCodeAttribute(code.toCodeAttribute());
|
||||
minfo.setAccessFlags(AccessFlag.PUBLIC);
|
||||
classfile.addMethod(minfo);
|
||||
}
|
||||
|
||||
private void transformInvokevirtualsIntoPutAndGetfields(ClassFile classfile)
|
||||
throws CannotCompileException {
|
||||
List methods = classfile.getMethods();
|
||||
for (Iterator method_iter = methods.iterator(); method_iter.hasNext();) {
|
||||
MethodInfo minfo = (MethodInfo) method_iter.next();
|
||||
String methodName = minfo.getName();
|
||||
if (methodName.startsWith(EACH_READ_METHOD_PREFIX)
|
||||
|| methodName.startsWith(EACH_WRITE_METHOD_PREFIX)
|
||||
|| methodName.equals(GETFIELDHANDLER_METHOD_NAME)
|
||||
|| methodName.equals(SETFIELDHANDLER_METHOD_NAME)) {
|
||||
continue;
|
||||
}
|
||||
CodeAttribute codeAttr = minfo.getCodeAttribute();
|
||||
if (codeAttr == null) {
|
||||
return;
|
||||
}
|
||||
CodeIterator iter = codeAttr.iterator();
|
||||
while (iter.hasNext()) {
|
||||
try {
|
||||
int pos = iter.next();
|
||||
pos = transformInvokevirtualsIntoGetfields(classfile, iter,
|
||||
pos);
|
||||
pos = transformInvokevirtualsIntoPutfields(classfile, iter,
|
||||
pos);
|
||||
|
||||
} catch (BadBytecode e) {
|
||||
throw new CannotCompileException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int transformInvokevirtualsIntoGetfields(ClassFile classfile,
|
||||
CodeIterator iter, int pos) {
|
||||
ConstPool cp = classfile.getConstPool();
|
||||
int c = iter.byteAt(pos);
|
||||
if (c != Opcode.GETFIELD) {
|
||||
return pos;
|
||||
}
|
||||
int index = iter.u16bitAt(pos + 1);
|
||||
String fieldName = cp.getFieldrefName(index);
|
||||
String className = cp.getFieldrefClassName(index);
|
||||
if ((!classfile.getName().equals(className))
|
||||
|| (!readableFields.containsKey(fieldName))) {
|
||||
return pos;
|
||||
}
|
||||
String desc = "()" + (String) readableFields.get(fieldName);
|
||||
int read_method_index = cp.addMethodrefInfo(cp.getThisClassInfo(),
|
||||
EACH_READ_METHOD_PREFIX + fieldName, desc);
|
||||
iter.writeByte(Opcode.INVOKEVIRTUAL, pos);
|
||||
iter.write16bit(read_method_index, pos + 1);
|
||||
return pos;
|
||||
}
|
||||
|
||||
private int transformInvokevirtualsIntoPutfields(ClassFile classfile,
|
||||
CodeIterator iter, int pos) {
|
||||
ConstPool cp = classfile.getConstPool();
|
||||
int c = iter.byteAt(pos);
|
||||
if (c != Opcode.PUTFIELD) {
|
||||
return pos;
|
||||
}
|
||||
int index = iter.u16bitAt(pos + 1);
|
||||
String fieldName = cp.getFieldrefName(index);
|
||||
String className = cp.getFieldrefClassName(index);
|
||||
if ((!classfile.getName().equals(className))
|
||||
|| (!writableFields.containsKey(fieldName))) {
|
||||
return pos;
|
||||
}
|
||||
String desc = "(" + (String) writableFields.get(fieldName) + ")V";
|
||||
int write_method_index = cp.addMethodrefInfo(cp.getThisClassInfo(),
|
||||
EACH_WRITE_METHOD_PREFIX + fieldName, desc);
|
||||
iter.writeByte(Opcode.INVOKEVIRTUAL, pos);
|
||||
iter.write16bit(write_method_index, pos + 1);
|
||||
return pos;
|
||||
}
|
||||
|
||||
private static void addInvokeFieldHandlerMethod(ClassFile classfile,
|
||||
Bytecode code, String typeName, boolean isReadMethod) {
|
||||
ConstPool cp = classfile.getConstPool();
|
||||
// invokeinterface
|
||||
int callback_type_index = cp.addClassInfo(FIELD_HANDLER_TYPE_NAME);
|
||||
if ((typeName.charAt(0) == 'L')
|
||||
&& (typeName.charAt(typeName.length() - 1) == ';')
|
||||
|| (typeName.charAt(0) == '[')) {
|
||||
// reference type
|
||||
int indexOfL = typeName.indexOf('L');
|
||||
String type;
|
||||
if (indexOfL == 0) {
|
||||
// not array
|
||||
type = typeName.substring(1, typeName.length() - 1);
|
||||
type = type.replace('/', '.');
|
||||
} else if (indexOfL == -1) {
|
||||
// array of primitive type
|
||||
// do nothing
|
||||
type = typeName;
|
||||
} else {
|
||||
// array of reference type
|
||||
type = typeName.replace('/', '.');
|
||||
}
|
||||
if (isReadMethod) {
|
||||
code
|
||||
.addInvokeinterface(
|
||||
callback_type_index,
|
||||
"readObject",
|
||||
"(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;)Ljava/lang/Object;",
|
||||
4);
|
||||
// checkcast
|
||||
code.addCheckcast(type);
|
||||
} else {
|
||||
code
|
||||
.addInvokeinterface(
|
||||
callback_type_index,
|
||||
"writeObject",
|
||||
"(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;",
|
||||
5);
|
||||
// checkcast
|
||||
code.addCheckcast(type);
|
||||
}
|
||||
} else if (typeName.equals("Z")) {
|
||||
// boolean
|
||||
if (isReadMethod) {
|
||||
code.addInvokeinterface(callback_type_index, "readBoolean",
|
||||
"(Ljava/lang/Object;Ljava/lang/String;Z)Z", 4);
|
||||
} else {
|
||||
code.addInvokeinterface(callback_type_index, "writeBoolean",
|
||||
"(Ljava/lang/Object;Ljava/lang/String;ZZ)Z", 5);
|
||||
}
|
||||
} else if (typeName.equals("B")) {
|
||||
// byte
|
||||
if (isReadMethod) {
|
||||
code.addInvokeinterface(callback_type_index, "readByte",
|
||||
"(Ljava/lang/Object;Ljava/lang/String;B)B", 4);
|
||||
} else {
|
||||
code.addInvokeinterface(callback_type_index, "writeByte",
|
||||
"(Ljava/lang/Object;Ljava/lang/String;BB)B", 5);
|
||||
}
|
||||
} else if (typeName.equals("C")) {
|
||||
// char
|
||||
if (isReadMethod) {
|
||||
code.addInvokeinterface(callback_type_index, "readChar",
|
||||
"(Ljava/lang/Object;Ljava/lang/String;C)C", 4);
|
||||
} else {
|
||||
code.addInvokeinterface(callback_type_index, "writeChar",
|
||||
"(Ljava/lang/Object;Ljava/lang/String;CC)C", 5);
|
||||
}
|
||||
} else if (typeName.equals("I")) {
|
||||
// int
|
||||
if (isReadMethod) {
|
||||
code.addInvokeinterface(callback_type_index, "readInt",
|
||||
"(Ljava/lang/Object;Ljava/lang/String;I)I", 4);
|
||||
} else {
|
||||
code.addInvokeinterface(callback_type_index, "writeInt",
|
||||
"(Ljava/lang/Object;Ljava/lang/String;II)I", 5);
|
||||
}
|
||||
} else if (typeName.equals("S")) {
|
||||
// short
|
||||
if (isReadMethod) {
|
||||
code.addInvokeinterface(callback_type_index, "readShort",
|
||||
"(Ljava/lang/Object;Ljava/lang/String;S)S", 4);
|
||||
} else {
|
||||
code.addInvokeinterface(callback_type_index, "writeShort",
|
||||
"(Ljava/lang/Object;Ljava/lang/String;SS)S", 5);
|
||||
}
|
||||
} else if (typeName.equals("D")) {
|
||||
// double
|
||||
if (isReadMethod) {
|
||||
code.addInvokeinterface(callback_type_index, "readDouble",
|
||||
"(Ljava/lang/Object;Ljava/lang/String;D)D", 5);
|
||||
} else {
|
||||
code.addInvokeinterface(callback_type_index, "writeDouble",
|
||||
"(Ljava/lang/Object;Ljava/lang/String;DD)D", 7);
|
||||
}
|
||||
} else if (typeName.equals("F")) {
|
||||
// float
|
||||
if (isReadMethod) {
|
||||
code.addInvokeinterface(callback_type_index, "readFloat",
|
||||
"(Ljava/lang/Object;Ljava/lang/String;F)F", 4);
|
||||
} else {
|
||||
code.addInvokeinterface(callback_type_index, "writeFloat",
|
||||
"(Ljava/lang/Object;Ljava/lang/String;FF)F", 5);
|
||||
}
|
||||
} else if (typeName.equals("J")) {
|
||||
// long
|
||||
if (isReadMethod) {
|
||||
code.addInvokeinterface(callback_type_index, "readLong",
|
||||
"(Ljava/lang/Object;Ljava/lang/String;J)J", 5);
|
||||
} else {
|
||||
code.addInvokeinterface(callback_type_index, "writeLong",
|
||||
"(Ljava/lang/Object;Ljava/lang/String;JJ)J", 7);
|
||||
}
|
||||
} else {
|
||||
// bad type
|
||||
throw new RuntimeException("bad type: " + typeName);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addTypeDependDataLoad(Bytecode code, String typeName,
|
||||
int i) {
|
||||
if ((typeName.charAt(0) == 'L')
|
||||
&& (typeName.charAt(typeName.length() - 1) == ';')
|
||||
|| (typeName.charAt(0) == '[')) {
|
||||
// reference type
|
||||
code.addAload(i);
|
||||
} else if (typeName.equals("Z") || typeName.equals("B")
|
||||
|| typeName.equals("C") || typeName.equals("I")
|
||||
|| typeName.equals("S")) {
|
||||
// boolean, byte, char, int, short
|
||||
code.addIload(i);
|
||||
} else if (typeName.equals("D")) {
|
||||
// double
|
||||
code.addDload(i);
|
||||
} else if (typeName.equals("F")) {
|
||||
// float
|
||||
code.addFload(i);
|
||||
} else if (typeName.equals("J")) {
|
||||
// long
|
||||
code.addLload(i);
|
||||
} else {
|
||||
// bad type
|
||||
throw new RuntimeException("bad type: " + typeName);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addTypeDependDataStore(Bytecode code, String typeName,
|
||||
int i) {
|
||||
if ((typeName.charAt(0) == 'L')
|
||||
&& (typeName.charAt(typeName.length() - 1) == ';')
|
||||
|| (typeName.charAt(0) == '[')) {
|
||||
// reference type
|
||||
code.addAstore(i);
|
||||
} else if (typeName.equals("Z") || typeName.equals("B")
|
||||
|| typeName.equals("C") || typeName.equals("I")
|
||||
|| typeName.equals("S")) {
|
||||
// boolean, byte, char, int, short
|
||||
code.addIstore(i);
|
||||
} else if (typeName.equals("D")) {
|
||||
// double
|
||||
code.addDstore(i);
|
||||
} else if (typeName.equals("F")) {
|
||||
// float
|
||||
code.addFstore(i);
|
||||
} else if (typeName.equals("J")) {
|
||||
// long
|
||||
code.addLstore(i);
|
||||
} else {
|
||||
// bad type
|
||||
throw new RuntimeException("bad type: " + typeName);
|
||||
}
|
||||
}
|
||||
|
||||
private static void addTypeDependDataReturn(Bytecode code, String typeName) {
|
||||
if ((typeName.charAt(0) == 'L')
|
||||
&& (typeName.charAt(typeName.length() - 1) == ';')
|
||||
|| (typeName.charAt(0) == '[')) {
|
||||
// reference type
|
||||
code.addOpcode(Opcode.ARETURN);
|
||||
} else if (typeName.equals("Z") || typeName.equals("B")
|
||||
|| typeName.equals("C") || typeName.equals("I")
|
||||
|| typeName.equals("S")) {
|
||||
// boolean, byte, char, int, short
|
||||
code.addOpcode(Opcode.IRETURN);
|
||||
} else if (typeName.equals("D")) {
|
||||
// double
|
||||
code.addOpcode(Opcode.DRETURN);
|
||||
} else if (typeName.equals("F")) {
|
||||
// float
|
||||
code.addOpcode(Opcode.FRETURN);
|
||||
} else if (typeName.equals("J")) {
|
||||
// long
|
||||
code.addOpcode(Opcode.LRETURN);
|
||||
} else {
|
||||
// bad type
|
||||
throw new RuntimeException("bad type: " + typeName);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package org.hibernate.bytecode.javassist;
|
||||
|
||||
import org.hibernate.bytecode.ReflectionOptimizer;
|
||||
import org.hibernate.InstantiationException;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* The {@link ReflectionOptimizer.InstantiationOptimizer} implementation for Javassist
|
||||
* which simply acts as an adpater to the {@link FastClass} class.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class InstantiationOptimizerAdapter implements ReflectionOptimizer.InstantiationOptimizer, Serializable {
|
||||
private final FastClass fastClass;
|
||||
|
||||
public InstantiationOptimizerAdapter(FastClass fastClass) {
|
||||
this.fastClass = fastClass;
|
||||
}
|
||||
|
||||
public Object newInstance() {
|
||||
try {
|
||||
return fastClass.newInstance();
|
||||
}
|
||||
catch ( Throwable t ) {
|
||||
throw new InstantiationException(
|
||||
"Could not instantiate entity with Javassist optimizer: ",
|
||||
fastClass.getJavaClass(), t
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
//$Id: $
|
||||
package org.hibernate.bytecode.javassist;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.security.ProtectionDomain;
|
||||
|
||||
import javassist.bytecode.ClassFile;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.bytecode.AbstractClassTransformerImpl;
|
||||
import org.hibernate.bytecode.util.ClassFilter;
|
||||
|
||||
/**
|
||||
* Enhance the classes allowing them to implements InterceptFieldEnabled
|
||||
* This interface is then used by Hibernate for some optimizations.
|
||||
*
|
||||
* @author Emmanuel Bernard
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class JavassistClassTransformer extends AbstractClassTransformerImpl {
|
||||
|
||||
private static Log log = LogFactory.getLog( JavassistClassTransformer.class.getName() );
|
||||
|
||||
public JavassistClassTransformer(ClassFilter classFilter, org.hibernate.bytecode.util.FieldFilter fieldFilter) {
|
||||
super( classFilter, fieldFilter );
|
||||
}
|
||||
|
||||
protected byte[] doTransform(
|
||||
ClassLoader loader,
|
||||
String className,
|
||||
Class classBeingRedefined,
|
||||
ProtectionDomain protectionDomain,
|
||||
byte[] classfileBuffer) {
|
||||
ClassFile classfile;
|
||||
try {
|
||||
// WARNING: classfile only
|
||||
classfile = new ClassFile( new DataInputStream( new ByteArrayInputStream( classfileBuffer ) ) );
|
||||
}
|
||||
catch (IOException e) {
|
||||
log.error( "Unable to build enhancement metamodel for " + className );
|
||||
return classfileBuffer;
|
||||
}
|
||||
FieldTransformer transformer = getFieldTransformer( classfile );
|
||||
if ( transformer != null ) {
|
||||
if ( log.isDebugEnabled() ) {
|
||||
log.debug( "Enhancing " + className );
|
||||
}
|
||||
DataOutputStream out = null;
|
||||
try {
|
||||
transformer.transform( classfile );
|
||||
ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
|
||||
out = new DataOutputStream( byteStream );
|
||||
classfile.write( out );
|
||||
return byteStream.toByteArray();
|
||||
}
|
||||
catch (Exception e) {
|
||||
log.error( "Unable to transform class", e );
|
||||
throw new HibernateException( "Unable to transform class: " + e.getMessage() );
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
if ( out != null ) out.close();
|
||||
}
|
||||
catch (IOException e) {
|
||||
//swallow
|
||||
}
|
||||
}
|
||||
}
|
||||
return classfileBuffer;
|
||||
}
|
||||
|
||||
protected FieldTransformer getFieldTransformer(final ClassFile classfile) {
|
||||
if ( alreadyInstrumented( classfile ) ) {
|
||||
return null;
|
||||
}
|
||||
return new FieldTransformer(
|
||||
new FieldFilter() {
|
||||
public boolean handleRead(String desc, String name) {
|
||||
return fieldFilter.shouldInstrumentField( classfile.getName(), name );
|
||||
}
|
||||
|
||||
public boolean handleWrite(String desc, String name) {
|
||||
return fieldFilter.shouldInstrumentField( classfile.getName(), name );
|
||||
}
|
||||
|
||||
public boolean handleReadAccess(String fieldOwnerClassName, String fieldName) {
|
||||
return fieldFilter.shouldTransformFieldAccess( classfile.getName(), fieldOwnerClassName, fieldName );
|
||||
}
|
||||
|
||||
public boolean handleWriteAccess(String fieldOwnerClassName, String fieldName) {
|
||||
return fieldFilter.shouldTransformFieldAccess( classfile.getName(), fieldOwnerClassName, fieldName );
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private boolean alreadyInstrumented(ClassFile classfile) {
|
||||
String[] intfs = classfile.getInterfaces();
|
||||
for ( int i = 0; i < intfs.length; i++ ) {
|
||||
if ( FieldHandled.class.getName().equals( intfs[i] ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,123 @@
|
|||
package org.hibernate.bytecode.javassist;
|
||||
|
||||
import org.hibernate.bytecode.ProxyFactoryFactory;
|
||||
import org.hibernate.bytecode.BasicProxyFactory;
|
||||
import org.hibernate.proxy.ProxyFactory;
|
||||
import org.hibernate.proxy.pojo.javassist.JavassistProxyFactory;
|
||||
import org.hibernate.AssertionFailure;
|
||||
import org.hibernate.HibernateException;
|
||||
import javassist.util.proxy.MethodFilter;
|
||||
import javassist.util.proxy.ProxyObject;
|
||||
import javassist.util.proxy.MethodHandler;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.HashMap;
|
||||
|
||||
/**
|
||||
* A factory for Javassist-based {@link ProxyFactory} instances.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class ProxyFactoryFactoryImpl implements ProxyFactoryFactory {
|
||||
|
||||
/**
|
||||
* Builds a Javassist-based proxy factory.
|
||||
*
|
||||
* @return a new Javassist-based proxy factory.
|
||||
*/
|
||||
public ProxyFactory buildProxyFactory() {
|
||||
return new JavassistProxyFactory();
|
||||
}
|
||||
|
||||
public BasicProxyFactory buildBasicProxyFactory(Class superClass, Class[] interfaces) {
|
||||
return new BasicProxyFactoryImpl( superClass, interfaces );
|
||||
}
|
||||
|
||||
private static class BasicProxyFactoryImpl implements BasicProxyFactory {
|
||||
private final Class proxyClass;
|
||||
|
||||
public BasicProxyFactoryImpl(Class superClass, Class[] interfaces) {
|
||||
if ( superClass == null && ( interfaces == null || interfaces.length < 1 ) ) {
|
||||
throw new AssertionFailure( "attempting to build proxy without any superclass or interfaces" );
|
||||
}
|
||||
javassist.util.proxy.ProxyFactory factory = new javassist.util.proxy.ProxyFactory();
|
||||
factory.setFilter( FINALIZE_FILTER );
|
||||
if ( superClass != null ) {
|
||||
factory.setSuperclass( superClass );
|
||||
}
|
||||
if ( interfaces != null && interfaces.length > 0 ) {
|
||||
factory.setInterfaces( interfaces );
|
||||
}
|
||||
proxyClass = factory.createClass();
|
||||
}
|
||||
|
||||
public Object getProxy() {
|
||||
try {
|
||||
ProxyObject proxy = ( ProxyObject ) proxyClass.newInstance();
|
||||
proxy.setHandler( new PassThroughHandler( proxy, proxyClass.getName() ) );
|
||||
return proxy;
|
||||
}
|
||||
catch ( Throwable t ) {
|
||||
throw new HibernateException( "Unable to instantiated proxy instance" );
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isInstance(Object object) {
|
||||
return proxyClass.isInstance( object );
|
||||
}
|
||||
}
|
||||
|
||||
private static final MethodFilter FINALIZE_FILTER = new MethodFilter() {
|
||||
public boolean isHandled(Method m) {
|
||||
// skip finalize methods
|
||||
return !( m.getParameterTypes().length == 0 && m.getName().equals( "finalize" ) );
|
||||
}
|
||||
};
|
||||
|
||||
private static class PassThroughHandler implements MethodHandler {
|
||||
private HashMap data = new HashMap();
|
||||
private final Object proxiedObject;
|
||||
private final String proxiedClassName;
|
||||
|
||||
public PassThroughHandler(Object proxiedObject, String proxiedClassName) {
|
||||
this.proxiedObject = proxiedObject;
|
||||
this.proxiedClassName = proxiedClassName;
|
||||
}
|
||||
|
||||
public Object invoke(
|
||||
Object object,
|
||||
Method method,
|
||||
Method method1,
|
||||
Object[] args) throws Exception {
|
||||
String name = method.getName();
|
||||
if ( "toString".equals( name ) ) {
|
||||
return proxiedClassName + "@" + System.identityHashCode( object );
|
||||
}
|
||||
else if ( "equals".equals( name ) ) {
|
||||
return proxiedObject == object ? Boolean.TRUE : Boolean.FALSE;
|
||||
}
|
||||
else if ( "hashCode".equals( name ) ) {
|
||||
return new Integer( System.identityHashCode( object ) );
|
||||
}
|
||||
boolean hasGetterSignature = method.getParameterTypes().length == 0 && method.getReturnType() != null;
|
||||
boolean hasSetterSignature = method.getParameterTypes().length == 1 && ( method.getReturnType() == null || method.getReturnType() == void.class );
|
||||
if ( name.startsWith( "get" ) && hasGetterSignature ) {
|
||||
String propName = name.substring( 3 );
|
||||
return data.get( propName );
|
||||
}
|
||||
else if ( name.startsWith( "is" ) && hasGetterSignature ) {
|
||||
String propName = name.substring( 2 );
|
||||
return data.get( propName );
|
||||
}
|
||||
else if ( name.startsWith( "set" ) && hasSetterSignature) {
|
||||
String propName = name.substring( 3 );
|
||||
data.put( propName, args[0] );
|
||||
return null;
|
||||
}
|
||||
else {
|
||||
// todo : what else to do here?
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
package org.hibernate.bytecode.javassist;
|
||||
|
||||
import org.hibernate.bytecode.ReflectionOptimizer;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* ReflectionOptimizer implementation for Javassist.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class ReflectionOptimizerImpl implements ReflectionOptimizer, Serializable {
|
||||
|
||||
private final InstantiationOptimizer instantiationOptimizer;
|
||||
private final AccessOptimizer accessOptimizer;
|
||||
|
||||
public ReflectionOptimizerImpl(
|
||||
InstantiationOptimizer instantiationOptimizer,
|
||||
AccessOptimizer accessOptimizer) {
|
||||
this.instantiationOptimizer = instantiationOptimizer;
|
||||
this.accessOptimizer = accessOptimizer;
|
||||
}
|
||||
|
||||
public InstantiationOptimizer getInstantiationOptimizer() {
|
||||
return instantiationOptimizer;
|
||||
}
|
||||
|
||||
public AccessOptimizer getAccessOptimizer() {
|
||||
return accessOptimizer;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
package org.hibernate.bytecode.javassist;
|
||||
|
||||
import javassist.ClassPool;
|
||||
import javassist.NotFoundException;
|
||||
import javassist.CtClass;
|
||||
import javassist.CannotCompileException;
|
||||
import org.hibernate.HibernateException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class TransformingClassLoader extends ClassLoader {
|
||||
private ClassLoader parent;
|
||||
private ClassPool classPool;
|
||||
|
||||
/*package*/ TransformingClassLoader(ClassLoader parent, String[] classpath) {
|
||||
this.parent = parent;
|
||||
classPool = new ClassPool( true );
|
||||
for ( int i = 0; i < classpath.length; i++ ) {
|
||||
try {
|
||||
classPool.appendClassPath( classpath[i] );
|
||||
}
|
||||
catch ( NotFoundException e ) {
|
||||
throw new HibernateException(
|
||||
"Unable to resolve requested classpath for transformation [" +
|
||||
classpath[i] + "] : " + e.getMessage()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Class findClass(String name) throws ClassNotFoundException {
|
||||
try {
|
||||
CtClass cc = classPool.get( name );
|
||||
// todo : modify the class definition if not already transformed...
|
||||
byte[] b = cc.toBytecode();
|
||||
return defineClass( name, b, 0, b.length );
|
||||
}
|
||||
catch ( NotFoundException e ) {
|
||||
throw new ClassNotFoundException();
|
||||
}
|
||||
catch ( IOException e ) {
|
||||
throw new ClassNotFoundException();
|
||||
}
|
||||
catch ( CannotCompileException e ) {
|
||||
throw new ClassNotFoundException();
|
||||
}
|
||||
}
|
||||
|
||||
public void release() {
|
||||
classPool = null;
|
||||
parent = null;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
<html>
|
||||
<head></head>
|
||||
<body>
|
||||
<p>
|
||||
This package defines the API for plugging in bytecode libraries
|
||||
for usage by Hibernate. Hibernate uses these bytecode libraries
|
||||
in three scenarios:<ol>
|
||||
<li>
|
||||
<b>Reflection optimization</b> - to speed up the performance of
|
||||
POJO entity and component conctruction and field/property access
|
||||
</li>
|
||||
<li>
|
||||
<b>Proxy generation</b> - runtime building of proxies used for
|
||||
deferred loading of lazy entities
|
||||
</li>
|
||||
<li>
|
||||
<b>Field-level interception</b> - build-time instrumentation of entity
|
||||
classes for the purpose of intercepting field-level access (read/write)
|
||||
for both lazy loading and dirty tracking.
|
||||
</li>
|
||||
</ol>
|
||||
</p>
|
||||
<p>
|
||||
Currently, both CGLIB and Javassist are supported out-of-the-box.
|
||||
</p>
|
||||
<p>
|
||||
Note that for field-level interception, simply plugging in a new {@link BytecodeProvider}
|
||||
is not enough for Hibernate to be able to recognize new providers. You would additionally
|
||||
need to make appropriate code changes to the {@link org.hibernate.intercept.Helper}
|
||||
class. This is because the detection of these enhanced classes is needed in a static
|
||||
environment (i.e. outside the scope of any {@link org.hibernate.SessionFactory}.
|
||||
</p>
|
||||
<p>
|
||||
Note that in the current form the ability to specify a different bytecode provider
|
||||
is actually considered a global settings (global to the JVM).
|
||||
</p>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,59 @@
|
|||
package org.hibernate.bytecode.util;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.HashSet;
|
||||
|
||||
/**
|
||||
* BasicClassFilter provides class filtering based on a series of packages to
|
||||
* be included and/or a series of explicit class names to be included. If
|
||||
* neither is specified, then no restrictions are applied.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class BasicClassFilter implements ClassFilter {
|
||||
private final String[] includedPackages;
|
||||
private final Set includedClassNames = new HashSet();
|
||||
private final boolean isAllEmpty;
|
||||
|
||||
public BasicClassFilter() {
|
||||
this( null, null );
|
||||
}
|
||||
|
||||
public BasicClassFilter(String[] includedPackages, String[] includedClassNames) {
|
||||
this.includedPackages = includedPackages;
|
||||
if ( includedClassNames != null ) {
|
||||
for ( int i = 0; i < includedClassNames.length; i++ ) {
|
||||
this.includedClassNames.add( includedClassNames[i] );
|
||||
}
|
||||
}
|
||||
|
||||
isAllEmpty = ( this.includedPackages == null || this.includedPackages.length == 0 )
|
||||
&& ( this.includedClassNames.isEmpty() );
|
||||
}
|
||||
|
||||
public boolean shouldInstrumentClass(String className) {
|
||||
if ( isAllEmpty ) {
|
||||
return true;
|
||||
}
|
||||
else if ( includedClassNames.contains( className ) ) {
|
||||
return true;
|
||||
}
|
||||
else if ( isInIncludedPackage( className ) ) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isInIncludedPackage(String className) {
|
||||
if ( includedPackages != null ) {
|
||||
for ( int i = 0; i < includedPackages.length; i++ ) {
|
||||
if ( className.startsWith( includedPackages[i] ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
package org.hibernate.bytecode.util;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.BufferedInputStream;
|
||||
import java.util.zip.ZipInputStream;
|
||||
|
||||
/**
|
||||
* A helper for reading byte code from various input sources.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public class ByteCodeHelper {
|
||||
private ByteCodeHelper() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads class byte array info from the given input stream.
|
||||
* <p/>
|
||||
* The stream is closed within this method!
|
||||
*
|
||||
* @param inputStream
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
public static byte[] readByteCode(InputStream inputStream) throws IOException {
|
||||
if ( inputStream == null ) {
|
||||
throw new IOException( "null input stream" );
|
||||
}
|
||||
|
||||
byte[] buffer = new byte[409600];
|
||||
byte[] classBytes = new byte[0];
|
||||
int r = 0;
|
||||
|
||||
try {
|
||||
r = inputStream.read( buffer );
|
||||
while ( r >= buffer.length ) {
|
||||
byte[] temp = new byte[ classBytes.length + buffer.length ];
|
||||
System.arraycopy( classBytes, 0, temp, 0, classBytes.length );
|
||||
System.arraycopy( buffer, 0, temp, classBytes.length, buffer.length );
|
||||
classBytes = temp;
|
||||
}
|
||||
if ( r != -1 ) {
|
||||
byte[] temp = new byte[ classBytes.length + r ];
|
||||
System.arraycopy( classBytes, 0, temp, 0, classBytes.length );
|
||||
System.arraycopy( buffer, 0, temp, classBytes.length, r );
|
||||
classBytes = temp;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
try {
|
||||
inputStream.close();
|
||||
}
|
||||
catch (IOException ignore) {
|
||||
// intentionally empty
|
||||
}
|
||||
}
|
||||
|
||||
return classBytes;
|
||||
}
|
||||
|
||||
public static byte[] readByteCode(File file) throws IOException {
|
||||
return ByteCodeHelper.readByteCode( new FileInputStream( file ) );
|
||||
}
|
||||
|
||||
public static byte[] readByteCode(ZipInputStream zip) throws IOException {
|
||||
ByteArrayOutputStream bout = new ByteArrayOutputStream();
|
||||
InputStream in = new BufferedInputStream( zip );
|
||||
int b;
|
||||
while ( ( b = in.read() ) != -1 ) {
|
||||
bout.write( b );
|
||||
}
|
||||
return bout.toByteArray();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package org.hibernate.bytecode.util;
|
||||
|
||||
/**
|
||||
* Contract describing the information Hibernate needs in terms of instrumenting
|
||||
* a class, either via ant task or dynamic classloader.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface ClassDescriptor {
|
||||
/**
|
||||
* The name of the class.
|
||||
*
|
||||
* @return The class name.
|
||||
*/
|
||||
public String getName();
|
||||
|
||||
/**
|
||||
* Determine if the class is already instrumented.
|
||||
*
|
||||
* @return True if already instrumented; false otherwise.
|
||||
*/
|
||||
public boolean isInstrumented();
|
||||
|
||||
/**
|
||||
* The bytes making up the class' bytecode.
|
||||
*
|
||||
* @return The bytecode bytes.
|
||||
*/
|
||||
public byte[] getBytes();
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
package org.hibernate.bytecode.util;
|
||||
|
||||
/**
|
||||
* Used to determine whether a class should be instrumented.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface ClassFilter {
|
||||
public boolean shouldInstrumentClass(String className);
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package org.hibernate.bytecode.util;
|
||||
|
||||
/**
|
||||
* Used to determine whether a field reference should be instrumented.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public interface FieldFilter {
|
||||
/**
|
||||
* Should this field definition be instrumented?
|
||||
*
|
||||
* @param className The name of the class currently being processed
|
||||
* @param fieldName The name of the field being checked.
|
||||
* @return True if we should instrument this field.
|
||||
*/
|
||||
public boolean shouldInstrumentField(String className, String fieldName);
|
||||
|
||||
/**
|
||||
* Should we instrument *access to* the given field. This differs from
|
||||
* {@link #shouldInstrumentField} in that here we are talking about a particular usage of
|
||||
* a field.
|
||||
*
|
||||
* @param transformingClassName The class currently being transformed.
|
||||
* @param fieldOwnerClassName The name of the class owning this field being checked.
|
||||
* @param fieldName The name of the field being checked.
|
||||
* @return True if this access should be transformed.
|
||||
*/
|
||||
public boolean shouldTransformFieldAccess(String transformingClassName, String fieldOwnerClassName, String fieldName);
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
// $Id$
|
||||
package org.hibernate.cache;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.naming.Context;
|
||||
import javax.naming.InitialContext;
|
||||
import javax.naming.NamingException;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.hibernate.cfg.Environment;
|
||||
import org.hibernate.util.NamingHelper;
|
||||
import org.hibernate.util.StringHelper;
|
||||
|
||||
/**
|
||||
* Support for CacheProvider implementations which are backed by caches bound
|
||||
* into JNDI namespace.
|
||||
*
|
||||
* @author Steve Ebersole
|
||||
*/
|
||||
public abstract class AbstractJndiBoundCacheProvider implements CacheProvider {
|
||||
|
||||
private static final Log log = LogFactory.getLog( AbstractJndiBoundCacheProvider.class );
|
||||
private Object cache;
|
||||
|
||||
protected void prepare(Properties properties) {
|
||||
// Do nothing; subclasses may override.
|
||||
}
|
||||
|
||||
protected void release() {
|
||||
// Do nothing; subclasses may override.
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to perform any necessary initialization of the underlying cache implementation during SessionFactory
|
||||
* construction.
|
||||
*
|
||||
* @param properties current configuration settings.
|
||||
*/
|
||||
public final void start(Properties properties) throws CacheException {
|
||||
String jndiNamespace = properties.getProperty( Environment.CACHE_NAMESPACE );
|
||||
if ( StringHelper.isEmpty( jndiNamespace ) ) {
|
||||
throw new CacheException( "No JNDI namespace specified for cache" );
|
||||
}
|
||||
cache = locateCache( jndiNamespace, NamingHelper.getJndiProperties( properties ) );
|
||||
prepare( properties );
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback to perform any necessary cleanup of the underlying cache
|
||||
* implementation during SessionFactory.close().
|
||||
*/
|
||||
public final void stop() {
|
||||
release();
|
||||
cache = null;
|
||||
}
|
||||
|
||||
private Object locateCache(String jndiNamespace, Properties jndiProperties) {
|
||||
|
||||
Context ctx = null;
|
||||
try {
|
||||
ctx = new InitialContext( jndiProperties );
|
||||
return ctx.lookup( jndiNamespace );
|
||||
}
|
||||
catch (NamingException ne) {
|
||||
String msg = "Unable to retreive Cache from JNDI [" + jndiNamespace + "]";
|
||||
log.info( msg, ne );
|
||||
throw new CacheException( msg );
|
||||
}
|
||||
finally {
|
||||
if ( ctx != null ) {
|
||||
try {
|
||||
ctx.close();
|
||||
}
|
||||
catch( NamingException ne ) {
|
||||
log.info( "Unable to release initial context", ne );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Object getCache() {
|
||||
return cache;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
//$Id$
|
||||
package org.hibernate.cache;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Implementors define a caching algorithm. All implementors
|
||||
* <b>must</b> be threadsafe.
|
||||
*/
|
||||
public interface Cache {
|
||||
/**
|
||||
* Get an item from the cache
|
||||
* @param key
|
||||
* @return the cached object or <tt>null</tt>
|
||||
* @throws CacheException
|
||||
*/
|
||||
public Object read(Object key) throws CacheException;
|
||||
/**
|
||||
* Get an item from the cache, nontransactionally
|
||||
* @param key
|
||||
* @return the cached object or <tt>null</tt>
|
||||
* @throws CacheException
|
||||
*/
|
||||
public Object get(Object key) throws CacheException;
|
||||
/**
|
||||
* Add an item to the cache, nontransactionally, with
|
||||
* failfast semantics
|
||||
* @param key
|
||||
* @param value
|
||||
* @throws CacheException
|
||||
*/
|
||||
public void put(Object key, Object value) throws CacheException;
|
||||
/**
|
||||
* Add an item to the cache
|
||||
* @param key
|
||||
* @param value
|
||||
* @throws CacheException
|
||||
*/
|
||||
public void update(Object key, Object value) throws CacheException;
|
||||
/**
|
||||
* Remove an item from the cache
|
||||
*/
|
||||
public void remove(Object key) throws CacheException;
|
||||
/**
|
||||
* Clear the cache
|
||||
*/
|
||||
public void clear() throws CacheException;
|
||||
/**
|
||||
* Clean up
|
||||
*/
|
||||
public void destroy() throws CacheException;
|
||||
/**
|
||||
* If this is a clustered cache, lock the item
|
||||
*/
|
||||
public void lock(Object key) throws CacheException;
|
||||
/**
|
||||
* If this is a clustered cache, unlock the item
|
||||
*/
|
||||
public void unlock(Object key) throws CacheException;
|
||||
/**
|
||||
* Generate a timestamp
|
||||
*/
|
||||
public long nextTimestamp();
|
||||
/**
|
||||
* Get a reasonable "lock timeout"
|
||||
*/
|
||||
public int getTimeout();
|
||||
|
||||
/**
|
||||
* Get the name of the cache region
|
||||
*/
|
||||
public String getRegionName();
|
||||
|
||||
/**
|
||||
* The number of bytes is this cache region currently consuming in memory.
|
||||
*
|
||||
* @return The number of bytes consumed by this region; -1 if unknown or
|
||||
* unsupported.
|
||||
*/
|
||||
public long getSizeInMemory();
|
||||
|
||||
/**
|
||||
* The count of entries currently contained in the regions in-memory store.
|
||||
*
|
||||
* @return The count of entries in memory; -1 if unknown or unsupported.
|
||||
*/
|
||||
public long getElementCountInMemory();
|
||||
|
||||
/**
|
||||
* The count of entries currently contained in the regions disk store.
|
||||
*
|
||||
* @return The count of entries on disk; -1 if unknown or unsupported.
|
||||
*/
|
||||
public long getElementCountOnDisk();
|
||||
|
||||
/**
|
||||
* optional operation
|
||||
*/
|
||||
public Map toMap();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
//$Id$
|
||||
package org.hibernate.cache;
|
||||
|
||||
import java.util.Comparator;
|
||||
|
||||
/**
|
||||
* Implementors manage transactional access to cached data. Transactions
|
||||
* pass in a timestamp indicating transaction start time. Two different
|
||||
* implementation patterns are provided for.<ul>
|
||||
* <li>A transaction-aware cache implementation might be wrapped by a
|
||||
* "synchronous" concurrency strategy, where updates to the cache are written
|
||||
* to the cache inside the transaction.</li>
|
||||
* <li>A non transaction-aware cache would be wrapped by an "asynchronous"
|
||||
* concurrency strategy, where items are merely "soft locked" during the
|
||||
* transaction and then updated during the "after transaction completion"
|
||||
* phase; the soft lock is not an actual lock on the database row -
|
||||
* only upon the cached representation of the item.</li>
|
||||
* </ul>
|
||||
* <p/>
|
||||
* In terms of entity caches, the expected call sequences are: <ul>
|
||||
* <li><b>DELETES</b> : {@link #lock} -> {@link #evict} -> {@link #release}</li>
|
||||
* <li><b>UPDATES</b> : {@link #lock} -> {@link #update} -> {@link #afterUpdate}</li>
|
||||
* <li><b>INSERTS</b> : {@link #insert} -> {@link #afterInsert}</li>
|
||||
* </ul>
|
||||
* <p/>
|
||||
* In terms of collection caches, all modification actions actually just
|
||||
* invalidate the entry(s). The call sequence here is:
|
||||
* {@link #lock} -> {@link #evict} -> {@link #release}
|
||||
* <p/>
|
||||
* Note that, for an asynchronous cache, cache invalidation must be a two
|
||||
* step process (lock->release, or lock-afterUpdate), since this is the only
|
||||
* way to guarantee consistency with the database for a nontransactional cache
|
||||
* implementation. For a synchronous cache, cache invalidation is a single
|
||||
* step process (evict, or update). Hence, this interface defines a three
|
||||
* step process, to cater for both models.
|
||||
* <p/>
|
||||
* Note that query result caching does not go through a concurrency strategy; they
|
||||
* are managed directly against the underlying {@link Cache cache regions}.
|
||||
*/
|
||||
public interface CacheConcurrencyStrategy {
|
||||
|
||||
/**
|
||||
* Attempt to retrieve an object from the cache. Mainly used in attempting
|
||||
* to resolve entities/collections from the second level cache.
|
||||
*
|
||||
* @param key
|
||||
* @param txTimestamp a timestamp prior to the transaction start time
|
||||
* @return the cached object or <tt>null</tt>
|
||||
* @throws CacheException
|
||||
*/
|
||||
public Object get(Object key, long txTimestamp) throws CacheException;
|
||||
|
||||
/**
|
||||
* Attempt to cache an object, after loading from the database.
|
||||
*
|
||||
* @param key
|
||||
* @param value
|
||||
* @param txTimestamp a timestamp prior to the transaction start time
|
||||
* @param version the item version number
|
||||
* @param versionComparator a comparator used to compare version numbers
|
||||
* @param minimalPut indicates that the cache should avoid a put is the item is already cached
|
||||
* @return <tt>true</tt> if the object was successfully cached
|
||||
* @throws CacheException
|
||||
*/
|
||||
public boolean put(
|
||||
Object key,
|
||||
Object value,
|
||||
long txTimestamp,
|
||||
Object version,
|
||||
Comparator versionComparator,
|
||||
boolean minimalPut)
|
||||
throws CacheException;
|
||||
|
||||
/**
|
||||
* We are going to attempt to update/delete the keyed object. This
|
||||
* method is used by "asynchronous" concurrency strategies.
|
||||
* <p/>
|
||||
* The returned object must be passed back to release(), to release the
|
||||
* lock. Concurrency strategies which do not support client-visible
|
||||
* locks may silently return null.
|
||||
*
|
||||
* @param key
|
||||
* @param version
|
||||
* @throws CacheException
|
||||
*/
|
||||
public SoftLock lock(Object key, Object version) throws CacheException;
|
||||
|
||||
/**
|
||||
* Called after an item has become stale (before the transaction completes).
|
||||
* This method is used by "synchronous" concurrency strategies.
|
||||
*/
|
||||
public void evict(Object key) throws CacheException;
|
||||
|
||||
/**
|
||||
* Called after an item has been updated (before the transaction completes),
|
||||
* instead of calling evict().
|
||||
* This method is used by "synchronous" concurrency strategies.
|
||||
*/
|
||||
public boolean update(Object key, Object value, Object currentVersion, Object previousVersion) throws CacheException;
|
||||
|
||||
/**
|
||||
* Called after an item has been inserted (before the transaction completes),
|
||||
* instead of calling evict().
|
||||
* This method is used by "synchronous" concurrency strategies.
|
||||
*/
|
||||
public boolean insert(Object key, Object value, Object currentVersion) throws CacheException;
|
||||
|
||||
|
||||
/**
|
||||
* Called when we have finished the attempted update/delete (which may or
|
||||
* may not have been successful), after transaction completion.
|
||||
* This method is used by "asynchronous" concurrency strategies.
|
||||
* @param key
|
||||
* @throws CacheException
|
||||
*/
|
||||
public void release(Object key, SoftLock lock) throws CacheException;
|
||||
/**
|
||||
* Called after an item has been updated (after the transaction completes),
|
||||
* instead of calling release().
|
||||
* This method is used by "asynchronous" concurrency strategies.
|
||||
*/
|
||||
public boolean afterUpdate(Object key, Object value, Object version, SoftLock lock)
|
||||
throws CacheException;
|
||||
/**
|
||||
* Called after an item has been inserted (after the transaction completes),
|
||||
* instead of calling release().
|
||||
* This method is used by "asynchronous" concurrency strategies.
|
||||
*/
|
||||
public boolean afterInsert(Object key, Object value, Object version)
|
||||
throws CacheException;
|
||||
|
||||
|
||||
/**
|
||||
* Evict an item from the cache immediately (without regard for transaction
|
||||
* isolation).
|
||||
* @param key
|
||||
* @throws CacheException
|
||||
*/
|
||||
public void remove(Object key) throws CacheException;
|
||||
/**
|
||||
* Evict all items from the cache immediately.
|
||||
* @throws CacheException
|
||||
*/
|
||||
public void clear() throws CacheException;
|
||||
/**
|
||||
* Clean up all resources.
|
||||
*/
|
||||
public void destroy();
|
||||
/**
|
||||
* Set the underlying cache implementation.
|
||||
* @param cache
|
||||
*/
|
||||
public void setCache(Cache cache);
|
||||
|
||||
/**
|
||||
* Marker interface, denoting a client-visible "soft lock"
|
||||
* on a cached item.
|
||||
* @author Gavin King
|
||||
*/
|
||||
public static interface SoftLock {}
|
||||
|
||||
/**
|
||||
* Get the cache region name
|
||||
*/
|
||||
public String getRegionName();
|
||||
|
||||
/**
|
||||
* Get the wrapped cache implementation
|
||||
*/
|
||||
public Cache getCache();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,23 @@
|
|||
//$Id$
|
||||
package org.hibernate.cache;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
|
||||
/**
|
||||
* Something went wrong in the cache
|
||||
*/
|
||||
public class CacheException extends HibernateException {
|
||||
|
||||
public CacheException(String s) {
|
||||
super(s);
|
||||
}
|
||||
|
||||
public CacheException(String s, Throwable e) {
|
||||
super(s, e);
|
||||
}
|
||||
|
||||
public CacheException(Throwable e) {
|
||||
super(e);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
//$Id$
|
||||
package org.hibernate.cache;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
import org.hibernate.HibernateException;
|
||||
import org.hibernate.MappingException;
|
||||
import org.hibernate.cfg.Settings;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* @author Gavin King
|
||||
*/
|
||||
public final class CacheFactory {
|
||||
|
||||
private static final Log log = LogFactory.getLog(CacheFactory.class);
|
||||
|
||||
private CacheFactory() {}
|
||||
|
||||
public static final String READ_ONLY = "read-only";
|
||||
public static final String READ_WRITE = "read-write";
|
||||
public static final String NONSTRICT_READ_WRITE = "nonstrict-read-write";
|
||||
public static final String TRANSACTIONAL = "transactional";
|
||||
|
||||
public static CacheConcurrencyStrategy createCache(
|
||||
final String concurrencyStrategy,
|
||||
String regionName,
|
||||
final boolean mutable,
|
||||
final Settings settings,
|
||||
final Properties properties)
|
||||
throws HibernateException {
|
||||
|
||||
if ( concurrencyStrategy==null || !settings.isSecondLevelCacheEnabled() ) return null; //no cache
|
||||
|
||||
String prefix = settings.getCacheRegionPrefix();
|
||||
if ( prefix!=null ) regionName = prefix + '.' + regionName;
|
||||
|
||||
if ( log.isDebugEnabled() ) log.debug("instantiating cache region: " + regionName + " usage strategy: " + concurrencyStrategy);
|
||||
|
||||
final CacheConcurrencyStrategy ccs;
|
||||
if ( concurrencyStrategy.equals(READ_ONLY) ) {
|
||||
if (mutable) log.warn( "read-only cache configured for mutable class: " + regionName );
|
||||
ccs = new ReadOnlyCache();
|
||||
}
|
||||
else if ( concurrencyStrategy.equals(READ_WRITE) ) {
|
||||
ccs = new ReadWriteCache();
|
||||
}
|
||||
else if ( concurrencyStrategy.equals(NONSTRICT_READ_WRITE) ) {
|
||||
ccs = new NonstrictReadWriteCache();
|
||||
}
|
||||
else if ( concurrencyStrategy.equals(TRANSACTIONAL) ) {
|
||||
ccs = new TransactionalCache();
|
||||
}
|
||||
else {
|
||||
throw new MappingException("cache usage attribute should be read-write, read-only, nonstrict-read-write or transactional");
|
||||
}
|
||||
|
||||
final Cache impl;
|
||||
try {
|
||||
impl = settings.getCacheProvider().buildCache(regionName, properties);
|
||||
}
|
||||
catch (CacheException e) {
|
||||
throw new HibernateException( "Could not instantiate cache implementation", e );
|
||||
}
|
||||
ccs.setCache(impl);
|
||||
|
||||
return ccs;
|
||||
}
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue