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:
Steve Ebersole 2007-06-29 19:51:50 +00:00
parent 975d485a74
commit c654277e62
1116 changed files with 153510 additions and 0 deletions

View File

@ -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);
}
}

View File

@ -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 );
}
}

View File

@ -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);
}
}

View File

@ -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 );
}
}

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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 {}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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 );
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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 );
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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 );
}
}

View File

@ -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);
}
}

View File

@ -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();
}
}

View File

@ -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);
}

View File

@ -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;
}
}

View File

@ -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 );
}
}

View File

@ -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);
}
}

93
src/org/hibernate/SQLQuery.java Executable file
View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 );
}
}

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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 );
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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 );
}
}
}
}

View File

@ -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() );
}
}
}

View File

@ -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() );
}
}
}

View File

@ -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() );
}
}
}

View File

@ -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() );
}
}
}

View File

@ -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 + ">";
}
}

View File

@ -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 );
}
}

View File

@ -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;
}
}

View File

@ -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() );
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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();
}

View File

@ -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>

View File

@ -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);
}

View File

@ -0,0 +1,10 @@
package org.hibernate.bytecode;
/**
* A proxy factory for "basic proxy" generation
*
* @author Steve Ebersole
*/
public interface BasicProxyFactory {
public Object getProxy();
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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 );
}
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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 );
}
}

View File

@ -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 );
}
}

View File

@ -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;
}
}

View File

@ -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 );
}
}

View File

@ -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;
}
}
}
}

View File

@ -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;
}
}

View File

@ -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 "?";
}
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

@ -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 );
}
}
}

View File

@ -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 );
}
}

View File

@ -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;
}
}

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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);
}
}
}

View File

@ -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
);
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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>

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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();
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}
}

106
src/org/hibernate/cache/Cache.java vendored Normal file
View File

@ -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();
}

View File

@ -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();
}

View File

@ -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);
}
}

View File

@ -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