Rename modules {org.springframework.*=>spring-*}

This renaming more intuitively expresses the relationship between
subprojects and the JAR artifacts they produce.

Tracking history across these renames is possible, but it requires
use of the --follow flag to `git log`, for example

    $ git log spring-aop/src/main/java/org/springframework/aop/Advisor.java

will show history up until the renaming event, where

    $ git log --follow spring-aop/src/main/java/org/springframework/aop/Advisor.java

will show history for all changes to the file, before and after the
renaming.

See http://chrisbeams.com/git-diff-across-renamed-directories
This commit is contained in:
Chris Beams
2012-01-20 22:51:02 +01:00
parent b6cb514d38
commit 02a4473c62
5671 changed files with 20 additions and 32 deletions

View File

@@ -0,0 +1,154 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm;
import org.springframework.dao.OptimisticLockingFailureException;
/**
* Exception thrown on an optimistic locking violation for a mapped object.
* Provides information about the persistent class and the identifier.
*
* @author Juergen Hoeller
* @since 13.10.2003
*/
public class ObjectOptimisticLockingFailureException extends OptimisticLockingFailureException {
private Object persistentClass;
private Object identifier;
/**
* Create a general ObjectOptimisticLockingFailureException with the given message,
* without any information on the affected object.
* @param msg the detail message
* @param cause the source exception
*/
public ObjectOptimisticLockingFailureException(String msg, Throwable cause) {
super(msg, cause);
}
/**
* Create a new ObjectOptimisticLockingFailureException for the given object,
* with the default "optimistic locking failed" message.
* @param persistentClass the persistent class
* @param identifier the ID of the object for which the locking failed
*/
public ObjectOptimisticLockingFailureException(Class persistentClass, Object identifier) {
this(persistentClass, identifier, null);
}
/**
* Create a new ObjectOptimisticLockingFailureException for the given object,
* with the default "optimistic locking failed" message.
* @param persistentClass the persistent class
* @param identifier the ID of the object for which the locking failed
* @param cause the source exception
*/
public ObjectOptimisticLockingFailureException(
Class persistentClass, Object identifier, Throwable cause) {
this(persistentClass, identifier,
"Object of class [" + persistentClass.getName() + "] with identifier [" + identifier +
"]: optimistic locking failed", cause);
}
/**
* Create a new ObjectOptimisticLockingFailureException for the given object,
* with the given explicit message.
* @param persistentClass the persistent class
* @param identifier the ID of the object for which the locking failed
* @param msg the detail message
* @param cause the source exception
*/
public ObjectOptimisticLockingFailureException(
Class persistentClass, Object identifier, String msg, Throwable cause) {
super(msg, cause);
this.persistentClass = persistentClass;
this.identifier = identifier;
}
/**
* Create a new ObjectOptimisticLockingFailureException for the given object,
* with the default "optimistic locking failed" message.
* @param persistentClassName the name of the persistent class
* @param identifier the ID of the object for which the locking failed
*/
public ObjectOptimisticLockingFailureException(String persistentClassName, Object identifier) {
this(persistentClassName, identifier, null);
}
/**
* Create a new ObjectOptimisticLockingFailureException for the given object,
* with the default "optimistic locking failed" message.
* @param persistentClassName the name of the persistent class
* @param identifier the ID of the object for which the locking failed
* @param cause the source exception
*/
public ObjectOptimisticLockingFailureException(
String persistentClassName, Object identifier, Throwable cause) {
this(persistentClassName, identifier,
"Object of class [" + persistentClassName + "] with identifier [" + identifier +
"]: optimistic locking failed", cause);
}
/**
* Create a new ObjectOptimisticLockingFailureException for the given object,
* with the given explicit message.
* @param persistentClassName the name of the persistent class
* @param identifier the ID of the object for which the locking failed
* @param msg the detail message
* @param cause the source exception
*/
public ObjectOptimisticLockingFailureException(
String persistentClassName, Object identifier, String msg, Throwable cause) {
super(msg, cause);
this.persistentClass = persistentClassName;
this.identifier = identifier;
}
/**
* Return the persistent class of the object for which the locking failed.
* If no Class was specified, this method returns null.
*/
public Class getPersistentClass() {
return (this.persistentClass instanceof Class ? (Class) this.persistentClass : null);
}
/**
* Return the name of the persistent class of the object for which the locking failed.
* Will work for both Class objects and String names.
*/
public String getPersistentClassName() {
if (this.persistentClass instanceof Class) {
return ((Class) this.persistentClass).getName();
}
return (this.persistentClass != null ? this.persistentClass.toString() : null);
}
/**
* Return the identifier of the object for which the locking failed.
*/
public Object getIdentifier() {
return identifier;
}
}

View File

@@ -0,0 +1,128 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm;
import org.springframework.dao.DataRetrievalFailureException;
/**
* Exception thrown if a mapped object could not be retrieved via its identifier.
* Provides information about the persistent class and the identifier.
*
* @author Juergen Hoeller
* @since 13.10.2003
*/
public class ObjectRetrievalFailureException extends DataRetrievalFailureException {
private Object persistentClass;
private Object identifier;
/**
* Create a general ObjectRetrievalFailureException with the given message,
* without any information on the affected object.
* @param msg the detail message
* @param cause the source exception
*/
public ObjectRetrievalFailureException(String msg, Throwable cause) {
super(msg, cause);
}
/**
* Create a new ObjectRetrievalFailureException for the given object,
* with the default "not found" message.
* @param persistentClass the persistent class
* @param identifier the ID of the object that should have been retrieved
*/
public ObjectRetrievalFailureException(Class persistentClass, Object identifier) {
this(persistentClass, identifier,
"Object of class [" + persistentClass.getName() + "] with identifier [" + identifier + "]: not found",
null);
}
/**
* Create a new ObjectRetrievalFailureException for the given object,
* with the given explicit message and exception.
* @param persistentClass the persistent class
* @param identifier the ID of the object that should have been retrieved
* @param msg the detail message
* @param cause the source exception
*/
public ObjectRetrievalFailureException(
Class persistentClass, Object identifier, String msg, Throwable cause) {
super(msg, cause);
this.persistentClass = persistentClass;
this.identifier = identifier;
}
/**
* Create a new ObjectRetrievalFailureException for the given object,
* with the default "not found" message.
* @param persistentClassName the name of the persistent class
* @param identifier the ID of the object that should have been retrieved
*/
public ObjectRetrievalFailureException(String persistentClassName, Object identifier) {
this(persistentClassName, identifier,
"Object of class [" + persistentClassName + "] with identifier [" + identifier + "]: not found",
null);
}
/**
* Create a new ObjectRetrievalFailureException for the given object,
* with the given explicit message and exception.
* @param persistentClassName the name of the persistent class
* @param identifier the ID of the object that should have been retrieved
* @param msg the detail message
* @param cause the source exception
*/
public ObjectRetrievalFailureException(
String persistentClassName, Object identifier, String msg, Throwable cause) {
super(msg, cause);
this.persistentClass = persistentClassName;
this.identifier = identifier;
}
/**
* Return the persistent class of the object that was not found.
* If no Class was specified, this method returns null.
*/
public Class getPersistentClass() {
return (this.persistentClass instanceof Class ? (Class) this.persistentClass : null);
}
/**
* Return the name of the persistent class of the object that was not found.
* Will work for both Class objects and String names.
*/
public String getPersistentClassName() {
if (this.persistentClass instanceof Class) {
return ((Class) this.persistentClass).getName();
}
return (this.persistentClass != null ? this.persistentClass.toString() : null);
}
/**
* Return the identifier of the object that was not found.
*/
public Object getIdentifier() {
return identifier;
}
}

View File

@@ -0,0 +1,278 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.SessionFactory;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
/**
* Abstract {@link org.springframework.beans.factory.FactoryBean} that creates
* a Hibernate {@link org.hibernate.SessionFactory} within a Spring application
* context, providing general infrastructure not related to Hibernate's
* specific configuration API.
*
* <p>This class implements the
* {@link org.springframework.dao.support.PersistenceExceptionTranslator}
* interface, as autodetected by Spring's
* {@link org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor},
* for AOP-based translation of native exceptions to Spring DataAccessExceptions.
* Hence, the presence of e.g. LocalSessionFactoryBean automatically enables
* a PersistenceExceptionTranslationPostProcessor to translate Hibernate exceptions.
*
* <p>This class mainly serves as common base class for {@link LocalSessionFactoryBean}.
* For details on typical SessionFactory setup, see the LocalSessionFactoryBean javadoc.
*
* @author Juergen Hoeller
* @since 2.0
* @see #setExposeTransactionAwareSessionFactory
* @see org.hibernate.SessionFactory#getCurrentSession()
* @see org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor
*/
public abstract class AbstractSessionFactoryBean extends HibernateExceptionTranslator
implements FactoryBean<SessionFactory>, InitializingBean, DisposableBean {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
private DataSource dataSource;
private boolean useTransactionAwareDataSource = false;
private boolean exposeTransactionAwareSessionFactory = true;
private SessionFactory sessionFactory;
/**
* Set the DataSource to be used by the SessionFactory.
* If set, this will override corresponding settings in Hibernate properties.
* <p>If this is set, the Hibernate settings should not define
* a connection provider to avoid meaningless double configuration.
* <p>If using HibernateTransactionManager as transaction strategy, consider
* proxying your target DataSource with a LazyConnectionDataSourceProxy.
* This defers fetching of an actual JDBC Connection until the first JDBC
* Statement gets executed, even within JDBC transactions (as performed by
* HibernateTransactionManager). Such lazy fetching is particularly beneficial
* for read-only operations, in particular if the chances of resolving the
* result in the second-level cache are high.
* <p>As JTA and transactional JNDI DataSources already provide lazy enlistment
* of JDBC Connections, LazyConnectionDataSourceProxy does not add value with
* JTA (i.e. Spring's JtaTransactionManager) as transaction strategy.
* @see #setUseTransactionAwareDataSource
* @see HibernateTransactionManager
* @see org.springframework.transaction.jta.JtaTransactionManager
* @see org.springframework.jdbc.datasource.LazyConnectionDataSourceProxy
*/
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
/**
* Return the DataSource to be used by the SessionFactory.
*/
public DataSource getDataSource() {
return this.dataSource;
}
/**
* Set whether to use a transaction-aware DataSource for the SessionFactory,
* i.e. whether to automatically wrap the passed-in DataSource with Spring's
* TransactionAwareDataSourceProxy.
* <p>Default is "false": LocalSessionFactoryBean is usually used with Spring's
* HibernateTransactionManager or JtaTransactionManager, both of which work nicely
* on a plain JDBC DataSource. Hibernate Sessions and their JDBC Connections are
* fully managed by the Hibernate/JTA transaction infrastructure in such a scenario.
* <p>If you switch this flag to "true", Spring's Hibernate access will be able to
* <i>participate in JDBC-based transactions managed outside of Hibernate</i>
* (for example, by Spring's DataSourceTransactionManager). This can be convenient
* if you need a different local transaction strategy for another O/R mapping tool,
* for example, but still want Hibernate access to join into those transactions.
* <p>A further benefit of this option is that <i>plain Sessions opened directly
* via the SessionFactory</i>, outside of Spring's Hibernate support, will still
* participate in active Spring-managed transactions. However, consider using
* Hibernate's <code>getCurrentSession()</code> method instead (see javadoc of
* "exposeTransactionAwareSessionFactory" property).
* <p><b>WARNING:</b> When using a transaction-aware JDBC DataSource in combination
* with OpenSessionInViewFilter/Interceptor, whether participating in JTA or
* external JDBC-based transactions, it is strongly recommended to set Hibernate's
* Connection release mode to "after_transaction" or "after_statement", which
* guarantees proper Connection handling in such a scenario. In contrast to that,
* HibernateTransactionManager generally requires release mode "on_close".
* <p>Note: If you want to use Hibernate's Connection release mode "after_statement"
* with a DataSource specified on this LocalSessionFactoryBean (for example, a
* JTA-aware DataSource fetched from JNDI), switch this setting to "true".
* Otherwise, the ConnectionProvider used underneath will vote against aggressive
* release and thus silently switch to release mode "after_transaction".
* @see #setDataSource
* @see #setExposeTransactionAwareSessionFactory
* @see org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager
* @see org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
* @see org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor
* @see HibernateTransactionManager
* @see org.springframework.transaction.jta.JtaTransactionManager
*/
public void setUseTransactionAwareDataSource(boolean useTransactionAwareDataSource) {
this.useTransactionAwareDataSource = useTransactionAwareDataSource;
}
/**
* Return whether to use a transaction-aware DataSource for the SessionFactory.
*/
protected boolean isUseTransactionAwareDataSource() {
return this.useTransactionAwareDataSource;
}
/**
* Set whether to expose a transaction-aware current Session from the
* SessionFactory's <code>getCurrentSession()</code> method, returning the
* Session that's associated with the current Spring-managed transaction, if any.
* <p>Default is "true", letting data access code work with the plain
* Hibernate SessionFactory and its <code>getCurrentSession()</code> method,
* while still being able to participate in current Spring-managed transactions:
* with any transaction management strategy, either local or JTA / EJB CMT,
* and any transaction synchronization mechanism, either Spring or JTA.
* Furthermore, <code>getCurrentSession()</code> will also seamlessly work with
* a request-scoped Session managed by OpenSessionInViewFilter/Interceptor.
* <p>Turn this flag off to expose the plain Hibernate SessionFactory with
* Hibernate's default <code>getCurrentSession()</code> behavior, supporting
* plain JTA synchronization only. Alternatively, simply override the
* corresponding Hibernate property "hibernate.current_session_context_class".
* @see SpringSessionContext
* @see org.hibernate.SessionFactory#getCurrentSession()
* @see org.springframework.transaction.jta.JtaTransactionManager
* @see HibernateTransactionManager
* @see org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
* @see org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor
*/
public void setExposeTransactionAwareSessionFactory(boolean exposeTransactionAwareSessionFactory) {
this.exposeTransactionAwareSessionFactory = exposeTransactionAwareSessionFactory;
}
/**
* Return whether to expose a transaction-aware proxy for the SessionFactory.
*/
protected boolean isExposeTransactionAwareSessionFactory() {
return this.exposeTransactionAwareSessionFactory;
}
/**
* Build and expose the SessionFactory.
* @see #buildSessionFactory()
* @see #wrapSessionFactoryIfNecessary
*/
public void afterPropertiesSet() throws Exception {
SessionFactory rawSf = buildSessionFactory();
this.sessionFactory = wrapSessionFactoryIfNecessary(rawSf);
afterSessionFactoryCreation();
}
/**
* Wrap the given SessionFactory with a proxy, if demanded.
* <p>The default implementation simply returns the given SessionFactory as-is.
* Subclasses may override this to implement transaction awareness through
* a SessionFactory proxy, for example.
* @param rawSf the raw SessionFactory as built by {@link #buildSessionFactory()}
* @return the SessionFactory reference to expose
* @see #buildSessionFactory()
*/
protected SessionFactory wrapSessionFactoryIfNecessary(SessionFactory rawSf) {
return rawSf;
}
/**
* Return the exposed SessionFactory.
* Will throw an exception if not initialized yet.
* @return the SessionFactory (never <code>null</code>)
* @throws IllegalStateException if the SessionFactory has not been initialized yet
*/
protected final SessionFactory getSessionFactory() {
if (this.sessionFactory == null) {
throw new IllegalStateException("SessionFactory not initialized yet");
}
return this.sessionFactory;
}
/**
* Close the SessionFactory on bean factory shutdown.
*/
public void destroy() throws HibernateException {
logger.info("Closing Hibernate SessionFactory");
try {
beforeSessionFactoryDestruction();
}
finally {
this.sessionFactory.close();
}
}
/**
* Return the singleton SessionFactory.
*/
public SessionFactory getObject() {
return this.sessionFactory;
}
public Class<? extends SessionFactory> getObjectType() {
return (this.sessionFactory != null ? this.sessionFactory.getClass() : SessionFactory.class);
}
public boolean isSingleton() {
return true;
}
/**
* Build the underlying Hibernate SessionFactory.
* @return the raw SessionFactory (potentially to be wrapped with a
* transaction-aware proxy before it is exposed to the application)
* @throws Exception in case of initialization failure
*/
protected abstract SessionFactory buildSessionFactory() throws Exception;
/**
* Hook that allows post-processing after the SessionFactory has been
* successfully created. The SessionFactory is already available through
* <code>getSessionFactory()</code> at this point.
* <p>This implementation is empty.
* @throws Exception in case of initialization failure
* @see #getSessionFactory()
*/
protected void afterSessionFactoryCreation() throws Exception {
}
/**
* Hook that allows shutdown processing before the SessionFactory
* will be closed. The SessionFactory is still available through
* <code>getSessionFactory()</code> at this point.
* <p>This implementation is empty.
* @see #getSessionFactory()
*/
protected void beforeSessionFactoryDestruction() {
}
}

View File

@@ -0,0 +1,163 @@
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.hibernate.engine.FilterDefinition;
import org.hibernate.type.Type;
import org.hibernate.type.TypeFactory;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.ReflectionUtils;
/**
* Convenient FactoryBean for defining Hibernate FilterDefinitions.
* Exposes a corresponding Hibernate FilterDefinition object.
*
* <p>Typically defined as an inner bean within a LocalSessionFactoryBean
* definition, as the list element for the "filterDefinitions" bean property.
* For example:
*
* <pre>
* &lt;bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"&gt;
* ...
* &lt;property name="filterDefinitions"&gt;
* &lt;list&gt;
* &lt;bean class="org.springframework.orm.hibernate3.FilterDefinitionFactoryBean"&gt;
* &lt;property name="filterName" value="myFilter"/&gt;
* &lt;property name="parameterTypes"&gt;
* &lt;map&gt;
* &lt;entry key="myParam" value="string"/&gt;
* &lt;entry key="myOtherParam" value="long"/&gt;
* &lt;/map&gt;
* &lt;/property&gt;
* &lt;/bean&gt;
* &lt;/list&gt;
* &lt;/property&gt;
* ...
* &lt;/bean&gt;</pre>
*
* Alternatively, specify a bean id (or name) attribute for the inner bean,
* instead of the "filterName" property.
*
* @author Juergen Hoeller
* @since 1.2
* @see org.hibernate.engine.FilterDefinition
* @see LocalSessionFactoryBean#setFilterDefinitions
*/
public class FilterDefinitionFactoryBean implements FactoryBean<FilterDefinition>, BeanNameAware, InitializingBean {
private static Method heuristicTypeMethod;
private static Object typeResolver;
static {
// Hibernate 3.6 TypeResolver class available?
try {
Class trClass = FilterDefinitionFactoryBean.class.getClassLoader().loadClass(
"org.hibernate.type.TypeResolver");
heuristicTypeMethod = trClass.getMethod("heuristicType", String.class);
typeResolver = trClass.newInstance();
}
catch (Exception ex) {
try {
heuristicTypeMethod = TypeFactory.class.getMethod("heuristicType", String.class);
typeResolver = null;
}
catch (Exception ex2) {
throw new IllegalStateException("Cannot find Hibernate's heuristicType method", ex2);
}
}
}
private String filterName;
private Map<String, Type> parameterTypeMap = new HashMap<String, Type>();
private String defaultFilterCondition;
private FilterDefinition filterDefinition;
/**
* Set the name of the filter.
*/
public void setFilterName(String filterName) {
this.filterName = filterName;
}
/**
* Set the parameter types for the filter,
* with parameter names as keys and type names as values.
* @see org.hibernate.type.TypeFactory#heuristicType(String)
*/
public void setParameterTypes(Map<String, String> parameterTypes) {
if (parameterTypes != null) {
this.parameterTypeMap = new HashMap<String, Type>(parameterTypes.size());
for (Map.Entry<String, String> entry : parameterTypes.entrySet()) {
this.parameterTypeMap.put(entry.getKey(),
(Type) ReflectionUtils.invokeMethod(heuristicTypeMethod, typeResolver, entry.getValue()));
}
}
else {
this.parameterTypeMap = new HashMap<String, Type>();
}
}
/**
* Specify a default filter condition for the filter, if any.
*/
public void setDefaultFilterCondition(String defaultFilterCondition) {
this.defaultFilterCondition = defaultFilterCondition;
}
/**
* If no explicit filter name has been specified, the bean name of
* the FilterDefinitionFactoryBean will be used.
* @see #setFilterName
*/
public void setBeanName(String name) {
if (this.filterName == null) {
this.filterName = name;
}
}
public void afterPropertiesSet() {
this.filterDefinition =
new FilterDefinition(this.filterName, this.defaultFilterCondition, this.parameterTypeMap);
}
public FilterDefinition getObject() {
return this.filterDefinition;
}
public Class<FilterDefinition> getObjectType() {
return FilterDefinition.class;
}
public boolean isSingleton() {
return true;
}
}

View File

@@ -0,0 +1,489 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3;
import java.sql.SQLException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.Interceptor;
import org.hibernate.JDBCException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.exception.GenericJDBCException;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.Constants;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.support.SQLExceptionTranslator;
/**
* Base class for {@link HibernateTemplate} and {@link HibernateInterceptor},
* defining common properties such as SessionFactory and flushing behavior.
*
* <p>Not intended to be used directly.
* See {@link HibernateTemplate} and {@link HibernateInterceptor}.
*
* @author Juergen Hoeller
* @since 1.2
* @see HibernateTemplate
* @see HibernateInterceptor
* @see #setFlushMode
*/
public abstract class HibernateAccessor implements InitializingBean, BeanFactoryAware {
/**
* Never flush is a good strategy for read-only units of work.
* Hibernate will not track and look for changes in this case,
* avoiding any overhead of modification detection.
* <p>In case of an existing Session, FLUSH_NEVER will turn the flush mode
* to NEVER for the scope of the current operation, resetting the previous
* flush mode afterwards.
* @see #setFlushMode
*/
public static final int FLUSH_NEVER = 0;
/**
* Automatic flushing is the default mode for a Hibernate Session.
* A session will get flushed on transaction commit, and on certain find
* operations that might involve already modified instances, but not
* after each unit of work like with eager flushing.
* <p>In case of an existing Session, FLUSH_AUTO will participate in the
* existing flush mode, not modifying it for the current operation.
* This in particular means that this setting will not modify an existing
* flush mode NEVER, in contrast to FLUSH_EAGER.
* @see #setFlushMode
*/
public static final int FLUSH_AUTO = 1;
/**
* Eager flushing leads to immediate synchronization with the database,
* even if in a transaction. This causes inconsistencies to show up and throw
* a respective exception immediately, and JDBC access code that participates
* in the same transaction will see the changes as the database is already
* aware of them then. But the drawbacks are:
* <ul>
* <li>additional communication roundtrips with the database, instead of a
* single batch at transaction commit;
* <li>the fact that an actual database rollback is needed if the Hibernate
* transaction rolls back (due to already submitted SQL statements).
* </ul>
* <p>In case of an existing Session, FLUSH_EAGER will turn the flush mode
* to AUTO for the scope of the current operation and issue a flush at the
* end, resetting the previous flush mode afterwards.
* @see #setFlushMode
*/
public static final int FLUSH_EAGER = 2;
/**
* Flushing at commit only is intended for units of work where no
* intermediate flushing is desired, not even for find operations
* that might involve already modified instances.
* <p>In case of an existing Session, FLUSH_COMMIT will turn the flush mode
* to COMMIT for the scope of the current operation, resetting the previous
* flush mode afterwards. The only exception is an existing flush mode
* NEVER, which will not be modified through this setting.
* @see #setFlushMode
*/
public static final int FLUSH_COMMIT = 3;
/**
* Flushing before every query statement is rarely necessary.
* It is only available for special needs.
* <p>In case of an existing Session, FLUSH_ALWAYS will turn the flush mode
* to ALWAYS for the scope of the current operation, resetting the previous
* flush mode afterwards.
* @see #setFlushMode
*/
public static final int FLUSH_ALWAYS = 4;
/** Constants instance for HibernateAccessor */
private static final Constants constants = new Constants(HibernateAccessor.class);
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
private SessionFactory sessionFactory;
private Object entityInterceptor;
private SQLExceptionTranslator jdbcExceptionTranslator;
private SQLExceptionTranslator defaultJdbcExceptionTranslator;
private int flushMode = FLUSH_AUTO;
private String[] filterNames;
/**
* Just needed for entityInterceptorBeanName.
* @see #setEntityInterceptorBeanName
*/
private BeanFactory beanFactory;
/**
* Set the Hibernate SessionFactory that should be used to create
* Hibernate Sessions.
*/
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
/**
* Return the Hibernate SessionFactory that should be used to create
* Hibernate Sessions.
*/
public SessionFactory getSessionFactory() {
return this.sessionFactory;
}
/**
* Set the bean name of a Hibernate entity interceptor that allows to inspect
* and change property values before writing to and reading from the database.
* Will get applied to any new Session created by this transaction manager.
* <p>Requires the bean factory to be known, to be able to resolve the bean
* name to an interceptor instance on session creation. Typically used for
* prototype interceptors, i.e. a new interceptor instance per session.
* <p>Can also be used for shared interceptor instances, but it is recommended
* to set the interceptor reference directly in such a scenario.
* @param entityInterceptorBeanName the name of the entity interceptor in
* the bean factory
* @see #setBeanFactory
* @see #setEntityInterceptor
*/
public void setEntityInterceptorBeanName(String entityInterceptorBeanName) {
this.entityInterceptor = entityInterceptorBeanName;
}
/**
* Set a Hibernate entity interceptor that allows to inspect and change
* property values before writing to and reading from the database.
* Will get applied to any <b>new</b> Session created by this object.
* <p>Such an interceptor can either be set at the SessionFactory level,
* i.e. on LocalSessionFactoryBean, or at the Session level, i.e. on
* HibernateTemplate, HibernateInterceptor, and HibernateTransactionManager.
* It's preferable to set it on LocalSessionFactoryBean or HibernateTransactionManager
* to avoid repeated configuration and guarantee consistent behavior in transactions.
* @see #setEntityInterceptorBeanName
* @see LocalSessionFactoryBean#setEntityInterceptor
* @see HibernateTransactionManager#setEntityInterceptor
*/
public void setEntityInterceptor(Interceptor entityInterceptor) {
this.entityInterceptor = entityInterceptor;
}
/**
* Return the current Hibernate entity interceptor, or <code>null</code> if none.
* Resolves an entity interceptor bean name via the bean factory,
* if necessary.
* @throws IllegalStateException if bean name specified but no bean factory set
* @throws org.springframework.beans.BeansException if bean name resolution via the bean factory failed
* @see #setEntityInterceptor
* @see #setEntityInterceptorBeanName
* @see #setBeanFactory
*/
public Interceptor getEntityInterceptor() throws IllegalStateException, BeansException {
if (this.entityInterceptor instanceof String) {
if (this.beanFactory == null) {
throw new IllegalStateException("Cannot get entity interceptor via bean name if no bean factory set");
}
return (Interceptor) this.beanFactory.getBean((String) this.entityInterceptor, Interceptor.class);
}
return (Interceptor) this.entityInterceptor;
}
/**
* Set the JDBC exception translator for this instance.
* <p>Applied to any SQLException root cause of a Hibernate JDBCException,
* overriding Hibernate's default SQLException translation (which is
* based on Hibernate's Dialect for a specific target database).
* @param jdbcExceptionTranslator the exception translator
* @see java.sql.SQLException
* @see org.hibernate.JDBCException
* @see org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator
* @see org.springframework.jdbc.support.SQLStateSQLExceptionTranslator
*/
public void setJdbcExceptionTranslator(SQLExceptionTranslator jdbcExceptionTranslator) {
this.jdbcExceptionTranslator = jdbcExceptionTranslator;
}
/**
* Return the JDBC exception translator for this instance, if any.
*/
public SQLExceptionTranslator getJdbcExceptionTranslator() {
return this.jdbcExceptionTranslator;
}
/**
* Set the flush behavior by the name of the respective constant
* in this class, e.g. "FLUSH_AUTO". Default is "FLUSH_AUTO".
* @param constantName name of the constant
* @see #setFlushMode
* @see #FLUSH_AUTO
*/
public void setFlushModeName(String constantName) {
setFlushMode(constants.asNumber(constantName).intValue());
}
/**
* Set the flush behavior to one of the constants in this class.
* Default is FLUSH_AUTO.
* @see #setFlushModeName
* @see #FLUSH_AUTO
*/
public void setFlushMode(int flushMode) {
this.flushMode = flushMode;
}
/**
* Return if a flush should be forced after executing the callback code.
*/
public int getFlushMode() {
return this.flushMode;
}
/**
* Set the name of a Hibernate filter to be activated for all
* Sessions that this accessor works with.
* <p>This filter will be enabled at the beginning of each operation
* and correspondingly disabled at the end of the operation.
* This will work for newly opened Sessions as well as for existing
* Sessions (for example, within a transaction).
* @see #enableFilters(org.hibernate.Session)
* @see org.hibernate.Session#enableFilter(String)
* @see LocalSessionFactoryBean#setFilterDefinitions
*/
public void setFilterName(String filter) {
this.filterNames = new String[] {filter};
}
/**
* Set one or more names of Hibernate filters to be activated for all
* Sessions that this accessor works with.
* <p>Each of those filters will be enabled at the beginning of each
* operation and correspondingly disabled at the end of the operation.
* This will work for newly opened Sessions as well as for existing
* Sessions (for example, within a transaction).
* @see #enableFilters(org.hibernate.Session)
* @see org.hibernate.Session#enableFilter(String)
* @see LocalSessionFactoryBean#setFilterDefinitions
*/
public void setFilterNames(String[] filterNames) {
this.filterNames = filterNames;
}
/**
* Return the names of Hibernate filters to be activated, if any.
*/
public String[] getFilterNames() {
return this.filterNames;
}
/**
* The bean factory just needs to be known for resolving entity interceptor
* bean names. It does not need to be set for any other mode of operation.
* @see #setEntityInterceptorBeanName
*/
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public void afterPropertiesSet() {
if (getSessionFactory() == null) {
throw new IllegalArgumentException("Property 'sessionFactory' is required");
}
}
/**
* Apply the flush mode that's been specified for this accessor
* to the given Session.
* @param session the current Hibernate Session
* @param existingTransaction if executing within an existing transaction
* @return the previous flush mode to restore after the operation,
* or <code>null</code> if none
* @see #setFlushMode
* @see org.hibernate.Session#setFlushMode
*/
protected FlushMode applyFlushMode(Session session, boolean existingTransaction) {
if (getFlushMode() == FLUSH_NEVER) {
if (existingTransaction) {
FlushMode previousFlushMode = session.getFlushMode();
if (!previousFlushMode.lessThan(FlushMode.COMMIT)) {
session.setFlushMode(FlushMode.MANUAL);
return previousFlushMode;
}
}
else {
session.setFlushMode(FlushMode.MANUAL);
}
}
else if (getFlushMode() == FLUSH_EAGER) {
if (existingTransaction) {
FlushMode previousFlushMode = session.getFlushMode();
if (!previousFlushMode.equals(FlushMode.AUTO)) {
session.setFlushMode(FlushMode.AUTO);
return previousFlushMode;
}
}
else {
// rely on default FlushMode.AUTO
}
}
else if (getFlushMode() == FLUSH_COMMIT) {
if (existingTransaction) {
FlushMode previousFlushMode = session.getFlushMode();
if (previousFlushMode.equals(FlushMode.AUTO) || previousFlushMode.equals(FlushMode.ALWAYS)) {
session.setFlushMode(FlushMode.COMMIT);
return previousFlushMode;
}
}
else {
session.setFlushMode(FlushMode.COMMIT);
}
}
else if (getFlushMode() == FLUSH_ALWAYS) {
if (existingTransaction) {
FlushMode previousFlushMode = session.getFlushMode();
if (!previousFlushMode.equals(FlushMode.ALWAYS)) {
session.setFlushMode(FlushMode.ALWAYS);
return previousFlushMode;
}
}
else {
session.setFlushMode(FlushMode.ALWAYS);
}
}
return null;
}
/**
* Flush the given Hibernate Session if necessary.
* @param session the current Hibernate Session
* @param existingTransaction if executing within an existing transaction
* @throws HibernateException in case of Hibernate flushing errors
*/
protected void flushIfNecessary(Session session, boolean existingTransaction) throws HibernateException {
if (getFlushMode() == FLUSH_EAGER || (!existingTransaction && getFlushMode() != FLUSH_NEVER)) {
logger.debug("Eagerly flushing Hibernate session");
session.flush();
}
}
/**
* Convert the given HibernateException to an appropriate exception
* from the <code>org.springframework.dao</code> hierarchy.
* <p>Will automatically apply a specified SQLExceptionTranslator to a
* Hibernate JDBCException, else rely on Hibernate's default translation.
* @param ex HibernateException that occured
* @return a corresponding DataAccessException
* @see SessionFactoryUtils#convertHibernateAccessException
* @see #setJdbcExceptionTranslator
*/
public DataAccessException convertHibernateAccessException(HibernateException ex) {
if (getJdbcExceptionTranslator() != null && ex instanceof JDBCException) {
return convertJdbcAccessException((JDBCException) ex, getJdbcExceptionTranslator());
}
else if (GenericJDBCException.class.equals(ex.getClass())) {
return convertJdbcAccessException((GenericJDBCException) ex, getDefaultJdbcExceptionTranslator());
}
return SessionFactoryUtils.convertHibernateAccessException(ex);
}
/**
* Convert the given Hibernate JDBCException to an appropriate exception
* from the <code>org.springframework.dao</code> hierarchy, using the
* given SQLExceptionTranslator.
* @param ex Hibernate JDBCException that occured
* @param translator the SQLExceptionTranslator to use
* @return a corresponding DataAccessException
*/
protected DataAccessException convertJdbcAccessException(JDBCException ex, SQLExceptionTranslator translator) {
return translator.translate("Hibernate operation: " + ex.getMessage(), ex.getSQL(), ex.getSQLException());
}
/**
* Convert the given SQLException to an appropriate exception from the
* <code>org.springframework.dao</code> hierarchy. Can be overridden in subclasses.
* <p>Note that a direct SQLException can just occur when callback code
* performs direct JDBC access via <code>Session.connection()</code>.
* @param ex the SQLException
* @return the corresponding DataAccessException instance
* @see #setJdbcExceptionTranslator
* @see org.hibernate.Session#connection()
*/
protected DataAccessException convertJdbcAccessException(SQLException ex) {
SQLExceptionTranslator translator = getJdbcExceptionTranslator();
if (translator == null) {
translator = getDefaultJdbcExceptionTranslator();
}
return translator.translate("Hibernate-related JDBC operation", null, ex);
}
/**
* Obtain a default SQLExceptionTranslator, lazily creating it if necessary.
* <p>Creates a default
* {@link org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator}
* for the SessionFactory's underlying DataSource.
*/
protected synchronized SQLExceptionTranslator getDefaultJdbcExceptionTranslator() {
if (this.defaultJdbcExceptionTranslator == null) {
this.defaultJdbcExceptionTranslator = SessionFactoryUtils.newJdbcExceptionTranslator(getSessionFactory());
}
return this.defaultJdbcExceptionTranslator;
}
/**
* Enable the specified filters on the given Session.
* @param session the current Hibernate Session
* @see #setFilterNames
* @see org.hibernate.Session#enableFilter(String)
*/
protected void enableFilters(Session session) {
String[] filterNames = getFilterNames();
if (filterNames != null) {
for (int i = 0; i < filterNames.length; i++) {
session.enableFilter(filterNames[i]);
}
}
}
/**
* Disable the specified filters on the given Session.
* @param session the current Hibernate Session
* @see #setFilterNames
* @see org.hibernate.Session#disableFilter(String)
*/
protected void disableFilters(Session session) {
String[] filterNames = getFilterNames();
if (filterNames != null) {
for (int i = 0; i < filterNames.length; i++) {
session.disableFilter(filterNames[i]);
}
}
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3;
import java.sql.SQLException;
import org.hibernate.HibernateException;
import org.hibernate.Session;
/**
* Callback interface for Hibernate code. To be used with {@link HibernateTemplate}'s
* execution methods, often as anonymous classes within a method implementation.
* A typical implementation will call <code>Session.load/find/update</code> to perform
* some operations on persistent objects. It can also perform direct JDBC operations
* via Hibernate's <code>Session.connection()</code>, operating on a JDBC Connection.
*
* <p>Note that Hibernate works on unmodified plain Java objects, performing dirty
* detection via copies made at load time. Returned objects can thus be used outside
* of an active Hibernate Session without any hassle, e.g. for display in a web GUI.
* Reassociating such instances with a new Session, e.g. for updates when coming
* back from the GUI, is straightforward, as the instance has kept its identity.
* You should care to reassociate them as early as possible though, to avoid having
* already loaded a version from the database in the same Session.
*
* @author Juergen Hoeller
* @since 1.2
* @see HibernateTemplate
* @see HibernateTransactionManager
*/
public interface HibernateCallback<T> {
/**
* Gets called by <code>HibernateTemplate.execute</code> with an active
* Hibernate <code>Session</code>. Does not need to care about activating
* or closing the <code>Session</code>, or handling transactions.
*
* <p>If called without a thread-bound Hibernate transaction (initiated
* by HibernateTransactionManager), the code will simply get executed on the
* underlying JDBC connection with its transactional semantics. If Hibernate
* is configured to use a JTA-aware DataSource, the JDBC connection and thus
* the callback code will be transactional if a JTA transaction is active.
*
* <p>Allows for returning a result object created within the callback,
* i.e. a domain object or a collection of domain objects.
* A thrown custom RuntimeException is treated as an application exception:
* It gets propagated to the caller of the template.
*
* @param session active Hibernate session
* @return a result object, or <code>null</code> if none
* @throws HibernateException if thrown by the Hibernate API
* @throws SQLException if thrown by Hibernate-exposed JDBC API
* @see HibernateTemplate#execute
* @see HibernateTemplate#executeFind
*/
T doInHibernate(Session session) throws HibernateException, SQLException;
}

View File

@@ -0,0 +1,92 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3;
import org.hibernate.HibernateException;
import org.hibernate.JDBCException;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.jdbc.support.SQLExceptionTranslator;
/**
* {@link PersistenceExceptionTranslator} capable of translating {@link HibernateException}
* instances to Spring's {@link DataAccessException} hierarchy.
*
* <p>Extended by {@link LocalSessionFactoryBean}, so there is no need to declare this
* translator in addition to a {@code LocalSessionFactoryBean}.
*
* <p>When configuring the container with {@code @Configuration} classes, a {@code @Bean}
* of this type must be registered manually.
*
* @author Juergen Hoeller
* @author Chris Beams
* @since 3.1
* @see org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor
* @see SessionFactoryUtils#convertHibernateAccessException(HibernateException)
* @see SQLExceptionTranslator
*/
public class HibernateExceptionTranslator implements PersistenceExceptionTranslator {
private SQLExceptionTranslator jdbcExceptionTranslator;
/**
* Set the JDBC exception translator for the SessionFactory,
* exposed via the PersistenceExceptionTranslator interface.
* <p>Applied to any SQLException root cause of a Hibernate JDBCException,
* overriding Hibernate's default SQLException translation (which is
* based on Hibernate's Dialect for a specific target database).
* @param jdbcExceptionTranslator the exception translator
* @see java.sql.SQLException
* @see org.hibernate.JDBCException
* @see org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator
* @see org.springframework.jdbc.support.SQLStateSQLExceptionTranslator
* @see org.springframework.dao.support.PersistenceExceptionTranslator
*/
public void setJdbcExceptionTranslator(SQLExceptionTranslator jdbcExceptionTranslator) {
this.jdbcExceptionTranslator = jdbcExceptionTranslator;
}
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
if (ex instanceof HibernateException) {
return convertHibernateAccessException((HibernateException) ex);
}
return null;
}
/**
* Convert the given HibernateException to an appropriate exception from the
* {@code org.springframework.dao} hierarchy.
* <p>Will automatically apply a specified SQLExceptionTranslator to a
* Hibernate JDBCException, else rely on Hibernate's default translation.
* @param ex HibernateException that occured
* @return a corresponding DataAccessException
* @see SessionFactoryUtils#convertHibernateAccessException
* @see #setJdbcExceptionTranslator
*/
protected DataAccessException convertHibernateAccessException(HibernateException ex) {
if (this.jdbcExceptionTranslator != null && ex instanceof JDBCException) {
JDBCException jdbcEx = (JDBCException) ex;
return this.jdbcExceptionTranslator.translate(
"Hibernate operation: " + jdbcEx.getMessage(), jdbcEx.getSQL(), jdbcEx.getSQLException());
}
return SessionFactoryUtils.convertHibernateAccessException(ex);
}
}

View File

@@ -0,0 +1,149 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
* This interceptor binds a new Hibernate Session to the thread before a method
* call, closing and removing it afterwards in case of any method outcome.
* If there already is a pre-bound Session (e.g. from HibernateTransactionManager,
* or from a surrounding Hibernate-intercepted method), the interceptor simply
* participates in it.
*
* <p>Application code must retrieve a Hibernate Session via the
* <code>SessionFactoryUtils.getSession</code> method or - preferably -
* Hibernate's own <code>SessionFactory.getCurrentSession()</code> method, to be
* able to detect a thread-bound Session. Typically, the code will look like as follows:
*
* <pre>
* public void doSomeDataAccessAction() {
* Session session = this.sessionFactory.getCurrentSession();
* ...
* // No need to close the Session or translate exceptions!
* }</pre>
*
* Note that this interceptor automatically translates HibernateExceptions,
* via delegating to the <code>SessionFactoryUtils.convertHibernateAccessException</code>
* method that converts them to exceptions that are compatible with the
* <code>org.springframework.dao</code> exception hierarchy (like HibernateTemplate does).
* This can be turned off if the raw exceptions are preferred.
*
* <p>This class can be considered a declarative alternative to HibernateTemplate's
* callback approach. The advantages are:
* <ul>
* <li>no anonymous classes necessary for callback implementations;
* <li>the possibility to throw any application exceptions from within data access code.
* </ul>
*
* <p>The drawback is the dependency on interceptor configuration. However, note
* that this interceptor is usually <i>not</i> necessary in scenarios where the
* data access code always executes within transactions. A transaction will always
* have a thread-bound Session in the first place, so adding this interceptor to the
* configuration just adds value when fine-tuning Session settings like the flush mode
* - or when relying on exception translation.
*
* @author Juergen Hoeller
* @since 1.2
* @see org.hibernate.SessionFactory#getCurrentSession()
* @see HibernateTransactionManager
* @see HibernateTemplate
*/
public class HibernateInterceptor extends HibernateAccessor implements MethodInterceptor {
private boolean exceptionConversionEnabled = true;
/**
* Set whether to convert any HibernateException raised to a Spring DataAccessException,
* compatible with the <code>org.springframework.dao</code> exception hierarchy.
* <p>Default is "true". Turn this flag off to let the caller receive raw exceptions
* as-is, without any wrapping.
* @see org.springframework.dao.DataAccessException
*/
public void setExceptionConversionEnabled(boolean exceptionConversionEnabled) {
this.exceptionConversionEnabled = exceptionConversionEnabled;
}
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
Session session = getSession();
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory());
boolean existingTransaction = (sessionHolder != null && sessionHolder.containsSession(session));
if (existingTransaction) {
logger.debug("Found thread-bound Session for HibernateInterceptor");
}
else {
if (sessionHolder != null) {
sessionHolder.addSession(session);
}
else {
TransactionSynchronizationManager.bindResource(getSessionFactory(), new SessionHolder(session));
}
}
FlushMode previousFlushMode = null;
try {
previousFlushMode = applyFlushMode(session, existingTransaction);
enableFilters(session);
Object retVal = methodInvocation.proceed();
flushIfNecessary(session, existingTransaction);
return retVal;
}
catch (HibernateException ex) {
if (this.exceptionConversionEnabled) {
throw convertHibernateAccessException(ex);
}
else {
throw ex;
}
}
finally {
if (existingTransaction) {
logger.debug("Not closing pre-bound Hibernate Session after HibernateInterceptor");
disableFilters(session);
if (previousFlushMode != null) {
session.setFlushMode(previousFlushMode);
}
}
else {
SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, getSessionFactory());
if (sessionHolder == null || sessionHolder.doesNotHoldNonDefaultSession()) {
TransactionSynchronizationManager.unbindResource(getSessionFactory());
}
}
}
}
/**
* Return a Session for use by this interceptor.
* @see SessionFactoryUtils#getSession
*/
protected Session getSession() {
return SessionFactoryUtils.getSession(
getSessionFactory(), getEntityInterceptor(), getJdbcExceptionTranslator());
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3;
import java.sql.SQLException;
import org.hibernate.JDBCException;
import org.springframework.dao.UncategorizedDataAccessException;
/**
* Hibernate-specific subclass of UncategorizedDataAccessException,
* for JDBC exceptions that Hibernate wrapped.
*
* @author Juergen Hoeller
* @since 1.2
* @see SessionFactoryUtils#convertHibernateAccessException
*/
public class HibernateJdbcException extends UncategorizedDataAccessException {
public HibernateJdbcException(JDBCException ex) {
super("JDBC exception on Hibernate data access: SQLException for SQL [" + ex.getSQL() + "]; SQL state [" +
ex.getSQLState() + "]; error code [" + ex.getErrorCode() + "]; " + ex.getMessage(), ex);
}
/**
* Return the underlying SQLException.
*/
public SQLException getSQLException() {
return ((JDBCException) getCause()).getSQLException();
}
/**
* Return the SQL that led to the problem.
*/
public String getSql() {
return ((JDBCException) getCause()).getSQL();
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3;
import org.hibernate.UnresolvableObjectException;
import org.hibernate.WrongClassException;
import org.springframework.orm.ObjectRetrievalFailureException;
/**
* Hibernate-specific subclass of ObjectRetrievalFailureException.
* Converts Hibernate's UnresolvableObjectException and WrongClassException.
*
* @author Juergen Hoeller
* @since 1.2
* @see SessionFactoryUtils#convertHibernateAccessException
*/
public class HibernateObjectRetrievalFailureException extends ObjectRetrievalFailureException {
public HibernateObjectRetrievalFailureException(UnresolvableObjectException ex) {
super(ex.getEntityName(), ex.getIdentifier(), ex.getMessage(), ex);
}
public HibernateObjectRetrievalFailureException(WrongClassException ex) {
super(ex.getEntityName(), ex.getIdentifier(), ex.getMessage(), ex);
}
}

View File

@@ -0,0 +1,938 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3;
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import org.hibernate.Filter;
import org.hibernate.LockMode;
import org.hibernate.ReplicationMode;
import org.hibernate.criterion.DetachedCriteria;
import org.springframework.dao.DataAccessException;
/**
* Interface that specifies a basic set of Hibernate operations,
* implemented by {@link HibernateTemplate}. Not often used, but a useful
* option to enhance testability, as it can easily be mocked or stubbed.
*
* <p>Defines <code>HibernateTemplate</code>'s data access methods that
* mirror various {@link org.hibernate.Session} methods. Users are
* strongly encouraged to read the Hibernate <code>Session</code> javadocs
* for details on the semantics of those methods.
*
* <p>Note that operations that return an {@link java.util.Iterator} (i.e.
* <code>iterate(..)</code>) are supposed to be used within Spring-driven
* or JTA-driven transactions (with {@link HibernateTransactionManager},
* {@link org.springframework.transaction.jta.JtaTransactionManager},
* or EJB CMT). Else, the <code>Iterator</code> won't be able to read
* results from its {@link java.sql.ResultSet} anymore, as the underlying
* Hibernate <code>Session</code> will already have been closed.
*
* <p>Note that lazy loading will just work with an open Hibernate
* <code>Session</code>, either within a transaction or within
* {@link org.springframework.orm.hibernate3.support.OpenSessionInViewFilter}/
* {@link org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor}.
* Furthermore, some operations just make sense within transactions,
* for example: <code>contains</code>, <code>evict</code>, <code>lock</code>,
* <code>flush</code>, <code>clear</code>.
*
* @author Juergen Hoeller
* @since 1.2
* @see HibernateTemplate
* @see org.hibernate.Session
* @see HibernateTransactionManager
* @see org.springframework.transaction.jta.JtaTransactionManager
* @see org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
* @see org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor
*/
public interface HibernateOperations {
/**
* Execute the action specified by the given action object within a
* {@link org.hibernate.Session}.
* <p>Application exceptions thrown by the action object get propagated
* to the caller (can only be unchecked). Hibernate exceptions are
* transformed into appropriate DAO ones. Allows for returning a result
* object, that is a domain object or a collection of domain objects.
* <p>Note: Callback code is not supposed to handle transactions itself!
* Use an appropriate transaction manager like
* {@link HibernateTransactionManager}. Generally, callback code must not
* touch any <code>Session</code> lifecycle methods, like close,
* disconnect, or reconnect, to let the template do its work.
* @param action callback object that specifies the Hibernate action
* @return a result object returned by the action, or <code>null</code>
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see HibernateTransactionManager
* @see org.springframework.dao
* @see org.springframework.transaction
* @see org.hibernate.Session
*/
<T> T execute(HibernateCallback<T> action) throws DataAccessException;
/**
* Execute the specified action assuming that the result object is a
* {@link List}.
* <p>This is a convenience method for executing Hibernate find calls or
* queries within an action.
* @param action calback object that specifies the Hibernate action
* @return a List result returned by the action, or <code>null</code>
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
*/
List executeFind(HibernateCallback<?> action) throws DataAccessException;
//-------------------------------------------------------------------------
// Convenience methods for loading individual objects
//-------------------------------------------------------------------------
/**
* Return the persistent instance of the given entity class
* with the given identifier, or <code>null</code> if not found.
* <p>This method is a thin wrapper around
* {@link org.hibernate.Session#get(Class, java.io.Serializable)} for convenience.
* For an explanation of the exact semantics of this method, please do refer to
* the Hibernate API documentation in the first instance.
* @param entityClass a persistent class
* @param id the identifier of the persistent instance
* @return the persistent instance, or <code>null</code> if not found
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#get(Class, java.io.Serializable)
*/
<T> T get(Class<T> entityClass, Serializable id) throws DataAccessException;
/**
* Return the persistent instance of the given entity class
* with the given identifier, or <code>null</code> if not found.
* <p>Obtains the specified lock mode if the instance exists.
* <p>This method is a thin wrapper around
* {@link org.hibernate.Session#get(Class, java.io.Serializable, LockMode)} for convenience.
* For an explanation of the exact semantics of this method, please do refer to
* the Hibernate API documentation in the first instance.
* @param entityClass a persistent class
* @param id the identifier of the persistent instance
* @param lockMode the lock mode to obtain
* @return the persistent instance, or <code>null</code> if not found
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#get(Class, java.io.Serializable, org.hibernate.LockMode)
*/
<T> T get(Class<T> entityClass, Serializable id, LockMode lockMode)
throws DataAccessException;
/**
* Return the persistent instance of the given entity class
* with the given identifier, or <code>null</code> if not found.
* <p>This method is a thin wrapper around
* {@link org.hibernate.Session#get(String, java.io.Serializable)} for convenience.
* For an explanation of the exact semantics of this method, please do refer to
* the Hibernate API documentation in the first instance.
* @param entityName the name of the persistent entity
* @param id the identifier of the persistent instance
* @return the persistent instance, or <code>null</code> if not found
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#get(Class, java.io.Serializable)
*/
Object get(String entityName, Serializable id) throws DataAccessException;
/**
* Return the persistent instance of the given entity class
* with the given identifier, or <code>null</code> if not found.
* Obtains the specified lock mode if the instance exists.
* <p>This method is a thin wrapper around
* {@link org.hibernate.Session#get(String, java.io.Serializable, LockMode)} for convenience.
* For an explanation of the exact semantics of this method, please do refer to
* the Hibernate API documentation in the first instance.
* @param entityName the name of the persistent entity
* @param id the identifier of the persistent instance
* @param lockMode the lock mode to obtain
* @return the persistent instance, or <code>null</code> if not found
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#get(Class, java.io.Serializable, org.hibernate.LockMode)
*/
Object get(String entityName, Serializable id, LockMode lockMode)
throws DataAccessException;
/**
* Return the persistent instance of the given entity class
* with the given identifier, throwing an exception if not found.
* <p>This method is a thin wrapper around
* {@link org.hibernate.Session#load(Class, java.io.Serializable)} for convenience.
* For an explanation of the exact semantics of this method, please do refer to
* the Hibernate API documentation in the first instance.
* @param entityClass a persistent class
* @param id the identifier of the persistent instance
* @return the persistent instance
* @throws org.springframework.orm.ObjectRetrievalFailureException if not found
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#load(Class, java.io.Serializable)
*/
<T> T load(Class<T> entityClass, Serializable id) throws DataAccessException;
/**
* Return the persistent instance of the given entity class
* with the given identifier, throwing an exception if not found.
* Obtains the specified lock mode if the instance exists.
* <p>This method is a thin wrapper around
* {@link org.hibernate.Session#load(Class, java.io.Serializable, LockMode)} for convenience.
* For an explanation of the exact semantics of this method, please do refer to
* the Hibernate API documentation in the first instance.
* @param entityClass a persistent class
* @param id the identifier of the persistent instance
* @param lockMode the lock mode to obtain
* @return the persistent instance
* @throws org.springframework.orm.ObjectRetrievalFailureException if not found
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#load(Class, java.io.Serializable)
*/
<T> T load(Class<T> entityClass, Serializable id, LockMode lockMode)
throws DataAccessException;
/**
* Return the persistent instance of the given entity class
* with the given identifier, throwing an exception if not found.
* <p>This method is a thin wrapper around
* {@link org.hibernate.Session#load(String, java.io.Serializable)} for convenience.
* For an explanation of the exact semantics of this method, please do refer to
* the Hibernate API documentation in the first instance.
* @param entityName the name of the persistent entity
* @param id the identifier of the persistent instance
* @return the persistent instance
* @throws org.springframework.orm.ObjectRetrievalFailureException if not found
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#load(Class, java.io.Serializable)
*/
Object load(String entityName, Serializable id) throws DataAccessException;
/**
* Return the persistent instance of the given entity class
* with the given identifier, throwing an exception if not found.
* <p>Obtains the specified lock mode if the instance exists.
* <p>This method is a thin wrapper around
* {@link org.hibernate.Session#load(String, java.io.Serializable, LockMode)} for convenience.
* For an explanation of the exact semantics of this method, please do refer to
* the Hibernate API documentation in the first instance.
* @param entityName the name of the persistent entity
* @param id the identifier of the persistent instance
* @param lockMode the lock mode to obtain
* @return the persistent instance
* @throws org.springframework.orm.ObjectRetrievalFailureException if not found
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#load(Class, java.io.Serializable)
*/
Object load(String entityName, Serializable id, LockMode lockMode)
throws DataAccessException;
/**
* Return all persistent instances of the given entity class.
* Note: Use queries or criteria for retrieving a specific subset.
* @param entityClass a persistent class
* @return a {@link List} containing 0 or more persistent instances
* @throws org.springframework.dao.DataAccessException if there is a Hibernate error
* @see org.hibernate.Session#createCriteria
*/
<T>List<T> loadAll(Class<T> entityClass) throws DataAccessException;
/**
* Load the persistent instance with the given identifier
* into the given object, throwing an exception if not found.
* <p>This method is a thin wrapper around
* {@link org.hibernate.Session#load(Object, java.io.Serializable)} for convenience.
* For an explanation of the exact semantics of this method, please do refer to
* the Hibernate API documentation in the first instance.
* @param entity the object (of the target class) to load into
* @param id the identifier of the persistent instance
* @throws org.springframework.orm.ObjectRetrievalFailureException if not found
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#load(Object, java.io.Serializable)
*/
void load(Object entity, Serializable id) throws DataAccessException;
/**
* Re-read the state of the given persistent instance.
* @param entity the persistent instance to re-read
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#refresh(Object)
*/
void refresh(Object entity) throws DataAccessException;
/**
* Re-read the state of the given persistent instance.
* Obtains the specified lock mode for the instance.
* @param entity the persistent instance to re-read
* @param lockMode the lock mode to obtain
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#refresh(Object, org.hibernate.LockMode)
*/
void refresh(Object entity, LockMode lockMode) throws DataAccessException;
/**
* Check whether the given object is in the Session cache.
* @param entity the persistence instance to check
* @return whether the given object is in the Session cache
* @throws org.springframework.dao.DataAccessException if there is a Hibernate error
* @see org.hibernate.Session#contains
*/
boolean contains(Object entity) throws DataAccessException;
/**
* Remove the given object from the {@link org.hibernate.Session} cache.
* @param entity the persistent instance to evict
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#evict
*/
void evict(Object entity) throws DataAccessException;
/**
* Force initialization of a Hibernate proxy or persistent collection.
* @param proxy a proxy for a persistent object or a persistent collection
* @throws DataAccessException if we can't initialize the proxy, for example
* because it is not associated with an active Session
* @see org.hibernate.Hibernate#initialize
*/
void initialize(Object proxy) throws DataAccessException;
/**
* Return an enabled Hibernate {@link Filter} for the given filter name.
* The returned <code>Filter</code> instance can be used to set filter parameters.
* @param filterName the name of the filter
* @return the enabled Hibernate <code>Filter</code> (either already
* enabled or enabled on the fly by this operation)
* @throws IllegalStateException if we are not running within a
* transactional Session (in which case this operation does not make sense)
*/
Filter enableFilter(String filterName) throws IllegalStateException;
//-------------------------------------------------------------------------
// Convenience methods for storing individual objects
//-------------------------------------------------------------------------
/**
* Obtain the specified lock level upon the given object, implicitly
* checking whether the corresponding database entry still exists.
* @param entity the persistent instance to lock
* @param lockMode the lock mode to obtain
* @throws org.springframework.orm.ObjectOptimisticLockingFailureException if not found
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#lock(Object, org.hibernate.LockMode)
*/
void lock(Object entity, LockMode lockMode) throws DataAccessException;
/**
* Obtain the specified lock level upon the given object, implicitly
* checking whether the corresponding database entry still exists.
* @param entityName the name of the persistent entity
* @param entity the persistent instance to lock
* @param lockMode the lock mode to obtain
* @throws org.springframework.orm.ObjectOptimisticLockingFailureException if not found
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#lock(String, Object, org.hibernate.LockMode)
*/
void lock(String entityName, Object entity, LockMode lockMode) throws DataAccessException;
/**
* Persist the given transient instance.
* @param entity the transient instance to persist
* @return the generated identifier
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#save(Object)
*/
Serializable save(Object entity) throws DataAccessException;
/**
* Persist the given transient instance.
* @param entityName the name of the persistent entity
* @param entity the transient instance to persist
* @return the generated identifier
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#save(String, Object)
*/
Serializable save(String entityName, Object entity) throws DataAccessException;
/**
* Update the given persistent instance,
* associating it with the current Hibernate {@link org.hibernate.Session}.
* @param entity the persistent instance to update
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#update(Object)
*/
void update(Object entity) throws DataAccessException;
/**
* Update the given persistent instance,
* associating it with the current Hibernate {@link org.hibernate.Session}.
* <p>Obtains the specified lock mode if the instance exists, implicitly
* checking whether the corresponding database entry still exists.
* @param entity the persistent instance to update
* @param lockMode the lock mode to obtain
* @throws org.springframework.orm.ObjectOptimisticLockingFailureException if not found
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#update(Object)
*/
void update(Object entity, LockMode lockMode) throws DataAccessException;
/**
* Update the given persistent instance,
* associating it with the current Hibernate {@link org.hibernate.Session}.
* @param entityName the name of the persistent entity
* @param entity the persistent instance to update
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#update(String, Object)
*/
void update(String entityName, Object entity) throws DataAccessException;
/**
* Update the given persistent instance,
* associating it with the current Hibernate {@link org.hibernate.Session}.
* <p>Obtains the specified lock mode if the instance exists, implicitly
* checking whether the corresponding database entry still exists.
* @param entityName the name of the persistent entity
* @param entity the persistent instance to update
* @param lockMode the lock mode to obtain
* @throws org.springframework.orm.ObjectOptimisticLockingFailureException if not found
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#update(String, Object)
*/
void update(String entityName, Object entity, LockMode lockMode) throws DataAccessException;
/**
* Save or update the given persistent instance,
* according to its id (matching the configured "unsaved-value"?).
* Associates the instance with the current Hibernate {@link org.hibernate.Session}.
* @param entity the persistent instance to save or update
* (to be associated with the Hibernate <code>Session</code>)
* @throws DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#saveOrUpdate(Object)
*/
void saveOrUpdate(Object entity) throws DataAccessException;
/**
* Save or update the given persistent instance,
* according to its id (matching the configured "unsaved-value"?).
* Associates the instance with the current Hibernate <code>Session</code>.
* @param entityName the name of the persistent entity
* @param entity the persistent instance to save or update
* (to be associated with the Hibernate <code>Session</code>)
* @throws DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#saveOrUpdate(String, Object)
*/
void saveOrUpdate(String entityName, Object entity) throws DataAccessException;
/**
* Save or update all given persistent instances,
* according to its id (matching the configured "unsaved-value"?).
* Associates the instances with the current Hibernate <code>Session</code>.
* @param entities the persistent instances to save or update
* (to be associated with the Hibernate <code>Session</code>)
* @throws DataAccessException in case of Hibernate errors
* @deprecated as of Spring 2.5, in favor of individual
* <code>saveOrUpdate</code> or <code>merge</code> usage
*/
@Deprecated
void saveOrUpdateAll(Collection entities) throws DataAccessException;
/**
* Persist the state of the given detached instance according to the
* given replication mode, reusing the current identifier value.
* @param entity the persistent object to replicate
* @param replicationMode the Hibernate ReplicationMode
* @throws DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#replicate(Object, org.hibernate.ReplicationMode)
*/
void replicate(Object entity, ReplicationMode replicationMode) throws DataAccessException;
/**
* Persist the state of the given detached instance according to the
* given replication mode, reusing the current identifier value.
* @param entityName the name of the persistent entity
* @param entity the persistent object to replicate
* @param replicationMode the Hibernate ReplicationMode
* @throws DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#replicate(String, Object, org.hibernate.ReplicationMode)
*/
void replicate(String entityName, Object entity, ReplicationMode replicationMode) throws DataAccessException;
/**
* Persist the given transient instance. Follows JSR-220 semantics.
* <p>Similar to <code>save</code>, associating the given object
* with the current Hibernate {@link org.hibernate.Session}.
* @param entity the persistent instance to persist
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#persist(Object)
* @see #save
*/
void persist(Object entity) throws DataAccessException;
/**
* Persist the given transient instance. Follows JSR-220 semantics.
* <p>Similar to <code>save</code>, associating the given object
* with the current Hibernate {@link org.hibernate.Session}.
* @param entityName the name of the persistent entity
* @param entity the persistent instance to persist
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#persist(String, Object)
* @see #save
*/
void persist(String entityName, Object entity) throws DataAccessException;
/**
* Copy the state of the given object onto the persistent object
* with the same identifier. Follows JSR-220 semantics.
* <p>Similar to <code>saveOrUpdate</code>, but never associates the given
* object with the current Hibernate Session. In case of a new entity,
* the state will be copied over as well.
* <p>Note that <code>merge</code> will <i>not</i> update the identifiers
* in the passed-in object graph (in contrast to TopLink)! Consider
* registering Spring's <code>IdTransferringMergeEventListener</code> if
* you would like to have newly assigned ids transferred to the original
* object graph too.
* @param entity the object to merge with the corresponding persistence instance
* @return the updated, registered persistent instance
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#merge(Object)
* @see #saveOrUpdate
* @see org.springframework.orm.hibernate3.support.IdTransferringMergeEventListener
*/
<T> T merge(T entity) throws DataAccessException;
/**
* Copy the state of the given object onto the persistent object
* with the same identifier. Follows JSR-220 semantics.
* <p>Similar to <code>saveOrUpdate</code>, but never associates the given
* object with the current Hibernate {@link org.hibernate.Session}. In
* the case of a new entity, the state will be copied over as well.
* <p>Note that <code>merge</code> will <i>not</i> update the identifiers
* in the passed-in object graph (in contrast to TopLink)! Consider
* registering Spring's <code>IdTransferringMergeEventListener</code>
* if you would like to have newly assigned ids transferred to the
* original object graph too.
* @param entityName the name of the persistent entity
* @param entity the object to merge with the corresponding persistence instance
* @return the updated, registered persistent instance
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#merge(String, Object)
* @see #saveOrUpdate
*/
<T> T merge(String entityName, T entity) throws DataAccessException;
/**
* Delete the given persistent instance.
* @param entity the persistent instance to delete
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#delete(Object)
*/
void delete(Object entity) throws DataAccessException;
/**
* Delete the given persistent instance.
* <p>Obtains the specified lock mode if the instance exists, implicitly
* checking whether the corresponding database entry still exists.
* @param entity the persistent instance to delete
* @param lockMode the lock mode to obtain
* @throws org.springframework.orm.ObjectOptimisticLockingFailureException if not found
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#delete(Object)
*/
void delete(Object entity, LockMode lockMode) throws DataAccessException;
/**
* Delete the given persistent instance.
* @param entityName the name of the persistent entity
* @param entity the persistent instance to delete
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#delete(Object)
*/
void delete(String entityName, Object entity) throws DataAccessException;
/**
* Delete the given persistent instance.
* <p>Obtains the specified lock mode if the instance exists, implicitly
* checking whether the corresponding database entry still exists.
* @param entityName the name of the persistent entity
* @param entity the persistent instance to delete
* @param lockMode the lock mode to obtain
* @throws org.springframework.orm.ObjectOptimisticLockingFailureException if not found
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#delete(Object)
*/
void delete(String entityName, Object entity, LockMode lockMode) throws DataAccessException;
/**
* Delete all given persistent instances.
* <p>This can be combined with any of the find methods to delete by query
* in two lines of code.
* @param entities the persistent instances to delete
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#delete(Object)
*/
void deleteAll(Collection entities) throws DataAccessException;
/**
* Flush all pending saves, updates and deletes to the database.
* <p>Only invoke this for selective eager flushing, for example when
* JDBC code needs to see certain changes within the same transaction.
* Else, it is preferable to rely on auto-flushing at transaction
* completion.
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#flush
*/
void flush() throws DataAccessException;
/**
* Remove all objects from the {@link org.hibernate.Session} cache, and
* cancel all pending saves, updates and deletes.
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#clear
*/
void clear() throws DataAccessException;
//-------------------------------------------------------------------------
// Convenience finder methods for HQL strings
//-------------------------------------------------------------------------
/**
* Execute an HQL query.
* @param queryString a query expressed in Hibernate's query language
* @return a {@link List} containing the results of the query execution
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#createQuery
*/
List find(String queryString) throws DataAccessException;
/**
* Execute an HQL query, binding one value to a "?" parameter in the
* query string.
* @param queryString a query expressed in Hibernate's query language
* @param value the value of the parameter
* @return a {@link List} containing the results of the query execution
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#createQuery
*/
List find(String queryString, Object value) throws DataAccessException;
/**
* Execute an HQL query, binding a number of values to "?" parameters
* in the query string.
* @param queryString a query expressed in Hibernate's query language
* @param values the values of the parameters
* @return a {@link List} containing the results of the query execution
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#createQuery
*/
List find(String queryString, Object... values) throws DataAccessException;
/**
* Execute an HQL query, binding one value to a ":" named parameter
* in the query string.
* @param queryString a query expressed in Hibernate's query language
* @param paramName the name of the parameter
* @param value the value of the parameter
* @return a {@link List} containing the results of the query execution
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#getNamedQuery(String)
*/
List findByNamedParam(String queryString, String paramName, Object value)
throws DataAccessException;
/**
* Execute an HQL query, binding a number of values to ":" named
* parameters in the query string.
* @param queryString a query expressed in Hibernate's query language
* @param paramNames the names of the parameters
* @param values the values of the parameters
* @return a {@link List} containing the results of the query execution
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#getNamedQuery(String)
*/
List findByNamedParam(String queryString, String[] paramNames, Object[] values)
throws DataAccessException;
/**
* Execute an HQL query, binding the properties of the given bean to
* <i>named</i> parameters in the query string.
* @param queryString a query expressed in Hibernate's query language
* @param valueBean the values of the parameters
* @return a {@link List} containing the results of the query execution
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Query#setProperties
* @see org.hibernate.Session#createQuery
*/
List findByValueBean(String queryString, Object valueBean) throws DataAccessException;
//-------------------------------------------------------------------------
// Convenience finder methods for named queries
//-------------------------------------------------------------------------
/**
* Execute a named query.
* <p>A named query is defined in a Hibernate mapping file.
* @param queryName the name of a Hibernate query in a mapping file
* @return a {@link List} containing the results of the query execution
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#getNamedQuery(String)
*/
List findByNamedQuery(String queryName) throws DataAccessException;
/**
* Execute a named query, binding one value to a "?" parameter in
* the query string.
* <p>A named query is defined in a Hibernate mapping file.
* @param queryName the name of a Hibernate query in a mapping file
* @param value the value of the parameter
* @return a {@link List} containing the results of the query execution
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#getNamedQuery(String)
*/
List findByNamedQuery(String queryName, Object value) throws DataAccessException;
/**
* Execute a named query binding a number of values to "?" parameters
* in the query string.
* <p>A named query is defined in a Hibernate mapping file.
* @param queryName the name of a Hibernate query in a mapping file
* @param values the values of the parameters
* @return a {@link List} containing the results of the query execution
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#getNamedQuery(String)
*/
List findByNamedQuery(String queryName, Object... values) throws DataAccessException;
/**
* Execute a named query, binding one value to a ":" named parameter
* in the query string.
* <p>A named query is defined in a Hibernate mapping file.
* @param queryName the name of a Hibernate query in a mapping file
* @param paramName the name of parameter
* @param value the value of the parameter
* @return a {@link List} containing the results of the query execution
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#getNamedQuery(String)
*/
List findByNamedQueryAndNamedParam(String queryName, String paramName, Object value)
throws DataAccessException;
/**
* Execute a named query, binding a number of values to ":" named
* parameters in the query string.
* <p>A named query is defined in a Hibernate mapping file.
* @param queryName the name of a Hibernate query in a mapping file
* @param paramNames the names of the parameters
* @param values the values of the parameters
* @return a {@link List} containing the results of the query execution
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#getNamedQuery(String)
*/
List findByNamedQueryAndNamedParam(String queryName, String[] paramNames, Object[] values)
throws DataAccessException;
/**
* Execute a named query, binding the properties of the given bean to
* ":" named parameters in the query string.
* <p>A named query is defined in a Hibernate mapping file.
* @param queryName the name of a Hibernate query in a mapping file
* @param valueBean the values of the parameters
* @return a {@link List} containing the results of the query execution
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Query#setProperties
* @see org.hibernate.Session#getNamedQuery(String)
*/
List findByNamedQueryAndValueBean(String queryName, Object valueBean)
throws DataAccessException;
//-------------------------------------------------------------------------
// Convenience finder methods for detached criteria
//-------------------------------------------------------------------------
/**
* Execute a query based on a given Hibernate criteria object.
* @param criteria the detached Hibernate criteria object.
* <b>Note: Do not reuse criteria objects! They need to recreated per execution,
* due to the suboptimal design of Hibernate's criteria facility.</b>
* @return a {@link List} containing 0 or more persistent instances
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.criterion.DetachedCriteria#getExecutableCriteria(org.hibernate.Session)
*/
List findByCriteria(DetachedCriteria criteria) throws DataAccessException;
/**
* Execute a query based on the given Hibernate criteria object.
* @param criteria the detached Hibernate criteria object.
* <b>Note: Do not reuse criteria objects! They need to recreated per execution,
* due to the suboptimal design of Hibernate's criteria facility.</b>
* @param firstResult the index of the first result object to be retrieved
* (numbered from 0)
* @param maxResults the maximum number of result objects to retrieve
* (or <=0 for no limit)
* @return a {@link List} containing 0 or more persistent instances
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.criterion.DetachedCriteria#getExecutableCriteria(org.hibernate.Session)
* @see org.hibernate.Criteria#setFirstResult(int)
* @see org.hibernate.Criteria#setMaxResults(int)
*/
List findByCriteria(DetachedCriteria criteria, int firstResult, int maxResults) throws DataAccessException;
/**
* Execute a query based on the given example entity object.
* @param exampleEntity an instance of the desired entity,
* serving as example for "query-by-example"
* @return a {@link List} containing 0 or more persistent instances
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.criterion.Example#create(Object)
*/
List findByExample(Object exampleEntity) throws DataAccessException;
/**
* Execute a query based on the given example entity object.
* @param entityName the name of the persistent entity
* @param exampleEntity an instance of the desired entity,
* serving as example for "query-by-example"
* @return a {@link List} containing 0 or more persistent instances
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.criterion.Example#create(Object)
*/
List findByExample(String entityName, Object exampleEntity) throws DataAccessException;
/**
* Execute a query based on a given example entity object.
* @param exampleEntity an instance of the desired entity,
* serving as example for "query-by-example"
* @param firstResult the index of the first result object to be retrieved
* (numbered from 0)
* @param maxResults the maximum number of result objects to retrieve
* (or <=0 for no limit)
* @return a {@link List} containing 0 or more persistent instances
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.criterion.Example#create(Object)
* @see org.hibernate.Criteria#setFirstResult(int)
* @see org.hibernate.Criteria#setMaxResults(int)
*/
List findByExample(Object exampleEntity, int firstResult, int maxResults) throws DataAccessException;
/**
* Execute a query based on a given example entity object.
* @param entityName the name of the persistent entity
* @param exampleEntity an instance of the desired entity,
* serving as example for "query-by-example"
* @param firstResult the index of the first result object to be retrieved
* (numbered from 0)
* @param maxResults the maximum number of result objects to retrieve
* (or <=0 for no limit)
* @return a {@link List} containing 0 or more persistent instances
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.criterion.Example#create(Object)
* @see org.hibernate.Criteria#setFirstResult(int)
* @see org.hibernate.Criteria#setMaxResults(int)
*/
List findByExample(String entityName, Object exampleEntity, int firstResult, int maxResults)
throws DataAccessException;
//-------------------------------------------------------------------------
// Convenience query methods for iteration and bulk updates/deletes
//-------------------------------------------------------------------------
/**
* Execute a query for persistent instances.
* <p>Returns the results as an {@link Iterator}. Entities returned are
* initialized on demand. See the Hibernate API documentation for details.
* @param queryString a query expressed in Hibernate's query language
* @return an {@link Iterator} containing 0 or more persistent instances
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#createQuery
* @see org.hibernate.Query#iterate
*/
Iterator iterate(String queryString) throws DataAccessException;
/**
* Execute a query for persistent instances, binding one value
* to a "?" parameter in the query string.
* <p>Returns the results as an {@link Iterator}. Entities returned are
* initialized on demand. See the Hibernate API documentation for details.
* @param queryString a query expressed in Hibernate's query language
* @param value the value of the parameter
* @return an {@link Iterator} containing 0 or more persistent instances
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#createQuery
* @see org.hibernate.Query#iterate
*/
Iterator iterate(String queryString, Object value) throws DataAccessException;
/**
* Execute a query for persistent instances, binding a number of
* values to "?" parameters in the query string.
* <p>Returns the results as an {@link Iterator}. Entities returned are
* initialized on demand. See the Hibernate API documentation for details.
* @param queryString a query expressed in Hibernate's query language
* @param values the values of the parameters
* @return an {@link Iterator} containing 0 or more persistent instances
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#createQuery
* @see org.hibernate.Query#iterate
*/
Iterator iterate(String queryString, Object... values) throws DataAccessException;
/**
* Immediately close an {@link Iterator} created by any of the various
* <code>iterate(..)</code> operations, instead of waiting until the
* session is closed or disconnected.
* @param it the <code>Iterator</code> to close
* @throws DataAccessException if the <code>Iterator</code> could not be closed
* @see org.hibernate.Hibernate#close
*/
void closeIterator(Iterator it) throws DataAccessException;
/**
* Update/delete all objects according to the given query.
* @param queryString an update/delete query expressed in Hibernate's query language
* @return the number of instances updated/deleted
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#createQuery
* @see org.hibernate.Query#executeUpdate
*/
int bulkUpdate(String queryString) throws DataAccessException;
/**
* Update/delete all objects according to the given query, binding one value
* to a "?" parameter in the query string.
* @param queryString an update/delete query expressed in Hibernate's query language
* @param value the value of the parameter
* @return the number of instances updated/deleted
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#createQuery
* @see org.hibernate.Query#executeUpdate
*/
int bulkUpdate(String queryString, Object value) throws DataAccessException;
/**
* Update/delete all objects according to the given query, binding a number of
* values to "?" parameters in the query string.
* @param queryString an update/delete query expressed in Hibernate's query language
* @param values the values of the parameters
* @return the number of instances updated/deleted
* @throws org.springframework.dao.DataAccessException in case of Hibernate errors
* @see org.hibernate.Session#createQuery
* @see org.hibernate.Query#executeUpdate
*/
int bulkUpdate(String queryString, Object... values) throws DataAccessException;
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3;
import org.hibernate.StaleObjectStateException;
import org.hibernate.StaleStateException;
import org.springframework.orm.ObjectOptimisticLockingFailureException;
/**
* Hibernate-specific subclass of ObjectOptimisticLockingFailureException.
* Converts Hibernate's StaleObjectStateException and StaleStateException.
*
* @author Juergen Hoeller
* @since 1.2
* @see SessionFactoryUtils#convertHibernateAccessException
*/
public class HibernateOptimisticLockingFailureException extends ObjectOptimisticLockingFailureException {
public HibernateOptimisticLockingFailureException(StaleObjectStateException ex) {
super(ex.getEntityName(), ex.getIdentifier(), ex);
}
public HibernateOptimisticLockingFailureException(StaleStateException ex) {
super(ex.getMessage(), ex);
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3;
import org.hibernate.QueryException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
/**
* Hibernate-specific subclass of InvalidDataAccessResourceUsageException,
* thrown on invalid HQL query syntax.
*
* @author Juergen Hoeller
* @since 1.2
* @see SessionFactoryUtils#convertHibernateAccessException
*/
public class HibernateQueryException extends InvalidDataAccessResourceUsageException {
public HibernateQueryException(QueryException ex) {
super(ex.getMessage(), ex);
}
/**
* Return the HQL query string that was invalid.
*/
public String getQueryString() {
return ((QueryException) getCause()).getQueryString();
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3;
import org.hibernate.HibernateException;
import org.springframework.dao.UncategorizedDataAccessException;
/**
* Hibernate-specific subclass of UncategorizedDataAccessException,
* for Hibernate system errors that do not match any concrete
* <code>org.springframework.dao</code> exceptions.
*
* @author Juergen Hoeller
* @since 1.2
* @see SessionFactoryUtils#convertHibernateAccessException
*/
public class HibernateSystemException extends UncategorizedDataAccessException {
/**
* Create a new HibernateSystemException,
* wrapping an arbitrary HibernateException.
* @param cause the HibernateException thrown
*/
public HibernateSystemException(HibernateException cause) {
super(cause != null ? cause.getMessage() : null, cause);
}
}

View File

@@ -0,0 +1,924 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3;
import java.sql.Connection;
import javax.sql.DataSource;
import org.hibernate.ConnectionReleaseMode;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.Interceptor;
import org.hibernate.JDBCException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.exception.GenericJDBCException;
import org.hibernate.impl.SessionImpl;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.jdbc.datasource.ConnectionHolder;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.datasource.JdbcTransactionObjectSupport;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator;
import org.springframework.jdbc.support.SQLExceptionTranslator;
import org.springframework.transaction.CannotCreateTransactionException;
import org.springframework.transaction.IllegalTransactionStateException;
import org.springframework.transaction.InvalidIsolationLevelException;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionSystemException;
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
import org.springframework.transaction.support.DefaultTransactionStatus;
import org.springframework.transaction.support.ResourceTransactionManager;
import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
* {@link org.springframework.transaction.PlatformTransactionManager}
* implementation for a single Hibernate {@link org.hibernate.SessionFactory}.
* Binds a Hibernate Session from the specified factory to the thread, potentially
* allowing for one thread-bound Session per factory. {@link SessionFactoryUtils}
* and {@link HibernateTemplate} are aware of thread-bound Sessions and participate
* in such transactions automatically. Using either of those or going through
* <code>SessionFactory.getCurrentSession()</code> is required for Hibernate
* access code that needs to support this transaction handling mechanism.
*
* <p>Supports custom isolation levels, and timeouts that get applied as
* Hibernate transaction timeouts.
*
* <p>This transaction manager is appropriate for applications that use a single
* Hibernate SessionFactory for transactional data access, but it also supports
* direct DataSource access within a transaction (i.e. plain JDBC code working
* with the same DataSource). This allows for mixing services which access Hibernate
* and services which use plain JDBC (without being aware of Hibernate)!
* Application code needs to stick to the same simple Connection lookup pattern as
* with {@link org.springframework.jdbc.datasource.DataSourceTransactionManager}
* (i.e. {@link org.springframework.jdbc.datasource.DataSourceUtils#getConnection}
* or going through a
* {@link org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy}).
*
* <p>Note: To be able to register a DataSource's Connection for plain JDBC code,
* this instance needs to be aware of the DataSource ({@link #setDataSource}).
* The given DataSource should obviously match the one used by the given
* SessionFactory. To achieve this, configure both to the same JNDI DataSource,
* or preferably create the SessionFactory with {@link LocalSessionFactoryBean} and
* a local DataSource (which will be autodetected by this transaction manager).
*
* <p>JTA (usually through {@link org.springframework.transaction.jta.JtaTransactionManager})
* is necessary for accessing multiple transactional resources within the same
* transaction. The DataSource that Hibernate uses needs to be JTA-enabled in
* such a scenario (see container setup). Normally, JTA setup for Hibernate is
* somewhat container-specific due to the JTA TransactionManager lookup, required
* for proper transactional handling of the SessionFactory-level read-write cache.
*
* <p>Fortunately, there is an easier way with Spring: {@link SessionFactoryUtils}
* (and thus {@link HibernateTemplate}) registers synchronizations with Spring's
* {@link org.springframework.transaction.support.TransactionSynchronizationManager}
* (as used by {@link org.springframework.transaction.jta.JtaTransactionManager}),
* for proper after-completion callbacks. Therefore, as long as Spring's
* JtaTransactionManager drives the JTA transactions, Hibernate does not require
* any special configuration for proper JTA participation. Note that there are
* special restrictions with EJB CMT and restrictive JTA subsystems: See
* {@link org.springframework.transaction.jta.JtaTransactionManager}'s javadoc for details.
*
* <p>On JDBC 3.0, this transaction manager supports nested transactions via JDBC 3.0
* Savepoints. The {@link #setNestedTransactionAllowed} "nestedTransactionAllowed"}
* flag defaults to "false", though, as nested transactions will just apply to the
* JDBC Connection, not to the Hibernate Session and its cached objects. You can
* manually set the flag to "true" if you want to use nested transactions for
* JDBC access code which participates in Hibernate transactions (provided that
* your JDBC driver supports Savepoints). <i>Note that Hibernate itself does not
* support nested transactions! Hence, do not expect Hibernate access code to
* semantically participate in a nested transaction.</i>
*
* <p>Requires Hibernate 3.2 or later, as of Spring 3.0.
*
* @author Juergen Hoeller
* @since 1.2
* @see #setSessionFactory
* @see #setDataSource
* @see LocalSessionFactoryBean
* @see SessionFactoryUtils#getSession
* @see SessionFactoryUtils#applyTransactionTimeout
* @see SessionFactoryUtils#releaseSession
* @see HibernateTemplate
* @see org.hibernate.SessionFactory#getCurrentSession()
* @see org.springframework.jdbc.datasource.DataSourceUtils#getConnection
* @see org.springframework.jdbc.datasource.DataSourceUtils#applyTransactionTimeout
* @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection
* @see org.springframework.jdbc.core.JdbcTemplate
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager
* @see org.springframework.transaction.jta.JtaTransactionManager
*/
public class HibernateTransactionManager extends AbstractPlatformTransactionManager
implements ResourceTransactionManager, BeanFactoryAware, InitializingBean {
private SessionFactory sessionFactory;
private DataSource dataSource;
private boolean autodetectDataSource = true;
private boolean prepareConnection = true;
private boolean hibernateManagedSession = false;
private boolean earlyFlushBeforeCommit = false;
private Object entityInterceptor;
private SQLExceptionTranslator jdbcExceptionTranslator;
private SQLExceptionTranslator defaultJdbcExceptionTranslator;
/**
* Just needed for entityInterceptorBeanName.
* @see #setEntityInterceptorBeanName
*/
private BeanFactory beanFactory;
/**
* Create a new HibernateTransactionManager instance.
* A SessionFactory has to be set to be able to use it.
* @see #setSessionFactory
*/
public HibernateTransactionManager() {
}
/**
* Create a new HibernateTransactionManager instance.
* @param sessionFactory SessionFactory to manage transactions for
*/
public HibernateTransactionManager(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
afterPropertiesSet();
}
/**
* Set the SessionFactory that this instance should manage transactions for.
*/
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
/**
* Return the SessionFactory that this instance should manage transactions for.
*/
public SessionFactory getSessionFactory() {
return this.sessionFactory;
}
/**
* Set the JDBC DataSource that this instance should manage transactions for.
* The DataSource should match the one used by the Hibernate SessionFactory:
* for example, you could specify the same JNDI DataSource for both.
* <p>If the SessionFactory was configured with LocalDataSourceConnectionProvider,
* i.e. by Spring's LocalSessionFactoryBean with a specified "dataSource",
* the DataSource will be auto-detected: You can still explictly specify the
* DataSource, but you don't need to in this case.
* <p>A transactional JDBC Connection for this DataSource will be provided to
* application code accessing this DataSource directly via DataSourceUtils
* or JdbcTemplate. The Connection will be taken from the Hibernate Session.
* <p>The DataSource specified here should be the target DataSource to manage
* transactions for, not a TransactionAwareDataSourceProxy. Only data access
* code may work with TransactionAwareDataSourceProxy, while the transaction
* manager needs to work on the underlying target DataSource. If there's
* nevertheless a TransactionAwareDataSourceProxy passed in, it will be
* unwrapped to extract its target DataSource.
* @see #setAutodetectDataSource
* @see LocalDataSourceConnectionProvider
* @see LocalSessionFactoryBean#setDataSource
* @see org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy
* @see org.springframework.jdbc.datasource.DataSourceUtils
* @see org.springframework.jdbc.core.JdbcTemplate
*/
public void setDataSource(DataSource dataSource) {
if (dataSource instanceof TransactionAwareDataSourceProxy) {
// If we got a TransactionAwareDataSourceProxy, we need to perform transactions
// for its underlying target DataSource, else data access code won't see
// properly exposed transactions (i.e. transactions for the target DataSource).
this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource();
}
else {
this.dataSource = dataSource;
}
}
/**
* Return the JDBC DataSource that this instance manages transactions for.
*/
public DataSource getDataSource() {
return this.dataSource;
}
/**
* Set whether to autodetect a JDBC DataSource used by the Hibernate SessionFactory,
* if set via LocalSessionFactoryBean's <code>setDataSource</code>. Default is "true".
* <p>Can be turned off to deliberately ignore an available DataSource, in order
* to not expose Hibernate transactions as JDBC transactions for that DataSource.
* @see #setDataSource
* @see LocalSessionFactoryBean#setDataSource
*/
public void setAutodetectDataSource(boolean autodetectDataSource) {
this.autodetectDataSource = autodetectDataSource;
}
/**
* Set whether to prepare the underlying JDBC Connection of a transactional
* Hibernate Session, that is, whether to apply a transaction-specific
* isolation level and/or the transaction's read-only flag to the underlying
* JDBC Connection.
* <p>Default is "true". If you turn this flag off, the transaction manager
* will not support per-transaction isolation levels anymore. It will not
* call <code>Connection.setReadOnly(true)</code> for read-only transactions
* anymore either. If this flag is turned off, no cleanup of a JDBC Connection
* is required after a transaction, since no Connection settings will get modified.
* @see java.sql.Connection#setTransactionIsolation
* @see java.sql.Connection#setReadOnly
*/
public void setPrepareConnection(boolean prepareConnection) {
this.prepareConnection = prepareConnection;
}
/**
* Set whether to operate on a Hibernate-managed Session instead of a
* Spring-managed Session, that is, whether to obtain the Session through
* Hibernate's {@link org.hibernate.SessionFactory#getCurrentSession()}
* instead of {@link org.hibernate.SessionFactory#openSession()} (with a Spring
* {@link org.springframework.transaction.support.TransactionSynchronizationManager}
* check preceding it).
* <p>Default is "false", i.e. using a Spring-managed Session: taking the current
* thread-bound Session if available (e.g. in an Open-Session-in-View scenario),
* creating a new Session for the current transaction otherwise.
* <p>Switch this flag to "true" in order to enforce use of a Hibernate-managed Session.
* Note that this requires {@link org.hibernate.SessionFactory#getCurrentSession()}
* to always return a proper Session when called for a Spring-managed transaction;
* transaction begin will fail if the <code>getCurrentSession()</code> call fails.
* <p>This mode will typically be used in combination with a custom Hibernate
* {@link org.hibernate.context.CurrentSessionContext} implementation that stores
* Sessions in a place other than Spring's TransactionSynchronizationManager.
* It may also be used in combination with Spring's Open-Session-in-View support
* (using Spring's default {@link SpringSessionContext}), in which case it subtly
* differs from the Spring-managed Session mode: The pre-bound Session will <i>not</i>
* receive a <code>clear()</code> call (on rollback) or a <code>disconnect()</code>
* call (on transaction completion) in such a scenario; this is rather left up
* to a custom CurrentSessionContext implementation (if desired).
*/
public void setHibernateManagedSession(boolean hibernateManagedSession) {
this.hibernateManagedSession = hibernateManagedSession;
}
/**
* Set whether to perform an early flush before proceeding with a commit.
* <p>Default is "false", performing an implicit flush as part of the actual
* commit step. Switch this to "true" in order to enforce an explicit early
* flush right <i>before</i> the actual commit step.
* <p>An early flush happens before the before-commit synchronization phase,
* making flushed state visible to <code>beforeCommit</code> callbacks of registered
* {@link org.springframework.transaction.support.TransactionSynchronization}
* objects. Such explicit flush behavior is consistent with Spring-driven
* flushing in a JTA transaction environment, so may also get enforced for
* consistency with JTA transaction behavior.
* @see #prepareForCommit
*/
public void setEarlyFlushBeforeCommit(boolean earlyFlushBeforeCommit) {
this.earlyFlushBeforeCommit = earlyFlushBeforeCommit;
}
/**
* Set the bean name of a Hibernate entity interceptor that allows to inspect
* and change property values before writing to and reading from the database.
* Will get applied to any new Session created by this transaction manager.
* <p>Requires the bean factory to be known, to be able to resolve the bean
* name to an interceptor instance on session creation. Typically used for
* prototype interceptors, i.e. a new interceptor instance per session.
* <p>Can also be used for shared interceptor instances, but it is recommended
* to set the interceptor reference directly in such a scenario.
* @param entityInterceptorBeanName the name of the entity interceptor in
* the bean factory
* @see #setBeanFactory
* @see #setEntityInterceptor
*/
public void setEntityInterceptorBeanName(String entityInterceptorBeanName) {
this.entityInterceptor = entityInterceptorBeanName;
}
/**
* Set a Hibernate entity interceptor that allows to inspect and change
* property values before writing to and reading from the database.
* Will get applied to any new Session created by this transaction manager.
* <p>Such an interceptor can either be set at the SessionFactory level,
* i.e. on LocalSessionFactoryBean, or at the Session level, i.e. on
* HibernateTemplate, HibernateInterceptor, and HibernateTransactionManager.
* It's preferable to set it on LocalSessionFactoryBean or HibernateTransactionManager
* to avoid repeated configuration and guarantee consistent behavior in transactions.
* @see LocalSessionFactoryBean#setEntityInterceptor
* @see HibernateTemplate#setEntityInterceptor
* @see HibernateInterceptor#setEntityInterceptor
*/
public void setEntityInterceptor(Interceptor entityInterceptor) {
this.entityInterceptor = entityInterceptor;
}
/**
* Return the current Hibernate entity interceptor, or <code>null</code> if none.
* Resolves an entity interceptor bean name via the bean factory,
* if necessary.
* @throws IllegalStateException if bean name specified but no bean factory set
* @throws BeansException if bean name resolution via the bean factory failed
* @see #setEntityInterceptor
* @see #setEntityInterceptorBeanName
* @see #setBeanFactory
*/
public Interceptor getEntityInterceptor() throws IllegalStateException, BeansException {
if (this.entityInterceptor instanceof Interceptor) {
return (Interceptor) entityInterceptor;
}
else if (this.entityInterceptor instanceof String) {
if (this.beanFactory == null) {
throw new IllegalStateException("Cannot get entity interceptor via bean name if no bean factory set");
}
String beanName = (String) this.entityInterceptor;
return this.beanFactory.getBean(beanName, Interceptor.class);
}
else {
return null;
}
}
/**
* Set the JDBC exception translator for this transaction manager.
* <p>Applied to any SQLException root cause of a Hibernate JDBCException that
* is thrown on flush, overriding Hibernate's default SQLException translation
* (which is based on Hibernate's Dialect for a specific target database).
* @param jdbcExceptionTranslator the exception translator
* @see java.sql.SQLException
* @see org.hibernate.JDBCException
* @see org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator
* @see org.springframework.jdbc.support.SQLStateSQLExceptionTranslator
*/
public void setJdbcExceptionTranslator(SQLExceptionTranslator jdbcExceptionTranslator) {
this.jdbcExceptionTranslator = jdbcExceptionTranslator;
}
/**
* Return the JDBC exception translator for this transaction manager, if any.
*/
public SQLExceptionTranslator getJdbcExceptionTranslator() {
return this.jdbcExceptionTranslator;
}
/**
* The bean factory just needs to be known for resolving entity interceptor
* bean names. It does not need to be set for any other mode of operation.
* @see #setEntityInterceptorBeanName
*/
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public void afterPropertiesSet() {
if (getSessionFactory() == null) {
throw new IllegalArgumentException("Property 'sessionFactory' is required");
}
if (this.entityInterceptor instanceof String && this.beanFactory == null) {
throw new IllegalArgumentException("Property 'beanFactory' is required for 'entityInterceptorBeanName'");
}
// Check for SessionFactory's DataSource.
if (this.autodetectDataSource && getDataSource() == null) {
DataSource sfds = SessionFactoryUtils.getDataSource(getSessionFactory());
if (sfds != null) {
// Use the SessionFactory's DataSource for exposing transactions to JDBC code.
if (logger.isInfoEnabled()) {
logger.info("Using DataSource [" + sfds +
"] of Hibernate SessionFactory for HibernateTransactionManager");
}
setDataSource(sfds);
}
}
}
public Object getResourceFactory() {
return getSessionFactory();
}
@Override
protected Object doGetTransaction() {
HibernateTransactionObject txObject = new HibernateTransactionObject();
txObject.setSavepointAllowed(isNestedTransactionAllowed());
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory());
if (sessionHolder != null) {
if (logger.isDebugEnabled()) {
logger.debug("Found thread-bound Session [" +
SessionFactoryUtils.toString(sessionHolder.getSession()) + "] for Hibernate transaction");
}
txObject.setSessionHolder(sessionHolder);
}
else if (this.hibernateManagedSession) {
try {
Session session = getSessionFactory().getCurrentSession();
if (logger.isDebugEnabled()) {
logger.debug("Found Hibernate-managed Session [" +
SessionFactoryUtils.toString(session) + "] for Spring-managed transaction");
}
txObject.setExistingSession(session);
}
catch (HibernateException ex) {
throw new DataAccessResourceFailureException(
"Could not obtain Hibernate-managed Session for Spring-managed transaction", ex);
}
}
if (getDataSource() != null) {
ConnectionHolder conHolder = (ConnectionHolder)
TransactionSynchronizationManager.getResource(getDataSource());
txObject.setConnectionHolder(conHolder);
}
return txObject;
}
@Override
protected boolean isExistingTransaction(Object transaction) {
HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
return (txObject.hasSpringManagedTransaction() ||
(this.hibernateManagedSession && txObject.hasHibernateManagedTransaction()));
}
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
if (txObject.hasConnectionHolder() && !txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
throw new IllegalTransactionStateException(
"Pre-bound JDBC Connection found! HibernateTransactionManager does not support " +
"running within DataSourceTransactionManager if told to manage the DataSource itself. " +
"It is recommended to use a single HibernateTransactionManager for all transactions " +
"on a single DataSource, no matter whether Hibernate or JDBC access.");
}
Session session = null;
try {
if (txObject.getSessionHolder() == null || txObject.getSessionHolder().isSynchronizedWithTransaction()) {
Interceptor entityInterceptor = getEntityInterceptor();
Session newSession = (entityInterceptor != null ?
getSessionFactory().openSession(entityInterceptor) : getSessionFactory().openSession());
if (logger.isDebugEnabled()) {
logger.debug("Opened new Session [" + SessionFactoryUtils.toString(newSession) +
"] for Hibernate transaction");
}
txObject.setSession(newSession);
}
session = txObject.getSessionHolder().getSession();
if (this.prepareConnection && isSameConnectionForEntireSession(session)) {
// We're allowed to change the transaction settings of the JDBC Connection.
if (logger.isDebugEnabled()) {
logger.debug(
"Preparing JDBC Connection of Hibernate Session [" + SessionFactoryUtils.toString(session) + "]");
}
Connection con = session.connection();
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
}
else {
// Not allowed to change the transaction settings of the JDBC Connection.
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
// We should set a specific isolation level but are not allowed to...
throw new InvalidIsolationLevelException(
"HibernateTransactionManager is not allowed to support custom isolation levels: " +
"make sure that its 'prepareConnection' flag is on (the default) and that the " +
"Hibernate connection release mode is set to 'on_close' (SpringTransactionFactory's default). " +
"Make sure that your LocalSessionFactoryBean actually uses SpringTransactionFactory: Your " +
"Hibernate properties should *not* include a 'hibernate.transaction.factory_class' property!");
}
if (logger.isDebugEnabled()) {
logger.debug(
"Not preparing JDBC Connection of Hibernate Session [" + SessionFactoryUtils.toString(session) + "]");
}
}
if (definition.isReadOnly() && txObject.isNewSession()) {
// Just set to NEVER in case of a new Session for this transaction.
session.setFlushMode(FlushMode.MANUAL);
}
if (!definition.isReadOnly() && !txObject.isNewSession()) {
// We need AUTO or COMMIT for a non-read-only transaction.
FlushMode flushMode = session.getFlushMode();
if (flushMode.lessThan(FlushMode.COMMIT)) {
session.setFlushMode(FlushMode.AUTO);
txObject.getSessionHolder().setPreviousFlushMode(flushMode);
}
}
Transaction hibTx;
// Register transaction timeout.
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
// Use Hibernate's own transaction timeout mechanism on Hibernate 3.1+
// Applies to all statements, also to inserts, updates and deletes!
hibTx = session.getTransaction();
hibTx.setTimeout(timeout);
hibTx.begin();
}
else {
// Open a plain Hibernate transaction without specified timeout.
hibTx = session.beginTransaction();
}
// Add the Hibernate transaction to the session holder.
txObject.getSessionHolder().setTransaction(hibTx);
// Register the Hibernate Session's JDBC Connection for the DataSource, if set.
if (getDataSource() != null) {
Connection con = session.connection();
ConnectionHolder conHolder = new ConnectionHolder(con);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
conHolder.setTimeoutInSeconds(timeout);
}
if (logger.isDebugEnabled()) {
logger.debug("Exposing Hibernate transaction as JDBC transaction [" + con + "]");
}
TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);
txObject.setConnectionHolder(conHolder);
}
// Bind the session holder to the thread.
if (txObject.isNewSessionHolder()) {
TransactionSynchronizationManager.bindResource(getSessionFactory(), txObject.getSessionHolder());
}
txObject.getSessionHolder().setSynchronizedWithTransaction(true);
}
catch (Exception ex) {
if (txObject.isNewSession()) {
try {
if (session.getTransaction().isActive()) {
session.getTransaction().rollback();
}
}
catch (Throwable ex2) {
logger.debug("Could not rollback Session after failed transaction begin", ex);
}
finally {
SessionFactoryUtils.closeSession(session);
}
}
throw new CannotCreateTransactionException("Could not open Hibernate Session for transaction", ex);
}
}
@Override
protected Object doSuspend(Object transaction) {
HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
txObject.setSessionHolder(null);
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.unbindResource(getSessionFactory());
txObject.setConnectionHolder(null);
ConnectionHolder connectionHolder = null;
if (getDataSource() != null) {
connectionHolder = (ConnectionHolder) TransactionSynchronizationManager.unbindResource(getDataSource());
}
return new SuspendedResourcesHolder(sessionHolder, connectionHolder);
}
@Override
protected void doResume(Object transaction, Object suspendedResources) {
SuspendedResourcesHolder resourcesHolder = (SuspendedResourcesHolder) suspendedResources;
if (TransactionSynchronizationManager.hasResource(getSessionFactory())) {
// From non-transactional code running in active transaction synchronization
// -> can be safely removed, will be closed on transaction completion.
TransactionSynchronizationManager.unbindResource(getSessionFactory());
}
TransactionSynchronizationManager.bindResource(getSessionFactory(), resourcesHolder.getSessionHolder());
if (getDataSource() != null) {
TransactionSynchronizationManager.bindResource(getDataSource(), resourcesHolder.getConnectionHolder());
}
}
@Override
protected void prepareForCommit(DefaultTransactionStatus status) {
if (this.earlyFlushBeforeCommit && status.isNewTransaction()) {
HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction();
Session session = txObject.getSessionHolder().getSession();
if (!session.getFlushMode().lessThan(FlushMode.COMMIT)) {
logger.debug("Performing an early flush for Hibernate transaction");
try {
session.flush();
}
catch (HibernateException ex) {
throw convertHibernateAccessException(ex);
}
finally {
session.setFlushMode(FlushMode.MANUAL);
}
}
}
}
@Override
protected void doCommit(DefaultTransactionStatus status) {
HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction();
if (status.isDebug()) {
logger.debug("Committing Hibernate transaction on Session [" +
SessionFactoryUtils.toString(txObject.getSessionHolder().getSession()) + "]");
}
try {
txObject.getSessionHolder().getTransaction().commit();
}
catch (org.hibernate.TransactionException ex) {
// assumably from commit call to the underlying JDBC connection
throw new TransactionSystemException("Could not commit Hibernate transaction", ex);
}
catch (HibernateException ex) {
// assumably failed to flush changes to database
throw convertHibernateAccessException(ex);
}
}
@Override
protected void doRollback(DefaultTransactionStatus status) {
HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction();
if (status.isDebug()) {
logger.debug("Rolling back Hibernate transaction on Session [" +
SessionFactoryUtils.toString(txObject.getSessionHolder().getSession()) + "]");
}
try {
txObject.getSessionHolder().getTransaction().rollback();
}
catch (org.hibernate.TransactionException ex) {
throw new TransactionSystemException("Could not roll back Hibernate transaction", ex);
}
catch (HibernateException ex) {
// Shouldn't really happen, as a rollback doesn't cause a flush.
throw convertHibernateAccessException(ex);
}
finally {
if (!txObject.isNewSession() && !this.hibernateManagedSession) {
// Clear all pending inserts/updates/deletes in the Session.
// Necessary for pre-bound Sessions, to avoid inconsistent state.
txObject.getSessionHolder().getSession().clear();
}
}
}
@Override
protected void doSetRollbackOnly(DefaultTransactionStatus status) {
HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction();
if (status.isDebug()) {
logger.debug("Setting Hibernate transaction on Session [" +
SessionFactoryUtils.toString(txObject.getSessionHolder().getSession()) + "] rollback-only");
}
txObject.setRollbackOnly();
}
@Override
protected void doCleanupAfterCompletion(Object transaction) {
HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
// Remove the session holder from the thread.
if (txObject.isNewSessionHolder()) {
TransactionSynchronizationManager.unbindResource(getSessionFactory());
}
// Remove the JDBC connection holder from the thread, if exposed.
if (getDataSource() != null) {
TransactionSynchronizationManager.unbindResource(getDataSource());
}
Session session = txObject.getSessionHolder().getSession();
if (this.prepareConnection && session.isConnected() && isSameConnectionForEntireSession(session)) {
// We're running with connection release mode "on_close": We're able to reset
// the isolation level and/or read-only flag of the JDBC Connection here.
// Else, we need to rely on the connection pool to perform proper cleanup.
try {
Connection con = session.connection();
DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());
}
catch (HibernateException ex) {
logger.debug("Could not access JDBC Connection of Hibernate Session", ex);
}
}
if (txObject.isNewSession()) {
if (logger.isDebugEnabled()) {
logger.debug("Closing Hibernate Session [" + SessionFactoryUtils.toString(session) +
"] after transaction");
}
SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, getSessionFactory());
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Not closing pre-bound Hibernate Session [" +
SessionFactoryUtils.toString(session) + "] after transaction");
}
if (txObject.getSessionHolder().getPreviousFlushMode() != null) {
session.setFlushMode(txObject.getSessionHolder().getPreviousFlushMode());
}
if (!this.hibernateManagedSession) {
session.disconnect();
}
}
txObject.getSessionHolder().clear();
}
/**
* Return whether the given Hibernate Session will always hold the same
* JDBC Connection. This is used to check whether the transaction manager
* can safely prepare and clean up the JDBC Connection used for a transaction.
* <p>Default implementation checks the Session's connection release mode
* to be "on_close". Unfortunately, this requires casting to SessionImpl,
* as of Hibernate 3.1. If that cast doesn't work, we'll simply assume
* we're safe and return <code>true</code>.
* @param session the Hibernate Session to check
* @see org.hibernate.impl.SessionImpl#getConnectionReleaseMode()
* @see org.hibernate.ConnectionReleaseMode#ON_CLOSE
*/
protected boolean isSameConnectionForEntireSession(Session session) {
if (!(session instanceof SessionImpl)) {
// The best we can do is to assume we're safe.
return true;
}
ConnectionReleaseMode releaseMode = ((SessionImpl) session).getConnectionReleaseMode();
return ConnectionReleaseMode.ON_CLOSE.equals(releaseMode);
}
/**
* Convert the given HibernateException to an appropriate exception
* from the <code>org.springframework.dao</code> hierarchy.
* <p>Will automatically apply a specified SQLExceptionTranslator to a
* Hibernate JDBCException, else rely on Hibernate's default translation.
* @param ex HibernateException that occured
* @return a corresponding DataAccessException
* @see SessionFactoryUtils#convertHibernateAccessException
* @see #setJdbcExceptionTranslator
*/
protected DataAccessException convertHibernateAccessException(HibernateException ex) {
if (getJdbcExceptionTranslator() != null && ex instanceof JDBCException) {
return convertJdbcAccessException((JDBCException) ex, getJdbcExceptionTranslator());
}
else if (GenericJDBCException.class.equals(ex.getClass())) {
return convertJdbcAccessException((GenericJDBCException) ex, getDefaultJdbcExceptionTranslator());
}
return SessionFactoryUtils.convertHibernateAccessException(ex);
}
/**
* Convert the given Hibernate JDBCException to an appropriate exception
* from the <code>org.springframework.dao</code> hierarchy, using the
* given SQLExceptionTranslator.
* @param ex Hibernate JDBCException that occured
* @param translator the SQLExceptionTranslator to use
* @return a corresponding DataAccessException
*/
protected DataAccessException convertJdbcAccessException(JDBCException ex, SQLExceptionTranslator translator) {
return translator.translate("Hibernate flushing: " + ex.getMessage(), ex.getSQL(), ex.getSQLException());
}
/**
* Obtain a default SQLExceptionTranslator, lazily creating it if necessary.
* <p>Creates a default
* {@link org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator}
* for the SessionFactory's underlying DataSource.
*/
protected synchronized SQLExceptionTranslator getDefaultJdbcExceptionTranslator() {
if (this.defaultJdbcExceptionTranslator == null) {
if (getDataSource() != null) {
this.defaultJdbcExceptionTranslator = new SQLErrorCodeSQLExceptionTranslator(getDataSource());
}
else {
this.defaultJdbcExceptionTranslator = SessionFactoryUtils.newJdbcExceptionTranslator(getSessionFactory());
}
}
return this.defaultJdbcExceptionTranslator;
}
/**
* Hibernate transaction object, representing a SessionHolder.
* Used as transaction object by HibernateTransactionManager.
*/
private class HibernateTransactionObject extends JdbcTransactionObjectSupport {
private SessionHolder sessionHolder;
private boolean newSessionHolder;
private boolean newSession;
public void setSession(Session session) {
this.sessionHolder = new SessionHolder(session);
this.newSessionHolder = true;
this.newSession = true;
}
public void setExistingSession(Session session) {
this.sessionHolder = new SessionHolder(session);
this.newSessionHolder = true;
this.newSession = false;
}
public void setSessionHolder(SessionHolder sessionHolder) {
this.sessionHolder = sessionHolder;
this.newSessionHolder = false;
this.newSession = false;
}
public SessionHolder getSessionHolder() {
return this.sessionHolder;
}
public boolean isNewSessionHolder() {
return this.newSessionHolder;
}
public boolean isNewSession() {
return this.newSession;
}
public boolean hasSpringManagedTransaction() {
return (this.sessionHolder != null && this.sessionHolder.getTransaction() != null);
}
public boolean hasHibernateManagedTransaction() {
return (this.sessionHolder != null && this.sessionHolder.getSession().getTransaction().isActive());
}
public void setRollbackOnly() {
this.sessionHolder.setRollbackOnly();
if (hasConnectionHolder()) {
getConnectionHolder().setRollbackOnly();
}
}
public boolean isRollbackOnly() {
return this.sessionHolder.isRollbackOnly() ||
(hasConnectionHolder() && getConnectionHolder().isRollbackOnly());
}
public void flush() {
try {
this.sessionHolder.getSession().flush();
}
catch (HibernateException ex) {
throw convertHibernateAccessException(ex);
}
}
}
/**
* Holder for suspended resources.
* Used internally by <code>doSuspend</code> and <code>doResume</code>.
*/
private static class SuspendedResourcesHolder {
private final SessionHolder sessionHolder;
private final ConnectionHolder connectionHolder;
private SuspendedResourcesHolder(SessionHolder sessionHolder, ConnectionHolder conHolder) {
this.sessionHolder = sessionHolder;
this.connectionHolder = conHolder;
}
private SessionHolder getSessionHolder() {
return this.sessionHolder;
}
private ConnectionHolder getConnectionHolder() {
return this.connectionHolder;
}
}
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3;
import java.util.Properties;
import org.hibernate.cache.Cache;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.CacheProvider;
/**
* Proxy for a Hibernate CacheProvider, delegating to a Spring-managed
* CacheProvider instance, determined by LocalSessionFactoryBean's
* "cacheProvider" property.
*
* @author Juergen Hoeller
* @since 2.5.1
* @see LocalSessionFactoryBean#setCacheProvider
*/
public class LocalCacheProviderProxy implements CacheProvider {
private final CacheProvider cacheProvider;
public LocalCacheProviderProxy() {
CacheProvider cp = LocalSessionFactoryBean.getConfigTimeCacheProvider();
// absolutely needs thread-bound CacheProvider to initialize
if (cp == null) {
throw new IllegalStateException("No Hibernate CacheProvider found - " +
"'cacheProvider' property must be set on LocalSessionFactoryBean");
}
this.cacheProvider = cp;
}
public Cache buildCache(String regionName, Properties properties) throws CacheException {
return this.cacheProvider.buildCache(regionName, properties);
}
public long nextTimestamp() {
return this.cacheProvider.nextTimestamp();
}
public void start(Properties properties) throws CacheException {
this.cacheProvider.start(properties);
}
public void stop() {
this.cacheProvider.stop();
}
public boolean isMinimalPutsEnabledByDefault() {
return this.cacheProvider.isMinimalPutsEnabledByDefault();
}
}

View File

@@ -0,0 +1,119 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import org.hibernate.HibernateException;
import org.hibernate.connection.ConnectionProvider;
import org.hibernate.util.JDBCExceptionReporter;
/**
* Hibernate connection provider for local DataSource instances
* in an application context. This provider will be used if
* LocalSessionFactoryBean's "dataSource" property is set
* without a Hibernate TransactionManagerLookup.
*
* @author Juergen Hoeller
* @since 1.2
* @see LocalSessionFactoryBean#setDataSource
*/
public class LocalDataSourceConnectionProvider implements ConnectionProvider {
private DataSource dataSource;
private DataSource dataSourceToUse;
public void configure(Properties props) throws HibernateException {
this.dataSource = LocalSessionFactoryBean.getConfigTimeDataSource();
// absolutely needs thread-bound DataSource to initialize
if (this.dataSource == null) {
throw new HibernateException("No local DataSource found for configuration - " +
"'dataSource' property must be set on LocalSessionFactoryBean");
}
this.dataSourceToUse = getDataSourceToUse(this.dataSource);
}
/**
* Return the DataSource to use for retrieving Connections.
* <p>This implementation returns the passed-in DataSource as-is.
* @param originalDataSource the DataSource as configured by the user
* on LocalSessionFactoryBean
* @return the DataSource to actually retrieve Connections from
* (potentially wrapped)
* @see LocalSessionFactoryBean#setDataSource
*/
protected DataSource getDataSourceToUse(DataSource originalDataSource) {
return originalDataSource;
}
/**
* Return the DataSource that this ConnectionProvider wraps.
*/
public DataSource getDataSource() {
return this.dataSource;
}
/**
* This implementation delegates to the underlying DataSource.
* @see javax.sql.DataSource#getConnection()
*/
public Connection getConnection() throws SQLException {
try {
return this.dataSourceToUse.getConnection();
}
catch (SQLException ex) {
JDBCExceptionReporter.logExceptions(ex);
throw ex;
}
}
/**
* This implementation simply calls <code>Connection.close</code>.
* @see java.sql.Connection#close()
*/
public void closeConnection(Connection con) throws SQLException {
try {
con.close();
}
catch (SQLException ex) {
JDBCExceptionReporter.logExceptions(ex);
throw ex;
}
}
/**
* This implementation does nothing:
* We're dealing with an externally managed DataSource.
*/
public void close() {
}
/**
* This implementation returns <code>false</code>: We cannot guarantee
* to receive the same Connection within a transaction, not even when
* dealing with a JNDI DataSource.
*/
public boolean supportsAggressiveRelease() {
return false;
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3;
/**
* Subclass of LocalDataSourceConnectionProvider that will be used
* if LocalSessionFactoryBean's "dataSource" property is set
* in combination with a Hibernate TransactionManagerLookup.
*
* @author Juergen Hoeller
* @since 2.5.1
*/
public class LocalJtaDataSourceConnectionProvider extends LocalDataSourceConnectionProvider {
/**
* This implementation returns <code>true</code>,
* since we're assuming a JTA DataSource.
*/
@Override
public boolean supportsAggressiveRelease() {
return true;
}
}

View File

@@ -0,0 +1,123 @@
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3;
import java.lang.reflect.Method;
import java.util.Properties;
import org.hibernate.cache.CacheDataDescription;
import org.hibernate.cache.CacheException;
import org.hibernate.cache.CollectionRegion;
import org.hibernate.cache.EntityRegion;
import org.hibernate.cache.QueryResultsRegion;
import org.hibernate.cache.RegionFactory;
import org.hibernate.cache.TimestampsRegion;
import org.hibernate.cache.access.AccessType;
import org.hibernate.cfg.Settings;
import org.springframework.util.ReflectionUtils;
/**
* Proxy for a Hibernate RegionFactory, delegating to a Spring-managed
* RegionFactory instance, determined by LocalSessionFactoryBean's
* "cacheRegionFactory" property.
*
* <p>Compatible with Hibernate 3.3 as well as Hibernate 3.5's version
* of the RegionFactory SPI.
*
* @author Juergen Hoeller
* @since 3.0
* @see LocalSessionFactoryBean#setCacheRegionFactory
*/
public class LocalRegionFactoryProxy implements RegionFactory {
private final RegionFactory regionFactory;
/**
* Standard constructor.
*/
public LocalRegionFactoryProxy() {
RegionFactory rf = (RegionFactory) LocalSessionFactoryBean.getConfigTimeRegionFactory();
// absolutely needs thread-bound RegionFactory to initialize
if (rf == null) {
throw new IllegalStateException("No Hibernate RegionFactory found - " +
"'cacheRegionFactory' property must be set on LocalSessionFactoryBean");
}
this.regionFactory = rf;
}
/**
* Properties constructor: not used by this class or formally required,
* but enforced by Hibernate when reflectively instantiating a RegionFactory.
*/
public LocalRegionFactoryProxy(Properties properties) {
this();
}
public void start(Settings settings, Properties properties) throws CacheException {
this.regionFactory.start(settings, properties);
}
public void stop() {
this.regionFactory.stop();
}
public boolean isMinimalPutsEnabledByDefault() {
return this.regionFactory.isMinimalPutsEnabledByDefault();
}
public AccessType getDefaultAccessType() {
try {
Method method = RegionFactory.class.getMethod("getDefaultAccessType");
return (AccessType) ReflectionUtils.invokeMethod(method, this.regionFactory);
}
catch (NoSuchMethodException ex) {
throw new IllegalStateException("getDefaultAccessType requires Hibernate 3.5+");
}
}
public long nextTimestamp() {
return this.regionFactory.nextTimestamp();
}
public EntityRegion buildEntityRegion(String regionName, Properties properties, CacheDataDescription metadata)
throws CacheException {
return this.regionFactory.buildEntityRegion(regionName, properties, metadata);
}
public CollectionRegion buildCollectionRegion(String regionName, Properties properties,
CacheDataDescription metadata) throws CacheException {
return this.regionFactory.buildCollectionRegion(regionName, properties, metadata);
}
public QueryResultsRegion buildQueryResultsRegion(String regionName, Properties properties)
throws CacheException {
return this.regionFactory.buildQueryResultsRegion(regionName, properties);
}
public TimestampsRegion buildTimestampsRegion(String regionName, Properties properties)
throws CacheException {
return this.regionFactory.buildTimestampsRegion(regionName, properties);
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3;
import java.util.Properties;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import org.hibernate.transaction.TransactionManagerLookup;
/**
* Implementation of Hibernate's {@link TransactionManagerLookup} interface
* that returns a Spring-managed JTA {@link TransactionManager}, determined
* by LocalSessionFactoryBean's "jtaTransactionManager" property.
*
* <p>The main advantage of this TransactionManagerLookup is that it avoids
* double configuration of JTA specifics. A single TransactionManager bean can
* be used for both JtaTransactionManager and LocalSessionFactoryBean, with no
* JTA setup in Hibernate configuration.
*
* <p>Alternatively, use Hibernate's own TransactionManagerLookup implementations:
* Spring's JtaTransactionManager only requires a TransactionManager for suspending
* and resuming transactions, so you might not need to apply such special Spring
* configuration at all.
*
* @author Juergen Hoeller
* @since 1.2
* @see LocalSessionFactoryBean#setJtaTransactionManager
* @see org.springframework.transaction.jta.JtaTransactionManager#setTransactionManager
*/
public class LocalTransactionManagerLookup implements TransactionManagerLookup {
private final TransactionManager transactionManager;
public LocalTransactionManagerLookup() {
TransactionManager tm = LocalSessionFactoryBean.getConfigTimeTransactionManager();
// absolutely needs thread-bound TransactionManager to initialize
if (tm == null) {
throw new IllegalStateException("No JTA TransactionManager found - " +
"'jtaTransactionManager' property must be set on LocalSessionFactoryBean");
}
this.transactionManager = tm;
}
public TransactionManager getTransactionManager(Properties props) {
return this.transactionManager;
}
public String getUserTransactionName() {
return null;
}
public Object getTransactionIdentifier(Transaction transaction) {
return transaction;
}
}

View File

@@ -0,0 +1,813 @@
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import javax.sql.DataSource;
import javax.transaction.Status;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.Criteria;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.Interceptor;
import org.hibernate.JDBCException;
import org.hibernate.NonUniqueObjectException;
import org.hibernate.NonUniqueResultException;
import org.hibernate.ObjectDeletedException;
import org.hibernate.PersistentObjectException;
import org.hibernate.PropertyValueException;
import org.hibernate.Query;
import org.hibernate.QueryException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.StaleObjectStateException;
import org.hibernate.StaleStateException;
import org.hibernate.TransientObjectException;
import org.hibernate.UnresolvableObjectException;
import org.hibernate.WrongClassException;
import org.hibernate.connection.ConnectionProvider;
import org.hibernate.engine.SessionFactoryImplementor;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.exception.DataException;
import org.hibernate.exception.JDBCConnectionException;
import org.hibernate.exception.LockAcquisitionException;
import org.hibernate.exception.SQLGrammarException;
import org.springframework.core.NamedThreadLocal;
import org.springframework.dao.CannotAcquireLockException;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator;
import org.springframework.jdbc.support.SQLExceptionTranslator;
import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator;
import org.springframework.transaction.jta.SpringJtaSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
/**
* Helper class featuring methods for Hibernate Session handling,
* allowing for reuse of Hibernate Session instances within transactions.
* Also provides support for exception translation.
*
* <p>Supports synchronization with both Spring-managed JTA transactions
* (see {@link org.springframework.transaction.jta.JtaTransactionManager})
* and non-Spring JTA transactions (i.e. plain JTA or EJB CMT),
* transparently providing transaction-scoped Hibernate Sessions.
* Note that for non-Spring JTA transactions, a JTA TransactionManagerLookup
* has to be specified in the Hibernate configuration.
*
* <p>Used internally by {@link HibernateTemplate}, {@link HibernateInterceptor}
* and {@link HibernateTransactionManager}. Can also be used directly in
* application code.
*
* @author Juergen Hoeller
* @since 1.2
* @see #getSession
* @see #releaseSession
* @see HibernateTransactionManager
* @see org.springframework.transaction.jta.JtaTransactionManager
* @see org.springframework.transaction.support.TransactionSynchronizationManager
*/
public abstract class SessionFactoryUtils {
/**
* Order value for TransactionSynchronization objects that clean up Hibernate Sessions.
* Returns <code>DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 100</code>
* to execute Session cleanup before JDBC Connection cleanup, if any.
* @see org.springframework.jdbc.datasource.DataSourceUtils#CONNECTION_SYNCHRONIZATION_ORDER
*/
public static final int SESSION_SYNCHRONIZATION_ORDER =
DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 100;
static final Log logger = LogFactory.getLog(SessionFactoryUtils.class);
private static final ThreadLocal<Map<SessionFactory, Set<Session>>> deferredCloseHolder =
new NamedThreadLocal<Map<SessionFactory, Set<Session>>>("Hibernate Sessions registered for deferred close");
/**
* Determine the DataSource of the given SessionFactory.
* @param sessionFactory the SessionFactory to check
* @return the DataSource, or <code>null</code> if none found
* @see org.hibernate.engine.SessionFactoryImplementor#getConnectionProvider
* @see LocalDataSourceConnectionProvider
*/
public static DataSource getDataSource(SessionFactory sessionFactory) {
if (sessionFactory instanceof SessionFactoryImplementor) {
ConnectionProvider cp = ((SessionFactoryImplementor) sessionFactory).getConnectionProvider();
if (cp instanceof LocalDataSourceConnectionProvider) {
return ((LocalDataSourceConnectionProvider) cp).getDataSource();
}
}
return null;
}
/**
* Create an appropriate SQLExceptionTranslator for the given SessionFactory.
* If a DataSource is found, a SQLErrorCodeSQLExceptionTranslator for the DataSource
* is created; else, a SQLStateSQLExceptionTranslator as fallback.
* @param sessionFactory the SessionFactory to create the translator for
* @return the SQLExceptionTranslator
* @see #getDataSource
* @see org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator
* @see org.springframework.jdbc.support.SQLStateSQLExceptionTranslator
*/
public static SQLExceptionTranslator newJdbcExceptionTranslator(SessionFactory sessionFactory) {
DataSource ds = getDataSource(sessionFactory);
if (ds != null) {
return new SQLErrorCodeSQLExceptionTranslator(ds);
}
return new SQLStateSQLExceptionTranslator();
}
/**
* Try to retrieve the JTA TransactionManager from the given SessionFactory
* and/or Session. Check the passed-in SessionFactory for implementing
* SessionFactoryImplementor (the usual case), falling back to the
* SessionFactory reference that the Session itself carries.
* @param sessionFactory Hibernate SessionFactory
* @param session Hibernate Session (can also be <code>null</code>)
* @return the JTA TransactionManager, if any
* @see javax.transaction.TransactionManager
* @see SessionFactoryImplementor#getTransactionManager
* @see Session#getSessionFactory
* @see org.hibernate.impl.SessionFactoryImpl
*/
public static TransactionManager getJtaTransactionManager(SessionFactory sessionFactory, Session session) {
SessionFactoryImplementor sessionFactoryImpl = null;
if (sessionFactory instanceof SessionFactoryImplementor) {
sessionFactoryImpl = ((SessionFactoryImplementor) sessionFactory);
}
else if (session != null) {
SessionFactory internalFactory = session.getSessionFactory();
if (internalFactory instanceof SessionFactoryImplementor) {
sessionFactoryImpl = (SessionFactoryImplementor) internalFactory;
}
}
return (sessionFactoryImpl != null ? sessionFactoryImpl.getTransactionManager() : null);
}
/**
* Get a Hibernate Session for the given SessionFactory. Is aware of and will
* return any existing corresponding Session bound to the current thread, for
* example when using {@link HibernateTransactionManager}. Will create a new
* Session otherwise, if "allowCreate" is <code>true</code>.
* <p>This is the <code>getSession</code> method used by typical data access code,
* in combination with <code>releaseSession</code> called when done with
* the Session. Note that HibernateTemplate allows to write data access code
* without caring about such resource handling.
* @param sessionFactory Hibernate SessionFactory to create the session with
* @param allowCreate whether a non-transactional Session should be created
* when no transactional Session can be found for the current thread
* @return the Hibernate Session
* @throws DataAccessResourceFailureException if the Session couldn't be created
* @throws IllegalStateException if no thread-bound Session found and
* "allowCreate" is <code>false</code>
* @see #getSession(SessionFactory, Interceptor, SQLExceptionTranslator)
* @see #releaseSession
* @see HibernateTemplate
*/
public static Session getSession(SessionFactory sessionFactory, boolean allowCreate)
throws DataAccessResourceFailureException, IllegalStateException {
try {
return doGetSession(sessionFactory, null, null, allowCreate);
}
catch (HibernateException ex) {
throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex);
}
}
/**
* Get a Hibernate Session for the given SessionFactory. Is aware of and will
* return any existing corresponding Session bound to the current thread, for
* example when using {@link HibernateTransactionManager}. Will always create
* a new Session otherwise.
* <p>Supports setting a Session-level Hibernate entity interceptor that allows
* to inspect and change property values before writing to and reading from the
* database. Such an interceptor can also be set at the SessionFactory level
* (i.e. on LocalSessionFactoryBean), on HibernateTransactionManager, or on
* HibernateInterceptor/HibernateTemplate.
* @param sessionFactory Hibernate SessionFactory to create the session with
* @param entityInterceptor Hibernate entity interceptor, or <code>null</code> if none
* @param jdbcExceptionTranslator SQLExcepionTranslator to use for flushing the
* Session on transaction synchronization (may be <code>null</code>; only used
* when actually registering a transaction synchronization)
* @return the Hibernate Session
* @throws DataAccessResourceFailureException if the Session couldn't be created
* @see LocalSessionFactoryBean#setEntityInterceptor
* @see HibernateInterceptor#setEntityInterceptor
* @see HibernateTemplate#setEntityInterceptor
*/
public static Session getSession(
SessionFactory sessionFactory, Interceptor entityInterceptor,
SQLExceptionTranslator jdbcExceptionTranslator) throws DataAccessResourceFailureException {
try {
return doGetSession(sessionFactory, entityInterceptor, jdbcExceptionTranslator, true);
}
catch (HibernateException ex) {
throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex);
}
}
/**
* Get a Hibernate Session for the given SessionFactory. Is aware of and will
* return any existing corresponding Session bound to the current thread, for
* example when using {@link HibernateTransactionManager}. Will create a new
* Session otherwise, if "allowCreate" is <code>true</code>.
* <p>Throws the original HibernateException, in contrast to {@link #getSession}.
* @param sessionFactory Hibernate SessionFactory to create the session with
* @param allowCreate whether a non-transactional Session should be created
* when no transactional Session can be found for the current thread
* @return the Hibernate Session
* @throws HibernateException if the Session couldn't be created
* @throws IllegalStateException if no thread-bound Session found and allowCreate false
*/
public static Session doGetSession(SessionFactory sessionFactory, boolean allowCreate)
throws HibernateException, IllegalStateException {
return doGetSession(sessionFactory, null, null, allowCreate);
}
/**
* Get a Hibernate Session for the given SessionFactory. Is aware of and will
* return any existing corresponding Session bound to the current thread, for
* example when using {@link HibernateTransactionManager}. Will create a new
* Session otherwise, if "allowCreate" is <code>true</code>.
* <p>Same as {@link #getSession}, but throwing the original HibernateException.
* @param sessionFactory Hibernate SessionFactory to create the session with
* @param entityInterceptor Hibernate entity interceptor, or <code>null</code> if none
* @param jdbcExceptionTranslator SQLExcepionTranslator to use for flushing the
* Session on transaction synchronization (may be <code>null</code>)
* @param allowCreate whether a non-transactional Session should be created
* when no transactional Session can be found for the current thread
* @return the Hibernate Session
* @throws HibernateException if the Session couldn't be created
* @throws IllegalStateException if no thread-bound Session found and
* "allowCreate" is <code>false</code>
*/
private static Session doGetSession(
SessionFactory sessionFactory, Interceptor entityInterceptor,
SQLExceptionTranslator jdbcExceptionTranslator, boolean allowCreate)
throws HibernateException, IllegalStateException {
Assert.notNull(sessionFactory, "No SessionFactory specified");
Object resource = TransactionSynchronizationManager.getResource(sessionFactory);
if (resource instanceof Session) {
return (Session) resource;
}
SessionHolder sessionHolder = (SessionHolder) resource;
if (sessionHolder != null && !sessionHolder.isEmpty()) {
// pre-bound Hibernate Session
Session session = null;
if (TransactionSynchronizationManager.isSynchronizationActive() &&
sessionHolder.doesNotHoldNonDefaultSession()) {
// Spring transaction management is active ->
// register pre-bound Session with it for transactional flushing.
session = sessionHolder.getValidatedSession();
if (session != null && !sessionHolder.isSynchronizedWithTransaction()) {
logger.debug("Registering Spring transaction synchronization for existing Hibernate Session");
TransactionSynchronizationManager.registerSynchronization(
new SpringSessionSynchronization(sessionHolder, sessionFactory, jdbcExceptionTranslator, false));
sessionHolder.setSynchronizedWithTransaction(true);
// Switch to FlushMode.AUTO, as we have to assume a thread-bound Session
// with FlushMode.MANUAL, which needs to allow flushing within the transaction.
FlushMode flushMode = session.getFlushMode();
if (flushMode.lessThan(FlushMode.COMMIT) &&
!TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
session.setFlushMode(FlushMode.AUTO);
sessionHolder.setPreviousFlushMode(flushMode);
}
}
}
else {
// No Spring transaction management active -> try JTA transaction synchronization.
session = getJtaSynchronizedSession(sessionHolder, sessionFactory, jdbcExceptionTranslator);
}
if (session != null) {
return session;
}
}
logger.debug("Opening Hibernate Session");
Session session = (entityInterceptor != null ?
sessionFactory.openSession(entityInterceptor) : sessionFactory.openSession());
// Use same Session for further Hibernate actions within the transaction.
// Thread object will get removed by synchronization at transaction completion.
if (TransactionSynchronizationManager.isSynchronizationActive()) {
// We're within a Spring-managed transaction, possibly from JtaTransactionManager.
logger.debug("Registering Spring transaction synchronization for new Hibernate Session");
SessionHolder holderToUse = sessionHolder;
if (holderToUse == null) {
holderToUse = new SessionHolder(session);
}
else {
holderToUse.addSession(session);
}
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
session.setFlushMode(FlushMode.MANUAL);
}
TransactionSynchronizationManager.registerSynchronization(
new SpringSessionSynchronization(holderToUse, sessionFactory, jdbcExceptionTranslator, true));
holderToUse.setSynchronizedWithTransaction(true);
if (holderToUse != sessionHolder) {
TransactionSynchronizationManager.bindResource(sessionFactory, holderToUse);
}
}
else {
// No Spring transaction management active -> try JTA transaction synchronization.
registerJtaSynchronization(session, sessionFactory, jdbcExceptionTranslator, sessionHolder);
}
// Check whether we are allowed to return the Session.
if (!allowCreate && !isSessionTransactional(session, sessionFactory)) {
closeSession(session);
throw new IllegalStateException("No Hibernate Session bound to thread, " +
"and configuration does not allow creation of non-transactional one here");
}
return session;
}
/**
* Retrieve a Session from the given SessionHolder, potentially from a
* JTA transaction synchronization.
* @param sessionHolder the SessionHolder to check
* @param sessionFactory the SessionFactory to get the JTA TransactionManager from
* @param jdbcExceptionTranslator SQLExcepionTranslator to use for flushing the
* Session on transaction synchronization (may be <code>null</code>)
* @return the associated Session, if any
* @throws DataAccessResourceFailureException if the Session couldn't be created
*/
private static Session getJtaSynchronizedSession(
SessionHolder sessionHolder, SessionFactory sessionFactory,
SQLExceptionTranslator jdbcExceptionTranslator) throws DataAccessResourceFailureException {
// JTA synchronization is only possible with a javax.transaction.TransactionManager.
// We'll check the Hibernate SessionFactory: If a TransactionManagerLookup is specified
// in Hibernate configuration, it will contain a TransactionManager reference.
TransactionManager jtaTm = getJtaTransactionManager(sessionFactory, sessionHolder.getAnySession());
if (jtaTm != null) {
// Check whether JTA transaction management is active ->
// fetch pre-bound Session for the current JTA transaction, if any.
// (just necessary for JTA transaction suspension, with an individual
// Hibernate Session per currently active/suspended transaction)
try {
// Look for transaction-specific Session.
Transaction jtaTx = jtaTm.getTransaction();
if (jtaTx != null) {
int jtaStatus = jtaTx.getStatus();
if (jtaStatus == Status.STATUS_ACTIVE || jtaStatus == Status.STATUS_MARKED_ROLLBACK) {
Session session = sessionHolder.getValidatedSession(jtaTx);
if (session == null && !sessionHolder.isSynchronizedWithTransaction()) {
// No transaction-specific Session found: If not already marked as
// synchronized with transaction, register the default thread-bound
// Session as JTA-transactional. If there is no default Session,
// we're a new inner JTA transaction with an outer one being suspended:
// In that case, we'll return null to trigger opening of a new Session.
session = sessionHolder.getValidatedSession();
if (session != null) {
logger.debug("Registering JTA transaction synchronization for existing Hibernate Session");
sessionHolder.addSession(jtaTx, session);
jtaTx.registerSynchronization(
new SpringJtaSynchronizationAdapter(
new SpringSessionSynchronization(sessionHolder, sessionFactory, jdbcExceptionTranslator, false),
jtaTm));
sessionHolder.setSynchronizedWithTransaction(true);
// Switch to FlushMode.AUTO, as we have to assume a thread-bound Session
// with FlushMode.NEVER, which needs to allow flushing within the transaction.
FlushMode flushMode = session.getFlushMode();
if (flushMode.lessThan(FlushMode.COMMIT)) {
session.setFlushMode(FlushMode.AUTO);
sessionHolder.setPreviousFlushMode(flushMode);
}
}
}
return session;
}
}
// No transaction active -> simply return default thread-bound Session, if any
// (possibly from OpenSessionInViewFilter/Interceptor).
return sessionHolder.getValidatedSession();
}
catch (Throwable ex) {
throw new DataAccessResourceFailureException("Could not check JTA transaction", ex);
}
}
else {
// No JTA TransactionManager -> simply return default thread-bound Session, if any
// (possibly from OpenSessionInViewFilter/Interceptor).
return sessionHolder.getValidatedSession();
}
}
/**
* Register a JTA synchronization for the given Session, if any.
* @param sessionHolder the existing thread-bound SessionHolder, if any
* @param session the Session to register
* @param sessionFactory the SessionFactory that the Session was created with
* @param jdbcExceptionTranslator SQLExcepionTranslator to use for flushing the
* Session on transaction synchronization (may be <code>null</code>)
*/
private static void registerJtaSynchronization(Session session, SessionFactory sessionFactory,
SQLExceptionTranslator jdbcExceptionTranslator, SessionHolder sessionHolder) {
// JTA synchronization is only possible with a javax.transaction.TransactionManager.
// We'll check the Hibernate SessionFactory: If a TransactionManagerLookup is specified
// in Hibernate configuration, it will contain a TransactionManager reference.
TransactionManager jtaTm = getJtaTransactionManager(sessionFactory, session);
if (jtaTm != null) {
try {
Transaction jtaTx = jtaTm.getTransaction();
if (jtaTx != null) {
int jtaStatus = jtaTx.getStatus();
if (jtaStatus == Status.STATUS_ACTIVE || jtaStatus == Status.STATUS_MARKED_ROLLBACK) {
logger.debug("Registering JTA transaction synchronization for new Hibernate Session");
SessionHolder holderToUse = sessionHolder;
// Register JTA Transaction with existing SessionHolder.
// Create a new SessionHolder if none existed before.
if (holderToUse == null) {
holderToUse = new SessionHolder(jtaTx, session);
}
else {
holderToUse.addSession(jtaTx, session);
}
jtaTx.registerSynchronization(
new SpringJtaSynchronizationAdapter(
new SpringSessionSynchronization(holderToUse, sessionFactory, jdbcExceptionTranslator, true),
jtaTm));
holderToUse.setSynchronizedWithTransaction(true);
if (holderToUse != sessionHolder) {
TransactionSynchronizationManager.bindResource(sessionFactory, holderToUse);
}
}
}
}
catch (Throwable ex) {
throw new DataAccessResourceFailureException(
"Could not register synchronization with JTA TransactionManager", ex);
}
}
}
/**
* Get a new Hibernate Session from the given SessionFactory.
* Will return a new Session even if there already is a pre-bound
* Session for the given SessionFactory.
* <p>Within a transaction, this method will create a new Session
* that shares the transaction's JDBC Connection. More specifically,
* it will use the same JDBC Connection as the pre-bound Hibernate Session.
* @param sessionFactory Hibernate SessionFactory to create the session with
* @return the new Session
*/
public static Session getNewSession(SessionFactory sessionFactory) {
return getNewSession(sessionFactory, null);
}
/**
* Get a new Hibernate Session from the given SessionFactory.
* Will return a new Session even if there already is a pre-bound
* Session for the given SessionFactory.
* <p>Within a transaction, this method will create a new Session
* that shares the transaction's JDBC Connection. More specifically,
* it will use the same JDBC Connection as the pre-bound Hibernate Session.
* @param sessionFactory Hibernate SessionFactory to create the session with
* @param entityInterceptor Hibernate entity interceptor, or <code>null</code> if none
* @return the new Session
*/
public static Session getNewSession(SessionFactory sessionFactory, Interceptor entityInterceptor) {
Assert.notNull(sessionFactory, "No SessionFactory specified");
try {
SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
if (sessionHolder != null && !sessionHolder.isEmpty()) {
if (entityInterceptor != null) {
return sessionFactory.openSession(sessionHolder.getAnySession().connection(), entityInterceptor);
}
else {
return sessionFactory.openSession(sessionHolder.getAnySession().connection());
}
}
else {
if (entityInterceptor != null) {
return sessionFactory.openSession(entityInterceptor);
}
else {
return sessionFactory.openSession();
}
}
}
catch (HibernateException ex) {
throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex);
}
}
/**
* Stringify the given Session for debug logging.
* Returns output equivalent to <code>Object.toString()</code>:
* the fully qualified class name + "@" + the identity hash code.
* <p>The sole reason why this is necessary is because Hibernate3's
* <code>Session.toString()</code> implementation is broken (and won't be fixed):
* it logs the toString representation of all persistent objects in the Session,
* which might lead to ConcurrentModificationExceptions if the persistent objects
* in turn refer to the Session (for example, for lazy loading).
* @param session the Hibernate Session to stringify
* @return the String representation of the given Session
*/
public static String toString(Session session) {
return session.getClass().getName() + "@" + Integer.toHexString(System.identityHashCode(session));
}
/**
* Return whether there is a transactional Hibernate Session for the current thread,
* that is, a Session bound to the current thread by Spring's transaction facilities.
* @param sessionFactory Hibernate SessionFactory to check (may be <code>null</code>)
* @return whether there is a transactional Session for current thread
*/
public static boolean hasTransactionalSession(SessionFactory sessionFactory) {
if (sessionFactory == null) {
return false;
}
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
return (sessionHolder != null && !sessionHolder.isEmpty());
}
/**
* Return whether the given Hibernate Session is transactional, that is,
* bound to the current thread by Spring's transaction facilities.
* @param session the Hibernate Session to check
* @param sessionFactory Hibernate SessionFactory that the Session was created with
* (may be <code>null</code>)
* @return whether the Session is transactional
*/
public static boolean isSessionTransactional(Session session, SessionFactory sessionFactory) {
if (sessionFactory == null) {
return false;
}
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
return (sessionHolder != null && sessionHolder.containsSession(session));
}
/**
* Apply the current transaction timeout, if any, to the given
* Hibernate Query object.
* @param query the Hibernate Query object
* @param sessionFactory Hibernate SessionFactory that the Query was created for
* (may be <code>null</code>)
* @see org.hibernate.Query#setTimeout
*/
public static void applyTransactionTimeout(Query query, SessionFactory sessionFactory) {
Assert.notNull(query, "No Query object specified");
if (sessionFactory != null) {
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
if (sessionHolder != null && sessionHolder.hasTimeout()) {
query.setTimeout(sessionHolder.getTimeToLiveInSeconds());
}
}
}
/**
* Apply the current transaction timeout, if any, to the given
* Hibernate Criteria object.
* @param criteria the Hibernate Criteria object
* @param sessionFactory Hibernate SessionFactory that the Criteria was created for
* @see org.hibernate.Criteria#setTimeout
*/
public static void applyTransactionTimeout(Criteria criteria, SessionFactory sessionFactory) {
Assert.notNull(criteria, "No Criteria object specified");
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
if (sessionHolder != null && sessionHolder.hasTimeout()) {
criteria.setTimeout(sessionHolder.getTimeToLiveInSeconds());
}
}
/**
* Convert the given HibernateException to an appropriate exception
* from the <code>org.springframework.dao</code> hierarchy.
* @param ex HibernateException that occured
* @return the corresponding DataAccessException instance
* @see HibernateAccessor#convertHibernateAccessException
* @see HibernateTransactionManager#convertHibernateAccessException
*/
public static DataAccessException convertHibernateAccessException(HibernateException ex) {
if (ex instanceof JDBCConnectionException) {
return new DataAccessResourceFailureException(ex.getMessage(), ex);
}
if (ex instanceof SQLGrammarException) {
SQLGrammarException jdbcEx = (SQLGrammarException) ex;
return new InvalidDataAccessResourceUsageException(ex.getMessage() + "; SQL [" + jdbcEx.getSQL() + "]", ex);
}
if (ex instanceof LockAcquisitionException) {
LockAcquisitionException jdbcEx = (LockAcquisitionException) ex;
return new CannotAcquireLockException(ex.getMessage() + "; SQL [" + jdbcEx.getSQL() + "]", ex);
}
if (ex instanceof ConstraintViolationException) {
ConstraintViolationException jdbcEx = (ConstraintViolationException) ex;
return new DataIntegrityViolationException(ex.getMessage() + "; SQL [" + jdbcEx.getSQL() +
"]; constraint [" + jdbcEx.getConstraintName() + "]", ex);
}
if (ex instanceof DataException) {
DataException jdbcEx = (DataException) ex;
return new DataIntegrityViolationException(ex.getMessage() + "; SQL [" + jdbcEx.getSQL() + "]", ex);
}
if (ex instanceof JDBCException) {
return new HibernateJdbcException((JDBCException) ex);
}
// end of JDBCException (subclass) handling
if (ex instanceof QueryException) {
return new HibernateQueryException((QueryException) ex);
}
if (ex instanceof NonUniqueResultException) {
return new IncorrectResultSizeDataAccessException(ex.getMessage(), 1, ex);
}
if (ex instanceof NonUniqueObjectException) {
return new DuplicateKeyException(ex.getMessage(), ex);
}
if (ex instanceof PropertyValueException) {
return new DataIntegrityViolationException(ex.getMessage(), ex);
}
if (ex instanceof PersistentObjectException) {
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
}
if (ex instanceof TransientObjectException) {
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
}
if (ex instanceof ObjectDeletedException) {
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
}
if (ex instanceof UnresolvableObjectException) {
return new HibernateObjectRetrievalFailureException((UnresolvableObjectException) ex);
}
if (ex instanceof WrongClassException) {
return new HibernateObjectRetrievalFailureException((WrongClassException) ex);
}
if (ex instanceof StaleObjectStateException) {
return new HibernateOptimisticLockingFailureException((StaleObjectStateException) ex);
}
if (ex instanceof StaleStateException) {
return new HibernateOptimisticLockingFailureException((StaleStateException) ex);
}
// fallback
return new HibernateSystemException(ex);
}
/**
* Determine whether deferred close is active for the current thread
* and the given SessionFactory.
* @param sessionFactory the Hibernate SessionFactory to check
* @return whether deferred close is active
*/
public static boolean isDeferredCloseActive(SessionFactory sessionFactory) {
Assert.notNull(sessionFactory, "No SessionFactory specified");
Map<SessionFactory, Set<Session>> holderMap = deferredCloseHolder.get();
return (holderMap != null && holderMap.containsKey(sessionFactory));
}
/**
* Initialize deferred close for the current thread and the given SessionFactory.
* Sessions will not be actually closed on close calls then, but rather at a
* {@link #processDeferredClose} call at a finishing point (like request completion).
* <p>Used by {@link org.springframework.orm.hibernate3.support.OpenSessionInViewFilter}
* and {@link org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor}
* when not configured for a single session.
* @param sessionFactory the Hibernate SessionFactory to initialize deferred close for
* @see #processDeferredClose
* @see #releaseSession
* @see org.springframework.orm.hibernate3.support.OpenSessionInViewFilter#setSingleSession
* @see org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor#setSingleSession
*/
public static void initDeferredClose(SessionFactory sessionFactory) {
Assert.notNull(sessionFactory, "No SessionFactory specified");
logger.debug("Initializing deferred close of Hibernate Sessions");
Map<SessionFactory, Set<Session>> holderMap = deferredCloseHolder.get();
if (holderMap == null) {
holderMap = new HashMap<SessionFactory, Set<Session>>();
deferredCloseHolder.set(holderMap);
}
holderMap.put(sessionFactory, new LinkedHashSet<Session>(4));
}
/**
* Process all Hibernate Sessions that have been registered for deferred close
* for the given SessionFactory.
* @param sessionFactory the Hibernate SessionFactory to process deferred close for
* @see #initDeferredClose
* @see #releaseSession
*/
public static void processDeferredClose(SessionFactory sessionFactory) {
Assert.notNull(sessionFactory, "No SessionFactory specified");
Map<SessionFactory, Set<Session>> holderMap = deferredCloseHolder.get();
if (holderMap == null || !holderMap.containsKey(sessionFactory)) {
throw new IllegalStateException("Deferred close not active for SessionFactory [" + sessionFactory + "]");
}
logger.debug("Processing deferred close of Hibernate Sessions");
Set<Session> sessions = holderMap.remove(sessionFactory);
for (Session session : sessions) {
closeSession(session);
}
if (holderMap.isEmpty()) {
deferredCloseHolder.remove();
}
}
/**
* Close the given Session, created via the given factory,
* if it is not managed externally (i.e. not bound to the thread).
* @param session the Hibernate Session to close (may be <code>null</code>)
* @param sessionFactory Hibernate SessionFactory that the Session was created with
* (may be <code>null</code>)
*/
public static void releaseSession(Session session, SessionFactory sessionFactory) {
if (session == null) {
return;
}
// Only close non-transactional Sessions.
if (!isSessionTransactional(session, sessionFactory)) {
closeSessionOrRegisterDeferredClose(session, sessionFactory);
}
}
/**
* Close the given Session or register it for deferred close.
* @param session the Hibernate Session to close
* @param sessionFactory Hibernate SessionFactory that the Session was created with
* (may be <code>null</code>)
* @see #initDeferredClose
* @see #processDeferredClose
*/
static void closeSessionOrRegisterDeferredClose(Session session, SessionFactory sessionFactory) {
Map<SessionFactory, Set<Session>> holderMap = deferredCloseHolder.get();
if (holderMap != null && sessionFactory != null && holderMap.containsKey(sessionFactory)) {
logger.debug("Registering Hibernate Session for deferred close");
// Switch Session to FlushMode.MANUAL for remaining lifetime.
session.setFlushMode(FlushMode.MANUAL);
Set<Session> sessions = holderMap.get(sessionFactory);
sessions.add(session);
}
else {
closeSession(session);
}
}
/**
* Perform actual closing of the Hibernate Session,
* catching and logging any cleanup exceptions thrown.
* @param session the Hibernate Session to close (may be <code>null</code>)
* @see org.hibernate.Session#close()
*/
public static void closeSession(Session session) {
if (session != null) {
logger.debug("Closing Hibernate Session");
try {
session.close();
}
catch (HibernateException ex) {
logger.debug("Could not close Hibernate Session", ex);
}
catch (Throwable ex) {
logger.debug("Unexpected exception on closing Hibernate Session", ex);
}
}
}
}

View File

@@ -0,0 +1,154 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.hibernate.FlushMode;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.springframework.transaction.support.ResourceHolderSupport;
import org.springframework.util.Assert;
/**
* Session holder, wrapping a Hibernate Session and a Hibernate Transaction.
* HibernateTransactionManager binds instances of this class to the thread,
* for a given SessionFactory.
*
* <p>Note: This is an SPI class, not intended to be used by applications.
*
* @author Juergen Hoeller
* @since 1.2
* @see HibernateTransactionManager
* @see SessionFactoryUtils
*/
public class SessionHolder extends ResourceHolderSupport {
private static final Object DEFAULT_KEY = new Object();
/**
* This Map needs to be synchronized because there might be multi-threaded
* access in the case of JTA with remote transaction propagation.
*/
private final Map<Object, Session> sessionMap = Collections.synchronizedMap(new HashMap<Object, Session>(1));
private Transaction transaction;
private FlushMode previousFlushMode;
public SessionHolder(Session session) {
addSession(session);
}
public SessionHolder(Object key, Session session) {
addSession(key, session);
}
public Session getSession() {
return getSession(DEFAULT_KEY);
}
public Session getSession(Object key) {
return this.sessionMap.get(key);
}
public Session getValidatedSession() {
return getValidatedSession(DEFAULT_KEY);
}
public Session getValidatedSession(Object key) {
Session session = this.sessionMap.get(key);
// Check for dangling Session that's around but already closed.
// Effectively an assertion: that should never happen in practice.
// We'll seamlessly remove the Session here, to not let it cause
// any side effects.
if (session != null && !session.isOpen()) {
this.sessionMap.remove(key);
session = null;
}
return session;
}
public Session getAnySession() {
synchronized (this.sessionMap) {
if (!this.sessionMap.isEmpty()) {
return this.sessionMap.values().iterator().next();
}
return null;
}
}
public void addSession(Session session) {
addSession(DEFAULT_KEY, session);
}
public void addSession(Object key, Session session) {
Assert.notNull(key, "Key must not be null");
Assert.notNull(session, "Session must not be null");
this.sessionMap.put(key, session);
}
public Session removeSession(Object key) {
return this.sessionMap.remove(key);
}
public boolean containsSession(Session session) {
return this.sessionMap.containsValue(session);
}
public boolean isEmpty() {
return this.sessionMap.isEmpty();
}
public boolean doesNotHoldNonDefaultSession() {
synchronized (this.sessionMap) {
return this.sessionMap.isEmpty() ||
(this.sessionMap.size() == 1 && this.sessionMap.containsKey(DEFAULT_KEY));
}
}
public void setTransaction(Transaction transaction) {
this.transaction = transaction;
}
public Transaction getTransaction() {
return this.transaction;
}
public void setPreviousFlushMode(FlushMode previousFlushMode) {
this.previousFlushMode = previousFlushMode;
}
public FlushMode getPreviousFlushMode() {
return this.previousFlushMode;
}
@Override
public void clear() {
super.clear();
this.transaction = null;
this.previousFlushMode = null;
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3;
import org.hibernate.HibernateException;
import org.hibernate.classic.Session;
import org.hibernate.context.CurrentSessionContext;
import org.hibernate.engine.SessionFactoryImplementor;
/**
* Implementation of Hibernate 3.1's CurrentSessionContext interface
* that delegates to Spring's SessionFactoryUtils for providing a
* Spring-managed current Session.
*
* <p>Used by Spring's {@link LocalSessionFactoryBean} when told to expose a
* transaction-aware SessionFactory. This is the default as of Spring 2.5.
*
* <p>This CurrentSessionContext implementation can also be specified in custom
* SessionFactory setup through the "hibernate.current_session_context_class"
* property, with the fully qualified name of this class as value.
*
* @author Juergen Hoeller
* @since 2.0
* @see SessionFactoryUtils#doGetSession
* @see LocalSessionFactoryBean#setExposeTransactionAwareSessionFactory
*/
public class SpringSessionContext implements CurrentSessionContext {
private final SessionFactoryImplementor sessionFactory;
/**
* Create a new SpringSessionContext for the given Hibernate SessionFactory.
* @param sessionFactory the SessionFactory to provide current Sessions for
*/
public SpringSessionContext(SessionFactoryImplementor sessionFactory) {
this.sessionFactory = sessionFactory;
}
/**
* Retrieve the Spring-managed Session for the current thread, if any.
*/
public Session currentSession() throws HibernateException {
try {
return (org.hibernate.classic.Session) SessionFactoryUtils.doGetSession(this.sessionFactory, false);
}
catch (IllegalStateException ex) {
throw new HibernateException(ex.getMessage());
}
}
}

View File

@@ -0,0 +1,258 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3;
import javax.transaction.SystemException;
import javax.transaction.Transaction;
import javax.transaction.TransactionManager;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.JDBCException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.engine.SessionImplementor;
import org.springframework.core.Ordered;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.jdbc.support.SQLExceptionTranslator;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
* Callback for resource cleanup at the end of a Spring-managed JTA transaction,
* that is, when participating in a JtaTransactionManager transaction.
*
* @author Juergen Hoeller
* @since 1.2
* @see SessionFactoryUtils
* @see org.springframework.transaction.jta.JtaTransactionManager
*/
class SpringSessionSynchronization implements TransactionSynchronization, Ordered {
private final SessionHolder sessionHolder;
private final SessionFactory sessionFactory;
private final SQLExceptionTranslator jdbcExceptionTranslator;
private final boolean newSession;
/**
* Whether Hibernate has a looked-up JTA TransactionManager that it will
* automatically register CacheSynchronizations with on Session connect.
*/
private boolean hibernateTransactionCompletion = false;
private Transaction jtaTransaction;
private boolean holderActive = true;
public SpringSessionSynchronization(
SessionHolder sessionHolder, SessionFactory sessionFactory,
SQLExceptionTranslator jdbcExceptionTranslator, boolean newSession) {
this.sessionHolder = sessionHolder;
this.sessionFactory = sessionFactory;
this.jdbcExceptionTranslator = jdbcExceptionTranslator;
this.newSession = newSession;
// Check whether the SessionFactory has a JTA TransactionManager.
TransactionManager jtaTm =
SessionFactoryUtils.getJtaTransactionManager(sessionFactory, sessionHolder.getAnySession());
if (jtaTm != null) {
this.hibernateTransactionCompletion = true;
// Fetch current JTA Transaction object
// (just necessary for JTA transaction suspension, with an individual
// Hibernate Session per currently active/suspended transaction).
try {
this.jtaTransaction = jtaTm.getTransaction();
}
catch (SystemException ex) {
throw new DataAccessResourceFailureException("Could not access JTA transaction", ex);
}
}
}
/**
* Check whether there is a Hibernate Session for the current JTA
* transaction. Else, fall back to the default thread-bound Session.
*/
private Session getCurrentSession() {
Session session = null;
if (this.jtaTransaction != null) {
session = this.sessionHolder.getSession(this.jtaTransaction);
}
if (session == null) {
session = this.sessionHolder.getSession();
}
return session;
}
public int getOrder() {
return SessionFactoryUtils.SESSION_SYNCHRONIZATION_ORDER;
}
public void suspend() {
if (this.holderActive) {
TransactionSynchronizationManager.unbindResource(this.sessionFactory);
// Eagerly disconnect the Session here, to make release mode "on_close" work on JBoss.
getCurrentSession().disconnect();
}
}
public void resume() {
if (this.holderActive) {
TransactionSynchronizationManager.bindResource(this.sessionFactory, this.sessionHolder);
}
}
public void flush() {
try {
SessionFactoryUtils.logger.debug("Flushing Hibernate Session on explicit request");
getCurrentSession().flush();
}
catch (HibernateException ex) {
throw translateException(ex);
}
}
public void beforeCommit(boolean readOnly) throws DataAccessException {
if (!readOnly) {
Session session = getCurrentSession();
// Read-write transaction -> flush the Hibernate Session.
// Further check: only flush when not FlushMode.NEVER/MANUAL.
if (!session.getFlushMode().lessThan(FlushMode.COMMIT)) {
try {
SessionFactoryUtils.logger.debug("Flushing Hibernate Session on transaction synchronization");
session.flush();
}
catch (HibernateException ex) {
throw translateException(ex);
}
}
}
}
private DataAccessException translateException(HibernateException ex) {
if (this.jdbcExceptionTranslator != null && ex instanceof JDBCException) {
JDBCException jdbcEx = (JDBCException) ex;
return this.jdbcExceptionTranslator.translate(
"Hibernate flushing: " + jdbcEx.getMessage(), jdbcEx.getSQL(), jdbcEx.getSQLException());
}
return SessionFactoryUtils.convertHibernateAccessException(ex);
}
public void beforeCompletion() {
if (this.jtaTransaction != null) {
// Typically in case of a suspended JTA transaction:
// Remove the Session for the current JTA transaction, but keep the holder.
Session session = this.sessionHolder.removeSession(this.jtaTransaction);
if (session != null) {
if (this.sessionHolder.isEmpty()) {
// No Sessions for JTA transactions bound anymore -> could remove it.
TransactionSynchronizationManager.unbindResourceIfPossible(this.sessionFactory);
this.holderActive = false;
}
// Do not close a pre-bound Session. In that case, we'll find the
// transaction-specific Session the same as the default Session.
if (session != this.sessionHolder.getSession()) {
SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, this.sessionFactory);
}
else {
if (this.sessionHolder.getPreviousFlushMode() != null) {
// In case of pre-bound Session, restore previous flush mode.
session.setFlushMode(this.sessionHolder.getPreviousFlushMode());
}
// Eagerly disconnect the Session here, to make release mode "on_close" work nicely.
session.disconnect();
}
return;
}
}
// We'll only get here if there was no specific JTA transaction to handle.
if (this.newSession) {
// Default behavior: unbind and close the thread-bound Hibernate Session.
TransactionSynchronizationManager.unbindResource(this.sessionFactory);
this.holderActive = false;
if (this.hibernateTransactionCompletion) {
// Close the Hibernate Session here in case of a Hibernate TransactionManagerLookup:
// Hibernate will automatically defer the actual closing until JTA transaction completion.
// Else, the Session will be closed in the afterCompletion method, to provide the
// correct transaction status for releasing the Session's cache locks.
SessionFactoryUtils.closeSessionOrRegisterDeferredClose(this.sessionHolder.getSession(), this.sessionFactory);
}
}
else {
Session session = this.sessionHolder.getSession();
if (this.sessionHolder.getPreviousFlushMode() != null) {
// In case of pre-bound Session, restore previous flush mode.
session.setFlushMode(this.sessionHolder.getPreviousFlushMode());
}
if (this.hibernateTransactionCompletion) {
// Eagerly disconnect the Session here, to make release mode "on_close" work nicely.
// We know that this is appropriate if a TransactionManagerLookup has been specified.
session.disconnect();
}
}
}
public void afterCommit() {
}
public void afterCompletion(int status) {
try {
if (!this.hibernateTransactionCompletion || !this.newSession) {
// No Hibernate TransactionManagerLookup: apply afterTransactionCompletion callback.
// Always perform explicit afterTransactionCompletion callback for pre-bound Session,
// even with Hibernate TransactionManagerLookup (which only applies to new Sessions).
Session session = this.sessionHolder.getSession();
// Provide correct transaction status for releasing the Session's cache locks,
// if possible. Else, closing will release all cache locks assuming a rollback.
try {
if (session instanceof SessionImplementor) {
((SessionImplementor) session).afterTransactionCompletion(status == STATUS_COMMITTED, null);
}
}
finally {
// Close the Hibernate Session here if necessary
// (closed in beforeCompletion in case of TransactionManagerLookup).
if (this.newSession) {
SessionFactoryUtils.closeSessionOrRegisterDeferredClose(session, this.sessionFactory);
}
else if (!this.hibernateTransactionCompletion) {
session.disconnect();
}
}
}
if (!this.newSession && status != STATUS_COMMITTED) {
// Clear all pending inserts/updates/deletes in the Session.
// Necessary for pre-bound Sessions, to avoid inconsistent state.
this.sessionHolder.getSession().clear();
}
}
finally {
if (this.sessionHolder.doesNotHoldNonDefaultSession()) {
this.sessionHolder.setSynchronizedWithTransaction(false);
}
}
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3;
import java.util.Properties;
import org.hibernate.ConnectionReleaseMode;
import org.hibernate.Transaction;
import org.hibernate.jdbc.JDBCContext;
import org.hibernate.transaction.JDBCTransaction;
import org.hibernate.transaction.TransactionFactory;
import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
* Spring-aware implementation of the Hibernate TransactionFactory interface, aware of
* Spring-synchronized transactions (in particular Spring-managed JTA transactions)
* and asking for default release mode ON_CLOSE. Otherwise identical to Hibernate's
* default {@link org.hibernate.transaction.JDBCTransactionFactory} implementation.
*
* @author Juergen Hoeller
* @since 2.5.4
* @see org.springframework.transaction.support.TransactionSynchronizationManager
* @see org.hibernate.transaction.JDBCTransactionFactory
*/
public class SpringTransactionFactory implements TransactionFactory {
/**
* Sets connection release mode "on_close" as default.
* <p>This was the case for Hibernate 3.0; Hibernate 3.1 changed
* it to "auto" (i.e. "after_statement" or "after_transaction").
* However, for Spring's resource management (in particular for
* HibernateTransactionManager), "on_close" is the better default.
*/
public ConnectionReleaseMode getDefaultReleaseMode() {
return ConnectionReleaseMode.ON_CLOSE;
}
public Transaction createTransaction(JDBCContext jdbcContext, Context transactionContext) {
return new JDBCTransaction(jdbcContext, transactionContext);
}
public void configure(Properties props) {
}
public boolean isTransactionManagerRequired() {
return false;
}
public boolean areCallbacksLocalToHibernateTransactions() {
return true;
}
public boolean isTransactionInProgress(
JDBCContext jdbcContext, Context transactionContext, Transaction transaction) {
return (transaction != null && transaction.isActive()) ||
TransactionSynchronizationManager.isActualTransactionActive();
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3;
import javax.sql.DataSource;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
/**
* Subclass of LocalDataSourceConnectionProvider that returns a
* transaction-aware proxy for the exposed DataSource. Used if
* LocalSessionFactoryBean's "useTransactionAwareDataSource" flag is on.
*
* @author Juergen Hoeller
* @since 1.2
* @see LocalSessionFactoryBean#setUseTransactionAwareDataSource
*/
public class TransactionAwareDataSourceConnectionProvider extends LocalDataSourceConnectionProvider {
/**
* Return a TransactionAwareDataSourceProxy for the given DataSource,
* provided that it isn't a TransactionAwareDataSourceProxy already.
*/
@Override
protected DataSource getDataSourceToUse(DataSource originalDataSource) {
if (originalDataSource instanceof TransactionAwareDataSourceProxy) {
return originalDataSource;
}
return new TransactionAwareDataSourceProxy(originalDataSource);
}
/**
* This implementation returns <code>true</code>: We can guarantee
* to receive the same Connection within a transaction, as we are
* exposing a TransactionAwareDataSourceProxy.
*/
@Override
public boolean supportsAggressiveRelease() {
return true;
}
}

View File

@@ -0,0 +1,129 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3;
import java.util.Properties;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.InitializingBean;
/**
* Bean that encapsulates a Hibernate type definition.
*
* <p>Typically defined as inner bean within a LocalSessionFactoryBean
* definition, as list element for the "typeDefinitions" bean property.
* For example:
*
* <pre>
* &lt;bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean"&gt;
* ...
* &lt;property name="typeDefinitions"&gt;
* &lt;list&gt;
* &lt;bean class="org.springframework.orm.hibernate3.TypeDefinitionBean"&gt;
* &lt;property name="typeName" value="myType"/&gt;
* &lt;property name="typeClass" value="mypackage.MyTypeClass"/&gt;
* &lt;/bean&gt;
* &lt;/list&gt;
* &lt;/property&gt;
* ...
* &lt;/bean&gt;</pre>
*
* Alternatively, specify a bean id (or name) attribute for the inner bean,
* instead of the "typeName" property.
*
* @author Juergen Hoeller
* @since 1.2
* @see LocalSessionFactoryBean#setTypeDefinitions(TypeDefinitionBean[])
*/
public class TypeDefinitionBean implements BeanNameAware, InitializingBean {
private String typeName;
private String typeClass;
private Properties parameters = new Properties();
/**
* Set the name of the type.
* @see org.hibernate.cfg.Mappings#addTypeDef(String, String, java.util.Properties)
*/
public void setTypeName(String typeName) {
this.typeName = typeName;
}
/**
* Return the name of the type.
*/
public String getTypeName() {
return typeName;
}
/**
* Set the type implementation class.
* @see org.hibernate.cfg.Mappings#addTypeDef(String, String, java.util.Properties)
*/
public void setTypeClass(String typeClass) {
this.typeClass = typeClass;
}
/**
* Return the type implementation class.
*/
public String getTypeClass() {
return typeClass;
}
/**
* Specify default parameters for the type.
* This only applies to parameterized types.
* @see org.hibernate.cfg.Mappings#addTypeDef(String, String, java.util.Properties)
* @see org.hibernate.usertype.ParameterizedType
*/
public void setParameters(Properties parameters) {
this.parameters = parameters;
}
/**
* Return the default parameters for the type.
*/
public Properties getParameters() {
return parameters;
}
/**
* If no explicit type name has been specified, the bean name of
* the TypeDefinitionBean will be used.
* @see #setTypeName
*/
public void setBeanName(String name) {
if (this.typeName == null) {
this.typeName = name;
}
}
public void afterPropertiesSet() {
if (this.typeName == null) {
throw new IllegalArgumentException("typeName is required");
}
if (this.typeClass == null) {
throw new IllegalArgumentException("typeClass is required");
}
}
}

View File

@@ -0,0 +1,248 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3.annotation;
import java.io.IOException;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.MappedSuperclass;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.cfg.AnnotationConfiguration;
import org.hibernate.cfg.Configuration;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.orm.hibernate3.LocalSessionFactoryBean;
import org.springframework.util.ClassUtils;
/**
* Subclass of Spring's standard LocalSessionFactoryBean for Hibernate,
* supporting JDK 1.5+ annotation metadata for mappings.
*
* <p>Note: This class requires Hibernate 3.2 or later, with the
* Java Persistence API and the Hibernate Annotations add-on present.
*
* <p>Example for an AnnotationSessionFactoryBean bean definition:
*
* <pre class="code">
* &lt;bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"&gt;
* &lt;property name="dataSource" ref="dataSource"/&gt;
* &lt;property name="annotatedClasses"&gt;
* &lt;list&gt;
* &lt;value&gt;test.package.Foo&lt;/value&gt;
* &lt;value&gt;test.package.Bar&lt;/value&gt;
* &lt;/list&gt;
* &lt;/property&gt;
* &lt;/bean&gt;</pre>
*
* Or when using classpath scanning for autodetection of entity classes:
*
* <pre class="code">
* &lt;bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean"&gt;
* &lt;property name="dataSource" ref="dataSource"/&gt;
* &lt;property name="packagesToScan" value="test.package"/&gt;
* &lt;/bean&gt;</pre>
*
* @author Juergen Hoeller
* @since 1.2.2
* @see #setDataSource
* @see #setHibernateProperties
* @see #setAnnotatedClasses
* @see #setAnnotatedPackages
*/
public class AnnotationSessionFactoryBean extends LocalSessionFactoryBean implements ResourceLoaderAware {
private static final String RESOURCE_PATTERN = "/**/*.class";
private Class[] annotatedClasses;
private String[] annotatedPackages;
private String[] packagesToScan;
private TypeFilter[] entityTypeFilters = new TypeFilter[] {
new AnnotationTypeFilter(Entity.class, false),
new AnnotationTypeFilter(Embeddable.class, false),
new AnnotationTypeFilter(MappedSuperclass.class, false),
new AnnotationTypeFilter(org.hibernate.annotations.Entity.class, false)};
private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
public AnnotationSessionFactoryBean() {
setConfigurationClass(AnnotationConfiguration.class);
}
@Override
public void setConfigurationClass(Class configurationClass) {
if (configurationClass == null || !AnnotationConfiguration.class.isAssignableFrom(configurationClass)) {
throw new IllegalArgumentException(
"AnnotationSessionFactoryBean only supports AnnotationConfiguration or subclasses");
}
super.setConfigurationClass(configurationClass);
}
/**
* Specify annotated classes, for which mappings will be read from
* class-level JDK 1.5+ annotation metadata.
* @see org.hibernate.cfg.AnnotationConfiguration#addAnnotatedClass(Class)
*/
public void setAnnotatedClasses(Class[] annotatedClasses) {
this.annotatedClasses = annotatedClasses;
}
/**
* Specify the names of annotated packages, for which package-level
* JDK 1.5+ annotation metadata will be read.
* @see org.hibernate.cfg.AnnotationConfiguration#addPackage(String)
*/
public void setAnnotatedPackages(String[] annotatedPackages) {
this.annotatedPackages = annotatedPackages;
}
/**
* Specify packages to search using Spring-based scanning for entity classes in
* the classpath. This is an alternative to listing annotated classes explicitly.
* <p>Default is none. Specify packages to search for autodetection of your entity
* classes in the classpath. This is analogous to Spring's component-scan feature
* ({@link org.springframework.context.annotation.ClassPathBeanDefinitionScanner}).
*/
public void setPackagesToScan(String[] packagesToScan) {
this.packagesToScan = packagesToScan;
}
/**
* Specify custom type filters for Spring-based scanning for entity classes.
* <p>Default is to search all specified packages for classes annotated with
* <code>@javax.persistence.Entity</code>, <code>@javax.persistence.Embeddable</code>
* or <code>@javax.persistence.MappedSuperclass</code>, as well as for
* Hibernate's special <code>@org.hibernate.annotations.Entity</code>.
* @see #setPackagesToScan
*/
public void setEntityTypeFilters(TypeFilter[] entityTypeFilters) {
this.entityTypeFilters = entityTypeFilters;
}
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
}
/**
* Reads metadata from annotated classes and packages into the
* AnnotationConfiguration instance.
*/
@Override
protected void postProcessMappings(Configuration config) throws HibernateException {
AnnotationConfiguration annConfig = (AnnotationConfiguration) config;
if (this.annotatedClasses != null) {
for (Class annotatedClass : this.annotatedClasses) {
annConfig.addAnnotatedClass(annotatedClass);
}
}
if (this.annotatedPackages != null) {
for (String annotatedPackage : this.annotatedPackages) {
annConfig.addPackage(annotatedPackage);
}
}
scanPackages(annConfig);
}
/**
* Perform Spring-based scanning for entity classes.
* @see #setPackagesToScan
*/
protected void scanPackages(AnnotationConfiguration config) {
if (this.packagesToScan != null) {
try {
for (String pkg : this.packagesToScan) {
String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
ClassUtils.convertClassNameToResourcePath(pkg) + RESOURCE_PATTERN;
Resource[] resources = this.resourcePatternResolver.getResources(pattern);
MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);
for (Resource resource : resources) {
if (resource.isReadable()) {
MetadataReader reader = readerFactory.getMetadataReader(resource);
String className = reader.getClassMetadata().getClassName();
if (matchesFilter(reader, readerFactory)) {
config.addAnnotatedClass(this.resourcePatternResolver.getClassLoader().loadClass(className));
}
}
}
}
}
catch (IOException ex) {
throw new MappingException("Failed to scan classpath for unlisted classes", ex);
}
catch (ClassNotFoundException ex) {
throw new MappingException("Failed to load annotated classes from classpath", ex);
}
}
}
/**
* Check whether any of the configured entity type filters matches
* the current class descriptor contained in the metadata reader.
*/
private boolean matchesFilter(MetadataReader reader, MetadataReaderFactory readerFactory) throws IOException {
if (this.entityTypeFilters != null) {
for (TypeFilter filter : this.entityTypeFilters) {
if (filter.match(reader, readerFactory)) {
return true;
}
}
}
return false;
}
/**
* This default implementation delegates to {@link #postProcessAnnotationConfiguration}.
*/
@Override
protected void postProcessConfiguration(Configuration config) throws HibernateException {
postProcessAnnotationConfiguration((AnnotationConfiguration) config);
}
/**
* To be implemented by subclasses which want to to perform custom
* post-processing of the AnnotationConfiguration object after this
* FactoryBean performed its default initialization.
* <p>Note: As of Hibernate 3.6, AnnotationConfiguration's features
* have been rolled into Configuration itself. Simply overriding
* {@link #postProcessConfiguration(org.hibernate.cfg.Configuration)}
* becomes an option as well then.
* @param config the current AnnotationConfiguration object
* @throws HibernateException in case of Hibernate initialization errors
*/
protected void postProcessAnnotationConfiguration(AnnotationConfiguration config) throws HibernateException {
}
}

View File

@@ -0,0 +1,9 @@
/**
*
* Support package for the Hibernate3 Annotation add-on,
* which supports EJB3-compliant JDK 1.5+ annotations for mappings.
*
*/
package org.springframework.orm.hibernate3.annotation;

View File

@@ -0,0 +1,17 @@
/**
*
* Package providing integration of
* <a href="http://www.hibernate.org">Hibernate3</a>
* with Spring concepts.
*
* <p>Contains SessionFactory helper classes, a template plus callback
* for Hibernate access, and an implementation of Spring's transaction SPI
* for local Hibernate transactions.
*
* <p><b>This package supports Hibernate 3.x only.</b>
* See the org.springframework.orm.hibernate package for Hibernate 2.1 support.
*
*/
package org.springframework.orm.hibernate3;

View File

@@ -0,0 +1,217 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3.support;
import java.io.IOException;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import javax.transaction.Status;
import javax.transaction.TransactionManager;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.usertype.UserType;
import org.hibernate.util.EqualsHelper;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.jdbc.support.lob.JtaLobCreatorSynchronization;
import org.springframework.jdbc.support.lob.LobCreator;
import org.springframework.jdbc.support.lob.LobHandler;
import org.springframework.jdbc.support.lob.SpringLobCreatorSynchronization;
import org.springframework.jdbc.support.lob.LobCreatorUtils;
import org.springframework.orm.hibernate3.LocalSessionFactoryBean;
import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
* Abstract base class for Hibernate UserType implementations that map to LOBs.
* Retrieves the LobHandler to use from LocalSessionFactoryBean at config time.
*
* <p>For writing LOBs, either an active Spring transaction synchronization
* or an active JTA transaction (with "jtaTransactionManager" specified on
* LocalSessionFactoryBean or a Hibernate TransactionManagerLookup configured
* through the corresponding Hibernate property) is required.
*
* <p>Offers template methods for setting parameters and getting result values,
* passing in the LobHandler or LobCreator to use.
*
* @author Juergen Hoeller
* @since 1.2
* @see org.springframework.jdbc.support.lob.LobHandler
* @see org.springframework.jdbc.support.lob.LobCreator
* @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#setLobHandler
* @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#setJtaTransactionManager
*/
public abstract class AbstractLobType implements UserType {
protected final Log logger = LogFactory.getLog(getClass());
private final LobHandler lobHandler;
private final TransactionManager jtaTransactionManager;
/**
* Constructor used by Hibernate: fetches config-time LobHandler and
* config-time JTA TransactionManager from LocalSessionFactoryBean.
* @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#getConfigTimeLobHandler
* @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#getConfigTimeTransactionManager
*/
protected AbstractLobType() {
this(LocalSessionFactoryBean.getConfigTimeLobHandler(),
LocalSessionFactoryBean.getConfigTimeTransactionManager());
}
/**
* Constructor used for testing: takes an explicit LobHandler
* and an explicit JTA TransactionManager (can be <code>null</code>).
*/
protected AbstractLobType(LobHandler lobHandler, TransactionManager jtaTransactionManager) {
this.lobHandler = lobHandler;
this.jtaTransactionManager = jtaTransactionManager;
}
/**
* This implementation returns false.
*/
public boolean isMutable() {
return false;
}
/**
* This implementation delegates to the Hibernate EqualsHelper.
* @see org.hibernate.util.EqualsHelper#equals
*/
public boolean equals(Object x, Object y) throws HibernateException {
return EqualsHelper.equals(x, y);
}
/**
* This implementation returns the hashCode of the given objectz.
*/
public int hashCode(Object x) throws HibernateException {
return x.hashCode();
}
/**
* This implementation returns the passed-in value as-is.
*/
public Object deepCopy(Object value) throws HibernateException {
return value;
}
/**
* This implementation returns the passed-in value as-is.
*/
public Serializable disassemble(Object value) throws HibernateException {
return (Serializable) value;
}
/**
* This implementation returns the passed-in value as-is.
*/
public Object assemble(Serializable cached, Object owner) throws HibernateException {
return cached;
}
/**
* This implementation returns the passed-in original as-is.
*/
public Object replace(Object original, Object target, Object owner) throws HibernateException {
return original;
}
/**
* This implementation delegates to nullSafeGetInternal,
* passing in the LobHandler of this type.
* @see #nullSafeGetInternal
*/
public final Object nullSafeGet(ResultSet rs, String[] names, Object owner)
throws HibernateException, SQLException {
if (this.lobHandler == null) {
throw new IllegalStateException("No LobHandler found for configuration - " +
"lobHandler property must be set on LocalSessionFactoryBean");
}
try {
return nullSafeGetInternal(rs, names, owner, this.lobHandler);
}
catch (IOException ex) {
throw new HibernateException("I/O errors during LOB access", ex);
}
}
/**
* This implementation delegates to nullSafeSetInternal,
* passing in a transaction-synchronized LobCreator for the
* LobHandler of this type.
* @see #nullSafeSetInternal
*/
public final void nullSafeSet(PreparedStatement st, Object value, int index)
throws HibernateException, SQLException {
if (this.lobHandler == null) {
throw new IllegalStateException("No LobHandler found for configuration - " +
"lobHandler property must be set on LocalSessionFactoryBean");
}
LobCreator lobCreator = this.lobHandler.getLobCreator();
try {
nullSafeSetInternal(st, index, value, lobCreator);
}
catch (IOException ex) {
throw new HibernateException("I/O errors during LOB access", ex);
}
LobCreatorUtils.registerTransactionSynchronization(lobCreator, this.jtaTransactionManager);
}
/**
* Template method to extract a value from the given result set.
* @param rs the ResultSet to extract from
* @param names the column names
* @param owner the containing entity
* @param lobHandler the LobHandler to use
* @return the extracted value
* @throws SQLException if thrown by JDBC methods
* @throws IOException if thrown by streaming methods
* @throws HibernateException in case of any other exceptions
*/
protected abstract Object nullSafeGetInternal(
ResultSet rs, String[] names, Object owner, LobHandler lobHandler)
throws SQLException, IOException, HibernateException;
/**
* Template method to set the given parameter value on the given statement.
* @param ps the PreparedStatement to set on
* @param index the statement parameter index
* @param value the value to set
* @param lobCreator the LobCreator to use
* @throws SQLException if thrown by JDBC methods
* @throws IOException if thrown by streaming methods
* @throws HibernateException in case of any other exceptions
*/
protected abstract void nullSafeSetInternal(
PreparedStatement ps, int index, Object value, LobCreator lobCreator)
throws SQLException, IOException, HibernateException;
}

View File

@@ -0,0 +1,109 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3.support;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.util.Arrays;
import javax.transaction.TransactionManager;
import org.springframework.jdbc.support.lob.LobCreator;
import org.springframework.jdbc.support.lob.LobHandler;
/**
* Hibernate UserType implementation for byte arrays that get mapped to BLOBs.
* Retrieves the LobHandler to use from LocalSessionFactoryBean at config time.
*
* <p>Can also be defined in generic Hibernate mappings, as DefaultLobCreator will
* work with most JDBC-compliant database drivers. In this case, the field type
* does not have to be BLOB: For databases like MySQL and MS SQL Server, any
* large enough binary type will work.
*
* @author Juergen Hoeller
* @since 1.2
* @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#setLobHandler
*/
public class BlobByteArrayType extends AbstractLobType {
/**
* Constructor used by Hibernate: fetches config-time LobHandler and
* config-time JTA TransactionManager from LocalSessionFactoryBean.
* @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#getConfigTimeLobHandler
* @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#getConfigTimeTransactionManager
*/
public BlobByteArrayType() {
super();
}
/**
* Constructor used for testing: takes an explicit LobHandler
* and an explicit JTA TransactionManager (can be <code>null</code>).
*/
protected BlobByteArrayType(LobHandler lobHandler, TransactionManager jtaTransactionManager) {
super(lobHandler, jtaTransactionManager);
}
public int[] sqlTypes() {
return new int[] {Types.BLOB};
}
public Class returnedClass() {
return byte[].class;
}
@Override
public boolean isMutable() {
return true;
}
@Override
public boolean equals(Object x, Object y) {
return (x == y) ||
(x instanceof byte[] && y instanceof byte[] && Arrays.equals((byte[]) x, (byte[]) y));
}
@Override
public Object deepCopy(Object value) {
if (value == null) {
return null;
}
byte[] original = (byte[]) value;
byte[] copy = new byte[original.length];
System.arraycopy(original, 0, copy, 0, original.length);
return copy;
}
@Override
protected Object nullSafeGetInternal(
ResultSet rs, String[] names, Object owner, LobHandler lobHandler)
throws SQLException {
return lobHandler.getBlobAsBytes(rs, names[0]);
}
@Override
protected void nullSafeSetInternal(
PreparedStatement ps, int index, Object value, LobCreator lobCreator)
throws SQLException {
lobCreator.setBlobAsBytes(ps, index, (byte[]) value);
}
}

View File

@@ -0,0 +1,168 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3.support;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import javax.transaction.TransactionManager;
import org.hibernate.HibernateException;
import org.springframework.jdbc.support.lob.LobCreator;
import org.springframework.jdbc.support.lob.LobHandler;
/**
* Hibernate UserType implementation for arbitrary objects that get serialized to BLOBs.
* Retrieves the LobHandler to use from LocalSessionFactoryBean at config time.
*
* <p>Can also be defined in generic Hibernate mappings, as DefaultLobCreator will
* work with most JDBC-compliant database drivers. In this case, the field type
* does not have to be BLOB: For databases like MySQL and MS SQL Server, any
* large enough binary type will work.
*
* @author Juergen Hoeller
* @since 1.2
* @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#setLobHandler
*/
public class BlobSerializableType extends AbstractLobType {
/**
* Initial size for ByteArrayOutputStreams used for serialization output.
* <p>If a serialized object is larger than these 1024 bytes, the size of
* the byte array used by the output stream will be doubled each time the
* limit is reached.
*/
private static final int OUTPUT_BYTE_ARRAY_INITIAL_SIZE = 1024;
/**
* Constructor used by Hibernate: fetches config-time LobHandler and
* config-time JTA TransactionManager from LocalSessionFactoryBean.
* @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#getConfigTimeLobHandler
* @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#getConfigTimeTransactionManager
*/
public BlobSerializableType() {
super();
}
/**
* Constructor used for testing: takes an explicit LobHandler
* and an explicit JTA TransactionManager (can be <code>null</code>).
*/
protected BlobSerializableType(LobHandler lobHandler, TransactionManager jtaTransactionManager) {
super(lobHandler, jtaTransactionManager);
}
public int[] sqlTypes() {
return new int[] {Types.BLOB};
}
public Class returnedClass() {
return Serializable.class;
}
@Override
public boolean isMutable() {
return true;
}
@Override
public Object deepCopy(Object value) throws HibernateException {
try {
// Write to new byte array to clone.
ByteArrayOutputStream baos = new ByteArrayOutputStream(OUTPUT_BYTE_ARRAY_INITIAL_SIZE);
ObjectOutputStream oos = new ObjectOutputStream(baos);
try {
oos.writeObject(value);
}
finally {
oos.close();
}
// Read it back and return a true copy.
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
ObjectInputStream ois = new ObjectInputStream(bais);
try {
return ois.readObject();
}
finally {
ois.close();
}
}
catch (ClassNotFoundException ex) {
throw new HibernateException("Couldn't clone BLOB contents", ex);
}
catch (IOException ex) {
throw new HibernateException("Couldn't clone BLOB contents", ex);
}
}
@Override
protected Object nullSafeGetInternal(
ResultSet rs, String[] names, Object owner, LobHandler lobHandler)
throws SQLException, IOException, HibernateException {
InputStream is = lobHandler.getBlobAsBinaryStream(rs, names[0]);
if (is != null) {
ObjectInputStream ois = new ObjectInputStream(is);
try {
return ois.readObject();
}
catch (ClassNotFoundException ex) {
throw new HibernateException("Could not deserialize BLOB contents", ex);
}
finally {
ois.close();
}
}
else {
return null;
}
}
@Override
protected void nullSafeSetInternal(
PreparedStatement ps, int index, Object value, LobCreator lobCreator)
throws SQLException, IOException {
if (value != null) {
ByteArrayOutputStream baos = new ByteArrayOutputStream(OUTPUT_BYTE_ARRAY_INITIAL_SIZE);
ObjectOutputStream oos = new ObjectOutputStream(baos);
try {
oos.writeObject(value);
oos.flush();
lobCreator.setBlobAsBytes(ps, index, baos.toByteArray());
}
finally {
oos.close();
}
}
else {
lobCreator.setBlobAsBytes(ps, index, null);
}
}
}

View File

@@ -0,0 +1,123 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3.support;
import java.io.UnsupportedEncodingException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import javax.transaction.TransactionManager;
import org.springframework.jdbc.support.lob.LobCreator;
import org.springframework.jdbc.support.lob.LobHandler;
/**
* Hibernate UserType implementation for Strings that get mapped to BLOBs.
* Retrieves the LobHandler to use from LocalSessionFactoryBean at config time.
*
* <p>This is intended for the (arguably unnatural, but still common) case
* where character data is stored in a binary LOB. This requires encoding
* and decoding the characters within this UserType; see the javadoc of the
* <code>getCharacterEncoding()</code> method.
*
* <p>Can also be defined in generic Hibernate mappings, as DefaultLobCreator will
* work with most JDBC-compliant database drivers. In this case, the field type
* does not have to be BLOB: For databases like MySQL and MS SQL Server, any
* large enough binary type will work.
*
* @author Juergen Hoeller
* @since 1.2.7
* @see #getCharacterEncoding()
* @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#setLobHandler
*/
public class BlobStringType extends AbstractLobType {
/**
* Constructor used by Hibernate: fetches config-time LobHandler and
* config-time JTA TransactionManager from LocalSessionFactoryBean.
* @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#getConfigTimeLobHandler
* @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#getConfigTimeTransactionManager
*/
public BlobStringType() {
super();
}
/**
* Constructor used for testing: takes an explicit LobHandler
* and an explicit JTA TransactionManager (can be <code>null</code>).
*/
protected BlobStringType(LobHandler lobHandler, TransactionManager jtaTransactionManager) {
super(lobHandler, jtaTransactionManager);
}
public int[] sqlTypes() {
return new int[] {Types.BLOB};
}
public Class returnedClass() {
return String.class;
}
@Override
protected Object nullSafeGetInternal(
ResultSet rs, String[] names, Object owner, LobHandler lobHandler)
throws SQLException, UnsupportedEncodingException {
byte[] bytes = lobHandler.getBlobAsBytes(rs, names[0]);
if (bytes != null) {
String encoding = getCharacterEncoding();
return (encoding != null ? new String(bytes, encoding) : new String(bytes));
}
else {
return null;
}
}
@Override
protected void nullSafeSetInternal(
PreparedStatement ps, int index, Object value, LobCreator lobCreator)
throws SQLException, UnsupportedEncodingException {
if (value != null) {
String str = (String) value;
String encoding = getCharacterEncoding();
byte[] bytes = (encoding != null ? str.getBytes(encoding) : str.getBytes());
lobCreator.setBlobAsBytes(ps, index, bytes);
}
else {
lobCreator.setBlobAsBytes(ps, index, null);
}
}
/**
* Determine the character encoding to apply to the BLOB's bytes
* to turn them into a String.
* <p>Default is <code>null</code>, indicating to use the platform
* default encoding. To be overridden in subclasses for a specific
* encoding such as "ISO-8859-1" or "UTF-8".
* @return the character encoding to use, or <code>null</code>
* to use the platform default encoding
* @see java.lang.String#String(byte[], String)
* @see java.lang.String#getBytes(String)
*/
protected String getCharacterEncoding() {
return null;
}
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3.support;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import javax.transaction.TransactionManager;
import org.springframework.jdbc.support.lob.LobCreator;
import org.springframework.jdbc.support.lob.LobHandler;
/**
* Hibernate UserType implementation for Strings that get mapped to CLOBs.
* Retrieves the LobHandler to use from LocalSessionFactoryBean at config time.
*
* <p>Particularly useful for storing Strings with more than 4000 characters in an
* Oracle database (only possible via CLOBs), in combination with OracleLobHandler.
*
* <p>Can also be defined in generic Hibernate mappings, as DefaultLobCreator will
* work with most JDBC-compliant database drivers. In this case, the field type
* does not have to be CLOB: For databases like MySQL and MS SQL Server, any
* large enough character type will work.
*
* @author Juergen Hoeller
* @since 1.2
* @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#setLobHandler
*/
public class ClobStringType extends AbstractLobType {
/**
* Constructor used by Hibernate: fetches config-time LobHandler and
* config-time JTA TransactionManager from LocalSessionFactoryBean.
* @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#getConfigTimeLobHandler
* @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#getConfigTimeTransactionManager
*/
public ClobStringType() {
super();
}
/**
* Constructor used for testing: takes an explicit LobHandler
* and an explicit JTA TransactionManager (can be <code>null</code>).
*/
protected ClobStringType(LobHandler lobHandler, TransactionManager jtaTransactionManager) {
super(lobHandler, jtaTransactionManager);
}
public int[] sqlTypes() {
return new int[] {Types.CLOB};
}
public Class returnedClass() {
return String.class;
}
@Override
protected Object nullSafeGetInternal(
ResultSet rs, String[] names, Object owner, LobHandler lobHandler)
throws SQLException {
return lobHandler.getClobAsString(rs, names[0]);
}
@Override
protected void nullSafeSetInternal(
PreparedStatement ps, int index, Object value, LobCreator lobCreator)
throws SQLException {
lobCreator.setClobAsString(ps, index, (String) value);
}
}

View File

@@ -0,0 +1,205 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3.support;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.support.DaoSupport;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
/**
* Convenient super class for Hibernate-based data access objects.
*
* <p>Requires a {@link org.hibernate.SessionFactory} to be set, providing a
* {@link org.springframework.orm.hibernate3.HibernateTemplate} based on it to
* subclasses through the {@link #getHibernateTemplate()} method.
* Can alternatively be initialized directly with a HibernateTemplate,
* in order to reuse the latter's settings such as the SessionFactory,
* exception translator, flush mode, etc.
*
* <p>This base class is mainly intended for HibernateTemplate usage but can
* also be used when working with a Hibernate Session directly, for example
* when relying on transactional Sessions. Convenience {@link #getSession}
* and {@link #releaseSession} methods are provided for that usage style.
*
* <p>This class will create its own HibernateTemplate instance if a SessionFactory
* is passed in. The "allowCreate" flag on that HibernateTemplate will be "true"
* by default. A custom HibernateTemplate instance can be used through overriding
* {@link #createHibernateTemplate}.
*
* @author Juergen Hoeller
* @since 1.2
* @see #setSessionFactory
* @see #getHibernateTemplate
* @see org.springframework.orm.hibernate3.HibernateTemplate
*/
public abstract class HibernateDaoSupport extends DaoSupport {
private HibernateTemplate hibernateTemplate;
/**
* Set the Hibernate SessionFactory to be used by this DAO.
* Will automatically create a HibernateTemplate for the given SessionFactory.
* @see #createHibernateTemplate
* @see #setHibernateTemplate
*/
public final void setSessionFactory(SessionFactory sessionFactory) {
if (this.hibernateTemplate == null || sessionFactory != this.hibernateTemplate.getSessionFactory()) {
this.hibernateTemplate = createHibernateTemplate(sessionFactory);
}
}
/**
* Create a HibernateTemplate for the given SessionFactory.
* Only invoked if populating the DAO with a SessionFactory reference!
* <p>Can be overridden in subclasses to provide a HibernateTemplate instance
* with different configuration, or a custom HibernateTemplate subclass.
* @param sessionFactory the Hibernate SessionFactory to create a HibernateTemplate for
* @return the new HibernateTemplate instance
* @see #setSessionFactory
*/
protected HibernateTemplate createHibernateTemplate(SessionFactory sessionFactory) {
return new HibernateTemplate(sessionFactory);
}
/**
* Return the Hibernate SessionFactory used by this DAO.
*/
public final SessionFactory getSessionFactory() {
return (this.hibernateTemplate != null ? this.hibernateTemplate.getSessionFactory() : null);
}
/**
* Set the HibernateTemplate for this DAO explicitly,
* as an alternative to specifying a SessionFactory.
* @see #setSessionFactory
*/
public final void setHibernateTemplate(HibernateTemplate hibernateTemplate) {
this.hibernateTemplate = hibernateTemplate;
}
/**
* Return the HibernateTemplate for this DAO,
* pre-initialized with the SessionFactory or set explicitly.
* <p><b>Note: The returned HibernateTemplate is a shared instance.</b>
* You may introspect its configuration, but not modify the configuration
* (other than from within an {@link #initDao} implementation).
* Consider creating a custom HibernateTemplate instance via
* <code>new HibernateTemplate(getSessionFactory())</code>, in which
* case you're allowed to customize the settings on the resulting instance.
*/
public final HibernateTemplate getHibernateTemplate() {
return this.hibernateTemplate;
}
@Override
protected final void checkDaoConfig() {
if (this.hibernateTemplate == null) {
throw new IllegalArgumentException("'sessionFactory' or 'hibernateTemplate' is required");
}
}
/**
* Obtain a Hibernate Session, either from the current transaction or
* a new one. The latter is only allowed if the
* {@link org.springframework.orm.hibernate3.HibernateTemplate#setAllowCreate "allowCreate"}
* setting of this bean's {@link #setHibernateTemplate HibernateTemplate} is "true".
* <p><b>Note that this is not meant to be invoked from HibernateTemplate code
* but rather just in plain Hibernate code.</b> Either rely on a thread-bound
* Session or use it in combination with {@link #releaseSession}.
* <p>In general, it is recommended to use HibernateTemplate, either with
* the provided convenience operations or with a custom HibernateCallback
* that provides you with a Session to work on. HibernateTemplate will care
* for all resource management and for proper exception conversion.
* @return the Hibernate Session
* @throws DataAccessResourceFailureException if the Session couldn't be created
* @throws IllegalStateException if no thread-bound Session found and allowCreate=false
* @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession(SessionFactory, boolean)
*/
protected final Session getSession()
throws DataAccessResourceFailureException, IllegalStateException {
return getSession(this.hibernateTemplate.isAllowCreate());
}
/**
* Obtain a Hibernate Session, either from the current transaction or
* a new one. The latter is only allowed if "allowCreate" is true.
* <p><b>Note that this is not meant to be invoked from HibernateTemplate code
* but rather just in plain Hibernate code.</b> Either rely on a thread-bound
* Session or use it in combination with {@link #releaseSession}.
* <p>In general, it is recommended to use
* {@link #getHibernateTemplate() HibernateTemplate}, either with
* the provided convenience operations or with a custom
* {@link org.springframework.orm.hibernate3.HibernateCallback} that
* provides you with a Session to work on. HibernateTemplate will care
* for all resource management and for proper exception conversion.
* @param allowCreate if a non-transactional Session should be created when no
* transactional Session can be found for the current thread
* @return the Hibernate Session
* @throws DataAccessResourceFailureException if the Session couldn't be created
* @throws IllegalStateException if no thread-bound Session found and allowCreate=false
* @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession(SessionFactory, boolean)
*/
protected final Session getSession(boolean allowCreate)
throws DataAccessResourceFailureException, IllegalStateException {
return (!allowCreate ?
SessionFactoryUtils.getSession(getSessionFactory(), false) :
SessionFactoryUtils.getSession(
getSessionFactory(),
this.hibernateTemplate.getEntityInterceptor(),
this.hibernateTemplate.getJdbcExceptionTranslator()));
}
/**
* Convert the given HibernateException to an appropriate exception from the
* <code>org.springframework.dao</code> hierarchy. Will automatically detect
* wrapped SQLExceptions and convert them accordingly.
* <p>Delegates to the
* {@link org.springframework.orm.hibernate3.HibernateTemplate#convertHibernateAccessException}
* method of this DAO's HibernateTemplate.
* <p>Typically used in plain Hibernate code, in combination with
* {@link #getSession} and {@link #releaseSession}.
* @param ex HibernateException that occurred
* @return the corresponding DataAccessException instance
* @see org.springframework.orm.hibernate3.SessionFactoryUtils#convertHibernateAccessException
*/
protected final DataAccessException convertHibernateAccessException(HibernateException ex) {
return this.hibernateTemplate.convertHibernateAccessException(ex);
}
/**
* Close the given Hibernate Session, created via this DAO's SessionFactory,
* if it isn't bound to the thread (i.e. isn't a transactional Session).
* <p>Typically used in plain Hibernate code, in combination with
* {@link #getSession} and {@link #convertHibernateAccessException}.
* @param session the Session to close
* @see org.springframework.orm.hibernate3.SessionFactoryUtils#releaseSession
*/
protected final void releaseSession(Session session) {
SessionFactoryUtils.releaseSession(session, getSessionFactory());
}
}

View File

@@ -0,0 +1,68 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3.support;
import java.io.Serializable;
import java.util.Map;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.event.MergeEvent;
import org.hibernate.event.def.DefaultMergeEventListener;
import org.hibernate.persister.entity.EntityPersister;
/**
* Extension of Hibernate's DefaultMergeEventListener, transferring the ids
* of newly saved objects to the corresponding original objects (that are part
* of the detached object graph passed into the <code>merge</code> method).
*
* <p>Transferring newly assigned ids to the original graph allows for continuing
* to use the original object graph, despite merged copies being registered with
* the current Hibernate Session. This is particularly useful for web applications
* that might want to store an object graph and then render it in a web view,
* with links that include the id of certain (potentially newly saved) objects.
*
* <p>The merge behavior given by this MergeEventListener is nearly identical
* to TopLink's merge behavior. See PetClinic for an example, which relies on
* ids being available for newly saved objects: the <code>HibernateClinic</code>
* and <code>TopLinkClinic</code> DAO implementations both use straight merge
* calls, with the Hibernate SessionFactory configuration specifying an
* <code>IdTransferringMergeEventListener</code>.
*
* <p>Typically specified as entry for LocalSessionFactoryBean's "eventListeners"
* map, with key "merge".
*
* @author Juergen Hoeller
* @since 1.2
* @see org.springframework.orm.hibernate3.LocalSessionFactoryBean#setEventListeners(java.util.Map)
*/
public class IdTransferringMergeEventListener extends DefaultMergeEventListener {
/**
* Hibernate 3.1 implementation of ID transferral.
*/
@Override
protected void entityIsTransient(MergeEvent event, Map copyCache) {
super.entityIsTransient(event, copyCache);
SessionImplementor session = event.getSession();
EntityPersister persister = session.getEntityPersister(event.getEntityName(), event.getEntity());
// Extract id from merged copy (which is currently registered with Session).
Serializable id = persister.getIdentifier(event.getResult(), session.getEntityMode());
// Set id on original object (which remains detached).
persister.setIdentifier(event.getOriginal(), id, session.getEntityMode());
}
}

View File

@@ -0,0 +1,282 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3.support;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.hibernate.FlushMode;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.orm.hibernate3.SessionHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.filter.OncePerRequestFilter;
/**
* Servlet 2.3 Filter that binds a Hibernate Session to the thread for the entire
* processing of the request. Intended for the "Open Session in View" pattern,
* i.e. to allow for lazy loading in web views despite the original transactions
* already being completed.
*
* <p>This filter makes Hibernate Sessions available via the current thread, which
* will be autodetected by transaction managers. It is suitable for service layer
* transactions via {@link org.springframework.orm.hibernate3.HibernateTransactionManager}
* or {@link org.springframework.transaction.jta.JtaTransactionManager} as well
* as for non-transactional execution (if configured appropriately).
*
* <p><b>NOTE</b>: This filter will by default <i>not</i> flush the Hibernate Session,
* with the flush mode set to <code>FlushMode.NEVER</code>. It assumes to be used
* in combination with service layer transactions that care for the flushing: The
* active transaction manager will temporarily change the flush mode to
* <code>FlushMode.AUTO</code> during a read-write transaction, with the flush
* mode reset to <code>FlushMode.NEVER</code> at the end of each transaction.
* If you intend to use this filter without transactions, consider changing
* the default flush mode (through the "flushMode" property).
*
* <p><b>WARNING:</b> Applying this filter to existing logic can cause issues that
* have not appeared before, through the use of a single Hibernate Session for the
* processing of an entire request. In particular, the reassociation of persistent
* objects with a Hibernate Session has to occur at the very beginning of request
* processing, to avoid clashes with already loaded instances of the same objects.
*
* <p>Alternatively, turn this filter into deferred close mode, by specifying
* "singleSession"="false": It will not use a single session per request then,
* but rather let each data access operation or transaction use its own session
* (like without Open Session in View). Each of those sessions will be registered
* for deferred close, though, actually processed at request completion.
*
* <p>A single session per request allows for most efficient first-level caching,
* but can cause side effects, for example on <code>saveOrUpdate</code> or when
* continuing after a rolled-back transaction. The deferred close strategy is as safe
* as no Open Session in View in that respect, while still allowing for lazy loading
* in views (but not providing a first-level cache for the entire request).
*
* <p>Looks up the SessionFactory in Spring's root web application context.
* Supports a "sessionFactoryBeanName" filter init-param in <code>web.xml</code>;
* the default bean name is "sessionFactory". Looks up the SessionFactory on each
* request, to avoid initialization order issues (when using ContextLoaderServlet,
* the root application context will get initialized <i>after</i> this filter).
*
* @author Juergen Hoeller
* @since 1.2
* @see #setSingleSession
* @see #setFlushMode
* @see #lookupSessionFactory
* @see OpenSessionInViewInterceptor
* @see org.springframework.orm.hibernate3.HibernateInterceptor
* @see org.springframework.orm.hibernate3.HibernateTransactionManager
* @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession
* @see org.springframework.transaction.support.TransactionSynchronizationManager
*/
public class OpenSessionInViewFilter extends OncePerRequestFilter {
public static final String DEFAULT_SESSION_FACTORY_BEAN_NAME = "sessionFactory";
private String sessionFactoryBeanName = DEFAULT_SESSION_FACTORY_BEAN_NAME;
private boolean singleSession = true;
private FlushMode flushMode = FlushMode.MANUAL;
/**
* Set the bean name of the SessionFactory to fetch from Spring's
* root application context. Default is "sessionFactory".
* @see #DEFAULT_SESSION_FACTORY_BEAN_NAME
*/
public void setSessionFactoryBeanName(String sessionFactoryBeanName) {
this.sessionFactoryBeanName = sessionFactoryBeanName;
}
/**
* Return the bean name of the SessionFactory to fetch from Spring's
* root application context.
*/
protected String getSessionFactoryBeanName() {
return this.sessionFactoryBeanName;
}
/**
* Set whether to use a single session for each request. Default is "true".
* <p>If set to "false", each data access operation or transaction will use
* its own session (like without Open Session in View). Each of those
* sessions will be registered for deferred close, though, actually
* processed at request completion.
* @see SessionFactoryUtils#initDeferredClose
* @see SessionFactoryUtils#processDeferredClose
*/
public void setSingleSession(boolean singleSession) {
this.singleSession = singleSession;
}
/**
* Return whether to use a single session for each request.
*/
protected boolean isSingleSession() {
return this.singleSession;
}
/**
* Specify the Hibernate FlushMode to apply to this filter's
* {@link org.hibernate.Session}. Only applied in single session mode.
* <p>Can be populated with the corresponding constant name in XML bean
* definitions: e.g. "AUTO".
* <p>The default is "MANUAL". Specify "AUTO" if you intend to use
* this filter without service layer transactions.
* @see org.hibernate.Session#setFlushMode
* @see org.hibernate.FlushMode#MANUAL
* @see org.hibernate.FlushMode#AUTO
*/
public void setFlushMode(FlushMode flushMode) {
this.flushMode = flushMode;
}
/**
* Return the Hibernate FlushMode that this filter applies to its
* {@link org.hibernate.Session} (in single session mode).
*/
protected FlushMode getFlushMode() {
return this.flushMode;
}
@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
SessionFactory sessionFactory = lookupSessionFactory(request);
boolean participate = false;
if (isSingleSession()) {
// single session mode
if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
// Do not modify the Session: just set the participate flag.
participate = true;
}
else {
logger.debug("Opening single Hibernate Session in OpenSessionInViewFilter");
Session session = getSession(sessionFactory);
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
}
}
else {
// deferred close mode
if (SessionFactoryUtils.isDeferredCloseActive(sessionFactory)) {
// Do not modify deferred close: just set the participate flag.
participate = true;
}
else {
SessionFactoryUtils.initDeferredClose(sessionFactory);
}
}
try {
filterChain.doFilter(request, response);
}
finally {
if (!participate) {
if (isSingleSession()) {
// single session mode
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
logger.debug("Closing single Hibernate Session in OpenSessionInViewFilter");
closeSession(sessionHolder.getSession(), sessionFactory);
}
else {
// deferred close mode
SessionFactoryUtils.processDeferredClose(sessionFactory);
}
}
}
}
/**
* Look up the SessionFactory that this filter should use,
* taking the current HTTP request as argument.
* <p>The default implementation delegates to the {@link #lookupSessionFactory()}
* variant without arguments.
* @param request the current request
* @return the SessionFactory to use
*/
protected SessionFactory lookupSessionFactory(HttpServletRequest request) {
return lookupSessionFactory();
}
/**
* Look up the SessionFactory that this filter should use.
* <p>The default implementation looks for a bean with the specified name
* in Spring's root application context.
* @return the SessionFactory to use
* @see #getSessionFactoryBeanName
*/
protected SessionFactory lookupSessionFactory() {
if (logger.isDebugEnabled()) {
logger.debug("Using SessionFactory '" + getSessionFactoryBeanName() + "' for OpenSessionInViewFilter");
}
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
return wac.getBean(getSessionFactoryBeanName(), SessionFactory.class);
}
/**
* Get a Session for the SessionFactory that this filter uses.
* Note that this just applies in single session mode!
* <p>The default implementation delegates to the
* <code>SessionFactoryUtils.getSession</code> method and
* sets the <code>Session</code>'s flush mode to "MANUAL".
* <p>Can be overridden in subclasses for creating a Session with a
* custom entity interceptor or JDBC exception translator.
* @param sessionFactory the SessionFactory that this filter uses
* @return the Session to use
* @throws DataAccessResourceFailureException if the Session could not be created
* @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession(SessionFactory, boolean)
* @see org.hibernate.FlushMode#MANUAL
*/
protected Session getSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
Session session = SessionFactoryUtils.getSession(sessionFactory, true);
FlushMode flushMode = getFlushMode();
if (flushMode != null) {
session.setFlushMode(flushMode);
}
return session;
}
/**
* Close the given Session.
* Note that this just applies in single session mode!
* <p>Can be overridden in subclasses, e.g. for flushing the Session before
* closing it. See class-level javadoc for a discussion of flush handling.
* Note that you should also override getSession accordingly, to set
* the flush mode to something else than NEVER.
* @param session the Session used for filtering
* @param sessionFactory the SessionFactory that this filter uses
*/
protected void closeSession(Session session, SessionFactory sessionFactory) {
SessionFactoryUtils.closeSession(session);
}
}

View File

@@ -0,0 +1,232 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3.support;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateAccessor;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.orm.hibernate3.SessionHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.ui.ModelMap;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.WebRequestInterceptor;
/**
* Spring web request interceptor that binds a Hibernate <code>Session</code> to the
* thread for the entire processing of the request.
*
* <p>This class is a concrete expression of the "Open Session in View" pattern, which
* is a pattern that allows for the lazy loading of associations in web views despite
* the original transactions already being completed.
*
* <p>This interceptor makes Hibernate <code>Sessions</code> available via the current
* thread, which will be autodetected by transaction managers. It is suitable for
* service layer transactions via
* {@link org.springframework.orm.hibernate3.HibernateTransactionManager} or
* {@link org.springframework.transaction.jta.JtaTransactionManager} as well as for
* non-transactional execution (if configured appropriately).
*
* <p><b>NOTE</b>: This interceptor will by default <i>not</i> flush the Hibernate
* <code>Session</code>, with the flush mode being set to <code>FlushMode.NEVER</code>.
* It assumes that it will be used in combination with service layer transactions
* that handle the flushing: the active transaction manager will temporarily change
* the flush mode to <code>FlushMode.AUTO</code> during a read-write transaction,
* with the flush mode reset to <code>FlushMode.NEVER</code> at the end of each
* transaction. If you intend to use this interceptor without transactions, consider
* changing the default flush mode (through the
* {@link #setFlushMode(int) "flushMode"} property).
*
* <p>In contrast to {@link OpenSessionInViewFilter}, this interceptor is
* configured in a Spring application context and can thus take advantage of bean
* wiring. It inherits common Hibernate configuration properties from
* {@link org.springframework.orm.hibernate3.HibernateAccessor},
* to be configured in a bean definition.
*
* <p><b>WARNING:</b> Applying this interceptor to existing logic can cause issues
* that have not appeared before, through the use of a single Hibernate
* <code>Session</code> for the processing of an entire request. In particular, the
* reassociation of persistent objects with a Hibernate <code>Session</code> has to
* occur at the very beginning of request processing, to avoid clashes with already
* loaded instances of the same objects.
*
* <p>Alternatively, turn this interceptor into deferred close mode, by specifying
* "singleSession"="false": It will not use a single session per request then,
* but rather let each data access operation or transaction use its own session
* (as would be the case without Open Session in View). Each of those sessions will
* be registered for deferred close though, which will actually be processed at
* request completion.
*
* <p>A single session per request allows for the most efficient first-level caching,
* but can cause side effects, for example on <code>saveOrUpdate</code> or when
* continuing after a rolled-back transaction. The deferred close strategy is as safe
* as no Open Session in View in that respect, while still allowing for lazy loading
* in views (but not providing a first-level cache for the entire request).
*
* @author Juergen Hoeller
* @since 1.2
* @see #setSingleSession
* @see #setFlushMode
* @see OpenSessionInViewFilter
* @see org.springframework.orm.hibernate3.HibernateInterceptor
* @see org.springframework.orm.hibernate3.HibernateTransactionManager
* @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession
* @see org.springframework.transaction.support.TransactionSynchronizationManager
*/
public class OpenSessionInViewInterceptor extends HibernateAccessor implements WebRequestInterceptor {
/**
* Suffix that gets appended to the <code>SessionFactory</code>
* <code>toString()</code> representation for the "participate in existing
* session handling" request attribute.
* @see #getParticipateAttributeName
*/
public static final String PARTICIPATE_SUFFIX = ".PARTICIPATE";
private boolean singleSession = true;
/**
* Create a new <code>OpenSessionInViewInterceptor</code>,
* turning the default flushMode to <code>FLUSH_NEVER</code>.
* @see #setFlushMode
*/
public OpenSessionInViewInterceptor() {
setFlushMode(FLUSH_NEVER);
}
/**
* Set whether to use a single session for each request. Default is "true".
* <p>If set to false, each data access operation or transaction will use
* its own session (like without Open Session in View). Each of those
* sessions will be registered for deferred close, though, actually
* processed at request completion.
* @see SessionFactoryUtils#initDeferredClose
* @see SessionFactoryUtils#processDeferredClose
*/
public void setSingleSession(boolean singleSession) {
this.singleSession = singleSession;
}
/**
* Return whether to use a single session for each request.
*/
protected boolean isSingleSession() {
return singleSession;
}
/**
* Open a new Hibernate <code>Session</code> according to the settings of this
* <code>HibernateAccessor</code> and bind it to the thread via the
* {@link TransactionSynchronizationManager}.
* @see org.springframework.orm.hibernate3.SessionFactoryUtils#getSession
*/
public void preHandle(WebRequest request) throws DataAccessException {
if ((isSingleSession() && TransactionSynchronizationManager.hasResource(getSessionFactory())) ||
SessionFactoryUtils.isDeferredCloseActive(getSessionFactory())) {
// Do not modify the Session: just mark the request accordingly.
String participateAttributeName = getParticipateAttributeName();
Integer count = (Integer) request.getAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
int newCount = (count != null ? count + 1 : 1);
request.setAttribute(getParticipateAttributeName(), newCount, WebRequest.SCOPE_REQUEST);
}
else {
if (isSingleSession()) {
// single session mode
logger.debug("Opening single Hibernate Session in OpenSessionInViewInterceptor");
Session session = SessionFactoryUtils.getSession(
getSessionFactory(), getEntityInterceptor(), getJdbcExceptionTranslator());
applyFlushMode(session, false);
TransactionSynchronizationManager.bindResource(getSessionFactory(), new SessionHolder(session));
}
else {
// deferred close mode
SessionFactoryUtils.initDeferredClose(getSessionFactory());
}
}
}
/**
* Flush the Hibernate <code>Session</code> before view rendering, if necessary.
* <p>Note that this just applies in {@link #isSingleSession() single session mode}!
* <p>The default is <code>FLUSH_NEVER</code> to avoid this extra flushing,
* assuming that service layer transactions have flushed their changes on commit.
* @see #setFlushMode
*/
public void postHandle(WebRequest request, ModelMap model) throws DataAccessException {
if (isSingleSession()) {
// Only potentially flush in single session mode.
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory());
logger.debug("Flushing single Hibernate Session in OpenSessionInViewInterceptor");
try {
flushIfNecessary(sessionHolder.getSession(), false);
}
catch (HibernateException ex) {
throw convertHibernateAccessException(ex);
}
}
}
/**
* Unbind the Hibernate <code>Session</code> from the thread and close it (in
* single session mode), or process deferred close for all sessions that have
* been opened during the current request (in deferred close mode).
* @see org.springframework.transaction.support.TransactionSynchronizationManager
*/
public void afterCompletion(WebRequest request, Exception ex) throws DataAccessException {
String participateAttributeName = getParticipateAttributeName();
Integer count = (Integer) request.getAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
if (count != null) {
// Do not modify the Session: just clear the marker.
if (count > 1) {
request.setAttribute(participateAttributeName, count - 1, WebRequest.SCOPE_REQUEST);
}
else {
request.removeAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
}
}
else {
if (isSingleSession()) {
// single session mode
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.unbindResource(getSessionFactory());
logger.debug("Closing single Hibernate Session in OpenSessionInViewInterceptor");
SessionFactoryUtils.closeSession(sessionHolder.getSession());
}
else {
// deferred close mode
SessionFactoryUtils.processDeferredClose(getSessionFactory());
}
}
}
/**
* Return the name of the request attribute that identifies that a request is
* already intercepted.
* <p>The default implementation takes the <code>toString()</code> representation
* of the <code>SessionFactory</code> instance and appends {@link #PARTICIPATE_SUFFIX}.
*/
protected String getParticipateAttributeName() {
return getSessionFactory().toString() + PARTICIPATE_SUFFIX;
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate3.support;
import org.hibernate.EmptyInterceptor;
import org.springframework.aop.scope.ScopedObject;
import org.springframework.aop.support.AopUtils;
/**
* Hibernate3 interceptor used for getting the proper entity name for scoped
* beans. As scoped bean classes are proxies generated at runtime, they are
* unrecognized by the persisting framework. Using this interceptor, the
* original scoped bean class is retrieved end exposed to Hibernate for
* persisting.
*
* <p>Usage example:
*
* <pre>
* &lt;bean id=&quot;sessionFactory&quot; class=&quot;org.springframework.orm.hibernate3.LocalSessionFactoryBean&quot;&gt;
* ...
* &lt;property name=&quot;entityInterceptor&quot;&gt;
* &lt;bean class=&quot;org.springframework.orm.hibernate3.support.ScopedBeanInterceptor&quot;/&gt;
* &lt;/property&gt;
* &lt;/bean&gt;</pre>
*
* @author Costin Leau
* @author Juergen Hoeller
* @since 2.0
*/
public class ScopedBeanInterceptor extends EmptyInterceptor {
@Override
public String getEntityName(Object entity) {
if (entity instanceof ScopedObject) {
// Determine underlying object's type.
Object targetObject = ((ScopedObject) entity).getTargetObject();
return AopUtils.getTargetClass(targetObject).getName();
}
// Any other object: delegate to the default implementation.
return super.getEntityName(entity);
}
}

View File

@@ -0,0 +1,9 @@
/**
*
* Classes supporting the <code>org.springframework.orm.hibernate3</code> package.
* Contains a DAO base class for HibernateTemplate usage.
*
*/
package org.springframework.orm.hibernate3.support;

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate4;
import org.hibernate.HibernateException;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
/**
* {@link PersistenceExceptionTranslator} capable of translating {@link HibernateException}
* instances to Spring's {@link DataAccessException} hierarchy.
*
* <p>Extended by {@link LocalSessionFactoryBean}, so there is no need to declare this
* translator in addition to a {@code LocalSessionFactoryBean}.
*
* <p>When configuring the container with {@code @Configuration} classes, a {@code @Bean}
* of this type must be registered manually.
*
* @author Juergen Hoeller
* @since 3.1
* @see org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor
* @see SessionFactoryUtils#convertHibernateAccessException(HibernateException)
*/
public class HibernateExceptionTranslator implements PersistenceExceptionTranslator {
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
if (ex instanceof HibernateException) {
return convertHibernateAccessException((HibernateException) ex);
}
return null;
}
/**
* Convert the given HibernateException to an appropriate exception from the
* {@code org.springframework.dao} hierarchy.
* @param ex HibernateException that occured
* @return a corresponding DataAccessException
* @see SessionFactoryUtils#convertHibernateAccessException
*/
protected DataAccessException convertHibernateAccessException(HibernateException ex) {
return SessionFactoryUtils.convertHibernateAccessException(ex);
}
}

View File

@@ -0,0 +1,54 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate4;
import java.sql.SQLException;
import org.hibernate.JDBCException;
import org.springframework.dao.UncategorizedDataAccessException;
/**
* Hibernate-specific subclass of UncategorizedDataAccessException,
* for JDBC exceptions that Hibernate wrapped.
*
* @author Juergen Hoeller
* @since 3.1
* @see SessionFactoryUtils#convertHibernateAccessException
*/
public class HibernateJdbcException extends UncategorizedDataAccessException {
public HibernateJdbcException(JDBCException ex) {
super("JDBC exception on Hibernate data access: SQLException for SQL [" + ex.getSQL() + "]; SQL state [" +
ex.getSQLState() + "]; error code [" + ex.getErrorCode() + "]; " + ex.getMessage(), ex);
}
/**
* Return the underlying SQLException.
*/
public SQLException getSQLException() {
return ((JDBCException) getCause()).getSQLException();
}
/**
* Return the SQL that led to the problem.
*/
public String getSql() {
return ((JDBCException) getCause()).getSQL();
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate4;
import org.hibernate.UnresolvableObjectException;
import org.hibernate.WrongClassException;
import org.springframework.orm.ObjectRetrievalFailureException;
/**
* Hibernate-specific subclass of ObjectRetrievalFailureException.
* Converts Hibernate's UnresolvableObjectException and WrongClassException.
*
* @author Juergen Hoeller
* @since 3.1
* @see SessionFactoryUtils#convertHibernateAccessException
*/
public class HibernateObjectRetrievalFailureException extends ObjectRetrievalFailureException {
public HibernateObjectRetrievalFailureException(UnresolvableObjectException ex) {
super(ex.getEntityName(), ex.getIdentifier(), ex.getMessage(), ex);
}
public HibernateObjectRetrievalFailureException(WrongClassException ex) {
super(ex.getEntityName(), ex.getIdentifier(), ex.getMessage(), ex);
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate4;
import org.hibernate.StaleObjectStateException;
import org.hibernate.StaleStateException;
import org.springframework.orm.ObjectOptimisticLockingFailureException;
/**
* Hibernate-specific subclass of ObjectOptimisticLockingFailureException.
* Converts Hibernate's StaleObjectStateException and StaleStateException.
*
* @author Juergen Hoeller
* @since 3.1
* @see SessionFactoryUtils#convertHibernateAccessException
*/
public class HibernateOptimisticLockingFailureException extends ObjectOptimisticLockingFailureException {
public HibernateOptimisticLockingFailureException(StaleObjectStateException ex) {
super(ex.getEntityName(), ex.getIdentifier(), ex);
}
public HibernateOptimisticLockingFailureException(StaleStateException ex) {
super(ex.getMessage(), ex);
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate4;
import org.hibernate.QueryException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
/**
* Hibernate-specific subclass of InvalidDataAccessResourceUsageException,
* thrown on invalid HQL query syntax.
*
* @author Juergen Hoeller
* @since 3.1
* @see SessionFactoryUtils#convertHibernateAccessException
*/
public class HibernateQueryException extends InvalidDataAccessResourceUsageException {
public HibernateQueryException(QueryException ex) {
super(ex.getMessage(), ex);
}
/**
* Return the HQL query string that was invalid.
*/
public String getQueryString() {
return ((QueryException) getCause()).getQueryString();
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate4;
import org.hibernate.HibernateException;
import org.springframework.dao.UncategorizedDataAccessException;
/**
* Hibernate-specific subclass of UncategorizedDataAccessException,
* for Hibernate system errors that do not match any concrete
* <code>org.springframework.dao</code> exceptions.
*
* @author Juergen Hoeller
* @since 3.1
* @see SessionFactoryUtils#convertHibernateAccessException
*/
public class HibernateSystemException extends UncategorizedDataAccessException {
/**
* Create a new HibernateSystemException,
* wrapping an arbitrary HibernateException.
* @param cause the HibernateException thrown
*/
public HibernateSystemException(HibernateException cause) {
super(cause != null ? cause.getMessage() : null, cause);
}
}

View File

@@ -0,0 +1,708 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate4;
import java.lang.reflect.Method;
import java.sql.Connection;
import javax.sql.DataSource;
import org.hibernate.ConnectionReleaseMode;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.engine.transaction.spi.TransactionContext;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.jdbc.datasource.ConnectionHolder;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.datasource.JdbcTransactionObjectSupport;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
import org.springframework.transaction.CannotCreateTransactionException;
import org.springframework.transaction.IllegalTransactionStateException;
import org.springframework.transaction.InvalidIsolationLevelException;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionSystemException;
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
import org.springframework.transaction.support.DefaultTransactionStatus;
import org.springframework.transaction.support.ResourceTransactionManager;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
/**
* {@link org.springframework.transaction.PlatformTransactionManager}
* implementation for a single Hibernate {@link org.hibernate.SessionFactory}.
* Binds a Hibernate Session from the specified factory to the thread,
* potentially allowing for one thread-bound Session per factory.
* <code>SessionFactory.getCurrentSession()</code> is required for Hibernate
* access code that needs to support this transaction handling mechanism,
* with the SessionFactory being configured with {@link SpringSessionContext}.
*
* <p>Supports custom isolation levels, and timeouts that get applied as
* Hibernate transaction timeouts.
*
* <p>This transaction manager is appropriate for applications that use a single
* Hibernate SessionFactory for transactional data access, but it also supports
* direct DataSource access within a transaction (i.e. plain JDBC code working
* with the same DataSource). This allows for mixing services which access Hibernate
* and services which use plain JDBC (without being aware of Hibernate)!
* Application code needs to stick to the same simple Connection lookup pattern as
* with {@link org.springframework.jdbc.datasource.DataSourceTransactionManager}
* (i.e. {@link org.springframework.jdbc.datasource.DataSourceUtils#getConnection}
* or going through a
* {@link org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy}).
*
* <p>Note: To be able to register a DataSource's Connection for plain JDBC code,
* this instance needs to be aware of the DataSource ({@link #setDataSource}).
* The given DataSource should obviously match the one used by the given SessionFactory.
*
* <p>JTA (usually through {@link org.springframework.transaction.jta.JtaTransactionManager})
* is necessary for accessing multiple transactional resources within the same
* transaction. The DataSource that Hibernate uses needs to be JTA-enabled in
* such a scenario (see container setup).
*
* <p>On JDBC 3.0, this transaction manager supports nested transactions via JDBC 3.0
* Savepoints. The {@link #setNestedTransactionAllowed} "nestedTransactionAllowed"}
* flag defaults to "false", though, as nested transactions will just apply to the
* JDBC Connection, not to the Hibernate Session and its cached objects. You can
* manually set the flag to "true" if you want to use nested transactions for
* JDBC access code which participates in Hibernate transactions (provided that
* your JDBC driver supports Savepoints). <i>Note that Hibernate itself does not
* support nested transactions! Hence, do not expect Hibernate access code to
* semantically participate in a nested transaction.</i>
*
* @author Juergen Hoeller
* @since 3.1
* @see #setSessionFactory
* @see #setDataSource
* @see org.hibernate.SessionFactory#getCurrentSession()
* @see org.springframework.jdbc.datasource.DataSourceUtils#getConnection
* @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection
* @see org.springframework.jdbc.core.JdbcTemplate
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager
* @see org.springframework.transaction.jta.JtaTransactionManager
*/
@SuppressWarnings("serial")
public class HibernateTransactionManager extends AbstractPlatformTransactionManager
implements ResourceTransactionManager, InitializingBean {
/**
* A Method handle for the <code>SessionFactory.getCurrentSession()</code> method.
* The return value differs between Hibernate 3.x and 4.x; for cross-compilation purposes,
* we have to use reflection here as long as we keep compiling against Hibernate 3.x jars.
*/
private static final Method getCurrentSessionMethod =
ClassUtils.getMethod(SessionFactory.class, "getCurrentSession");
private SessionFactory sessionFactory;
private DataSource dataSource;
private boolean autodetectDataSource = true;
private boolean prepareConnection = true;
private boolean hibernateManagedSession = false;
/**
* Create a new HibernateTransactionManager instance.
* A SessionFactory has to be set to be able to use it.
* @see #setSessionFactory
*/
public HibernateTransactionManager() {
}
/**
* Create a new HibernateTransactionManager instance.
* @param sessionFactory SessionFactory to manage transactions for
*/
public HibernateTransactionManager(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
afterPropertiesSet();
}
/**
* Set the SessionFactory that this instance should manage transactions for.
*/
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
/**
* Return the SessionFactory that this instance should manage transactions for.
*/
public SessionFactory getSessionFactory() {
return this.sessionFactory;
}
/**
* Set the JDBC DataSource that this instance should manage transactions for.
* The DataSource should match the one used by the Hibernate SessionFactory:
* for example, you could specify the same JNDI DataSource for both.
* <p>If the SessionFactory was configured with LocalDataSourceConnectionProvider,
* i.e. by Spring's SessionFactoryBuilder with a specified "dataSource",
* the DataSource will be auto-detected: You can still explictly specify the
* DataSource, but you don't need to in this case.
* <p>A transactional JDBC Connection for this DataSource will be provided to
* application code accessing this DataSource directly via DataSourceUtils
* or JdbcTemplate. The Connection will be taken from the Hibernate Session.
* <p>The DataSource specified here should be the target DataSource to manage
* transactions for, not a TransactionAwareDataSourceProxy. Only data access
* code may work with TransactionAwareDataSourceProxy, while the transaction
* manager needs to work on the underlying target DataSource. If there's
* nevertheless a TransactionAwareDataSourceProxy passed in, it will be
* unwrapped to extract its target DataSource.
* @see #setAutodetectDataSource
* @see org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy
* @see org.springframework.jdbc.datasource.DataSourceUtils
* @see org.springframework.jdbc.core.JdbcTemplate
*/
public void setDataSource(DataSource dataSource) {
if (dataSource instanceof TransactionAwareDataSourceProxy) {
// If we got a TransactionAwareDataSourceProxy, we need to perform transactions
// for its underlying target DataSource, else data access code won't see
// properly exposed transactions (i.e. transactions for the target DataSource).
this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource();
}
else {
this.dataSource = dataSource;
}
}
/**
* Return the JDBC DataSource that this instance manages transactions for.
*/
public DataSource getDataSource() {
return this.dataSource;
}
/**
* Set whether to autodetect a JDBC DataSource used by the Hibernate SessionFactory,
* if set via SessionFactoryBuilder's <code>setDataSource</code>. Default is "true".
* <p>Can be turned off to deliberately ignore an available DataSource, in order
* to not expose Hibernate transactions as JDBC transactions for that DataSource.
* @see #setDataSource
*/
public void setAutodetectDataSource(boolean autodetectDataSource) {
this.autodetectDataSource = autodetectDataSource;
}
/**
* Set whether to prepare the underlying JDBC Connection of a transactional
* Hibernate Session, that is, whether to apply a transaction-specific
* isolation level and/or the transaction's read-only flag to the underlying
* JDBC Connection.
* <p>Default is "true". If you turn this flag off, the transaction manager
* will not support per-transaction isolation levels anymore. It will not
* call <code>Connection.setReadOnly(true)</code> for read-only transactions
* anymore either. If this flag is turned off, no cleanup of a JDBC Connection
* is required after a transaction, since no Connection settings will get modified.
* @see java.sql.Connection#setTransactionIsolation
* @see java.sql.Connection#setReadOnly
*/
public void setPrepareConnection(boolean prepareConnection) {
this.prepareConnection = prepareConnection;
}
/**
* Set whether to operate on a Hibernate-managed Session instead of a
* Spring-managed Session, that is, whether to obtain the Session through
* Hibernate's {@link org.hibernate.SessionFactory#getCurrentSession()}
* instead of {@link org.hibernate.SessionFactory#openSession()} (with a Spring
* {@link org.springframework.transaction.support.TransactionSynchronizationManager}
* check preceding it).
* <p>Default is "false", i.e. using a Spring-managed Session: taking the current
* thread-bound Session if available (e.g. in an Open-Session-in-View scenario),
* creating a new Session for the current transaction otherwise.
* <p>Switch this flag to "true" in order to enforce use of a Hibernate-managed Session.
* Note that this requires {@link org.hibernate.SessionFactory#getCurrentSession()}
* to always return a proper Session when called for a Spring-managed transaction;
* transaction begin will fail if the <code>getCurrentSession()</code> call fails.
* <p>This mode will typically be used in combination with a custom Hibernate
* {@link org.hibernate.context.CurrentSessionContext} implementation that stores
* Sessions in a place other than Spring's TransactionSynchronizationManager.
* It may also be used in combination with Spring's Open-Session-in-View support
* (using Spring's default {@link SpringSessionContext}), in which case it subtly
* differs from the Spring-managed Session mode: The pre-bound Session will <i>not</i>
* receive a <code>clear()</code> call (on rollback) or a <code>disconnect()</code>
* call (on transaction completion) in such a scenario; this is rather left up
* to a custom CurrentSessionContext implementation (if desired).
*/
public void setHibernateManagedSession(boolean hibernateManagedSession) {
this.hibernateManagedSession = hibernateManagedSession;
}
public void afterPropertiesSet() {
if (getSessionFactory() == null) {
throw new IllegalArgumentException("Property 'sessionFactory' is required");
}
// Check for SessionFactory's DataSource.
if (this.autodetectDataSource && getDataSource() == null) {
DataSource sfds = SessionFactoryUtils.getDataSource(getSessionFactory());
if (sfds != null) {
// Use the SessionFactory's DataSource for exposing transactions to JDBC code.
if (logger.isInfoEnabled()) {
logger.info("Using DataSource [" + sfds +
"] of Hibernate SessionFactory for HibernateTransactionManager");
}
setDataSource(sfds);
}
}
}
public Object getResourceFactory() {
return getSessionFactory();
}
@Override
protected Object doGetTransaction() {
HibernateTransactionObject txObject = new HibernateTransactionObject();
txObject.setSavepointAllowed(isNestedTransactionAllowed());
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.getResource(getSessionFactory());
if (sessionHolder != null) {
if (logger.isDebugEnabled()) {
logger.debug("Found thread-bound Session [" + sessionHolder.getSession() + "] for Hibernate transaction");
}
txObject.setSessionHolder(sessionHolder);
}
else if (this.hibernateManagedSession) {
try {
Session session = (Session) ReflectionUtils.invokeMethod(getCurrentSessionMethod, this.sessionFactory);
if (logger.isDebugEnabled()) {
logger.debug("Found Hibernate-managed Session [" + session + "] for Spring-managed transaction");
}
txObject.setExistingSession(session);
}
catch (HibernateException ex) {
throw new DataAccessResourceFailureException(
"Could not obtain Hibernate-managed Session for Spring-managed transaction", ex);
}
}
if (getDataSource() != null) {
ConnectionHolder conHolder = (ConnectionHolder)
TransactionSynchronizationManager.getResource(getDataSource());
txObject.setConnectionHolder(conHolder);
}
return txObject;
}
@Override
protected boolean isExistingTransaction(Object transaction) {
HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
return (txObject.hasSpringManagedTransaction() ||
(this.hibernateManagedSession && txObject.hasHibernateManagedTransaction()));
}
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
if (txObject.hasConnectionHolder() && !txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
throw new IllegalTransactionStateException(
"Pre-bound JDBC Connection found! HibernateTransactionManager does not support " +
"running within DataSourceTransactionManager if told to manage the DataSource itself. " +
"It is recommended to use a single HibernateTransactionManager for all transactions " +
"on a single DataSource, no matter whether Hibernate or JDBC access.");
}
Session session = null;
try {
if (txObject.getSessionHolder() == null || txObject.getSessionHolder().isSynchronizedWithTransaction()) {
Session newSession = SessionFactoryUtils.openSession(getSessionFactory());
if (logger.isDebugEnabled()) {
logger.debug("Opened new Session [" + newSession + "] for Hibernate transaction");
}
txObject.setSession(newSession);
}
session = txObject.getSessionHolder().getSession();
if (this.prepareConnection && isSameConnectionForEntireSession(session)) {
// We're allowed to change the transaction settings of the JDBC Connection.
if (logger.isDebugEnabled()) {
logger.debug("Preparing JDBC Connection of Hibernate Session [" + session + "]");
}
Connection con = ((SessionImplementor) session).connection();
Integer previousIsolationLevel = DataSourceUtils.prepareConnectionForTransaction(con, definition);
txObject.setPreviousIsolationLevel(previousIsolationLevel);
}
else {
// Not allowed to change the transaction settings of the JDBC Connection.
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
// We should set a specific isolation level but are not allowed to...
throw new InvalidIsolationLevelException(
"HibernateTransactionManager is not allowed to support custom isolation levels: " +
"make sure that its 'prepareConnection' flag is on (the default) and that the " +
"Hibernate connection release mode is set to 'on_close' (SpringTransactionFactory's default).");
}
if (logger.isDebugEnabled()) {
logger.debug("Not preparing JDBC Connection of Hibernate Session [" + session + "]");
}
}
if (definition.isReadOnly() && txObject.isNewSession()) {
// Just set to NEVER in case of a new Session for this transaction.
session.setFlushMode(FlushMode.MANUAL);
}
if (!definition.isReadOnly() && !txObject.isNewSession()) {
// We need AUTO or COMMIT for a non-read-only transaction.
FlushMode flushMode = session.getFlushMode();
if (FlushMode.isManualFlushMode(session.getFlushMode())) {
session.setFlushMode(FlushMode.AUTO);
txObject.getSessionHolder().setPreviousFlushMode(flushMode);
}
}
Transaction hibTx;
// Register transaction timeout.
int timeout = determineTimeout(definition);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
// Use Hibernate's own transaction timeout mechanism on Hibernate 3.1+
// Applies to all statements, also to inserts, updates and deletes!
hibTx = session.getTransaction();
hibTx.setTimeout(timeout);
hibTx.begin();
}
else {
// Open a plain Hibernate transaction without specified timeout.
hibTx = session.beginTransaction();
}
// Add the Hibernate transaction to the session holder.
txObject.getSessionHolder().setTransaction(hibTx);
// Register the Hibernate Session's JDBC Connection for the DataSource, if set.
if (getDataSource() != null) {
Connection con = ((SessionImplementor) session).connection();
ConnectionHolder conHolder = new ConnectionHolder(con);
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
conHolder.setTimeoutInSeconds(timeout);
}
if (logger.isDebugEnabled()) {
logger.debug("Exposing Hibernate transaction as JDBC transaction [" + con + "]");
}
TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);
txObject.setConnectionHolder(conHolder);
}
// Bind the session holder to the thread.
if (txObject.isNewSessionHolder()) {
TransactionSynchronizationManager.bindResource(getSessionFactory(), txObject.getSessionHolder());
}
txObject.getSessionHolder().setSynchronizedWithTransaction(true);
}
catch (Exception ex) {
if (txObject.isNewSession()) {
try {
if (session.getTransaction().isActive()) {
session.getTransaction().rollback();
}
}
catch (Throwable ex2) {
logger.debug("Could not rollback Session after failed transaction begin", ex);
}
finally {
SessionFactoryUtils.closeSession(session);
}
}
throw new CannotCreateTransactionException("Could not open Hibernate Session for transaction", ex);
}
}
@Override
protected Object doSuspend(Object transaction) {
HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
txObject.setSessionHolder(null);
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.unbindResource(getSessionFactory());
txObject.setConnectionHolder(null);
ConnectionHolder connectionHolder = null;
if (getDataSource() != null) {
connectionHolder = (ConnectionHolder) TransactionSynchronizationManager.unbindResource(getDataSource());
}
return new SuspendedResourcesHolder(sessionHolder, connectionHolder);
}
@Override
protected void doResume(Object transaction, Object suspendedResources) {
SuspendedResourcesHolder resourcesHolder = (SuspendedResourcesHolder) suspendedResources;
if (TransactionSynchronizationManager.hasResource(getSessionFactory())) {
// From non-transactional code running in active transaction synchronization
// -> can be safely removed, will be closed on transaction completion.
TransactionSynchronizationManager.unbindResource(getSessionFactory());
}
TransactionSynchronizationManager.bindResource(getSessionFactory(), resourcesHolder.getSessionHolder());
if (getDataSource() != null) {
TransactionSynchronizationManager.bindResource(getDataSource(), resourcesHolder.getConnectionHolder());
}
}
@Override
protected void doCommit(DefaultTransactionStatus status) {
HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction();
if (status.isDebug()) {
logger.debug("Committing Hibernate transaction on Session [" +
txObject.getSessionHolder().getSession() + "]");
}
try {
txObject.getSessionHolder().getTransaction().commit();
}
catch (org.hibernate.TransactionException ex) {
// assumably from commit call to the underlying JDBC connection
throw new TransactionSystemException("Could not commit Hibernate transaction", ex);
}
catch (HibernateException ex) {
// assumably failed to flush changes to database
throw convertHibernateAccessException(ex);
}
}
@Override
protected void doRollback(DefaultTransactionStatus status) {
HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction();
if (status.isDebug()) {
logger.debug("Rolling back Hibernate transaction on Session [" +
txObject.getSessionHolder().getSession() + "]");
}
try {
txObject.getSessionHolder().getTransaction().rollback();
}
catch (org.hibernate.TransactionException ex) {
throw new TransactionSystemException("Could not roll back Hibernate transaction", ex);
}
catch (HibernateException ex) {
// Shouldn't really happen, as a rollback doesn't cause a flush.
throw convertHibernateAccessException(ex);
}
finally {
if (!txObject.isNewSession() && !this.hibernateManagedSession) {
// Clear all pending inserts/updates/deletes in the Session.
// Necessary for pre-bound Sessions, to avoid inconsistent state.
txObject.getSessionHolder().getSession().clear();
}
}
}
@Override
protected void doSetRollbackOnly(DefaultTransactionStatus status) {
HibernateTransactionObject txObject = (HibernateTransactionObject) status.getTransaction();
if (status.isDebug()) {
logger.debug("Setting Hibernate transaction on Session [" +
txObject.getSessionHolder().getSession() + "] rollback-only");
}
txObject.setRollbackOnly();
}
@Override
protected void doCleanupAfterCompletion(Object transaction) {
HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
// Remove the session holder from the thread.
if (txObject.isNewSessionHolder()) {
TransactionSynchronizationManager.unbindResource(getSessionFactory());
}
// Remove the JDBC connection holder from the thread, if exposed.
if (getDataSource() != null) {
TransactionSynchronizationManager.unbindResource(getDataSource());
}
Session session = txObject.getSessionHolder().getSession();
if (this.prepareConnection && session.isConnected() && isSameConnectionForEntireSession(session)) {
// We're running with connection release mode "on_close": We're able to reset
// the isolation level and/or read-only flag of the JDBC Connection here.
// Else, we need to rely on the connection pool to perform proper cleanup.
try {
Connection con = ((SessionImplementor) session).connection();
DataSourceUtils.resetConnectionAfterTransaction(con, txObject.getPreviousIsolationLevel());
}
catch (HibernateException ex) {
logger.debug("Could not access JDBC Connection of Hibernate Session", ex);
}
}
if (txObject.isNewSession()) {
if (logger.isDebugEnabled()) {
logger.debug("Closing Hibernate Session [" + session + "] after transaction");
}
SessionFactoryUtils.closeSession(session);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Not closing pre-bound Hibernate Session [" + session + "] after transaction");
}
if (txObject.getSessionHolder().getPreviousFlushMode() != null) {
session.setFlushMode(txObject.getSessionHolder().getPreviousFlushMode());
}
if (!this.hibernateManagedSession) {
session.disconnect();
}
}
txObject.getSessionHolder().clear();
}
/**
* Return whether the given Hibernate Session will always hold the same
* JDBC Connection. This is used to check whether the transaction manager
* can safely prepare and clean up the JDBC Connection used for a transaction.
* <p>The default implementation checks the Session's connection release mode
* to be "on_close".
* @param session the Hibernate Session to check
* @see org.hibernate.engine.transaction.spi.TransactionContext#getConnectionReleaseMode()
* @see org.hibernate.ConnectionReleaseMode#ON_CLOSE
*/
protected boolean isSameConnectionForEntireSession(Session session) {
if (!(session instanceof TransactionContext)) {
// The best we can do is to assume we're safe.
return true;
}
ConnectionReleaseMode releaseMode = ((TransactionContext) session).getConnectionReleaseMode();
return ConnectionReleaseMode.ON_CLOSE.equals(releaseMode);
}
/**
* Convert the given HibernateException to an appropriate exception
* from the <code>org.springframework.dao</code> hierarchy.
* <p>Will automatically apply a specified SQLExceptionTranslator to a
* Hibernate JDBCException, else rely on Hibernate's default translation.
* @param ex HibernateException that occured
* @return a corresponding DataAccessException
* @see SessionFactoryUtils#convertHibernateAccessException
*/
protected DataAccessException convertHibernateAccessException(HibernateException ex) {
return SessionFactoryUtils.convertHibernateAccessException(ex);
}
/**
* Hibernate transaction object, representing a SessionHolder.
* Used as transaction object by HibernateTransactionManager.
*/
private class HibernateTransactionObject extends JdbcTransactionObjectSupport {
private SessionHolder sessionHolder;
private boolean newSessionHolder;
private boolean newSession;
public void setSession(Session session) {
this.sessionHolder = new SessionHolder(session);
this.newSessionHolder = true;
this.newSession = true;
}
public void setExistingSession(Session session) {
this.sessionHolder = new SessionHolder(session);
this.newSessionHolder = true;
this.newSession = false;
}
public void setSessionHolder(SessionHolder sessionHolder) {
this.sessionHolder = sessionHolder;
this.newSessionHolder = false;
this.newSession = false;
}
public SessionHolder getSessionHolder() {
return this.sessionHolder;
}
public boolean isNewSessionHolder() {
return this.newSessionHolder;
}
public boolean isNewSession() {
return this.newSession;
}
public boolean hasSpringManagedTransaction() {
return (this.sessionHolder != null && this.sessionHolder.getTransaction() != null);
}
public boolean hasHibernateManagedTransaction() {
return (this.sessionHolder != null && this.sessionHolder.getSession().getTransaction().isActive());
}
public void setRollbackOnly() {
this.sessionHolder.setRollbackOnly();
if (hasConnectionHolder()) {
getConnectionHolder().setRollbackOnly();
}
}
public boolean isRollbackOnly() {
return this.sessionHolder.isRollbackOnly() ||
(hasConnectionHolder() && getConnectionHolder().isRollbackOnly());
}
@Override
public void flush() {
try {
this.sessionHolder.getSession().flush();
}
catch (HibernateException ex) {
throw convertHibernateAccessException(ex);
}
}
}
/**
* Holder for suspended resources.
* Used internally by <code>doSuspend</code> and <code>doResume</code>.
*/
private static class SuspendedResourcesHolder {
private final SessionHolder sessionHolder;
private final ConnectionHolder connectionHolder;
private SuspendedResourcesHolder(SessionHolder sessionHolder, ConnectionHolder conHolder) {
this.sessionHolder = sessionHolder;
this.connectionHolder = conHolder;
}
private SessionHolder getSessionHolder() {
return this.sessionHolder;
}
private ConnectionHolder getConnectionHolder() {
return this.connectionHolder;
}
}
}

View File

@@ -0,0 +1,383 @@
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate4;
import java.io.File;
import java.io.IOException;
import java.util.Properties;
import javax.sql.DataSource;
import org.hibernate.Interceptor;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.NamingStrategy;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
/**
* {@link org.springframework.beans.factory.FactoryBean} that creates a Hibernate
* {@link org.hibernate.SessionFactory}. This is the usual way to set up a shared
* Hibernate SessionFactory in a Spring application context; the SessionFactory can
* then be passed to Hibernate-based data access objects via dependency injection.
*
* <p><b>NOTE:</b> This variant of LocalSessionFactoryBean requires Hibernate 4.0 or higher.
* It is similar in role to the same-named class in the <code>orm.hibernate3</code> package.
* However, in practice, it is closer to <code>AnnotationSessionFactoryBean</code> since
* its core purpose is to bootstrap a <code>SessionFactory</code> from annotation scanning.
*
* @author Juergen Hoeller
* @since 3.1
* @see #setDataSource
* @see #setPackagesToScan
*/
public class LocalSessionFactoryBean extends HibernateExceptionTranslator
implements FactoryBean<SessionFactory>, ResourceLoaderAware, InitializingBean, DisposableBean {
private DataSource dataSource;
private Resource[] configLocations;
private String[] mappingResources;
private Resource[] mappingLocations;
private Resource[] cacheableMappingLocations;
private Resource[] mappingJarLocations;
private Resource[] mappingDirectoryLocations;
private Interceptor entityInterceptor;
private NamingStrategy namingStrategy;
private Properties hibernateProperties;
private Class<?>[] annotatedClasses;
private String[] annotatedPackages;
private String[] packagesToScan;
private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
private Configuration configuration;
private SessionFactory sessionFactory;
/**
* Set the DataSource to be used by the SessionFactory.
* If set, this will override corresponding settings in Hibernate properties.
* <p>If this is set, the Hibernate settings should not define
* a connection provider to avoid meaningless double configuration.
*/
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
/**
* Set the location of a single Hibernate XML config file, for example as
* classpath resource "classpath:hibernate.cfg.xml".
* <p>Note: Can be omitted when all necessary properties and mapping
* resources are specified locally via this bean.
* @see org.hibernate.cfg.Configuration#configure(java.net.URL)
*/
public void setConfigLocation(Resource configLocation) {
this.configLocations = new Resource[] {configLocation};
}
/**
* Set the locations of multiple Hibernate XML config files, for example as
* classpath resources "classpath:hibernate.cfg.xml,classpath:extension.cfg.xml".
* <p>Note: Can be omitted when all necessary properties and mapping
* resources are specified locally via this bean.
* @see org.hibernate.cfg.Configuration#configure(java.net.URL)
*/
public void setConfigLocations(Resource[] configLocations) {
this.configLocations = configLocations;
}
/**
* Set Hibernate mapping resources to be found in the class path,
* like "example.hbm.xml" or "mypackage/example.hbm.xml".
* Analogous to mapping entries in a Hibernate XML config file.
* Alternative to the more generic setMappingLocations method.
* <p>Can be used to add to mappings from a Hibernate XML config file,
* or to specify all mappings locally.
* @see #setMappingLocations
* @see org.hibernate.cfg.Configuration#addResource
*/
public void setMappingResources(String[] mappingResources) {
this.mappingResources = mappingResources;
}
/**
* Set locations of Hibernate mapping files, for example as classpath
* resource "classpath:example.hbm.xml". Supports any resource location
* via Spring's resource abstraction, for example relative paths like
* "WEB-INF/mappings/example.hbm.xml" when running in an application context.
* <p>Can be used to add to mappings from a Hibernate XML config file,
* or to specify all mappings locally.
* @see org.hibernate.cfg.Configuration#addInputStream
*/
public void setMappingLocations(Resource[] mappingLocations) {
this.mappingLocations = mappingLocations;
}
/**
* Set locations of cacheable Hibernate mapping files, for example as web app
* resource "/WEB-INF/mapping/example.hbm.xml". Supports any resource location
* via Spring's resource abstraction, as long as the resource can be resolved
* in the file system.
* <p>Can be used to add to mappings from a Hibernate XML config file,
* or to specify all mappings locally.
* @see org.hibernate.cfg.Configuration#addCacheableFile(java.io.File)
*/
public void setCacheableMappingLocations(Resource[] cacheableMappingLocations) {
this.cacheableMappingLocations = cacheableMappingLocations;
}
/**
* Set locations of jar files that contain Hibernate mapping resources,
* like "WEB-INF/lib/example.hbm.jar".
* <p>Can be used to add to mappings from a Hibernate XML config file,
* or to specify all mappings locally.
* @see org.hibernate.cfg.Configuration#addJar(java.io.File)
*/
public void setMappingJarLocations(Resource[] mappingJarLocations) {
this.mappingJarLocations = mappingJarLocations;
}
/**
* Set locations of directories that contain Hibernate mapping resources,
* like "WEB-INF/mappings".
* <p>Can be used to add to mappings from a Hibernate XML config file,
* or to specify all mappings locally.
* @see org.hibernate.cfg.Configuration#addDirectory(java.io.File)
*/
public void setMappingDirectoryLocations(Resource[] mappingDirectoryLocations) {
this.mappingDirectoryLocations = mappingDirectoryLocations;
}
/**
* Set a Hibernate entity interceptor that allows to inspect and change
* property values before writing to and reading from the database.
* Will get applied to any new Session created by this factory.
* @see org.hibernate.cfg.Configuration#setInterceptor
*/
public void setEntityInterceptor(Interceptor entityInterceptor) {
this.entityInterceptor = entityInterceptor;
}
/**
* Set a Hibernate NamingStrategy for the SessionFactory, determining the
* physical column and table names given the info in the mapping document.
* @see org.hibernate.cfg.Configuration#setNamingStrategy
*/
public void setNamingStrategy(NamingStrategy namingStrategy) {
this.namingStrategy = namingStrategy;
}
/**
* Set Hibernate properties, such as "hibernate.dialect".
* <p>Note: Do not specify a transaction provider here when using
* Spring-driven transactions. It is also advisable to omit connection
* provider settings and use a Spring-set DataSource instead.
* @see #setDataSource
*/
public void setHibernateProperties(Properties hibernateProperties) {
this.hibernateProperties = hibernateProperties;
}
/**
* Return the Hibernate properties, if any. Mainly available for
* configuration through property paths that specify individual keys.
*/
public Properties getHibernateProperties() {
if (this.hibernateProperties == null) {
this.hibernateProperties = new Properties();
}
return this.hibernateProperties;
}
/**
* Specify annotated entity classes to register with this Hibernate SessionFactory.
* @see org.hibernate.cfg.Configuration#addAnnotatedClass(String)
*/
public void setAnnotatedClasses(Class<?>[] annotatedClasses) {
this.annotatedClasses = annotatedClasses;
}
/**
* Specify the names of annotated packages, for which package-level
* annotation metadata will be read.
* @see org.hibernate.cfg.Configuration#addPackage(String)
*/
public void setAnnotatedPackages(String[] annotatedPackages) {
this.annotatedPackages = annotatedPackages;
}
/**
* Specify packages to search for autodetection of your entity classes in the
* classpath. This is analogous to Spring's component-scan feature
* ({@link org.springframework.context.annotation.ClassPathBeanDefinitionScanner}).
*/
public void setPackagesToScan(String... packagesToScan) {
this.packagesToScan = packagesToScan;
}
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
}
public void afterPropertiesSet() throws IOException {
LocalSessionFactoryBuilder sfb = new LocalSessionFactoryBuilder(this.dataSource, this.resourcePatternResolver);
if (this.configLocations != null) {
for (Resource resource : this.configLocations) {
// Load Hibernate configuration from given location.
sfb.configure(resource.getURL());
}
}
if (this.mappingResources != null) {
// Register given Hibernate mapping definitions, contained in resource files.
for (String mapping : this.mappingResources) {
Resource mr = new ClassPathResource(mapping.trim(), this.resourcePatternResolver.getClassLoader());
sfb.addInputStream(mr.getInputStream());
}
}
if (this.mappingLocations != null) {
// Register given Hibernate mapping definitions, contained in resource files.
for (Resource resource : this.mappingLocations) {
sfb.addInputStream(resource.getInputStream());
}
}
if (this.cacheableMappingLocations != null) {
// Register given cacheable Hibernate mapping definitions, read from the file system.
for (Resource resource : this.cacheableMappingLocations) {
sfb.addCacheableFile(resource.getFile());
}
}
if (this.mappingJarLocations != null) {
// Register given Hibernate mapping definitions, contained in jar files.
for (Resource resource : this.mappingJarLocations) {
sfb.addJar(resource.getFile());
}
}
if (this.mappingDirectoryLocations != null) {
// Register all Hibernate mapping definitions in the given directories.
for (Resource resource : this.mappingDirectoryLocations) {
File file = resource.getFile();
if (!file.isDirectory()) {
throw new IllegalArgumentException(
"Mapping directory location [" + resource + "] does not denote a directory");
}
sfb.addDirectory(file);
}
}
if (this.entityInterceptor != null) {
sfb.setInterceptor(this.entityInterceptor);
}
if (this.namingStrategy != null) {
sfb.setNamingStrategy(this.namingStrategy);
}
if (this.hibernateProperties != null) {
sfb.addProperties(this.hibernateProperties);
}
if (this.annotatedClasses != null) {
sfb.addAnnotatedClasses(this.annotatedClasses);
}
if (this.annotatedPackages != null) {
sfb.addPackages(this.annotatedPackages);
}
if (this.packagesToScan != null) {
sfb.scanPackages(this.packagesToScan);
}
// Build SessionFactory instance.
this.configuration = sfb;
this.sessionFactory = buildSessionFactory(sfb);
}
/**
* Subclasses can override this method to perform custom initialization
* of the SessionFactory instance, creating it via the given Configuration
* object that got prepared by this LocalSessionFactoryBean.
* <p>The default implementation invokes LocalSessionFactoryBuilder's buildSessionFactory.
* A custom implementation could prepare the instance in a specific way (e.g. applying
* a custom ServiceRegistry) or use a custom SessionFactoryImpl subclass.
* @param sfb LocalSessionFactoryBuilder prepared by this LocalSessionFactoryBean
* @return the SessionFactory instance
* @see LocalSessionFactoryBuilder#buildSessionFactory
*/
protected SessionFactory buildSessionFactory(LocalSessionFactoryBuilder sfb) {
return sfb.buildSessionFactory();
}
/**
* Return the Hibernate Configuration object used to build the SessionFactory.
* Allows for access to configuration metadata stored there (rarely needed).
* @throws IllegalStateException if the Configuration object has not been initialized yet
*/
public final Configuration getConfiguration() {
if (this.configuration == null) {
throw new IllegalStateException("Configuration not initialized yet");
}
return this.configuration;
}
public SessionFactory getObject() {
return this.sessionFactory;
}
public Class<?> getObjectType() {
return (this.sessionFactory != null ? this.sessionFactory.getClass() : SessionFactory.class);
}
public boolean isSingleton() {
return true;
}
public void destroy() {
this.sessionFactory.close();
}
}

View File

@@ -0,0 +1,192 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate4;
import java.io.IOException;
import java.lang.reflect.Method;
import javax.persistence.Embeddable;
import javax.persistence.Entity;
import javax.persistence.MappedSuperclass;
import javax.sql.DataSource;
import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.AnnotationTypeFilter;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
/**
* A Spring-provided extension of the standard Hibernate {@link Configuration} class,
* adding {@link SpringSessionContext} as a default and providing convenient ways
* to specify a DataSource and an application class loader.
*
* <p>This is designed for programmatic use, e.g. in {@code @Bean} factory methods.
* Consider using {@link LocalSessionFactoryBean} for XML bean definition files.
*
* @author Juergen Hoeller
* @since 3.1
* @see LocalSessionFactoryBean
*/
@SuppressWarnings("serial")
public class LocalSessionFactoryBuilder extends Configuration {
private static final String RESOURCE_PATTERN = "/**/*.class";
private static final TypeFilter[] ENTITY_TYPE_FILTERS = new TypeFilter[] {
new AnnotationTypeFilter(Entity.class, false),
new AnnotationTypeFilter(Embeddable.class, false),
new AnnotationTypeFilter(MappedSuperclass.class, false)};
private static final Method addAnnotatedClassMethod =
ClassUtils.getMethod(Configuration.class, "addAnnotatedClass", Class.class);
private static final Method addPackageMethod =
ClassUtils.getMethod(Configuration.class, "addPackage", String.class);
private final ResourcePatternResolver resourcePatternResolver;
/**
* Create a new LocalSessionFactoryBuilder for the given DataSource.
* @param dataSource the JDBC DataSource that the resulting Hibernate SessionFactory should be using
* (may be <code>null</code>)
*/
public LocalSessionFactoryBuilder(DataSource dataSource) {
this(dataSource, new PathMatchingResourcePatternResolver());
}
/**
* Create a new LocalSessionFactoryBuilder for the given DataSource.
* @param dataSource the JDBC DataSource that the resulting Hibernate SessionFactory should be using
* (may be <code>null</code>)
* @param classLoader the ClassLoader to load application classes from
*/
public LocalSessionFactoryBuilder(DataSource dataSource, ClassLoader classLoader) {
this(dataSource, new PathMatchingResourcePatternResolver(classLoader));
}
/**
* Create a new LocalSessionFactoryBuilder for the given DataSource.
* @param dataSource the JDBC DataSource that the resulting Hibernate SessionFactory should be using
* (may be <code>null</code>)
* @param classLoader the ResourceLoader to load application classes from
*/
public LocalSessionFactoryBuilder(DataSource dataSource, ResourceLoader resourceLoader) {
getProperties().put(Environment.CURRENT_SESSION_CONTEXT_CLASS, SpringSessionContext.class.getName());
if (dataSource != null) {
getProperties().put(Environment.DATASOURCE, dataSource);
}
getProperties().put("hibernate.classLoader.application", resourceLoader.getClassLoader());
this.resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
}
/**
* Add the given annotated classes in a batch.
* @see #addAnnotatedClass
* @see #scanPackages
*/
public LocalSessionFactoryBuilder addAnnotatedClasses(Class<?>... annotatedClasses) {
for (Class<?> annotatedClass : annotatedClasses) {
ReflectionUtils.invokeMethod(addAnnotatedClassMethod, this, annotatedClass);
}
return this;
}
/**
* Add the given annotated packages in a batch.
* @see #addPackage
* @see #scanPackages
*/
public LocalSessionFactoryBuilder addPackages(String... annotatedPackages) {
for (String annotatedPackage :annotatedPackages) {
ReflectionUtils.invokeMethod(addPackageMethod, this, annotatedPackage);
}
return this;
}
/**
* Perform Spring-based scanning for entity classes, registering them
* as annotated classes with this {@code Configuration}.
* @param packagesToScan one or more Java package names
* @throws HibernateException if scanning fails for any reason
*/
public LocalSessionFactoryBuilder scanPackages(String... packagesToScan) throws HibernateException {
try {
for (String pkg : packagesToScan) {
String pattern = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
ClassUtils.convertClassNameToResourcePath(pkg) + RESOURCE_PATTERN;
Resource[] resources = this.resourcePatternResolver.getResources(pattern);
MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(this.resourcePatternResolver);
for (Resource resource : resources) {
if (resource.isReadable()) {
MetadataReader reader = readerFactory.getMetadataReader(resource);
String className = reader.getClassMetadata().getClassName();
if (matchesFilter(reader, readerFactory)) {
addAnnotatedClasses(this.resourcePatternResolver.getClassLoader().loadClass(className));
}
}
}
}
return this;
}
catch (IOException ex) {
throw new MappingException("Failed to scan classpath for unlisted classes", ex);
}
catch (ClassNotFoundException ex) {
throw new MappingException("Failed to load annotated classes from classpath", ex);
}
}
/**
* Check whether any of the configured entity type filters matches
* the current class descriptor contained in the metadata reader.
*/
private boolean matchesFilter(MetadataReader reader, MetadataReaderFactory readerFactory) throws IOException {
for (TypeFilter filter : ENTITY_TYPE_FILTERS) {
if (filter.match(reader, readerFactory)) {
return true;
}
}
return false;
}
/**
* Build the {@code SessionFactory}.
*/
@Override
@SuppressWarnings("deprecation")
public SessionFactory buildSessionFactory() throws HibernateException {
return super.buildSessionFactory();
}
}

View File

@@ -0,0 +1,209 @@
/*
* Copyright 2002-2012 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate4;
import java.lang.reflect.Method;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.JDBCException;
import org.hibernate.NonUniqueObjectException;
import org.hibernate.NonUniqueResultException;
import org.hibernate.ObjectDeletedException;
import org.hibernate.PersistentObjectException;
import org.hibernate.PropertyValueException;
import org.hibernate.QueryException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.StaleObjectStateException;
import org.hibernate.StaleStateException;
import org.hibernate.TransientObjectException;
import org.hibernate.UnresolvableObjectException;
import org.hibernate.WrongClassException;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.exception.ConstraintViolationException;
import org.hibernate.exception.DataException;
import org.hibernate.exception.JDBCConnectionException;
import org.hibernate.exception.LockAcquisitionException;
import org.hibernate.exception.SQLGrammarException;
import org.hibernate.service.jdbc.connections.spi.ConnectionProvider;
import org.springframework.dao.CannotAcquireLockException;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.dao.InvalidDataAccessResourceUsageException;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
/**
* Helper class featuring methods for Hibernate Session handling.
* Also provides support for exception translation.
*
* <p>Used internally by {@link HibernateTransactionManager}.
* Can also be used directly in application code.
*
* @author Juergen Hoeller
* @since 3.1
* @see HibernateExceptionTranslator
* @see HibernateTransactionManager
*/
public abstract class SessionFactoryUtils {
/**
* Order value for TransactionSynchronization objects that clean up Hibernate Sessions.
* Returns <code>DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 100</code>
* to execute Session cleanup before JDBC Connection cleanup, if any.
* @see org.springframework.jdbc.datasource.DataSourceUtils#CONNECTION_SYNCHRONIZATION_ORDER
*/
public static final int SESSION_SYNCHRONIZATION_ORDER =
DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 100;
/**
* A Method handle for the <code>SessionFactory.openSession()</code> method.
* The return value differs between Hibernate 3.x and 4.x; for cross-compilation purposes,
* we have to use reflection here as long as we keep compiling against Hibernate 3.x jars.
*/
private static final Method openSessionMethod = ClassUtils.getMethod(SessionFactory.class, "openSession");
static final Log logger = LogFactory.getLog(SessionFactoryUtils.class);
/**
* Determine the DataSource of the given SessionFactory.
* @param sessionFactory the SessionFactory to check
* @return the DataSource, or <code>null</code> if none found
* @see org.hibernate.engine.spi.SessionFactoryImplementor#getConnectionProvider
*/
public static DataSource getDataSource(SessionFactory sessionFactory) {
if (sessionFactory instanceof SessionFactoryImplementor) {
ConnectionProvider cp = ((SessionFactoryImplementor) sessionFactory).getConnectionProvider();
return cp.unwrap(DataSource.class);
}
return null;
}
/**
* Obtain a new Session from the given SessionFactory.
* <p>Bridges between Hibernate signature differences.
* @param sessionFactory the SessionFactory to use
* @return the new Session
* @see org.hibernate.SessionFactory#openSession()
*/
public static Session openSession(SessionFactory sessionFactory) {
return (Session) ReflectionUtils.invokeMethod(openSessionMethod, sessionFactory);
}
/**
* Perform actual closing of the Hibernate Session,
* catching and logging any cleanup exceptions thrown.
* @param session the Hibernate Session to close (may be <code>null</code>)
* @see org.hibernate.Session#close()
*/
public static void closeSession(Session session) {
if (session != null) {
try {
session.close();
}
catch (HibernateException ex) {
logger.debug("Could not close Hibernate Session", ex);
}
catch (Throwable ex) {
logger.debug("Unexpected exception on closing Hibernate Session", ex);
}
}
}
/**
* Convert the given HibernateException to an appropriate exception
* from the <code>org.springframework.dao</code> hierarchy.
* @param ex HibernateException that occured
* @return the corresponding DataAccessException instance
* @see HibernateExceptionTranslator#convertHibernateAccessException
* @see HibernateTransactionManager#convertHibernateAccessException
*/
public static DataAccessException convertHibernateAccessException(HibernateException ex) {
if (ex instanceof JDBCConnectionException) {
return new DataAccessResourceFailureException(ex.getMessage(), ex);
}
if (ex instanceof SQLGrammarException) {
SQLGrammarException jdbcEx = (SQLGrammarException) ex;
return new InvalidDataAccessResourceUsageException(ex.getMessage() + "; SQL [" + jdbcEx.getSQL() + "]", ex);
}
if (ex instanceof LockAcquisitionException) {
LockAcquisitionException jdbcEx = (LockAcquisitionException) ex;
return new CannotAcquireLockException(ex.getMessage() + "; SQL [" + jdbcEx.getSQL() + "]", ex);
}
if (ex instanceof ConstraintViolationException) {
ConstraintViolationException jdbcEx = (ConstraintViolationException) ex;
return new DataIntegrityViolationException(ex.getMessage() + "; SQL [" + jdbcEx.getSQL() +
"]; constraint [" + jdbcEx.getConstraintName() + "]", ex);
}
if (ex instanceof DataException) {
DataException jdbcEx = (DataException) ex;
return new DataIntegrityViolationException(ex.getMessage() + "; SQL [" + jdbcEx.getSQL() + "]", ex);
}
if (ex instanceof JDBCException) {
return new HibernateJdbcException((JDBCException) ex);
}
// end of JDBCException (subclass) handling
if (ex instanceof QueryException) {
return new HibernateQueryException((QueryException) ex);
}
if (ex instanceof NonUniqueResultException) {
return new IncorrectResultSizeDataAccessException(ex.getMessage(), 1, ex);
}
if (ex instanceof NonUniqueObjectException) {
return new DuplicateKeyException(ex.getMessage(), ex);
}
if (ex instanceof PropertyValueException) {
return new DataIntegrityViolationException(ex.getMessage(), ex);
}
if (ex instanceof PersistentObjectException) {
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
}
if (ex instanceof TransientObjectException) {
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
}
if (ex instanceof ObjectDeletedException) {
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
}
if (ex instanceof UnresolvableObjectException) {
return new HibernateObjectRetrievalFailureException((UnresolvableObjectException) ex);
}
if (ex instanceof WrongClassException) {
return new HibernateObjectRetrievalFailureException((WrongClassException) ex);
}
if (ex instanceof StaleObjectStateException) {
return new HibernateOptimisticLockingFailureException((StaleObjectStateException) ex);
}
if (ex instanceof StaleStateException) {
return new HibernateOptimisticLockingFailureException((StaleStateException) ex);
}
// fallback
return new HibernateSystemException(ex);
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate4;
import org.hibernate.FlushMode;
import org.hibernate.Session;
import org.hibernate.Transaction;
import org.springframework.transaction.support.ResourceHolderSupport;
import org.springframework.util.Assert;
/**
* Session holder, wrapping a Hibernate Session and a Hibernate Transaction.
* HibernateTransactionManager binds instances of this class to the thread,
* for a given SessionFactory.
*
* <p>Note: This is an SPI class, not intended to be used by applications.
*
* @author Juergen Hoeller
* @since 3.1
* @see HibernateTransactionManager
* @see SessionFactoryUtils
*/
public class SessionHolder extends ResourceHolderSupport {
private Session session;
private Transaction transaction;
private FlushMode previousFlushMode;
public SessionHolder(Session session) {
Assert.notNull(session, "Session must not be null");
this.session = session;
}
public Session getSession() {
return this.session;
}
public void setTransaction(Transaction transaction) {
this.transaction = transaction;
}
public Transaction getTransaction() {
return this.transaction;
}
public void setPreviousFlushMode(FlushMode previousFlushMode) {
this.previousFlushMode = previousFlushMode;
}
public FlushMode getPreviousFlushMode() {
return this.previousFlushMode;
}
@Override
public void clear() {
super.clear();
this.transaction = null;
this.previousFlushMode = null;
}
}

View File

@@ -0,0 +1,64 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate4;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
/**
* Simple synchronization adapter that propagates a <code>flush()</code> call
* to the underlying Hibernate Session. Used in combination with JTA.
*
* @author Juergen Hoeller
* @since 3.1
*/
class SpringFlushSynchronization extends TransactionSynchronizationAdapter {
private final Session session;
public SpringFlushSynchronization(Session session) {
this.session = session;
}
@Override
public void flush() {
try {
SessionFactoryUtils.logger.debug("Flushing Hibernate Session on explicit request");
this.session.flush();
}
catch (HibernateException ex) {
throw SessionFactoryUtils.convertHibernateAccessException(ex);
}
}
@Override
public boolean equals(Object obj) {
return (obj instanceof SpringFlushSynchronization &&
this.session == ((SpringFlushSynchronization) obj).session);
}
@Override
public int hashCode() {
return this.session.hashCode();
}
}

View File

@@ -0,0 +1,48 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate4;
import org.hibernate.FlushMode;
import org.hibernate.Session;
import org.hibernate.context.internal.JTASessionContext;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
* Spring-specific subclass of Hibernate's JTASessionContext,
* setting <code>FlushMode.MANUAL</code> for read-only transactions.
*
* @author Juergen Hoeller
* @since 3.1
*/
public class SpringJtaSessionContext extends JTASessionContext {
public SpringJtaSessionContext(SessionFactoryImplementor factory) {
super(factory);
}
@Override
protected Session buildOrObtainSession() {
Session session = super.buildOrObtainSession();
if (TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
session.setFlushMode(FlushMode.MANUAL);
}
return session;
}
}

View File

@@ -0,0 +1,101 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate4;
import javax.transaction.TransactionManager;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.context.spi.CurrentSessionContext;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.service.jta.platform.spi.JtaPlatform;
import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
* Implementation of Hibernate 3.1's CurrentSessionContext interface
* that delegates to Spring's SessionFactoryUtils for providing a
* Spring-managed current Session.
*
* <p>This CurrentSessionContext implementation can also be specified in custom
* SessionFactory setup through the "hibernate.current_session_context_class"
* property, with the fully qualified name of this class as value.
*
* @author Juergen Hoeller
* @since 3.1
*/
@SuppressWarnings("serial")
public class SpringSessionContext implements CurrentSessionContext {
private final SessionFactoryImplementor sessionFactory;
private final CurrentSessionContext jtaSessionContext;
/**
* Create a new SpringSessionContext for the given Hibernate SessionFactory.
* @param sessionFactory the SessionFactory to provide current Sessions for
*/
public SpringSessionContext(SessionFactoryImplementor sessionFactory) {
this.sessionFactory = sessionFactory;
JtaPlatform jtaPlatform = sessionFactory.getServiceRegistry().getService(JtaPlatform.class);
TransactionManager transactionManager = jtaPlatform.retrieveTransactionManager();
this.jtaSessionContext = (transactionManager != null ? new SpringJtaSessionContext(sessionFactory) : null);
}
/**
* Retrieve the Spring-managed Session for the current thread, if any.
*/
public Session currentSession() throws HibernateException {
Object value = TransactionSynchronizationManager.getResource(this.sessionFactory);
if (value instanceof Session) {
return (Session) value;
}
else if (value instanceof SessionHolder) {
SessionHolder sessionHolder = (SessionHolder) value;
Session session = sessionHolder.getSession();
if (TransactionSynchronizationManager.isSynchronizationActive() &&
!sessionHolder.isSynchronizedWithTransaction()) {
TransactionSynchronizationManager.registerSynchronization(
new SpringSessionSynchronization(sessionHolder, this.sessionFactory));
sessionHolder.setSynchronizedWithTransaction(true);
// Switch to FlushMode.AUTO, as we have to assume a thread-bound Session
// with FlushMode.MANUAL, which needs to allow flushing within the transaction.
FlushMode flushMode = session.getFlushMode();
if (FlushMode.isManualFlushMode(flushMode) &&
!TransactionSynchronizationManager.isCurrentTransactionReadOnly()) {
session.setFlushMode(FlushMode.AUTO);
sessionHolder.setPreviousFlushMode(flushMode);
}
}
return session;
}
else if (this.jtaSessionContext != null) {
Session session = this.jtaSessionContext.currentSession();
if (TransactionSynchronizationManager.isSynchronizationActive()) {
TransactionSynchronizationManager.registerSynchronization(new SpringFlushSynchronization(session));
}
return session;
}
else {
throw new HibernateException("No Session found for current thread");
}
}
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate4;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.core.Ordered;
import org.springframework.dao.DataAccessException;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
* Callback for resource cleanup at the end of a Spring-managed transaction
* for a pre-bound Hibernate Session.
*
* @author Juergen Hoeller
* @since 3.1
*/
class SpringSessionSynchronization implements TransactionSynchronization, Ordered {
private final SessionHolder sessionHolder;
private final SessionFactory sessionFactory;
private boolean holderActive = true;
public SpringSessionSynchronization(SessionHolder sessionHolder, SessionFactory sessionFactory) {
this.sessionHolder = sessionHolder;
this.sessionFactory = sessionFactory;
}
private Session getCurrentSession() {
return this.sessionHolder.getSession();
}
public int getOrder() {
return SessionFactoryUtils.SESSION_SYNCHRONIZATION_ORDER;
}
public void suspend() {
if (this.holderActive) {
TransactionSynchronizationManager.unbindResource(this.sessionFactory);
// Eagerly disconnect the Session here, to make release mode "on_close" work on JBoss.
getCurrentSession().disconnect();
}
}
public void resume() {
if (this.holderActive) {
TransactionSynchronizationManager.bindResource(this.sessionFactory, this.sessionHolder);
}
}
public void flush() {
try {
SessionFactoryUtils.logger.debug("Flushing Hibernate Session on explicit request");
getCurrentSession().flush();
}
catch (HibernateException ex) {
throw SessionFactoryUtils.convertHibernateAccessException(ex);
}
}
public void beforeCommit(boolean readOnly) throws DataAccessException {
if (!readOnly) {
Session session = getCurrentSession();
// Read-write transaction -> flush the Hibernate Session.
// Further check: only flush when not FlushMode.MANUAL.
if (!FlushMode.isManualFlushMode(session.getFlushMode())) {
try {
SessionFactoryUtils.logger.debug("Flushing Hibernate Session on transaction synchronization");
session.flush();
}
catch (HibernateException ex) {
throw SessionFactoryUtils.convertHibernateAccessException(ex);
}
}
}
}
public void beforeCompletion() {
Session session = this.sessionHolder.getSession();
if (this.sessionHolder.getPreviousFlushMode() != null) {
// In case of pre-bound Session, restore previous flush mode.
session.setFlushMode(this.sessionHolder.getPreviousFlushMode());
}
// Eagerly disconnect the Session here, to make release mode "on_close" work nicely.
session.disconnect();
}
public void afterCommit() {
}
public void afterCompletion(int status) {
try {
if (status != STATUS_COMMITTED) {
// Clear all pending inserts/updates/deletes in the Session.
// Necessary for pre-bound Sessions, to avoid inconsistent state.
this.sessionHolder.getSession().clear();
}
}
finally {
this.sessionHolder.setSynchronizedWithTransaction(false);
}
}
}

View File

@@ -0,0 +1,17 @@
/**
*
* Package providing integration of
* <a href="http://www.hibernate.org">Hibernate 4.0</a>
* with Spring concepts.
*
* <p>Contains an implementation of Spring's transaction SPI for local Hibernate transactions.
* This package is intentionally rather minimal, relying on native Hibernate builder APIs
* for building a SessionFactory (for example in an @Bean method in a @Configuration class).
*
* <p><b>This package supports Hibernate 4.x only.</b>
* See the org.springframework.orm.hibernate3 package for Hibernate 3.x support.
*
*/
package org.springframework.orm.hibernate4;

View File

@@ -0,0 +1,180 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate4.support;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.orm.hibernate4.SessionFactoryUtils;
import org.springframework.orm.hibernate4.SessionHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.filter.OncePerRequestFilter;
/**
* Servlet 2.3 Filter that binds a Hibernate Session to the thread for the entire
* processing of the request. Intended for the "Open Session in View" pattern,
* i.e. to allow for lazy loading in web views despite the original transactions
* already being completed.
*
* <p>This filter makes Hibernate Sessions available via the current thread, which
* will be autodetected by transaction managers. It is suitable for service layer
* transactions via {@link org.springframework.orm.hibernate4.HibernateTransactionManager}
* as well as for non-transactional execution (if configured appropriately).
*
* <p><b>NOTE</b>: This filter will by default <i>not</i> flush the Hibernate Session,
* with the flush mode set to <code>FlushMode.NEVER</code>. It assumes to be used
* in combination with service layer transactions that care for the flushing: The
* active transaction manager will temporarily change the flush mode to
* <code>FlushMode.AUTO</code> during a read-write transaction, with the flush
* mode reset to <code>FlushMode.NEVER</code> at the end of each transaction.
*
* <p><b>WARNING:</b> Applying this filter to existing logic can cause issues that
* have not appeared before, through the use of a single Hibernate Session for the
* processing of an entire request. In particular, the reassociation of persistent
* objects with a Hibernate Session has to occur at the very beginning of request
* processing, to avoid clashes with already loaded instances of the same objects.
*
* <p>Looks up the SessionFactory in Spring's root web application context.
* Supports a "sessionFactoryBeanName" filter init-param in <code>web.xml</code>;
* the default bean name is "sessionFactory". Looks up the SessionFactory on each
* request, to avoid initialization order issues (when using ContextLoaderServlet,
* the root application context will get initialized <i>after</i> this filter).
*
* @author Juergen Hoeller
* @since 3.1
* @see #lookupSessionFactory
* @see OpenSessionInViewInterceptor
* @see org.springframework.orm.hibernate4.HibernateTransactionManager
* @see org.springframework.transaction.support.TransactionSynchronizationManager
*/
public class OpenSessionInViewFilter extends OncePerRequestFilter {
public static final String DEFAULT_SESSION_FACTORY_BEAN_NAME = "sessionFactory";
private String sessionFactoryBeanName = DEFAULT_SESSION_FACTORY_BEAN_NAME;
/**
* Set the bean name of the SessionFactory to fetch from Spring's
* root application context. Default is "sessionFactory".
* @see #DEFAULT_SESSION_FACTORY_BEAN_NAME
*/
public void setSessionFactoryBeanName(String sessionFactoryBeanName) {
this.sessionFactoryBeanName = sessionFactoryBeanName;
}
/**
* Return the bean name of the SessionFactory to fetch from Spring's
* root application context.
*/
protected String getSessionFactoryBeanName() {
return this.sessionFactoryBeanName;
}
@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
SessionFactory sessionFactory = lookupSessionFactory(request);
boolean participate = false;
if (TransactionSynchronizationManager.hasResource(sessionFactory)) {
// Do not modify the Session: just set the participate flag.
participate = true;
}
else {
logger.debug("Opening Hibernate Session in OpenSessionInViewFilter");
Session session = openSession(sessionFactory);
TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(session));
}
try {
filterChain.doFilter(request, response);
}
finally {
if (!participate) {
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.unbindResource(sessionFactory);
logger.debug("Closing Hibernate Session in OpenSessionInViewFilter");
SessionFactoryUtils.closeSession(sessionHolder.getSession());
}
}
}
/**
* Look up the SessionFactory that this filter should use,
* taking the current HTTP request as argument.
* <p>The default implementation delegates to the {@link #lookupSessionFactory()}
* variant without arguments.
* @param request the current request
* @return the SessionFactory to use
*/
protected SessionFactory lookupSessionFactory(HttpServletRequest request) {
return lookupSessionFactory();
}
/**
* Look up the SessionFactory that this filter should use.
* <p>The default implementation looks for a bean with the specified name
* in Spring's root application context.
* @return the SessionFactory to use
* @see #getSessionFactoryBeanName
*/
protected SessionFactory lookupSessionFactory() {
if (logger.isDebugEnabled()) {
logger.debug("Using SessionFactory '" + getSessionFactoryBeanName() + "' for OpenSessionInViewFilter");
}
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
return wac.getBean(getSessionFactoryBeanName(), SessionFactory.class);
}
/**
* Open a Session for the SessionFactory that this filter uses.
* <p>The default implementation delegates to the
* <code>SessionFactory.openSession</code> method and
* sets the <code>Session</code>'s flush mode to "MANUAL".
* @param sessionFactory the SessionFactory that this filter uses
* @return the Session to use
* @throws DataAccessResourceFailureException if the Session could not be created
* @see org.hibernate.FlushMode#MANUAL
*/
protected Session openSession(SessionFactory sessionFactory) throws DataAccessResourceFailureException {
try {
Session session = SessionFactoryUtils.openSession(sessionFactory);
session.setFlushMode(FlushMode.MANUAL);
return session;
}
catch (HibernateException ex) {
throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex);
}
}
}

View File

@@ -0,0 +1,176 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.hibernate4.support;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.FlushMode;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.orm.hibernate4.SessionFactoryUtils;
import org.springframework.orm.hibernate4.SessionHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.ui.ModelMap;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.WebRequestInterceptor;
/**
* Spring web request interceptor that binds a Hibernate <code>Session</code> to the
* thread for the entire processing of the request.
*
* <p>This class is a concrete expression of the "Open Session in View" pattern, which
* is a pattern that allows for the lazy loading of associations in web views despite
* the original transactions already being completed.
*
* <p>This interceptor makes Hibernate Sessions available via the current thread,
* which will be autodetected by transaction managers. It is suitable for service layer
* transactions via {@link org.springframework.orm.hibernate4.HibernateTransactionManager}
* as well as for non-transactional execution (if configured appropriately).
*
* <p><b>NOTE</b>: This interceptor will by default <i>not</i> flush the Hibernate
* <code>Session</code>, with the flush mode being set to <code>FlushMode.NEVER</code>.
* It assumes that it will be used in combination with service layer transactions
* that handle the flushing: the active transaction manager will temporarily change
* the flush mode to <code>FlushMode.AUTO</code> during a read-write transaction,
* with the flush mode reset to <code>FlushMode.NEVER</code> at the end of each
* transaction. If you intend to use this interceptor without transactions, consider
* changing the default flush mode (through the {@link #setFlushMode "flushMode"} property).
*
* <p>In contrast to {@link OpenSessionInViewFilter}, this interceptor is configured
* in a Spring application context and can thus take advantage of bean wiring..
*
* <p><b>WARNING:</b> Applying this interceptor to existing logic can cause issues
* that have not appeared before, through the use of a single Hibernate
* <code>Session</code> for the processing of an entire request. In particular, the
* reassociation of persistent objects with a Hibernate <code>Session</code> has to
* occur at the very beginning of request processing, to avoid clashes with already
* loaded instances of the same objects.
*
* @author Juergen Hoeller
* @since 3.1
* @see #setSingleSession
* @see #setFlushMode
* @see OpenSessionInViewFilter
* @see org.springframework.orm.hibernate4.HibernateTransactionManager
* @see org.springframework.transaction.support.TransactionSynchronizationManager
*/
public class OpenSessionInViewInterceptor implements WebRequestInterceptor {
/**
* Suffix that gets appended to the <code>SessionFactory</code>
* <code>toString()</code> representation for the "participate in existing
* session handling" request attribute.
* @see #getParticipateAttributeName
*/
public static final String PARTICIPATE_SUFFIX = ".PARTICIPATE";
protected final Log logger = LogFactory.getLog(getClass());
private SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public SessionFactory getSessionFactory() {
return this.sessionFactory;
}
/**
* Open a new Hibernate <code>Session</code> according to the settings of this
* <code>HibernateAccessor</code> and bind it to the thread via the
* {@link org.springframework.transaction.support.TransactionSynchronizationManager}.
*/
public void preHandle(WebRequest request) throws DataAccessException {
if (TransactionSynchronizationManager.hasResource(getSessionFactory())) {
// Do not modify the Session: just mark the request accordingly.
String participateAttributeName = getParticipateAttributeName();
Integer count = (Integer) request.getAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
int newCount = (count != null ? count + 1 : 1);
request.setAttribute(getParticipateAttributeName(), newCount, WebRequest.SCOPE_REQUEST);
}
else {
logger.debug("Opening Hibernate Session in OpenSessionInViewInterceptor");
Session session = openSession();
TransactionSynchronizationManager.bindResource(getSessionFactory(), new SessionHolder(session));
}
}
public void postHandle(WebRequest request, ModelMap model) {
}
/**
* Unbind the Hibernate <code>Session</code> from the thread and close it).
* @see org.springframework.transaction.support.TransactionSynchronizationManager
*/
public void afterCompletion(WebRequest request, Exception ex) throws DataAccessException {
String participateAttributeName = getParticipateAttributeName();
Integer count = (Integer) request.getAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
if (count != null) {
// Do not modify the Session: just clear the marker.
if (count > 1) {
request.setAttribute(participateAttributeName, count - 1, WebRequest.SCOPE_REQUEST);
}
else {
request.removeAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
}
}
else {
SessionHolder sessionHolder =
(SessionHolder) TransactionSynchronizationManager.unbindResource(getSessionFactory());
logger.debug("Closing Hibernate Session in OpenSessionInViewInterceptor");
SessionFactoryUtils.closeSession(sessionHolder.getSession());
}
}
/**
* Open a Session for the SessionFactory that this interceptor uses.
* <p>The default implementation delegates to the
* <code>SessionFactory.openSession</code> method and
* sets the <code>Session</code>'s flush mode to "MANUAL".
* @return the Session to use
* @throws DataAccessResourceFailureException if the Session could not be created
* @see org.hibernate.FlushMode#MANUAL
*/
protected Session openSession() throws DataAccessResourceFailureException {
try {
Session session = SessionFactoryUtils.openSession(getSessionFactory());
session.setFlushMode(FlushMode.MANUAL);
return session;
}
catch (HibernateException ex) {
throw new DataAccessResourceFailureException("Could not open Hibernate Session", ex);
}
}
/**
* Return the name of the request attribute that identifies that a request is
* already intercepted.
* <p>The default implementation takes the <code>toString()</code> representation
* of the <code>SessionFactory</code> instance and appends {@link #PARTICIPATE_SUFFIX}.
*/
protected String getParticipateAttributeName() {
return getSessionFactory().toString() + PARTICIPATE_SUFFIX;
}
}

View File

@@ -0,0 +1,24 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
*
* Classes supporting the <code>org.springframework.orm.hibernate4</code> package.
*
*/
package org.springframework.orm.hibernate4.support;

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.ibatis;
import java.sql.SQLException;
import com.ibatis.sqlmap.client.SqlMapExecutor;
/**
* Callback interface for data access code that works with the iBATIS
* {@link com.ibatis.sqlmap.client.SqlMapExecutor} interface. To be used
* with {@link SqlMapClientTemplate}'s <code>execute</code> method,
* assumably often as anonymous classes within a method implementation.
*
* @author Juergen Hoeller
* @since 24.02.2004
* @see SqlMapClientTemplate
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager
*/
public interface SqlMapClientCallback<T> {
/**
* Gets called by <code>SqlMapClientTemplate.execute</code> with an active
* <code>SqlMapExecutor</code>. Does not need to care about activating
* or closing the <code>SqlMapExecutor</code>, or handling transactions.
*
* <p>If called without a thread-bound JDBC transaction (initiated by
* DataSourceTransactionManager), the code will simply get executed on the
* underlying JDBC connection with its transactional semantics. If using
* a JTA-aware DataSource, the JDBC connection and thus the callback code
* will be transactional if a JTA transaction is active.
*
* <p>Allows for returning a result object created within the callback,
* i.e. a domain object or a collection of domain objects.
* A thrown custom RuntimeException is treated as an application exception:
* It gets propagated to the caller of the template.
*
* @param executor an active iBATIS SqlMapSession, passed-in as
* SqlMapExecutor interface here to avoid manual lifecycle handling
* @return a result object, or <code>null</code> if none
* @throws SQLException if thrown by the iBATIS SQL Maps API
* @see SqlMapClientTemplate#execute
* @see SqlMapClientTemplate#executeWithListResult
* @see SqlMapClientTemplate#executeWithMapResult
*/
T doInSqlMapClient(SqlMapExecutor executor) throws SQLException;
}

View File

@@ -0,0 +1,418 @@
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.ibatis;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.Properties;
import javax.sql.DataSource;
import com.ibatis.common.xml.NodeletException;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.client.SqlMapClientBuilder;
import com.ibatis.sqlmap.engine.builder.xml.SqlMapConfigParser;
import com.ibatis.sqlmap.engine.builder.xml.SqlMapParser;
import com.ibatis.sqlmap.engine.builder.xml.XmlParserState;
import com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient;
import com.ibatis.sqlmap.engine.transaction.TransactionConfig;
import com.ibatis.sqlmap.engine.transaction.TransactionManager;
import com.ibatis.sqlmap.engine.transaction.external.ExternalTransactionConfig;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.NestedIOException;
import org.springframework.core.io.Resource;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
import org.springframework.jdbc.support.lob.LobHandler;
import org.springframework.util.ObjectUtils;
/**
* {@link org.springframework.beans.factory.FactoryBean} that creates an
* iBATIS {@link com.ibatis.sqlmap.client.SqlMapClient}. This is the usual
* way to set up a shared iBATIS SqlMapClient in a Spring application context;
* the SqlMapClient can then be passed to iBATIS-based DAOs via dependency
* injection.
*
* <p>Either {@link org.springframework.jdbc.datasource.DataSourceTransactionManager}
* or {@link org.springframework.transaction.jta.JtaTransactionManager} can be
* used for transaction demarcation in combination with a SqlMapClient,
* with JTA only necessary for transactions which span multiple databases.
*
* <p>Allows for specifying a DataSource at the SqlMapClient level. This
* is preferable to per-DAO DataSource references, as it allows for lazy
* loading and avoids repeated DataSource references in every DAO.
*
* <p><b>Note:</b> As of Spring 2.5.5, this class (finally) requires iBATIS 2.3
* or higher. The new "mappingLocations" feature requires iBATIS 2.3.2.
*
* @author Juergen Hoeller
* @since 24.02.2004
* @see #setConfigLocation
* @see #setDataSource
* @see SqlMapClientTemplate#setSqlMapClient
* @see SqlMapClientTemplate#setDataSource
*/
public class SqlMapClientFactoryBean implements FactoryBean<SqlMapClient>, InitializingBean {
private static final ThreadLocal<LobHandler> configTimeLobHandlerHolder = new ThreadLocal<LobHandler>();
/**
* Return the LobHandler for the currently configured iBATIS SqlMapClient,
* to be used by TypeHandler implementations like ClobStringTypeHandler.
* <p>This instance will be set before initialization of the corresponding
* SqlMapClient, and reset immediately afterwards. It is thus only available
* during configuration.
* @see #setLobHandler
* @see org.springframework.orm.ibatis.support.ClobStringTypeHandler
* @see org.springframework.orm.ibatis.support.BlobByteArrayTypeHandler
* @see org.springframework.orm.ibatis.support.BlobSerializableTypeHandler
*/
public static LobHandler getConfigTimeLobHandler() {
return configTimeLobHandlerHolder.get();
}
private Resource[] configLocations;
private Resource[] mappingLocations;
private Properties sqlMapClientProperties;
private DataSource dataSource;
private boolean useTransactionAwareDataSource = true;
private Class transactionConfigClass = ExternalTransactionConfig.class;
private Properties transactionConfigProperties;
private LobHandler lobHandler;
private SqlMapClient sqlMapClient;
public SqlMapClientFactoryBean() {
this.transactionConfigProperties = new Properties();
this.transactionConfigProperties.setProperty("SetAutoCommitAllowed", "false");
}
/**
* Set the location of the iBATIS SqlMapClient config file.
* A typical value is "WEB-INF/sql-map-config.xml".
* @see #setConfigLocations
*/
public void setConfigLocation(Resource configLocation) {
this.configLocations = (configLocation != null ? new Resource[] {configLocation} : null);
}
/**
* Set multiple locations of iBATIS SqlMapClient config files that
* are going to be merged into one unified configuration at runtime.
*/
public void setConfigLocations(Resource[] configLocations) {
this.configLocations = configLocations;
}
/**
* Set locations of iBATIS sql-map mapping files that are going to be
* merged into the SqlMapClient configuration at runtime.
* <p>This is an alternative to specifying "&lt;sqlMap&gt;" entries
* in a sql-map-client config file. This property being based on Spring's
* resource abstraction also allows for specifying resource patterns here:
* e.g. "/myApp/*-map.xml".
* <p>Note that this feature requires iBATIS 2.3.2; it will not work
* with any previous iBATIS version.
*/
public void setMappingLocations(Resource[] mappingLocations) {
this.mappingLocations = mappingLocations;
}
/**
* Set optional properties to be passed into the SqlMapClientBuilder, as
* alternative to a <code>&lt;properties&gt;</code> tag in the sql-map-config.xml
* file. Will be used to resolve placeholders in the config file.
* @see #setConfigLocation
* @see com.ibatis.sqlmap.client.SqlMapClientBuilder#buildSqlMapClient(java.io.InputStream, java.util.Properties)
*/
public void setSqlMapClientProperties(Properties sqlMapClientProperties) {
this.sqlMapClientProperties = sqlMapClientProperties;
}
/**
* Set the DataSource to be used by iBATIS SQL Maps. This will be passed to the
* SqlMapClient as part of a TransactionConfig instance.
* <p>If specified, this will override corresponding settings in the SqlMapClient
* properties. Usually, you will specify DataSource and transaction configuration
* <i>either</i> here <i>or</i> in SqlMapClient properties.
* <p>Specifying a DataSource for the SqlMapClient rather than for each individual
* DAO allows for lazy loading, for example when using PaginatedList results.
* <p>With a DataSource passed in here, you don't need to specify one for each DAO.
* Passing the SqlMapClient to the DAOs is enough, as it already carries a DataSource.
* Thus, it's recommended to specify the DataSource at this central location only.
* <p>Thanks to Brandon Goodin from the iBATIS team for the hint on how to make
* this work with Spring's integration strategy!
* @see #setTransactionConfigClass
* @see #setTransactionConfigProperties
* @see com.ibatis.sqlmap.client.SqlMapClient#getDataSource
* @see SqlMapClientTemplate#setDataSource
* @see SqlMapClientTemplate#queryForPaginatedList
*/
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
/**
* Set whether to use a transaction-aware DataSource for the SqlMapClient,
* i.e. whether to automatically wrap the passed-in DataSource with Spring's
* TransactionAwareDataSourceProxy.
* <p>Default is "true": When the SqlMapClient performs direct database operations
* outside of Spring's SqlMapClientTemplate (for example, lazy loading or direct
* SqlMapClient access), it will still participate in active Spring-managed
* transactions.
* <p>As a further effect, using a transaction-aware DataSource will apply
* remaining transaction timeouts to all created JDBC Statements. This means
* that all operations performed by the SqlMapClient will automatically
* participate in Spring-managed transaction timeouts.
* <p>Turn this flag off to get raw DataSource handling, without Spring transaction
* checks. Operations on Spring's SqlMapClientTemplate will still detect
* Spring-managed transactions, but lazy loading or direct SqlMapClient access won't.
* @see #setDataSource
* @see org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager
* @see SqlMapClientTemplate
* @see com.ibatis.sqlmap.client.SqlMapClient
*/
public void setUseTransactionAwareDataSource(boolean useTransactionAwareDataSource) {
this.useTransactionAwareDataSource = useTransactionAwareDataSource;
}
/**
* Set the iBATIS TransactionConfig class to use. Default is
* <code>com.ibatis.sqlmap.engine.transaction.external.ExternalTransactionConfig</code>.
* <p>Will only get applied when using a Spring-managed DataSource.
* An instance of this class will get populated with the given DataSource
* and initialized with the given properties.
* <p>The default ExternalTransactionConfig is appropriate if there is
* external transaction management that the SqlMapClient should participate
* in: be it Spring transaction management, EJB CMT or plain JTA. This
* should be the typical scenario. If there is no active transaction,
* SqlMapClient operations will execute SQL statements non-transactionally.
* <p>JdbcTransactionConfig or JtaTransactionConfig is only necessary
* when using the iBATIS SqlMapTransactionManager API instead of external
* transactions. If there is no explicit transaction, SqlMapClient operations
* will automatically start a transaction for their own scope (in contrast
* to the external transaction mode, see above).
* <p><b>It is strongly recommended to use iBATIS SQL Maps with Spring
* transaction management (or EJB CMT).</b> In this case, the default
* ExternalTransactionConfig is fine. Lazy loading and SQL Maps operations
* without explicit transaction demarcation will execute non-transactionally.
* <p>Even with Spring transaction management, it might be desirable to
* specify JdbcTransactionConfig: This will still participate in existing
* Spring-managed transactions, but lazy loading and operations without
* explicit transaction demaration will execute in their own auto-started
* transactions. However, this is usually not necessary.
* @see #setDataSource
* @see #setTransactionConfigProperties
* @see com.ibatis.sqlmap.engine.transaction.TransactionConfig
* @see com.ibatis.sqlmap.engine.transaction.external.ExternalTransactionConfig
* @see com.ibatis.sqlmap.engine.transaction.jdbc.JdbcTransactionConfig
* @see com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig
* @see com.ibatis.sqlmap.client.SqlMapTransactionManager
*/
public void setTransactionConfigClass(Class transactionConfigClass) {
if (transactionConfigClass == null || !TransactionConfig.class.isAssignableFrom(transactionConfigClass)) {
throw new IllegalArgumentException("Invalid transactionConfigClass: does not implement " +
"com.ibatis.sqlmap.engine.transaction.TransactionConfig");
}
this.transactionConfigClass = transactionConfigClass;
}
/**
* Set properties to be passed to the TransactionConfig instance used
* by this SqlMapClient. Supported properties depend on the concrete
* TransactionConfig implementation used:
* <p><ul>
* <li><b>ExternalTransactionConfig</b> supports "DefaultAutoCommit"
* (default: false) and "SetAutoCommitAllowed" (default: true).
* Note that Spring uses SetAutoCommitAllowed = false as default,
* in contrast to the iBATIS default, to always keep the original
* autoCommit value as provided by the connection pool.
* <li><b>JdbcTransactionConfig</b> does not supported any properties.
* <li><b>JtaTransactionConfig</b> supports "UserTransaction"
* (no default), specifying the JNDI location of the JTA UserTransaction
* (usually "java:comp/UserTransaction").
* </ul>
* @see com.ibatis.sqlmap.engine.transaction.TransactionConfig#initialize
* @see com.ibatis.sqlmap.engine.transaction.external.ExternalTransactionConfig
* @see com.ibatis.sqlmap.engine.transaction.jdbc.JdbcTransactionConfig
* @see com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig
*/
public void setTransactionConfigProperties(Properties transactionConfigProperties) {
this.transactionConfigProperties = transactionConfigProperties;
}
/**
* Set the LobHandler to be used by the SqlMapClient.
* Will be exposed at config time for TypeHandler implementations.
* @see #getConfigTimeLobHandler
* @see com.ibatis.sqlmap.engine.type.TypeHandler
* @see org.springframework.orm.ibatis.support.ClobStringTypeHandler
* @see org.springframework.orm.ibatis.support.BlobByteArrayTypeHandler
* @see org.springframework.orm.ibatis.support.BlobSerializableTypeHandler
*/
public void setLobHandler(LobHandler lobHandler) {
this.lobHandler = lobHandler;
}
public void afterPropertiesSet() throws Exception {
if (this.lobHandler != null) {
// Make given LobHandler available for SqlMapClient configuration.
// Do early because because mapping resource might refer to custom types.
configTimeLobHandlerHolder.set(this.lobHandler);
}
try {
this.sqlMapClient = buildSqlMapClient(this.configLocations, this.mappingLocations, this.sqlMapClientProperties);
// Tell the SqlMapClient to use the given DataSource, if any.
if (this.dataSource != null) {
TransactionConfig transactionConfig = (TransactionConfig) this.transactionConfigClass.newInstance();
DataSource dataSourceToUse = this.dataSource;
if (this.useTransactionAwareDataSource && !(this.dataSource instanceof TransactionAwareDataSourceProxy)) {
dataSourceToUse = new TransactionAwareDataSourceProxy(this.dataSource);
}
transactionConfig.setDataSource(dataSourceToUse);
transactionConfig.initialize(this.transactionConfigProperties);
applyTransactionConfig(this.sqlMapClient, transactionConfig);
}
}
finally {
if (this.lobHandler != null) {
// Reset LobHandler holder.
configTimeLobHandlerHolder.remove();
}
}
}
/**
* Build a SqlMapClient instance based on the given standard configuration.
* <p>The default implementation uses the standard iBATIS {@link SqlMapClientBuilder}
* API to build a SqlMapClient instance based on an InputStream (if possible,
* on iBATIS 2.3 and higher) or on a Reader (on iBATIS up to version 2.2).
* @param configLocations the config files to load from
* @param properties the SqlMapClient properties (if any)
* @return the SqlMapClient instance (never <code>null</code>)
* @throws IOException if loading the config file failed
* @see com.ibatis.sqlmap.client.SqlMapClientBuilder#buildSqlMapClient
*/
protected SqlMapClient buildSqlMapClient(
Resource[] configLocations, Resource[] mappingLocations, Properties properties)
throws IOException {
if (ObjectUtils.isEmpty(configLocations)) {
throw new IllegalArgumentException("At least 1 'configLocation' entry is required");
}
SqlMapClient client = null;
SqlMapConfigParser configParser = new SqlMapConfigParser();
for (Resource configLocation : configLocations) {
InputStream is = configLocation.getInputStream();
try {
client = configParser.parse(is, properties);
}
catch (RuntimeException ex) {
throw new NestedIOException("Failed to parse config resource: " + configLocation, ex.getCause());
}
}
if (mappingLocations != null) {
SqlMapParser mapParser = SqlMapParserFactory.createSqlMapParser(configParser);
for (Resource mappingLocation : mappingLocations) {
try {
mapParser.parse(mappingLocation.getInputStream());
}
catch (NodeletException ex) {
throw new NestedIOException("Failed to parse mapping resource: " + mappingLocation, ex);
}
}
}
return client;
}
/**
* Apply the given iBATIS TransactionConfig to the SqlMapClient.
* <p>The default implementation casts to ExtendedSqlMapClient, retrieves the maximum
* number of concurrent transactions from the SqlMapExecutorDelegate, and sets
* an iBATIS TransactionManager with the given TransactionConfig.
* @param sqlMapClient the SqlMapClient to apply the TransactionConfig to
* @param transactionConfig the iBATIS TransactionConfig to apply
* @see com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient
* @see com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate#getMaxTransactions
* @see com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate#setTxManager
*/
protected void applyTransactionConfig(SqlMapClient sqlMapClient, TransactionConfig transactionConfig) {
if (!(sqlMapClient instanceof ExtendedSqlMapClient)) {
throw new IllegalArgumentException(
"Cannot set TransactionConfig with DataSource for SqlMapClient if not of type " +
"ExtendedSqlMapClient: " + sqlMapClient);
}
ExtendedSqlMapClient extendedClient = (ExtendedSqlMapClient) sqlMapClient;
transactionConfig.setMaximumConcurrentTransactions(extendedClient.getDelegate().getMaxTransactions());
extendedClient.getDelegate().setTxManager(new TransactionManager(transactionConfig));
}
public SqlMapClient getObject() {
return this.sqlMapClient;
}
public Class<? extends SqlMapClient> getObjectType() {
return (this.sqlMapClient != null ? this.sqlMapClient.getClass() : SqlMapClient.class);
}
public boolean isSingleton() {
return true;
}
/**
* Inner class to avoid hard-coded iBATIS 2.3.2 dependency (XmlParserState class).
*/
private static class SqlMapParserFactory {
public static SqlMapParser createSqlMapParser(SqlMapConfigParser configParser) {
// Ideally: XmlParserState state = configParser.getState();
// Should raise an enhancement request with iBATIS...
XmlParserState state = null;
try {
Field stateField = SqlMapConfigParser.class.getDeclaredField("state");
stateField.setAccessible(true);
state = (XmlParserState) stateField.get(configParser);
}
catch (Exception ex) {
throw new IllegalStateException("iBATIS 2.3.2 'state' field not found in SqlMapConfigParser class - " +
"please upgrade to IBATIS 2.3.2 or higher in order to use the new 'mappingLocations' feature. " + ex);
}
return new SqlMapParser(state);
}
}
}

View File

@@ -0,0 +1,182 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.ibatis;
import java.util.List;
import java.util.Map;
import com.ibatis.common.util.PaginatedList;
import com.ibatis.sqlmap.client.event.RowHandler;
import org.springframework.dao.DataAccessException;
/**
* Interface that specifies a basic set of iBATIS SqlMapClient operations,
* implemented by {@link SqlMapClientTemplate}. Not often used, but a useful
* option to enhance testability, as it can easily be mocked or stubbed.
*
* <p>Defines SqlMapClientTemplate's convenience methods that mirror
* the iBATIS {@link com.ibatis.sqlmap.client.SqlMapExecutor}'s execution
* methods. Users are strongly encouraged to read the iBATIS javadocs
* for details on the semantics of those methods.
*
* @author Juergen Hoeller
* @since 24.02.2004
* @see SqlMapClientTemplate
* @see com.ibatis.sqlmap.client.SqlMapClient
* @see com.ibatis.sqlmap.client.SqlMapExecutor
*/
public interface SqlMapClientOperations {
/**
* @see com.ibatis.sqlmap.client.SqlMapExecutor#queryForObject(String)
* @throws org.springframework.dao.DataAccessException in case of errors
*/
Object queryForObject(String statementName) throws DataAccessException;
/**
* @see com.ibatis.sqlmap.client.SqlMapExecutor#queryForObject(String, Object)
* @throws org.springframework.dao.DataAccessException in case of errors
*/
Object queryForObject(String statementName, Object parameterObject)
throws DataAccessException;
/**
* @see com.ibatis.sqlmap.client.SqlMapExecutor#queryForObject(String, Object, Object)
* @throws org.springframework.dao.DataAccessException in case of errors
*/
Object queryForObject(String statementName, Object parameterObject, Object resultObject)
throws DataAccessException;
/**
* @see com.ibatis.sqlmap.client.SqlMapExecutor#queryForList(String)
* @throws org.springframework.dao.DataAccessException in case of errors
*/
List queryForList(String statementName) throws DataAccessException;
/**
* @see com.ibatis.sqlmap.client.SqlMapExecutor#queryForList(String, Object)
* @throws org.springframework.dao.DataAccessException in case of errors
*/
List queryForList(String statementName, Object parameterObject)
throws DataAccessException;
/**
* @see com.ibatis.sqlmap.client.SqlMapExecutor#queryForList(String, int, int)
* @throws org.springframework.dao.DataAccessException in case of errors
*/
List queryForList(String statementName, int skipResults, int maxResults)
throws DataAccessException;
/**
* @see com.ibatis.sqlmap.client.SqlMapExecutor#queryForList(String, Object, int, int)
* @throws org.springframework.dao.DataAccessException in case of errors
*/
List queryForList(String statementName, Object parameterObject, int skipResults, int maxResults)
throws DataAccessException;
/**
* @see com.ibatis.sqlmap.client.SqlMapExecutor#queryWithRowHandler(String, RowHandler)
* @throws org.springframework.dao.DataAccessException in case of errors
*/
void queryWithRowHandler(String statementName, RowHandler rowHandler)
throws DataAccessException;
/**
* @see com.ibatis.sqlmap.client.SqlMapExecutor#queryWithRowHandler(String, Object, RowHandler)
* @throws org.springframework.dao.DataAccessException in case of errors
*/
void queryWithRowHandler(String statementName, Object parameterObject, RowHandler rowHandler)
throws DataAccessException;
/**
* @see com.ibatis.sqlmap.client.SqlMapExecutor#queryForMap(String, Object, String)
* @throws org.springframework.dao.DataAccessException in case of errors
*/
Map queryForMap(String statementName, Object parameterObject, String keyProperty)
throws DataAccessException;
/**
* @see com.ibatis.sqlmap.client.SqlMapExecutor#queryForMap(String, Object, String, String)
* @throws org.springframework.dao.DataAccessException in case of errors
*/
Map queryForMap(String statementName, Object parameterObject, String keyProperty, String valueProperty)
throws DataAccessException;
/**
* @see com.ibatis.sqlmap.client.SqlMapExecutor#insert(String)
* @throws org.springframework.dao.DataAccessException in case of errors
*/
Object insert(String statementName) throws DataAccessException;
/**
* @see com.ibatis.sqlmap.client.SqlMapExecutor#insert(String, Object)
* @throws org.springframework.dao.DataAccessException in case of errors
*/
Object insert(String statementName, Object parameterObject) throws DataAccessException;
/**
* @see com.ibatis.sqlmap.client.SqlMapExecutor#update(String)
* @throws org.springframework.dao.DataAccessException in case of errors
*/
int update(String statementName) throws DataAccessException;
/**
* @see com.ibatis.sqlmap.client.SqlMapExecutor#update(String, Object)
* @throws org.springframework.dao.DataAccessException in case of errors
*/
int update(String statementName, Object parameterObject) throws DataAccessException;
/**
* Convenience method provided by Spring: execute an update operation
* with an automatic check that the update affected the given required
* number of rows.
* @param statementName the name of the mapped statement
* @param parameterObject the parameter object
* @param requiredRowsAffected the number of rows that the update is
* required to affect
* @throws org.springframework.dao.DataAccessException in case of errors
*/
void update(String statementName, Object parameterObject, int requiredRowsAffected)
throws DataAccessException;
/**
* @see com.ibatis.sqlmap.client.SqlMapExecutor#delete(String)
* @throws org.springframework.dao.DataAccessException in case of errors
*/
int delete(String statementName) throws DataAccessException;
/**
* @see com.ibatis.sqlmap.client.SqlMapExecutor#delete(String, Object)
* @throws org.springframework.dao.DataAccessException in case of errors
*/
int delete(String statementName, Object parameterObject) throws DataAccessException;
/**
* Convenience method provided by Spring: execute a delete operation
* with an automatic check that the delete affected the given required
* number of rows.
* @param statementName the name of the mapped statement
* @param parameterObject the parameter object
* @param requiredRowsAffected the number of rows that the delete is
* required to affect
* @throws org.springframework.dao.DataAccessException in case of errors
*/
void delete(String statementName, Object parameterObject, int requiredRowsAffected)
throws DataAccessException;
}

View File

@@ -0,0 +1,419 @@
/*
* Copyright 2002-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.ibatis;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import javax.sql.DataSource;
import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.client.SqlMapExecutor;
import com.ibatis.sqlmap.client.SqlMapSession;
import com.ibatis.sqlmap.client.event.RowHandler;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.CannotGetJdbcConnectionException;
import org.springframework.jdbc.JdbcUpdateAffectedIncorrectNumberOfRowsException;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
import org.springframework.jdbc.support.JdbcAccessor;
import org.springframework.util.Assert;
/**
* Helper class that simplifies data access via the iBATIS
* {@link com.ibatis.sqlmap.client.SqlMapClient} API, converting checked
* SQLExceptions into unchecked DataAccessExceptions, following the
* <code>org.springframework.dao</code> exception hierarchy.
* Uses the same {@link org.springframework.jdbc.support.SQLExceptionTranslator}
* mechanism as {@link org.springframework.jdbc.core.JdbcTemplate}.
*
* <p>The main method of this class executes a callback that implements a
* data access action. Furthermore, this class provides numerous convenience
* methods that mirror {@link com.ibatis.sqlmap.client.SqlMapExecutor}'s
* execution methods.
*
* <p>It is generally recommended to use the convenience methods on this template
* for plain query/insert/update/delete operations. However, for more complex
* operations like batch updates, a custom SqlMapClientCallback must be implemented,
* usually as anonymous inner class. For example:
*
* <pre class="code">
* getSqlMapClientTemplate().execute(new SqlMapClientCallback() {
* public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
* executor.startBatch();
* executor.update("insertSomething", "myParamValue");
* executor.update("insertSomethingElse", "myOtherParamValue");
* executor.executeBatch();
* return null;
* }
* });</pre>
*
* The template needs a SqlMapClient to work on, passed in via the "sqlMapClient"
* property. A Spring context typically uses a {@link SqlMapClientFactoryBean}
* to build the SqlMapClient. The template an additionally be configured with a
* DataSource for fetching Connections, although this is not necessary if a
* DataSource is specified for the SqlMapClient itself (typically through
* SqlMapClientFactoryBean's "dataSource" property).
*
* @author Juergen Hoeller
* @since 24.02.2004
* @see #execute
* @see #setSqlMapClient
* @see #setDataSource
* @see #setExceptionTranslator
* @see SqlMapClientFactoryBean#setDataSource
* @see com.ibatis.sqlmap.client.SqlMapClient#getDataSource
* @see com.ibatis.sqlmap.client.SqlMapExecutor
*/
public class SqlMapClientTemplate extends JdbcAccessor implements SqlMapClientOperations {
private SqlMapClient sqlMapClient;
/**
* Create a new SqlMapClientTemplate.
*/
public SqlMapClientTemplate() {
}
/**
* Create a new SqlMapTemplate.
* @param sqlMapClient iBATIS SqlMapClient that defines the mapped statements
*/
public SqlMapClientTemplate(SqlMapClient sqlMapClient) {
setSqlMapClient(sqlMapClient);
afterPropertiesSet();
}
/**
* Create a new SqlMapTemplate.
* @param dataSource JDBC DataSource to obtain connections from
* @param sqlMapClient iBATIS SqlMapClient that defines the mapped statements
*/
public SqlMapClientTemplate(DataSource dataSource, SqlMapClient sqlMapClient) {
setDataSource(dataSource);
setSqlMapClient(sqlMapClient);
afterPropertiesSet();
}
/**
* Set the iBATIS Database Layer SqlMapClient that defines the mapped statements.
*/
public void setSqlMapClient(SqlMapClient sqlMapClient) {
this.sqlMapClient = sqlMapClient;
}
/**
* Return the iBATIS Database Layer SqlMapClient that this template works with.
*/
public SqlMapClient getSqlMapClient() {
return this.sqlMapClient;
}
/**
* If no DataSource specified, use SqlMapClient's DataSource.
* @see com.ibatis.sqlmap.client.SqlMapClient#getDataSource()
*/
@Override
public DataSource getDataSource() {
DataSource ds = super.getDataSource();
return (ds != null ? ds : this.sqlMapClient.getDataSource());
}
@Override
public void afterPropertiesSet() {
if (this.sqlMapClient == null) {
throw new IllegalArgumentException("Property 'sqlMapClient' is required");
}
super.afterPropertiesSet();
}
/**
* Execute the given data access action on a SqlMapExecutor.
* @param action callback object that specifies the data access action
* @return a result object returned by the action, or <code>null</code>
* @throws DataAccessException in case of SQL Maps errors
*/
public <T> T execute(SqlMapClientCallback<T> action) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
Assert.notNull(this.sqlMapClient, "No SqlMapClient specified");
// We always need to use a SqlMapSession, as we need to pass a Spring-managed
// Connection (potentially transactional) in. This shouldn't be necessary if
// we run against a TransactionAwareDataSourceProxy underneath, but unfortunately
// we still need it to make iBATIS batch execution work properly: If iBATIS
// doesn't recognize an existing transaction, it automatically executes the
// batch for every single statement...
SqlMapSession session = this.sqlMapClient.openSession();
if (logger.isDebugEnabled()) {
logger.debug("Opened SqlMapSession [" + session + "] for iBATIS operation");
}
Connection ibatisCon = null;
try {
Connection springCon = null;
DataSource dataSource = getDataSource();
boolean transactionAware = (dataSource instanceof TransactionAwareDataSourceProxy);
// Obtain JDBC Connection to operate on...
try {
ibatisCon = session.getCurrentConnection();
if (ibatisCon == null) {
springCon = (transactionAware ?
dataSource.getConnection() : DataSourceUtils.doGetConnection(dataSource));
session.setUserConnection(springCon);
if (logger.isDebugEnabled()) {
logger.debug("Obtained JDBC Connection [" + springCon + "] for iBATIS operation");
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Reusing JDBC Connection [" + ibatisCon + "] for iBATIS operation");
}
}
}
catch (SQLException ex) {
throw new CannotGetJdbcConnectionException("Could not get JDBC Connection", ex);
}
// Execute given callback...
try {
return action.doInSqlMapClient(session);
}
catch (SQLException ex) {
throw getExceptionTranslator().translate("SqlMapClient operation", null, ex);
}
finally {
try {
if (springCon != null) {
if (transactionAware) {
springCon.close();
}
else {
DataSourceUtils.doReleaseConnection(springCon, dataSource);
}
}
}
catch (Throwable ex) {
logger.debug("Could not close JDBC Connection", ex);
}
}
// Processing finished - potentially session still to be closed.
}
finally {
// Only close SqlMapSession if we know we've actually opened it
// at the present level.
if (ibatisCon == null) {
session.close();
}
}
}
/**
* Execute the given data access action on a SqlMapExecutor,
* expecting a List result.
* @param action callback object that specifies the data access action
* @return the List result
* @throws DataAccessException in case of SQL Maps errors
* @deprecated as of Spring 3.0 - not really needed anymore with generic
* {@link #execute} method
*/
@Deprecated
public List executeWithListResult(SqlMapClientCallback<List> action) throws DataAccessException {
return execute(action);
}
/**
* Execute the given data access action on a SqlMapExecutor,
* expecting a Map result.
* @param action callback object that specifies the data access action
* @return the Map result
* @throws DataAccessException in case of SQL Maps errors
* @deprecated as of Spring 3.0 - not really needed anymore with generic
* {@link #execute} method
*/
@Deprecated
public Map executeWithMapResult(SqlMapClientCallback<Map> action) throws DataAccessException {
return execute(action);
}
public Object queryForObject(String statementName) throws DataAccessException {
return queryForObject(statementName, null);
}
public Object queryForObject(final String statementName, final Object parameterObject)
throws DataAccessException {
return execute(new SqlMapClientCallback<Object>() {
public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
return executor.queryForObject(statementName, parameterObject);
}
});
}
public Object queryForObject(
final String statementName, final Object parameterObject, final Object resultObject)
throws DataAccessException {
return execute(new SqlMapClientCallback<Object>() {
public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
return executor.queryForObject(statementName, parameterObject, resultObject);
}
});
}
public List queryForList(String statementName) throws DataAccessException {
return queryForList(statementName, null);
}
public List queryForList(final String statementName, final Object parameterObject)
throws DataAccessException {
return execute(new SqlMapClientCallback<List>() {
public List doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
return executor.queryForList(statementName, parameterObject);
}
});
}
public List queryForList(String statementName, int skipResults, int maxResults)
throws DataAccessException {
return queryForList(statementName, null, skipResults, maxResults);
}
public List queryForList(
final String statementName, final Object parameterObject, final int skipResults, final int maxResults)
throws DataAccessException {
return execute(new SqlMapClientCallback<List>() {
public List doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
return executor.queryForList(statementName, parameterObject, skipResults, maxResults);
}
});
}
public void queryWithRowHandler(String statementName, RowHandler rowHandler)
throws DataAccessException {
queryWithRowHandler(statementName, null, rowHandler);
}
public void queryWithRowHandler(
final String statementName, final Object parameterObject, final RowHandler rowHandler)
throws DataAccessException {
execute(new SqlMapClientCallback<Object>() {
public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
executor.queryWithRowHandler(statementName, parameterObject, rowHandler);
return null;
}
});
}
public Map queryForMap(
final String statementName, final Object parameterObject, final String keyProperty)
throws DataAccessException {
return execute(new SqlMapClientCallback<Map>() {
public Map doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
return executor.queryForMap(statementName, parameterObject, keyProperty);
}
});
}
public Map queryForMap(
final String statementName, final Object parameterObject, final String keyProperty, final String valueProperty)
throws DataAccessException {
return execute(new SqlMapClientCallback<Map>() {
public Map doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
return executor.queryForMap(statementName, parameterObject, keyProperty, valueProperty);
}
});
}
public Object insert(String statementName) throws DataAccessException {
return insert(statementName, null);
}
public Object insert(final String statementName, final Object parameterObject)
throws DataAccessException {
return execute(new SqlMapClientCallback<Object>() {
public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
return executor.insert(statementName, parameterObject);
}
});
}
public int update(String statementName) throws DataAccessException {
return update(statementName, null);
}
public int update(final String statementName, final Object parameterObject)
throws DataAccessException {
return execute(new SqlMapClientCallback<Integer>() {
public Integer doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
return executor.update(statementName, parameterObject);
}
});
}
public void update(String statementName, Object parameterObject, int requiredRowsAffected)
throws DataAccessException {
int actualRowsAffected = update(statementName, parameterObject);
if (actualRowsAffected != requiredRowsAffected) {
throw new JdbcUpdateAffectedIncorrectNumberOfRowsException(
statementName, requiredRowsAffected, actualRowsAffected);
}
}
public int delete(String statementName) throws DataAccessException {
return delete(statementName, null);
}
public int delete(final String statementName, final Object parameterObject)
throws DataAccessException {
return execute(new SqlMapClientCallback<Integer>() {
public Integer doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
return executor.delete(statementName, parameterObject);
}
});
}
public void delete(String statementName, Object parameterObject, int requiredRowsAffected)
throws DataAccessException {
int actualRowsAffected = delete(statementName, parameterObject);
if (actualRowsAffected != requiredRowsAffected) {
throw new JdbcUpdateAffectedIncorrectNumberOfRowsException(
statementName, requiredRowsAffected, actualRowsAffected);
}
}
}

View File

@@ -0,0 +1,13 @@
/**
*
* Package providing integration of
* <a href="http://ibatis.apache.org">iBATIS Database Layer</a>
* with Spring concepts.
*
* <p>Contains resource helper classes and template classes for
* data access with the iBATIS SqlMapClient API.
*
*/
package org.springframework.orm.ibatis;

View File

@@ -0,0 +1,195 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.ibatis.support;
import java.io.IOException;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import com.ibatis.sqlmap.engine.type.BaseTypeHandler;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.support.lob.LobCreator;
import org.springframework.jdbc.support.lob.LobHandler;
import org.springframework.orm.ibatis.SqlMapClientFactoryBean;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
* Abstract base class for iBATIS TypeHandler implementations that map to LOBs.
* Retrieves the LobHandler to use from SqlMapClientFactoryBean at config time.
*
* <p>For writing LOBs, an active Spring transaction synchronization is required,
* to be able to register a synchronization that closes the LobCreator.
*
* <p>Offers template methods for setting parameters and getting result values,
* passing in the LobHandler or LobCreator to use.
*
* @author Juergen Hoeller
* @since 1.1.5
* @see org.springframework.jdbc.support.lob.LobHandler
* @see org.springframework.jdbc.support.lob.LobCreator
* @see org.springframework.orm.ibatis.SqlMapClientFactoryBean#setLobHandler
*/
public abstract class AbstractLobTypeHandler extends BaseTypeHandler {
/**
* Order value for TransactionSynchronization objects that clean up LobCreators.
* Return DataSourceUtils.#CONNECTION_SYNCHRONIZATION_ORDER - 100 to execute
* LobCreator cleanup before JDBC Connection cleanup, if any.
* @see org.springframework.jdbc.datasource.DataSourceUtils#CONNECTION_SYNCHRONIZATION_ORDER
*/
public static final int LOB_CREATOR_SYNCHRONIZATION_ORDER =
DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 200;
private LobHandler lobHandler;
/**
* Constructor used by iBATIS: fetches config-time LobHandler from
* SqlMapClientFactoryBean.
* @see org.springframework.orm.ibatis.SqlMapClientFactoryBean#getConfigTimeLobHandler
*/
public AbstractLobTypeHandler() {
this(SqlMapClientFactoryBean.getConfigTimeLobHandler());
}
/**
* Constructor used for testing: takes an explicit LobHandler.
*/
protected AbstractLobTypeHandler(LobHandler lobHandler) {
if (lobHandler == null) {
throw new IllegalStateException("No LobHandler found for configuration - " +
"lobHandler property must be set on SqlMapClientFactoryBean");
}
this.lobHandler = lobHandler;
}
/**
* This implementation delegates to setParameterInternal,
* passing in a transaction-synchronized LobCreator for the
* LobHandler of this type.
* @see #setParameterInternal
*/
public final void setParameter(PreparedStatement ps, int i, Object parameter, String jdbcType)
throws SQLException {
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
throw new IllegalStateException("Spring transaction synchronization needs to be active for " +
"setting values in iBATIS TypeHandlers that delegate to a Spring LobHandler");
}
final LobCreator lobCreator = this.lobHandler.getLobCreator();
try {
setParameterInternal(ps, i, parameter, jdbcType, lobCreator);
}
catch (IOException ex) {
throw new SQLException("I/O errors during LOB access: " + ex.getMessage());
}
TransactionSynchronizationManager.registerSynchronization(
new LobCreatorSynchronization(lobCreator));
}
/**
* This implementation delegates to the getResult version
* that takes a column index.
* @see #getResult(java.sql.ResultSet, String)
* @see java.sql.ResultSet#findColumn
*/
public final Object getResult(ResultSet rs, String columnName) throws SQLException {
return getResult(rs, rs.findColumn(columnName));
}
/**
* This implementation delegates to getResultInternal,
* passing in the LobHandler of this type.
* @see #getResultInternal
*/
public final Object getResult(ResultSet rs, int columnIndex) throws SQLException {
try {
return getResultInternal(rs, columnIndex, this.lobHandler);
}
catch (IOException ex) {
throw new SQLException(
"I/O errors during LOB access: " + ex.getClass().getName() + ": " + ex.getMessage());
}
}
/**
* This implementation always throws a SQLException:
* retrieving LOBs from a CallableStatement is not supported.
*/
public Object getResult(CallableStatement cs, int columnIndex) throws SQLException {
throw new SQLException("Retrieving LOBs from a CallableStatement is not supported");
}
/**
* Template method to set the given value on the given statement.
* @param ps the PreparedStatement to set on
* @param index the statement parameter index
* @param value the parameter value to set
* @param jdbcType the JDBC type of the parameter
* @param lobCreator the LobCreator to use
* @throws SQLException if thrown by JDBC methods
* @throws IOException if thrown by streaming methods
*/
protected abstract void setParameterInternal(
PreparedStatement ps, int index, Object value, String jdbcType, LobCreator lobCreator)
throws SQLException, IOException;
/**
* Template method to extract a value from the given result set.
* @param rs the ResultSet to extract from
* @param index the index in the ResultSet
* @param lobHandler the LobHandler to use
* @return the extracted value
* @throws SQLException if thrown by JDBC methods
* @throws IOException if thrown by streaming methods
*/
protected abstract Object getResultInternal(ResultSet rs, int index, LobHandler lobHandler)
throws SQLException, IOException;
/**
* Callback for resource cleanup at the end of a Spring transaction.
* Invokes LobCreator.close to clean up temporary LOBs that might have been created.
* @see org.springframework.jdbc.support.lob.LobCreator#close
*/
private static class LobCreatorSynchronization extends TransactionSynchronizationAdapter {
private final LobCreator lobCreator;
public LobCreatorSynchronization(LobCreator lobCreator) {
this.lobCreator = lobCreator;
}
@Override
public int getOrder() {
return LOB_CREATOR_SYNCHRONIZATION_ORDER;
}
@Override
public void beforeCompletion() {
this.lobCreator.close();
}
}
}

View File

@@ -0,0 +1,74 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.ibatis.support;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.support.lob.LobCreator;
import org.springframework.jdbc.support.lob.LobHandler;
/**
* iBATIS TypeHandler implementation for byte arrays that get mapped to BLOBs.
* Retrieves the LobHandler to use from SqlMapClientFactoryBean at config time.
*
* <p>Can also be defined in generic iBATIS mappings, as DefaultLobCreator will
* work with most JDBC-compliant database drivers. In this case, the field type
* does not have to be BLOB: For databases like MySQL and MS SQL Server, any
* large enough binary type will work.
*
* @author Juergen Hoeller
* @since 1.1.5
* @see org.springframework.orm.ibatis.SqlMapClientFactoryBean#setLobHandler
*/
public class BlobByteArrayTypeHandler extends AbstractLobTypeHandler {
/**
* Constructor used by iBATIS: fetches config-time LobHandler from
* SqlMapClientFactoryBean.
* @see org.springframework.orm.ibatis.SqlMapClientFactoryBean#getConfigTimeLobHandler
*/
public BlobByteArrayTypeHandler() {
super();
}
/**
* Constructor used for testing: takes an explicit LobHandler.
*/
protected BlobByteArrayTypeHandler(LobHandler lobHandler) {
super(lobHandler);
}
@Override
protected void setParameterInternal(
PreparedStatement ps, int index, Object value, String jdbcType, LobCreator lobCreator)
throws SQLException {
lobCreator.setBlobAsBytes(ps, index, (byte[]) value);
}
@Override
protected Object getResultInternal(ResultSet rs, int index, LobHandler lobHandler)
throws SQLException {
return lobHandler.getBlobAsBytes(rs, index);
}
public Object valueOf(String s) {
return s.getBytes();
}
}

View File

@@ -0,0 +1,110 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.ibatis.support;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.support.lob.LobCreator;
import org.springframework.jdbc.support.lob.LobHandler;
/**
* iBATIS TypeHandler implementation for arbitrary objects that get serialized to BLOBs.
* Retrieves the LobHandler to use from SqlMapClientFactoryBean at config time.
*
* <p>Can also be defined in generic iBATIS mappings, as DefaultLobCreator will
* work with most JDBC-compliant database drivers. In this case, the field type
* does not have to be BLOB: For databases like MySQL and MS SQL Server, any
* large enough binary type will work.
*
* @author Juergen Hoeller
* @since 1.1.5
* @see org.springframework.orm.ibatis.SqlMapClientFactoryBean#setLobHandler
*/
public class BlobSerializableTypeHandler extends AbstractLobTypeHandler {
/**
* Constructor used by iBATIS: fetches config-time LobHandler from
* SqlMapClientFactoryBean.
* @see org.springframework.orm.ibatis.SqlMapClientFactoryBean#getConfigTimeLobHandler
*/
public BlobSerializableTypeHandler() {
super();
}
/**
* Constructor used for testing: takes an explicit LobHandler.
*/
protected BlobSerializableTypeHandler(LobHandler lobHandler) {
super(lobHandler);
}
@Override
protected void setParameterInternal(
PreparedStatement ps, int index, Object value, String jdbcType, LobCreator lobCreator)
throws SQLException, IOException {
if (value != null) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
try {
oos.writeObject(value);
oos.flush();
lobCreator.setBlobAsBytes(ps, index, baos.toByteArray());
}
finally {
oos.close();
}
}
else {
lobCreator.setBlobAsBytes(ps, index, null);
}
}
@Override
protected Object getResultInternal(ResultSet rs, int index, LobHandler lobHandler)
throws SQLException, IOException {
InputStream is = lobHandler.getBlobAsBinaryStream(rs, index);
if (is != null) {
ObjectInputStream ois = new ObjectInputStream(is);
try {
return ois.readObject();
}
catch (ClassNotFoundException ex) {
throw new SQLException("Could not deserialize BLOB contents: " + ex.getMessage());
}
finally {
ois.close();
}
}
else {
return null;
}
}
public Object valueOf(String s) {
return s;
}
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.ibatis.support;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.springframework.jdbc.support.lob.LobCreator;
import org.springframework.jdbc.support.lob.LobHandler;
/**
* iBATIS TypeHandler implementation for Strings that get mapped to CLOBs.
* Retrieves the LobHandler to use from SqlMapClientFactoryBean at config time.
*
* <p>Particularly useful for storing Strings with more than 4000 characters in an
* Oracle database (only possible via CLOBs), in combination with OracleLobHandler.
*
* <p>Can also be defined in generic iBATIS mappings, as DefaultLobCreator will
* work with most JDBC-compliant database drivers. In this case, the field type
* does not have to be BLOB: For databases like MySQL and MS SQL Server, any
* large enough binary type will work.
*
* @author Juergen Hoeller
* @since 1.1.5
* @see org.springframework.orm.ibatis.SqlMapClientFactoryBean#setLobHandler
*/
public class ClobStringTypeHandler extends AbstractLobTypeHandler {
/**
* Constructor used by iBATIS: fetches config-time LobHandler from
* SqlMapClientFactoryBean.
* @see org.springframework.orm.ibatis.SqlMapClientFactoryBean#getConfigTimeLobHandler
*/
public ClobStringTypeHandler() {
super();
}
/**
* Constructor used for testing: takes an explicit LobHandler.
*/
protected ClobStringTypeHandler(LobHandler lobHandler) {
super(lobHandler);
}
@Override
protected void setParameterInternal(
PreparedStatement ps, int index, Object value, String jdbcType, LobCreator lobCreator)
throws SQLException {
lobCreator.setClobAsString(ps, index, (String) value);
}
@Override
protected Object getResultInternal(ResultSet rs, int index, LobHandler lobHandler)
throws SQLException {
return lobHandler.getClobAsString(rs, index);
}
public Object valueOf(String s) {
return s;
}
}

View File

@@ -0,0 +1,113 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.ibatis.support;
import javax.sql.DataSource;
import com.ibatis.sqlmap.client.SqlMapClient;
import org.springframework.dao.support.DaoSupport;
import org.springframework.orm.ibatis.SqlMapClientTemplate;
import org.springframework.util.Assert;
/**
* Convenient super class for iBATIS SqlMapClient data access objects.
* Requires a SqlMapClient to be set, providing a SqlMapClientTemplate
* based on it to subclasses.
*
* <p>Instead of a plain SqlMapClient, you can also pass a preconfigured
* SqlMapClientTemplate instance in. This allows you to share your
* SqlMapClientTemplate configuration for all your DAOs, for example
* a custom SQLExceptionTranslator to use.
*
* @author Juergen Hoeller
* @since 24.02.2004
* @see #setSqlMapClient
* @see #setSqlMapClientTemplate
* @see org.springframework.orm.ibatis.SqlMapClientTemplate
* @see org.springframework.orm.ibatis.SqlMapClientTemplate#setExceptionTranslator
*/
public abstract class SqlMapClientDaoSupport extends DaoSupport {
private SqlMapClientTemplate sqlMapClientTemplate = new SqlMapClientTemplate();
private boolean externalTemplate = false;
/**
* Set the JDBC DataSource to be used by this DAO.
* Not required: The SqlMapClient might carry a shared DataSource.
* @see #setSqlMapClient
*/
public final void setDataSource(DataSource dataSource) {
if (!this.externalTemplate) {
this.sqlMapClientTemplate.setDataSource(dataSource);
}
}
/**
* Return the JDBC DataSource used by this DAO.
*/
public final DataSource getDataSource() {
return this.sqlMapClientTemplate.getDataSource();
}
/**
* Set the iBATIS Database Layer SqlMapClient to work with.
* Either this or a "sqlMapClientTemplate" is required.
* @see #setSqlMapClientTemplate
*/
public final void setSqlMapClient(SqlMapClient sqlMapClient) {
if (!this.externalTemplate) {
this.sqlMapClientTemplate.setSqlMapClient(sqlMapClient);
}
}
/**
* Return the iBATIS Database Layer SqlMapClient that this template works with.
*/
public final SqlMapClient getSqlMapClient() {
return this.sqlMapClientTemplate.getSqlMapClient();
}
/**
* Set the SqlMapClientTemplate for this DAO explicitly,
* as an alternative to specifying a SqlMapClient.
* @see #setSqlMapClient
*/
public final void setSqlMapClientTemplate(SqlMapClientTemplate sqlMapClientTemplate) {
Assert.notNull(sqlMapClientTemplate, "SqlMapClientTemplate must not be null");
this.sqlMapClientTemplate = sqlMapClientTemplate;
this.externalTemplate = true;
}
/**
* Return the SqlMapClientTemplate for this DAO,
* pre-initialized with the SqlMapClient or set explicitly.
*/
public final SqlMapClientTemplate getSqlMapClientTemplate() {
return this.sqlMapClientTemplate;
}
@Override
protected final void checkDaoConfig() {
if (!this.externalTemplate) {
this.sqlMapClientTemplate.afterPropertiesSet();
}
}
}

View File

@@ -0,0 +1,9 @@
/**
*
* Classes supporting the <code>org.springframework.orm.ibatis</code> package.
* Contains a DAO base class for SqlMapClientTemplate usage.
*
*/
package org.springframework.orm.ibatis.support;

View File

@@ -0,0 +1,274 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jdo;
import java.lang.reflect.Method;
import java.sql.Connection;
import java.sql.SQLException;
import javax.jdo.JDOException;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.jdo.Transaction;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.jdbc.datasource.ConnectionHandle;
import org.springframework.jdbc.support.JdbcUtils;
import org.springframework.jdbc.support.SQLExceptionTranslator;
import org.springframework.transaction.InvalidIsolationLevelException;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;
/**
* Default implementation of the {@link JdoDialect} interface.
* Updated to build on JDO 2.0 or higher, as of Spring 2.5.
* Used as default dialect by {@link JdoAccessor} and {@link JdoTransactionManager}.
*
* <p>Simply begins a standard JDO transaction in <code>beginTransaction</code>.
* Returns a handle for a JDO2 DataStoreConnection on <code>getJdbcConnection</code>.
* Calls the corresponding JDO2 PersistenceManager operation on <code>flush</code>
* Ignores a given query timeout in <code>applyQueryTimeout</code>.
* Uses a Spring SQLExceptionTranslator for exception translation, if applicable.
*
* <p>Note that, even with JDO2, vendor-specific subclasses are still necessary
* for special transaction semantics and more sophisticated exception translation.
* Furthermore, vendor-specific subclasses are encouraged to expose the native JDBC
* Connection on <code>getJdbcConnection</code>, rather than JDO2's wrapper handle.
*
* <p>This class also implements the PersistenceExceptionTranslator interface,
* as autodetected by Spring's PersistenceExceptionTranslationPostProcessor,
* for AOP-based translation of native exceptions to Spring DataAccessExceptions.
* Hence, the presence of a standard DefaultJdoDialect bean automatically enables
* a PersistenceExceptionTranslationPostProcessor to translate JDO exceptions.
*
* @author Juergen Hoeller
* @since 1.1
* @see #setJdbcExceptionTranslator
* @see JdoAccessor#setJdoDialect
* @see JdoTransactionManager#setJdoDialect
* @see org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor
*/
public class DefaultJdoDialect implements JdoDialect, PersistenceExceptionTranslator {
// JDO 3.0 setTimeoutMillis method available?
private static final Method setTimeoutMillisMethod =
ClassUtils.getMethodIfAvailable(Query.class, "setTimeoutMillis", Integer.class);
protected final Log logger = LogFactory.getLog(getClass());
private SQLExceptionTranslator jdbcExceptionTranslator;
/**
* Create a new DefaultJdoDialect.
*/
public DefaultJdoDialect() {
}
/**
* Create a new DefaultJdoDialect.
* @param connectionFactory the connection factory of the JDO PersistenceManagerFactory,
* which is used to initialize the default JDBC exception translator
* @see javax.jdo.PersistenceManagerFactory#getConnectionFactory()
* @see PersistenceManagerFactoryUtils#newJdbcExceptionTranslator(Object)
*/
public DefaultJdoDialect(Object connectionFactory) {
this.jdbcExceptionTranslator = PersistenceManagerFactoryUtils.newJdbcExceptionTranslator(connectionFactory);
}
/**
* Set the JDBC exception translator for this dialect.
* <p>Applied to any SQLException root cause of a JDOException, if specified.
* The default is to rely on the JDO provider's native exception translation.
* @param jdbcExceptionTranslator exception translator
* @see java.sql.SQLException
* @see javax.jdo.JDOException#getCause()
* @see org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator
* @see org.springframework.jdbc.support.SQLStateSQLExceptionTranslator
*/
public void setJdbcExceptionTranslator(SQLExceptionTranslator jdbcExceptionTranslator) {
this.jdbcExceptionTranslator = jdbcExceptionTranslator;
}
/**
* Return the JDBC exception translator for this dialect, if any.
*/
public SQLExceptionTranslator getJdbcExceptionTranslator() {
return this.jdbcExceptionTranslator;
}
//-------------------------------------------------------------------------
// Hooks for transaction management (used by JdoTransactionManager)
//-------------------------------------------------------------------------
/**
* This implementation invokes the standard JDO <code>Transaction.begin</code>
* method. Throws an InvalidIsolationLevelException if a non-default isolation
* level is set.
* @see javax.jdo.Transaction#begin
* @see org.springframework.transaction.InvalidIsolationLevelException
*/
public Object beginTransaction(Transaction transaction, TransactionDefinition definition)
throws JDOException, SQLException, TransactionException {
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
throw new InvalidIsolationLevelException(
"Standard JDO does not support custom isolation levels: " +
"use a special JdoDialect implementation for your JDO provider");
}
transaction.begin();
return null;
}
/**
* This implementation does nothing, as the default beginTransaction implementation
* does not require any cleanup.
* @see #beginTransaction
*/
public void cleanupTransaction(Object transactionData) {
}
/**
* This implementation returns a DataStoreConnectionHandle for JDO2,
* which will also work on JDO1 until actually accessing the JDBC Connection.
* <p>For pre-JDO2 implementations, override this method to return the
* Connection through the corresponding vendor-specific mechanism, or <code>null</code>
* if the Connection is not retrievable.
* <p><b>NOTE:</b> A JDO2 DataStoreConnection is always a wrapper,
* never the native JDBC Connection. If you need access to the native JDBC
* Connection (or the connection pool handle, to be unwrapped via a Spring
* NativeJdbcExtractor), override this method to return the native
* Connection through the corresponding vendor-specific mechanism.
* <p>A JDO2 DataStoreConnection is only "borrowed" from the PersistenceManager:
* it needs to be returned as early as possible. Effectively, JDO2 requires the
* fetched Connection to be closed before continuing PersistenceManager work.
* For this reason, the exposed ConnectionHandle eagerly releases its JDBC
* Connection at the end of each JDBC data access operation (that is, on
* <code>DataSourceUtils.releaseConnection</code>).
* @see javax.jdo.PersistenceManager#getDataStoreConnection()
* @see org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor
* @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection
*/
public ConnectionHandle getJdbcConnection(PersistenceManager pm, boolean readOnly)
throws JDOException, SQLException {
return new DataStoreConnectionHandle(pm);
}
/**
* This implementation does nothing, assuming that the Connection
* will implicitly be closed with the PersistenceManager.
* <p>If the JDO provider returns a Connection handle that it
* expects the application to close, the dialect needs to invoke
* <code>Connection.close</code> here.
* @see java.sql.Connection#close()
*/
public void releaseJdbcConnection(ConnectionHandle conHandle, PersistenceManager pm)
throws JDOException, SQLException {
}
/**
* This implementation applies a JDO 3.0 query timeout, if available. Otherwise,
* it sets the JPA 2.0 query hints "javax.persistence.lock.timeout" and
* "javax.persistence.query.timeout", assuming that JDO providers are often
* JPA providers as well.
*/
public void applyQueryTimeout(Query query, int remainingTimeInSeconds) throws JDOException {
if (setTimeoutMillisMethod != null) {
ReflectionUtils.invokeMethod(setTimeoutMillisMethod, query, remainingTimeInSeconds);
}
else {
query.addExtension("javax.persistence.lock.timeout", remainingTimeInSeconds);
query.addExtension("javax.persistence.query.timeout", remainingTimeInSeconds);
}
}
//-----------------------------------------------------------------------------------
// Hook for exception translation (used by JdoTransactionManager and JdoTemplate)
//-----------------------------------------------------------------------------------
/**
* Implementation of the PersistenceExceptionTranslator interface,
* as autodetected by Spring's PersistenceExceptionTranslationPostProcessor.
* <p>Converts the exception if it is a JDOException, using this JdoDialect.
* Else returns <code>null</code> to indicate an unknown exception.
* @see org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor
* @see #translateException
*/
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
if (ex instanceof JDOException) {
return translateException((JDOException) ex);
}
return null;
}
/**
* This implementation delegates to PersistenceManagerFactoryUtils.
* @see PersistenceManagerFactoryUtils#convertJdoAccessException
*/
public DataAccessException translateException(JDOException ex) {
if (getJdbcExceptionTranslator() != null && ex.getCause() instanceof SQLException) {
return getJdbcExceptionTranslator().translate("JDO operation: " + ex.getMessage(),
extractSqlStringFromException(ex), (SQLException) ex.getCause());
}
return PersistenceManagerFactoryUtils.convertJdoAccessException(ex);
}
/**
* Template method for extracting a SQL String from the given exception.
* <p>Default implementation always returns <code>null</code>. Can be overridden in
* subclasses to extract SQL Strings for vendor-specific exception classes.
* @param ex the JDOException, containing a SQLException
* @return the SQL String, or <code>null</code> if none found
*/
protected String extractSqlStringFromException(JDOException ex) {
return null;
}
/**
* ConnectionHandle implementation that fetches a new JDO2 DataStoreConnection
* for every <code>getConnection</code> call and closes the Connection on
* <code>releaseConnection</code>. This is necessary because JDO2 requires the
* fetched Connection to be closed before continuing PersistenceManager work.
* @see javax.jdo.PersistenceManager#getDataStoreConnection()
*/
private static class DataStoreConnectionHandle implements ConnectionHandle {
private final PersistenceManager persistenceManager;
public DataStoreConnectionHandle(PersistenceManager persistenceManager) {
this.persistenceManager = persistenceManager;
}
public Connection getConnection() {
return (Connection) this.persistenceManager.getDataStoreConnection();
}
public void releaseConnection(Connection con) {
JdbcUtils.closeConnection(con);
}
}
}

View File

@@ -0,0 +1,172 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jdo;
import javax.jdo.JDOException;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessException;
/**
* Base class for JdoTemplate and JdoInterceptor, defining common
* properties such as PersistenceManagerFactory and flushing behavior.
*
* <p>Note: With JDO, modifications to persistent objects are just possible
* within a transaction (in contrast to Hibernate). Therefore, eager flushing
* will just get applied when in a transaction. Furthermore, there is no
* explicit notion of flushing never, as this would not imply a performance
* gain due to JDO's field interception mechanism (which doesn't involve
* the overhead of snapshot comparisons).
*
* <p>Eager flushing is just available for specific JDO providers.
* You need to a corresponding JdoDialect to make eager flushing work.
*
* <p>Not intended to be used directly. See JdoTemplate and JdoInterceptor.
*
* @author Juergen Hoeller
* @since 02.11.2003
* @see JdoTemplate
* @see JdoInterceptor
* @see #setFlushEager
* @deprecated as of Spring 3.1, in favor of native PersistenceManager usage
* (see {@link TransactionAwarePersistenceManagerFactoryProxy} and
* {@link org.springframework.orm.jdo.support.SpringPersistenceManagerProxyBean})
*/
@Deprecated
public abstract class JdoAccessor implements InitializingBean {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
private PersistenceManagerFactory persistenceManagerFactory;
private JdoDialect jdoDialect;
private boolean flushEager = false;
/**
* Set the JDO PersistenceManagerFactory that should be used to create
* PersistenceManagers.
*/
public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) {
this.persistenceManagerFactory = pmf;
}
/**
* Return the JDO PersistenceManagerFactory that should be used to create
* PersistenceManagers.
*/
public PersistenceManagerFactory getPersistenceManagerFactory() {
return this.persistenceManagerFactory;
}
/**
* Set the JDO dialect to use for this accessor.
* <p>The dialect object can be used to retrieve the underlying JDBC
* connection and to eagerly flush changes to the database.
* <p>Default is a DefaultJdoDialect based on the PersistenceManagerFactory's
* underlying DataSource, if any.
*/
public void setJdoDialect(JdoDialect jdoDialect) {
this.jdoDialect = jdoDialect;
}
/**
* Return the JDO dialect to use for this accessor.
* <p>Creates a default one for the specified PersistenceManagerFactory if none set.
*/
public JdoDialect getJdoDialect() {
if (this.jdoDialect == null) {
this.jdoDialect = new DefaultJdoDialect();
}
return this.jdoDialect;
}
/**
* Set if this accessor should flush changes to the database eagerly.
* <p>Eager flushing leads to immediate synchronization with the database,
* even if in a transaction. This causes inconsistencies to show up and throw
* a respective exception immediately, and JDBC access code that participates
* in the same transaction will see the changes as the database is already
* aware of them then. But the drawbacks are:
* <ul>
* <li>additional communication roundtrips with the database, instead of a
* single batch at transaction commit;
* <li>the fact that an actual database rollback is needed if the JDO
* transaction rolls back (due to already submitted SQL statements).
* </ul>
*/
public void setFlushEager(boolean flushEager) {
this.flushEager = flushEager;
}
/**
* Return if this accessor should flush changes to the database eagerly.
*/
public boolean isFlushEager() {
return this.flushEager;
}
/**
* Eagerly initialize the JDO dialect, creating a default one
* for the specified PersistenceManagerFactory if none set.
*/
public void afterPropertiesSet() {
if (getPersistenceManagerFactory() == null) {
throw new IllegalArgumentException("Property 'persistenceManagerFactory' is required");
}
// Build default JdoDialect if none explicitly specified.
if (this.jdoDialect == null) {
this.jdoDialect = new DefaultJdoDialect(getPersistenceManagerFactory().getConnectionFactory());
}
}
/**
* Flush the given JDO persistence manager if necessary.
* @param pm the current JDO PersistenceManager
* @param existingTransaction if executing within an existing transaction
* (within an existing JDO PersistenceManager that won't be closed immediately)
* @throws JDOException in case of JDO flushing errors
*/
protected void flushIfNecessary(PersistenceManager pm, boolean existingTransaction) throws JDOException {
if (isFlushEager()) {
logger.debug("Eagerly flushing JDO persistence manager");
pm.flush();
}
}
/**
* Convert the given JDOException to an appropriate exception from the
* <code>org.springframework.dao</code> hierarchy.
* <p>Default implementation delegates to the JdoDialect.
* May be overridden in subclasses.
* @param ex JDOException that occured
* @return the corresponding DataAccessException instance
* @see JdoDialect#translateException
*/
public DataAccessException convertJdoAccessException(JDOException ex) {
return getJdoDialect().translateException(ex);
}
}

View File

@@ -0,0 +1,73 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jdo;
import javax.jdo.JDOException;
import javax.jdo.PersistenceManager;
/**
* Callback interface for JDO code. To be used with {@link JdoTemplate}'s
* execution methods, often as anonymous classes within a method implementation.
* A typical implementation will call PersistenceManager CRUD to perform
* some operations on persistent objects.
*
* <p>Note that JDO works on bytecode-modified Java objects, to be able to
* perform dirty detection on each modification of a persistent instance field.
* In contrast to Hibernate, using returned objects outside of an active
* PersistenceManager poses a problem: To be able to read and modify fields
* e.g. in a web GUI, one has to explicitly make the instances "transient".
* Reassociation with a new PersistenceManager, e.g. for updates when coming
* back from the GUI, isn't possible, as the JDO instances have lost their
* identity when turned transient. This means that either value objects have
* to be used as parameters, or the contents of the outside-modified instance
* have to be copied to a freshly loaded active instance on reassociation.
*
* @author Juergen Hoeller
* @since 03.06.2003
* @see JdoTemplate
* @see JdoTransactionManager
* @deprecated as of Spring 3.1, in favor of native PersistenceManager usage
* (see {@link TransactionAwarePersistenceManagerFactoryProxy} and
* {@link org.springframework.orm.jdo.support.SpringPersistenceManagerProxyBean})
*/
@Deprecated
public interface JdoCallback<T> {
/**
* Gets called by <code>JdoTemplate.execute</code> with an active JDO
* <code>PersistenceManager</code>. Does not need to care about activating
* or closing the <code>PersistenceManager</code>, or handling transactions.
*
* <p>Note that JDO callback code will not flush any modifications to the
* database if not executed within a transaction. Thus, you need to make
* sure that JdoTransactionManager has initiated a JDO transaction when
* the callback gets called, at least if you want to write to the database.
*
* <p>Allows for returning a result object created within the callback,
* i.e. a domain object or a collection of domain objects.
* A thrown custom RuntimeException is treated as an application exception:
* It gets propagated to the caller of the template.
*
* @param pm active PersistenceManager
* @return a result object, or <code>null</code> if none
* @throws JDOException if thrown by the JDO API
* @see JdoTemplate#execute
* @see JdoTemplate#executeFind
*/
T doInJdo(PersistenceManager pm) throws JDOException;
}

View File

@@ -0,0 +1,182 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jdo;
import java.sql.SQLException;
import javax.jdo.JDOException;
import javax.jdo.PersistenceManager;
import javax.jdo.Query;
import javax.jdo.Transaction;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.datasource.ConnectionHandle;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
/**
* SPI strategy that allows for customizing integration with a specific JDO provider,
* in particular regarding transaction management and exception translation. To be
* implemented for specific JDO providers such as JPOX, Kodo, Lido, Versant Open Access.
*
* <p>JDO 2.0 defines standard ways for most of the functionality covered here.
* Hence, Spring's {@link DefaultJdoDialect} uses the corresponding JDO 2.0 methods
* by default, to be overridden in a vendor-specific fashion if necessary.
* Vendor-specific subclasses of {@link DefaultJdoDialect} are still required for special
* transaction semantics and more sophisticated exception translation (if needed).
*
* <p>In general, it is recommended to derive from {@link DefaultJdoDialect} instead
* of implementing this interface directly. This allows for inheriting common
* behavior (present and future) from {@link DefaultJdoDialect}, only overriding
* specific hooks to plug in concrete vendor-specific behavior.
*
* @author Juergen Hoeller
* @since 02.11.2003
* @see JdoTransactionManager#setJdoDialect
* @see JdoAccessor#setJdoDialect
* @see DefaultJdoDialect
*/
public interface JdoDialect {
//-------------------------------------------------------------------------
// Hooks for transaction management (used by JdoTransactionManager)
//-------------------------------------------------------------------------
/**
* Begin the given JDO transaction, applying the semantics specified by the
* given Spring transaction definition (in particular, an isolation level
* and a timeout). Invoked by JdoTransactionManager on transaction begin.
* <p>An implementation can configure the JDO Transaction object and then
* invoke <code>begin</code>, or invoke a special begin method that takes,
* for example, an isolation level.
* <p>An implementation can also apply read-only flag and isolation level to the
* underlying JDBC Connection before beginning the transaction. In that case,
* a transaction data object can be returned that holds the previous isolation
* level (and possibly other data), to be reset in <code>cleanupTransaction</code>.
* <p>Implementations can also use the Spring transaction name, as exposed by the
* passed-in TransactionDefinition, to optimize for specific data access use cases
* (effectively using the current transaction name as use case identifier).
* @param transaction the JDO transaction to begin
* @param definition the Spring transaction definition that defines semantics
* @return an arbitrary object that holds transaction data, if any
* (to be passed into cleanupTransaction)
* @throws JDOException if thrown by JDO methods
* @throws SQLException if thrown by JDBC methods
* @throws TransactionException in case of invalid arguments
* @see #cleanupTransaction
* @see javax.jdo.Transaction#begin
* @see org.springframework.jdbc.datasource.DataSourceUtils#prepareConnectionForTransaction
*/
Object beginTransaction(Transaction transaction, TransactionDefinition definition)
throws JDOException, SQLException, TransactionException;
/**
* Clean up the transaction via the given transaction data.
* Invoked by JdoTransactionManager on transaction cleanup.
* <p>An implementation can, for example, reset read-only flag and
* isolation level of the underlying JDBC Connection. Furthermore,
* an exposed data access use case can be reset here.
* @param transactionData arbitrary object that holds transaction data, if any
* (as returned by beginTransaction)
* @see #beginTransaction
* @see org.springframework.jdbc.datasource.DataSourceUtils#resetConnectionAfterTransaction
*/
void cleanupTransaction(Object transactionData);
/**
* Retrieve the JDBC Connection that the given JDO PersistenceManager uses underneath,
* if accessing a relational database. This method will just get invoked if actually
* needing access to the underlying JDBC Connection, usually within an active JDO
* transaction (for example, by JdoTransactionManager). The returned handle will
* be passed into the <code>releaseJdbcConnection</code> method when not needed anymore.
* <p>Implementations are encouraged to return an unwrapped Connection object, i.e.
* the Connection as they got it from the connection pool. This makes it easier for
* application code to get at the underlying native JDBC Connection, like an
* OracleConnection, which is sometimes necessary for LOB handling etc. We assume
* that calling code knows how to properly handle the returned Connection object.
* <p>In a simple case where the returned Connection will be auto-closed with the
* PersistenceManager or can be released via the Connection object itself, an
* implementation can return a SimpleConnectionHandle that just contains the
* Connection. If some other object is needed in <code>releaseJdbcConnection</code>,
* an implementation should use a special handle that references that other object.
* @param pm the current JDO PersistenceManager
* @param readOnly whether the Connection is only needed for read-only purposes
* @return a handle for the JDBC Connection, to be passed into
* <code>releaseJdbcConnection</code>, or <code>null</code>
* if no JDBC Connection can be retrieved
* @throws JDOException if thrown by JDO methods
* @throws SQLException if thrown by JDBC methods
* @see #releaseJdbcConnection
* @see org.springframework.jdbc.datasource.ConnectionHandle#getConnection
* @see org.springframework.jdbc.datasource.SimpleConnectionHandle
* @see JdoTransactionManager#setDataSource
* @see org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor
*/
ConnectionHandle getJdbcConnection(PersistenceManager pm, boolean readOnly)
throws JDOException, SQLException;
/**
* Release the given JDBC Connection, which has originally been retrieved
* via <code>getJdbcConnection</code>. This should be invoked in any case,
* to allow for proper release of the retrieved Connection handle.
* <p>An implementation might simply do nothing, if the Connection returned
* by <code>getJdbcConnection</code> will be implicitly closed when the JDO
* transaction completes or when the PersistenceManager is closed.
* @param conHandle the JDBC Connection handle to release
* @param pm the current JDO PersistenceManager
* @throws JDOException if thrown by JDO methods
* @throws SQLException if thrown by JDBC methods
* @see #getJdbcConnection
*/
void releaseJdbcConnection(ConnectionHandle conHandle, PersistenceManager pm)
throws JDOException, SQLException;
/**
* Apply the given timeout to the given JDO query object.
* <p>Invoked with the remaining time of a specified transaction timeout, if any.
* @param query the JDO query object to apply the timeout to
* @param timeout the timeout value (seconds) to apply
* @throws JDOException if thrown by JDO methods
* @see JdoTemplate#prepareQuery
*/
void applyQueryTimeout(Query query, int timeout) throws JDOException;
//-----------------------------------------------------------------------------------
// Hook for exception translation (used by JdoTransactionManager and JdoTemplate)
//-----------------------------------------------------------------------------------
/**
* Translate the given JDOException to a corresponding exception from Spring's
* generic DataAccessException hierarchy. An implementation should apply
* PersistenceManagerFactoryUtils' standard exception translation if can't do
* anything more specific.
* <p>Of particular importance is the correct translation to
* DataIntegrityViolationException, for example on constraint violation.
* Unfortunately, standard JDO does not allow for portable detection of this.
* <p>Can use a SQLExceptionTranslator for translating underlying SQLExceptions
* in a database-specific fashion.
* @param ex the JDOException thrown
* @return the corresponding DataAccessException (must not be <code>null</code>)
* @see JdoAccessor#convertJdoAccessException
* @see JdoTransactionManager#convertJdoAccessException
* @see PersistenceManagerFactoryUtils#convertJdoAccessException
* @see org.springframework.dao.DataIntegrityViolationException
* @see org.springframework.jdbc.support.SQLExceptionTranslator
*/
DataAccessException translateException(JDOException ex);
}

View File

@@ -0,0 +1,128 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jdo;
import javax.jdo.JDOException;
import javax.jdo.PersistenceManager;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
* This interceptor binds a new JDO PersistenceManager to the thread before a method
* call, closing and removing it afterwards in case of any method outcome.
* If there already is a pre-bound PersistenceManager (e.g. from JdoTransactionManager,
* or from a surrounding JDO-intercepted method), the interceptor simply participates in it.
*
* <p>Application code must retrieve a JDO PersistenceManager via the
* <code>PersistenceManagerFactoryUtils.getPersistenceManager</code> method,
* to be able to detect a thread-bound PersistenceManager. It is preferable to use
* <code>getPersistenceManager</code> with allowCreate=false, if the code relies on
* the interceptor to provide proper PersistenceManager handling. Typically, the code
* will look like as follows:
*
* <pre>
* public void doSomeDataAccessAction() {
* PersistenceManager pm = PersistenceManagerFactoryUtils.getPersistenceManager(this.pmf, false);
* ...
* }</pre>
*
* <p>Note that this interceptor automatically translates JDOExceptions, via
* delegating to the <code>PersistenceManagerFactoryUtils.convertJdoAccessException</code>
* method that converts them to exceptions that are compatible with the
* <code>org.springframework.dao</code> exception hierarchy (like JdoTemplate does).
* This can be turned off if the raw exceptions are preferred.
*
* <p>This class can be considered a declarative alternative to JdoTemplate's
* callback approach. The advantages are:
* <ul>
* <li>no anonymous classes necessary for callback implementations;
* <li>the possibility to throw any application exceptions from within data access code.
* </ul>
*
* <p>The drawback is the dependency on interceptor configuration. However, note
* that this interceptor is usually <i>not</i> necessary in scenarios where the
* data access code always executes within transactions. A transaction will always
* have a thread-bound PersistenceManager in the first place, so adding this interceptor
* to the configuration just adds value when fine-tuning PersistenceManager settings
* like the flush mode - or when relying on exception translation.
*
* @author Juergen Hoeller
* @since 13.06.2003
* @see PersistenceManagerFactoryUtils#getPersistenceManager
* @see JdoTransactionManager
* @see JdoTemplate
* @deprecated as of Spring 3.1, in favor of native PersistenceManager usage
* and AOP-driven exception translation through
* {@link org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor}
*/
@Deprecated
public class JdoInterceptor extends JdoAccessor implements MethodInterceptor {
private boolean exceptionConversionEnabled = true;
/**
* Set whether to convert any JDOException raised to a Spring DataAccessException,
* compatible with the <code>org.springframework.dao</code> exception hierarchy.
* <p>Default is "true". Turn this flag off to let the caller receive raw exceptions
* as-is, without any wrapping.
* @see org.springframework.dao.DataAccessException
*/
public void setExceptionConversionEnabled(boolean exceptionConversionEnabled) {
this.exceptionConversionEnabled = exceptionConversionEnabled;
}
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
boolean existingTransaction = false;
PersistenceManager pm = PersistenceManagerFactoryUtils.getPersistenceManager(getPersistenceManagerFactory(), true);
if (TransactionSynchronizationManager.hasResource(getPersistenceManagerFactory())) {
logger.debug("Found thread-bound PersistenceManager for JDO interceptor");
existingTransaction = true;
}
else {
logger.debug("Using new PersistenceManager for JDO interceptor");
TransactionSynchronizationManager.bindResource(getPersistenceManagerFactory(), new PersistenceManagerHolder(pm));
}
try {
Object retVal = methodInvocation.proceed();
flushIfNecessary(pm, existingTransaction);
return retVal;
}
catch (JDOException ex) {
if (this.exceptionConversionEnabled) {
throw convertJdoAccessException(ex);
}
else {
throw ex;
}
}
finally {
if (existingTransaction) {
logger.debug("Not closing pre-bound JDO PersistenceManager after interceptor");
}
else {
TransactionSynchronizationManager.unbindResource(getPersistenceManagerFactory());
PersistenceManagerFactoryUtils.releasePersistenceManager(pm, getPersistenceManagerFactory());
}
}
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jdo;
import javax.jdo.JDOHelper;
import javax.jdo.JDOObjectNotFoundException;
import org.springframework.orm.ObjectRetrievalFailureException;
/**
* JDO-specific subclass of ObjectRetrievalFailureException.
* Converts JDO's JDOObjectNotFoundException.
*
* @author Juergen Hoeller
* @since 1.1
* @see PersistenceManagerFactoryUtils#convertJdoAccessException
*/
public class JdoObjectRetrievalFailureException extends ObjectRetrievalFailureException {
public JdoObjectRetrievalFailureException(JDOObjectNotFoundException ex) {
// Extract information about the failed object from the JDOException, if available.
super((ex.getFailedObject() != null ? ex.getFailedObject().getClass() : null),
(ex.getFailedObject() != null ? JDOHelper.getObjectId(ex.getFailedObject()) : null),
ex.getMessage(), ex);
}
}

View File

@@ -0,0 +1,423 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jdo;
import java.util.Collection;
import java.util.Map;
import org.springframework.dao.DataAccessException;
/**
* Interface that specifies a basic set of JDO operations,
* implemented by {@link JdoTemplate}. Not often used, but a useful
* option to enhance testability, as it can easily be mocked or stubbed.
*
* <p>Defines <code>JdoTemplate</code>'s data access methods that mirror
* various JDO {@link javax.jdo.PersistenceManager} methods. Users are
* strongly encouraged to read the JDO <code>PersistenceManager</code>
* javadocs for details on the semantics of those methods.
*
* <p>Note that lazy loading will just work with an open JDO
* <code>PersistenceManager</code>, either within a managed transaction or within
* {@link org.springframework.orm.jdo.support.OpenPersistenceManagerInViewFilter}/
* {@link org.springframework.orm.jdo.support.OpenPersistenceManagerInViewInterceptor}.
* Furthermore, some operations just make sense within transactions,
* for example: <code>evict</code>, <code>evictAll</code>, <code>flush</code>.
*
* <p>Updated to build on JDO 2.0 or higher, as of Spring 2.5.
*
* @author Juergen Hoeller
* @since 1.1
* @see JdoTemplate
* @see javax.jdo.PersistenceManager
* @see JdoTransactionManager
* @see JdoDialect
* @see org.springframework.orm.jdo.support.OpenPersistenceManagerInViewFilter
* @see org.springframework.orm.jdo.support.OpenPersistenceManagerInViewInterceptor
* @deprecated as of Spring 3.1, in favor of native PersistenceManager usage
* (see {@link TransactionAwarePersistenceManagerFactoryProxy} and
* {@link org.springframework.orm.jdo.support.SpringPersistenceManagerProxyBean})
*/
@Deprecated
public interface JdoOperations {
/**
* Execute the action specified by the given action object within a
* PersistenceManager. Application exceptions thrown by the action object
* get propagated to the caller (can only be unchecked). JDO exceptions
* are transformed into appropriate DAO ones. Allows for returning a
* result object, i.e. a domain object or a collection of domain objects.
* <p>Note: Callback code is not supposed to handle transactions itself!
* Use an appropriate transaction manager like JdoTransactionManager.
* @param action callback object that specifies the JDO action
* @return a result object returned by the action, or <code>null</code>
* @throws org.springframework.dao.DataAccessException in case of JDO errors
* @see JdoTransactionManager
* @see org.springframework.dao
* @see org.springframework.transaction
* @see javax.jdo.PersistenceManager
*/
<T> T execute(JdoCallback<T> action) throws DataAccessException;
/**
* Execute the specified action assuming that the result object is a
* Collection. This is a convenience method for executing JDO queries
* within an action.
* @param action callback object that specifies the JDO action
* @return a Collection result returned by the action, or <code>null</code>
* @throws org.springframework.dao.DataAccessException in case of JDO errors
*/
Collection executeFind(JdoCallback<?> action) throws DataAccessException;
//-------------------------------------------------------------------------
// Convenience methods for load, save, delete
//-------------------------------------------------------------------------
/**
* Return the persistent instance with the given JDO object id,
* throwing an exception if not found.
* <p>A JDO object id identifies both the persistent class and the id
* within the namespace of that class.
* @param objectId a JDO object id of the persistent instance
* @return the persistent instance
* @throws org.springframework.orm.ObjectRetrievalFailureException if not found
* @throws org.springframework.dao.DataAccessException in case of JDO errors
* @see javax.jdo.PersistenceManager#getObjectById(Object, boolean)
*/
Object getObjectById(Object objectId) throws DataAccessException;
/**
* Return the persistent instance of the given entity class
* with the given id value, throwing an exception if not found.
* <p>The given id value is typically just unique within the namespace
* of the persistent class. Its toString value must correspond to the
* toString value of the corresponding JDO object id.
* <p>Usually, the passed-in value will have originated from the primary
* key field of a persistent object that uses JDO's application identity.
* @param entityClass a persistent class
* @param idValue an id value of the persistent instance
* @return the persistent instance
* @throws org.springframework.orm.ObjectRetrievalFailureException if not found
* @throws org.springframework.dao.DataAccessException in case of JDO errors
* @see javax.jdo.PersistenceManager#getObjectById(Object, boolean)
* @see javax.jdo.PersistenceManager#getObjectById(Class, Object)
*/
<T> T getObjectById(Class<T> entityClass, Object idValue) throws DataAccessException;
/**
* Remove the given object from the PersistenceManager cache.
* @param entity the persistent instance to evict
* @throws org.springframework.dao.DataAccessException in case of JDO errors
* @see javax.jdo.PersistenceManager#evict(Object)
*/
void evict(Object entity) throws DataAccessException;
/**
* Remove all given objects from the PersistenceManager cache.
* @param entities the persistent instances to evict
* @throws org.springframework.dao.DataAccessException in case of JDO errors
* @see javax.jdo.PersistenceManager#evictAll(java.util.Collection)
*/
void evictAll(Collection entities) throws DataAccessException;
/**
* Remove all objects from the PersistenceManager cache.
* @throws org.springframework.dao.DataAccessException in case of JDO errors
* @see javax.jdo.PersistenceManager#evictAll()
*/
void evictAll() throws DataAccessException;
/**
* Re-read the state of the given persistent instance.
* @param entity the persistent instance to re-read
* @throws org.springframework.dao.DataAccessException in case of JDO errors
* @see javax.jdo.PersistenceManager#refresh(Object)
*/
void refresh(Object entity) throws DataAccessException;
/**
* Re-read the state of all given persistent instances.
* @param entities the persistent instances to re-read
* @throws org.springframework.dao.DataAccessException in case of JDO errors
* @see javax.jdo.PersistenceManager#refreshAll(java.util.Collection)
*/
void refreshAll(Collection entities) throws DataAccessException;
/**
* Re-read the state of all persistent instances.
* @throws org.springframework.dao.DataAccessException in case of JDO errors
* @see javax.jdo.PersistenceManager#refreshAll()
*/
void refreshAll() throws DataAccessException;
/**
* Make the given transient instance persistent.
* Attach the given entity if the instance is detached.
* @param entity the transient instance to make persistent
* @return the persistent instance
* @throws org.springframework.dao.DataAccessException in case of JDO errors
* @see javax.jdo.PersistenceManager#makePersistent(Object)
*/
<T> T makePersistent(T entity) throws DataAccessException;
/**
* Make the given transient instances persistent.
* Attach the given entities if the instances are detached.
* @param entities the transient instances to make persistent
* @return the persistent instances
* @throws org.springframework.dao.DataAccessException in case of JDO errors
* @see javax.jdo.PersistenceManager#makePersistentAll(java.util.Collection)
*/
<T> Collection<T> makePersistentAll(Collection<T> entities) throws DataAccessException;
/**
* Delete the given persistent instance.
* @param entity the persistent instance to delete
* @throws org.springframework.dao.DataAccessException in case of JDO errors
* @see javax.jdo.PersistenceManager#deletePersistent(Object)
*/
void deletePersistent(Object entity) throws DataAccessException;
/**
* Delete all given persistent instances.
* <p>This can be combined with any of the find methods to delete by query
* in two lines of code.
* @param entities the persistent instances to delete
* @throws org.springframework.dao.DataAccessException in case of JDO errors
* @see javax.jdo.PersistenceManager#deletePersistentAll(java.util.Collection)
*/
void deletePersistentAll(Collection entities) throws DataAccessException;
/**
* Detach a copy of the given persistent instance from the current JDO transaction,
* for use outside a JDO transaction (for example, as web form object).
* @param entity the persistent instance to detach
* @return the corresponding detached instance
* @see javax.jdo.PersistenceManager#detachCopy(Object)
*/
<T> T detachCopy(T entity);
/**
* Detach copies of the given persistent instances from the current JDO transaction,
* for use outside a JDO transaction (for example, as web form objects).
* @param entities the persistent instances to detach
* @return the corresponding detached instances
* @see javax.jdo.PersistenceManager#detachCopyAll(Collection)
*/
<T> Collection<T> detachCopyAll(Collection<T> entities);
/**
* Flush all transactional modifications to the database.
* <p>Only invoke this for selective eager flushing, for example when JDBC code
* needs to see certain changes within the same transaction. Else, it's preferable
* to rely on auto-flushing at transaction completion.
* @throws org.springframework.dao.DataAccessException in case of JDO errors
* @see javax.jdo.PersistenceManager#flush()
* @see JdoDialect#flush(javax.jdo.PersistenceManager)
*/
void flush() throws DataAccessException;
//-------------------------------------------------------------------------
// Convenience finder methods
//-------------------------------------------------------------------------
/**
* Find all persistent instances of the given class.
* @param entityClass a persistent class
* @return the persistent instances
* @throws org.springframework.dao.DataAccessException in case of JDO errors
* @see javax.jdo.PersistenceManager#newQuery(Class)
*/
<T> Collection<T> find(Class<T> entityClass) throws DataAccessException;
/**
* Find all persistent instances of the given class that match the given
* JDOQL filter.
* @param entityClass a persistent class
* @param filter the JDOQL filter to match (or <code>null</code> if none)
* @return the persistent instances
* @throws org.springframework.dao.DataAccessException in case of JDO errors
* @see javax.jdo.PersistenceManager#newQuery(Class, String)
*/
<T> Collection<T> find(Class<T> entityClass, String filter) throws DataAccessException;
/**
* Find all persistent instances of the given class that match the given
* JDOQL filter, with the given result ordering.
* @param entityClass a persistent class
* @param filter the JDOQL filter to match (or <code>null</code> if none)
* @param ordering the ordering of the result (or <code>null</code> if none)
* @return the persistent instances
* @throws org.springframework.dao.DataAccessException in case of JDO errors
* @see javax.jdo.PersistenceManager#newQuery(Class, String)
* @see javax.jdo.Query#setOrdering
*/
<T> Collection<T> find(Class<T> entityClass, String filter, String ordering) throws DataAccessException;
/**
* Find all persistent instances of the given class that match the given
* JDOQL filter, using the given parameter declarations and parameter values.
* @param entityClass a persistent class
* @param filter the JDOQL filter to match
* @param parameters the JDOQL parameter declarations
* @param values the corresponding parameter values
* @return the persistent instances
* @throws org.springframework.dao.DataAccessException in case of JDO errors
* @see javax.jdo.PersistenceManager#newQuery(Class, String)
* @see javax.jdo.Query#declareParameters
* @see javax.jdo.Query#executeWithArray
*/
<T> Collection<T> find(Class<T> entityClass, String filter, String parameters, Object... values)
throws DataAccessException;
/**
* Find all persistent instances of the given class that match the given
* JDOQL filter, using the given parameter declarations and parameter values,
* with the given result ordering.
* @param entityClass a persistent class
* @param filter the JDOQL filter to match
* @param parameters the JDOQL parameter declarations
* @param values the corresponding parameter values
* @param ordering the ordering of the result (or <code>null</code> if none)
* @return the persistent instances
* @throws org.springframework.dao.DataAccessException in case of JDO errors
* @see javax.jdo.PersistenceManager#newQuery(Class, String)
* @see javax.jdo.Query#declareParameters
* @see javax.jdo.Query#executeWithArray
* @see javax.jdo.Query#setOrdering
*/
<T> Collection<T> find(
Class<T> entityClass, String filter, String parameters, Object[] values, String ordering)
throws DataAccessException;
/**
* Find all persistent instances of the given class that match the given
* JDOQL filter, using the given parameter declarations and parameter values.
* @param entityClass a persistent class
* @param filter the JDOQL filter to match
* @param parameters the JDOQL parameter declarations
* @param values a Map with parameter names as keys and parameter values
* @return the persistent instances
* @throws org.springframework.dao.DataAccessException in case of JDO errors
* @see javax.jdo.PersistenceManager#newQuery(Class, String)
* @see javax.jdo.Query#declareParameters
* @see javax.jdo.Query#executeWithMap
*/
<T> Collection<T> find(Class<T> entityClass, String filter, String parameters, Map<String, ?> values)
throws DataAccessException;
/**
* Find all persistent instances of the given class that match the given
* JDOQL filter, using the given parameter declarations and parameter values,
* with the given result ordering.
* @param entityClass a persistent class
* @param filter the JDOQL filter to match
* @param parameters the JDOQL parameter declarations
* @param values a Map with parameter names as keys and parameter values
* @param ordering the ordering of the result (or <code>null</code> if none)
* @return the persistent instances
* @throws org.springframework.dao.DataAccessException in case of JDO errors
* @see javax.jdo.PersistenceManager#newQuery(Class, String)
* @see javax.jdo.Query#declareParameters
* @see javax.jdo.Query#executeWithMap
* @see javax.jdo.Query#setOrdering
*/
<T> Collection<T> find(
Class<T> entityClass, String filter, String parameters, Map<String, ?> values, String ordering)
throws DataAccessException;
/**
* Find persistent instances through the given query object
* in the specified query language.
* @param language the query language (<code>javax.jdo.Query#JDOQL</code>
* or <code>javax.jdo.Query#SQL</code>, for example)
* @param queryObject the query object for the specified language
* @return the persistent instances
* @throws org.springframework.dao.DataAccessException in case of JDO errors
* @see javax.jdo.PersistenceManager#newQuery(String, Object)
* @see javax.jdo.Query#JDOQL
* @see javax.jdo.Query#SQL
*/
Collection find(String language, Object queryObject) throws DataAccessException;
/**
* Find persistent instances through the given single-string JDOQL query.
* @param queryString the single-string JDOQL query
* @return the persistent instances
* @throws org.springframework.dao.DataAccessException in case of JDO errors
* @see javax.jdo.PersistenceManager#newQuery(String)
*/
Collection find(String queryString) throws DataAccessException;
/**
* Find persistent instances through the given single-string JDOQL query.
* @param queryString the single-string JDOQL query
* @param values the corresponding parameter values
* @return the persistent instances
* @throws org.springframework.dao.DataAccessException in case of JDO errors
* @see javax.jdo.PersistenceManager#newQuery(String)
*/
Collection find(String queryString, Object... values) throws DataAccessException;
/**
* Find persistent instances through the given single-string JDOQL query.
* @param queryString the single-string JDOQL query
* @param values a Map with parameter names as keys and parameter values
* @return the persistent instances
* @throws org.springframework.dao.DataAccessException in case of JDO errors
* @see javax.jdo.PersistenceManager#newQuery(String)
*/
Collection find(String queryString, Map<String, ?> values) throws DataAccessException;
/**
* Find persistent instances through the given named query.
* @param entityClass a persistent class
* @param queryName the name of the query
* @return the persistent instances
* @throws org.springframework.dao.DataAccessException in case of JDO errors
* @see javax.jdo.PersistenceManager#newNamedQuery(Class, String)
*/
<T> Collection<T> findByNamedQuery(Class<T> entityClass, String queryName)
throws DataAccessException;
/**
* Find persistent instances through the given named query.
* @param entityClass a persistent class
* @param queryName the name of the query
* @param values the corresponding parameter values
* @return the persistent instances
* @throws org.springframework.dao.DataAccessException in case of JDO errors
* @see javax.jdo.PersistenceManager#newNamedQuery(Class, String)
*/
<T> Collection<T> findByNamedQuery(Class<T> entityClass, String queryName, Object... values)
throws DataAccessException;
/**
* Find persistent instances through the given named query.
* @param entityClass a persistent class
* @param queryName the name of the query
* @param values a Map with parameter names as keys and parameter values
* @return the persistent instances
* @throws org.springframework.dao.DataAccessException in case of JDO errors
* @see javax.jdo.PersistenceManager#newNamedQuery(Class, String)
*/
<T> Collection<T> findByNamedQuery(Class<T> entityClass, String queryName, Map<String, ?> values)
throws DataAccessException;
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2002-2006 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jdo;
import javax.jdo.JDOHelper;
import javax.jdo.JDOOptimisticVerificationException;
import org.springframework.orm.ObjectOptimisticLockingFailureException;
/**
* JDO-specific subclass of ObjectOptimisticLockingFailureException.
* Converts JDO's JDOOptimisticVerificationException.
*
* @author Juergen Hoeller
* @since 1.1
* @see PersistenceManagerFactoryUtils#convertJdoAccessException
*/
public class JdoOptimisticLockingFailureException extends ObjectOptimisticLockingFailureException {
public JdoOptimisticLockingFailureException(JDOOptimisticVerificationException ex) {
// Extract information about the failed object from the JDOException, if available.
super((ex.getFailedObject() != null ? ex.getFailedObject().getClass() : null),
(ex.getFailedObject() != null ? JDOHelper.getObjectId(ex.getFailedObject()) : null),
ex.getMessage(), ex);
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jdo;
import javax.jdo.JDODataStoreException;
import javax.jdo.JDOFatalDataStoreException;
import org.springframework.dao.DataAccessResourceFailureException;
/**
* JDO-specific subclass of DataAccessResourceFailureException.
* Converts JDO's JDODataStoreException and JDOFatalDataStoreException.
*
* @author Juergen Hoeller
* @since 1.1
* @see PersistenceManagerFactoryUtils#convertJdoAccessException
*/
public class JdoResourceFailureException extends DataAccessResourceFailureException {
public JdoResourceFailureException(JDODataStoreException ex) {
super(ex.getMessage(), ex);
}
public JdoResourceFailureException(JDOFatalDataStoreException ex) {
super(ex.getMessage(), ex);
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jdo;
import javax.jdo.JDOException;
import org.springframework.dao.UncategorizedDataAccessException;
/**
* JDO-specific subclass of UncategorizedDataAccessException,
* for JDO system errors that do not match any concrete
* <code>org.springframework.dao</code> exceptions.
*
* @author Juergen Hoeller
* @since 03.06.2003
* @see PersistenceManagerFactoryUtils#convertJdoAccessException
*/
public class JdoSystemException extends UncategorizedDataAccessException {
public JdoSystemException(JDOException ex) {
super(ex.getMessage(), ex);
}
}

View File

@@ -0,0 +1,619 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jdo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Collection;
import java.util.Map;
import javax.jdo.JDOException;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Query;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Helper class that simplifies JDO data access code, and converts
* JDOExceptions into Spring DataAccessExceptions, following the
* <code>org.springframework.dao</code> exception hierarchy.
*
* <p>The central method is <code>execute</code>, supporting JDO access code
* implementing the {@link JdoCallback} interface. It provides JDO PersistenceManager
* handling such that neither the JdoCallback implementation nor the calling
* code needs to explicitly care about retrieving/closing PersistenceManagers,
* or handling JDO lifecycle exceptions.
*
* <p>Typically used to implement data access or business logic services that
* use JDO within their implementation but are JDO-agnostic in their interface.
* The latter or code calling the latter only have to deal with business
* objects, query objects, and <code>org.springframework.dao</code> exceptions.
*
* <p>Can be used within a service implementation via direct instantiation
* with a PersistenceManagerFactory reference, or get prepared in an
* application context and given to services as bean reference.
* Note: The PersistenceManagerFactory should always be configured as bean in
* the application context, in the first case given to the service directly,
* in the second case to the prepared template.
*
* <p>This class can be considered as direct alternative to working with the
* raw JDO PersistenceManager API (through
* <code>PersistenceManagerFactoryUtils.getPersistenceManager()</code>).
* The major advantage is its automatic conversion to DataAccessExceptions, the
* major disadvantage that no checked application exceptions can get thrown from
* within data access code. Corresponding checks and the actual throwing of such
* exceptions can often be deferred to after callback execution, though.
*
* <p>{@link LocalPersistenceManagerFactoryBean} is the preferred way of obtaining
* a reference to a specific PersistenceManagerFactory, at least in a non-EJB
* environment. The Spring application context will manage its lifecycle,
* initializing and shutting down the factory as part of the application.
*
* <p>Note that lazy loading will just work with an open JDO PersistenceManager,
* either within a Spring-driven transaction (with JdoTransactionManager or
* JtaTransactionManager) or within OpenPersistenceManagerInViewFilter/Interceptor.
* Furthermore, some operations just make sense within transactions,
* for example: <code>evict</code>, <code>evictAll</code>, <code>flush</code>.
*
* <p><b>NOTE: This class requires JDO 2.0 or higher, as of Spring 2.5.</b>
* As of Spring 3.0, it follows JDO 2.1 conventions in terms of generic
* parameter and return types, which still remaining compatible with JDO 2.0.
*
* @author Juergen Hoeller
* @since 03.06.2003
* @see #setPersistenceManagerFactory
* @see JdoCallback
* @see javax.jdo.PersistenceManager
* @see LocalPersistenceManagerFactoryBean
* @see JdoTransactionManager
* @see org.springframework.transaction.jta.JtaTransactionManager
* @see org.springframework.orm.jdo.support.OpenPersistenceManagerInViewFilter
* @see org.springframework.orm.jdo.support.OpenPersistenceManagerInViewInterceptor
* @deprecated as of Spring 3.1, in favor of native PersistenceManager usage
* (see {@link TransactionAwarePersistenceManagerFactoryProxy} and
* {@link org.springframework.orm.jdo.support.SpringPersistenceManagerProxyBean})
*/
@Deprecated
public class JdoTemplate extends JdoAccessor implements JdoOperations {
private boolean allowCreate = true;
private boolean exposeNativePersistenceManager = false;
/**
* Create a new JdoTemplate instance.
*/
public JdoTemplate() {
}
/**
* Create a new JdoTemplate instance.
* @param pmf PersistenceManagerFactory to create PersistenceManagers
*/
public JdoTemplate(PersistenceManagerFactory pmf) {
setPersistenceManagerFactory(pmf);
afterPropertiesSet();
}
/**
* Create a new JdoTemplate instance.
* @param pmf PersistenceManagerFactory to create PersistenceManagers
* @param allowCreate if a non-transactional PersistenceManager should be created
* when no transactional PersistenceManager can be found for the current thread
*/
public JdoTemplate(PersistenceManagerFactory pmf, boolean allowCreate) {
setPersistenceManagerFactory(pmf);
setAllowCreate(allowCreate);
afterPropertiesSet();
}
/**
* Set if a new PersistenceManager should be created when no transactional
* PersistenceManager can be found for the current thread.
* <p>JdoTemplate is aware of a corresponding PersistenceManager bound to the
* current thread, for example when using JdoTransactionManager.
* If allowCreate is true, a new non-transactional PersistenceManager will be
* created if none found, which needs to be closed at the end of the operation.
* If false, an IllegalStateException will get thrown in this case.
* @see PersistenceManagerFactoryUtils#getPersistenceManager(javax.jdo.PersistenceManagerFactory, boolean)
*/
public void setAllowCreate(boolean allowCreate) {
this.allowCreate = allowCreate;
}
/**
* Return if a new PersistenceManager should be created if no thread-bound found.
*/
public boolean isAllowCreate() {
return this.allowCreate;
}
/**
* Set whether to expose the native JDO PersistenceManager to JdoCallback
* code. Default is "false": a PersistenceManager proxy will be returned,
* suppressing <code>close</code> calls and automatically applying transaction
* timeouts (if any).
* <p>As there is often a need to cast to a provider-specific PersistenceManager
* class in DAOs that use provider-specific functionality, the exposed proxy
* implements all interfaces implemented by the original PersistenceManager.
* If this is not sufficient, turn this flag to "true".
* @see JdoCallback
* @see javax.jdo.PersistenceManager
* @see #prepareQuery
*/
public void setExposeNativePersistenceManager(boolean exposeNativePersistenceManager) {
this.exposeNativePersistenceManager = exposeNativePersistenceManager;
}
/**
* Return whether to expose the native JDO PersistenceManager to JdoCallback
* code, or rather a PersistenceManager proxy.
*/
public boolean isExposeNativePersistenceManager() {
return this.exposeNativePersistenceManager;
}
public <T> T execute(JdoCallback<T> action) throws DataAccessException {
return execute(action, isExposeNativePersistenceManager());
}
public Collection executeFind(JdoCallback<?> action) throws DataAccessException {
Object result = execute(action, isExposeNativePersistenceManager());
if (result != null && !(result instanceof Collection)) {
throw new InvalidDataAccessApiUsageException(
"Result object returned from JdoCallback isn't a Collection: [" + result + "]");
}
return (Collection) result;
}
/**
* Execute the action specified by the given action object within a
* PersistenceManager.
* @param action callback object that specifies the JDO action
* @param exposeNativePersistenceManager whether to expose the native
* JDO persistence manager to callback code
* @return a result object returned by the action, or <code>null</code>
* @throws org.springframework.dao.DataAccessException in case of JDO errors
*/
public <T> T execute(JdoCallback<T> action, boolean exposeNativePersistenceManager) throws DataAccessException {
Assert.notNull(action, "Callback object must not be null");
PersistenceManager pm = PersistenceManagerFactoryUtils.getPersistenceManager(
getPersistenceManagerFactory(), isAllowCreate());
boolean existingTransaction =
TransactionSynchronizationManager.hasResource(getPersistenceManagerFactory());
try {
PersistenceManager pmToExpose = (exposeNativePersistenceManager ? pm : createPersistenceManagerProxy(pm));
T result = action.doInJdo(pmToExpose);
flushIfNecessary(pm, existingTransaction);
return postProcessResult(result, pm, existingTransaction);
}
catch (JDOException ex) {
throw convertJdoAccessException(ex);
}
catch (RuntimeException ex) {
// callback code threw application exception
throw ex;
}
finally {
PersistenceManagerFactoryUtils.releasePersistenceManager(pm, getPersistenceManagerFactory());
}
}
/**
* Create a close-suppressing proxy for the given JDO PersistenceManager.
* Called by the <code>execute</code> method.
* <p>The proxy also prepares returned JDO Query objects.
* @param pm the JDO PersistenceManager to create a proxy for
* @return the PersistenceManager proxy, implementing all interfaces
* implemented by the passed-in PersistenceManager object (that is,
* also implementing all provider-specific extension interfaces)
* @see javax.jdo.PersistenceManager#close()
* @see #execute(JdoCallback, boolean)
* @see #prepareQuery
*/
protected PersistenceManager createPersistenceManagerProxy(PersistenceManager pm) {
Class[] ifcs = ClassUtils.getAllInterfacesForClass(pm.getClass(), getClass().getClassLoader());
return (PersistenceManager) Proxy.newProxyInstance(
pm.getClass().getClassLoader(), ifcs, new CloseSuppressingInvocationHandler(pm));
}
/**
* Post-process the given result object, which might be a Collection.
* Called by the <code>execute</code> method.
* <p>Default implementation always returns the passed-in Object as-is.
* Subclasses might override this to automatically detach result
* collections or even single result objects.
* @param pm the current JDO PersistenceManager
* @param result the result object (might be a Collection)
* @param existingTransaction if executing within an existing transaction
* (within an existing JDO PersistenceManager that won't be closed immediately)
* @return the post-processed result object (can be simply be the passed-in object)
* @see #execute(JdoCallback, boolean)
*/
protected <T> T postProcessResult(T result, PersistenceManager pm, boolean existingTransaction) {
return result;
}
//-------------------------------------------------------------------------
// Convenience methods for load, save, delete
//-------------------------------------------------------------------------
public Object getObjectById(final Object objectId) throws DataAccessException {
return execute(new JdoCallback<Object>() {
public Object doInJdo(PersistenceManager pm) throws JDOException {
return pm.getObjectById(objectId, true);
}
}, true);
}
public <T> T getObjectById(final Class<T> entityClass, final Object idValue) throws DataAccessException {
return execute(new JdoCallback<T>() {
public T doInJdo(PersistenceManager pm) throws JDOException {
return pm.getObjectById(entityClass, idValue);
}
}, true);
}
public void evict(final Object entity) throws DataAccessException {
execute(new JdoCallback<Object>() {
public Object doInJdo(PersistenceManager pm) throws JDOException {
pm.evict(entity);
return null;
}
}, true);
}
public void evictAll(final Collection entities) throws DataAccessException {
execute(new JdoCallback<Object>() {
public Object doInJdo(PersistenceManager pm) throws JDOException {
pm.evictAll(entities);
return null;
}
}, true);
}
public void evictAll() throws DataAccessException {
execute(new JdoCallback<Object>() {
public Object doInJdo(PersistenceManager pm) throws JDOException {
pm.evictAll();
return null;
}
}, true);
}
public void refresh(final Object entity) throws DataAccessException {
execute(new JdoCallback<Object>() {
public Object doInJdo(PersistenceManager pm) throws JDOException {
pm.refresh(entity);
return null;
}
}, true);
}
public void refreshAll(final Collection entities) throws DataAccessException {
execute(new JdoCallback<Object>() {
public Object doInJdo(PersistenceManager pm) throws JDOException {
pm.refreshAll(entities);
return null;
}
}, true);
}
public void refreshAll() throws DataAccessException {
execute(new JdoCallback<Object>() {
public Object doInJdo(PersistenceManager pm) throws JDOException {
pm.refreshAll();
return null;
}
}, true);
}
public <T> T makePersistent(final T entity) throws DataAccessException {
return execute(new JdoCallback<T>() {
public T doInJdo(PersistenceManager pm) throws JDOException {
return pm.makePersistent(entity);
}
}, true);
}
public <T> Collection<T> makePersistentAll(final Collection<T> entities) throws DataAccessException {
return execute(new JdoCallback<Collection<T>>() {
public Collection<T> doInJdo(PersistenceManager pm) throws JDOException {
return pm.makePersistentAll(entities);
}
}, true);
}
public void deletePersistent(final Object entity) throws DataAccessException {
execute(new JdoCallback<Object>() {
public Object doInJdo(PersistenceManager pm) throws JDOException {
pm.deletePersistent(entity);
return null;
}
}, true);
}
public void deletePersistentAll(final Collection entities) throws DataAccessException {
execute(new JdoCallback<Object>() {
public Object doInJdo(PersistenceManager pm) throws JDOException {
pm.deletePersistentAll(entities);
return null;
}
}, true);
}
public <T> T detachCopy(final T entity) {
return execute(new JdoCallback<T>() {
public T doInJdo(PersistenceManager pm) throws JDOException {
return pm.detachCopy(entity);
}
}, true);
}
public <T> Collection<T> detachCopyAll(final Collection<T> entities) {
return execute(new JdoCallback<Collection<T>>() {
public Collection<T> doInJdo(PersistenceManager pm) throws JDOException {
return pm.detachCopyAll(entities);
}
}, true);
}
public void flush() throws DataAccessException {
execute(new JdoCallback<Object>() {
public Object doInJdo(PersistenceManager pm) throws JDOException {
pm.flush();
return null;
}
}, true);
}
//-------------------------------------------------------------------------
// Convenience finder methods
//-------------------------------------------------------------------------
public <T> Collection<T> find(Class<T> entityClass) throws DataAccessException {
return find(entityClass, null, null);
}
public <T> Collection<T> find(Class<T> entityClass, String filter) throws DataAccessException {
return find(entityClass, filter, null);
}
public <T> Collection<T> find(final Class<T> entityClass, final String filter, final String ordering)
throws DataAccessException {
return execute(new JdoCallback<Collection<T>>() {
@SuppressWarnings("unchecked")
public Collection<T> doInJdo(PersistenceManager pm) throws JDOException {
Query query = (filter != null ? pm.newQuery(entityClass, filter) : pm.newQuery(entityClass));
prepareQuery(query);
if (ordering != null) {
query.setOrdering(ordering);
}
return (Collection<T>) query.execute();
}
}, true);
}
public <T> Collection<T> find(Class<T> entityClass, String filter, String parameters, Object... values)
throws DataAccessException {
return find(entityClass, filter, parameters, values, null);
}
public <T> Collection<T> find(
final Class<T> entityClass, final String filter, final String parameters, final Object[] values,
final String ordering) throws DataAccessException {
return execute(new JdoCallback<Collection<T>>() {
@SuppressWarnings("unchecked")
public Collection<T> doInJdo(PersistenceManager pm) throws JDOException {
Query query = pm.newQuery(entityClass, filter);
prepareQuery(query);
query.declareParameters(parameters);
if (ordering != null) {
query.setOrdering(ordering);
}
return (Collection<T>) query.executeWithArray(values);
}
}, true);
}
public <T> Collection<T> find(
Class<T> entityClass, String filter, String parameters, Map<String, ?> values)
throws DataAccessException {
return find(entityClass, filter, parameters, values, null);
}
public <T> Collection<T> find(
final Class<T> entityClass, final String filter, final String parameters,
final Map<String, ?> values, final String ordering) throws DataAccessException {
return execute(new JdoCallback<Collection<T>>() {
@SuppressWarnings("unchecked")
public Collection<T> doInJdo(PersistenceManager pm) throws JDOException {
Query query = pm.newQuery(entityClass, filter);
prepareQuery(query);
query.declareParameters(parameters);
if (ordering != null) {
query.setOrdering(ordering);
}
return (Collection<T>) query.executeWithMap(values);
}
}, true);
}
public Collection find(final String language, final Object queryObject) throws DataAccessException {
return execute(new JdoCallback<Collection>() {
public Collection doInJdo(PersistenceManager pm) throws JDOException {
Query query = pm.newQuery(language, queryObject);
prepareQuery(query);
return (Collection) query.execute();
}
}, true);
}
public Collection find(final String queryString) throws DataAccessException {
return execute(new JdoCallback<Collection>() {
@SuppressWarnings("unchecked")
public Collection doInJdo(PersistenceManager pm) throws JDOException {
Query query = pm.newQuery(queryString);
prepareQuery(query);
return (Collection) query.execute();
}
}, true);
}
public Collection find(final String queryString, final Object... values) throws DataAccessException {
return execute(new JdoCallback<Collection>() {
public Collection doInJdo(PersistenceManager pm) throws JDOException {
Query query = pm.newQuery(queryString);
prepareQuery(query);
return (Collection) query.executeWithArray(values);
}
}, true);
}
public Collection find(final String queryString, final Map<String, ?> values) throws DataAccessException {
return execute(new JdoCallback<Collection>() {
public Collection doInJdo(PersistenceManager pm) throws JDOException {
Query query = pm.newQuery(queryString);
prepareQuery(query);
return (Collection) query.executeWithMap(values);
}
}, true);
}
public <T> Collection<T> findByNamedQuery(final Class<T> entityClass, final String queryName)
throws DataAccessException {
return execute(new JdoCallback<Collection<T>>() {
@SuppressWarnings("unchecked")
public Collection<T> doInJdo(PersistenceManager pm) throws JDOException {
Query query = pm.newNamedQuery(entityClass, queryName);
prepareQuery(query);
return (Collection<T>) query.execute();
}
}, true);
}
public <T> Collection<T> findByNamedQuery(final Class<T> entityClass, final String queryName, final Object... values)
throws DataAccessException {
return execute(new JdoCallback<Collection<T>>() {
@SuppressWarnings("unchecked")
public Collection<T> doInJdo(PersistenceManager pm) throws JDOException {
Query query = pm.newNamedQuery(entityClass, queryName);
prepareQuery(query);
return (Collection<T>) query.executeWithArray(values);
}
}, true);
}
public <T> Collection<T> findByNamedQuery(
final Class<T> entityClass, final String queryName, final Map<String, ?> values)
throws DataAccessException {
return execute(new JdoCallback<Collection<T>>() {
@SuppressWarnings("unchecked")
public Collection<T> doInJdo(PersistenceManager pm) throws JDOException {
Query query = pm.newNamedQuery(entityClass, queryName);
prepareQuery(query);
return (Collection<T>) query.executeWithMap(values);
}
}, true);
}
/**
* Prepare the given JDO query object. To be used within a JdoCallback.
* <p>Applies a transaction timeout, if any. If you don't use such timeouts,
* the call is a no-op.
* <p>In general, prefer a proxied PersistenceManager instead, which will
* automatically apply the transaction timeout (through the use of a special
* PersistenceManager proxy). You need to set the "exposeNativePersistenceManager"
* property to "false" to activate this. Note that you won't be able to cast
* to a provider-specific JDO PersistenceManager class anymore then.
* @param query the JDO query object
* @throws JDOException if the query could not be properly prepared
* @see JdoCallback#doInJdo
* @see PersistenceManagerFactoryUtils#applyTransactionTimeout
* @see #setExposeNativePersistenceManager
*/
public void prepareQuery(Query query) throws JDOException {
PersistenceManagerFactoryUtils.applyTransactionTimeout(
query, getPersistenceManagerFactory(), getJdoDialect());
}
/**
* Invocation handler that suppresses close calls on JDO PersistenceManagers.
* Also prepares returned Query objects.
* @see javax.jdo.PersistenceManager#close()
*/
private class CloseSuppressingInvocationHandler implements InvocationHandler {
private final PersistenceManager target;
public CloseSuppressingInvocationHandler(PersistenceManager target) {
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Invocation on PersistenceManager interface (or provider-specific extension) coming in...
if (method.getName().equals("equals")) {
// Only consider equal when proxies are identical.
return (proxy == args[0]);
}
else if (method.getName().equals("hashCode")) {
// Use hashCode of PersistenceManager proxy.
return System.identityHashCode(proxy);
}
else if (method.getName().equals("close")) {
// Handle close method: suppress, not valid.
return null;
}
// Invoke method on target PersistenceManager.
try {
Object retVal = method.invoke(this.target, args);
// If return value is a JDO Query object, apply transaction timeout.
if (retVal instanceof Query) {
prepareQuery(((Query) retVal));
}
return retVal;
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
}

View File

@@ -0,0 +1,613 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jdo;
import javax.jdo.JDOException;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Transaction;
import javax.sql.DataSource;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.datasource.ConnectionHandle;
import org.springframework.jdbc.datasource.ConnectionHolder;
import org.springframework.jdbc.datasource.JdbcTransactionObjectSupport;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
import org.springframework.transaction.CannotCreateTransactionException;
import org.springframework.transaction.IllegalTransactionStateException;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
import org.springframework.transaction.TransactionSystemException;
import org.springframework.transaction.support.AbstractPlatformTransactionManager;
import org.springframework.transaction.support.DefaultTransactionStatus;
import org.springframework.transaction.support.DelegatingTransactionDefinition;
import org.springframework.transaction.support.ResourceTransactionManager;
import org.springframework.transaction.support.TransactionSynchronizationManager;
/**
* {@link org.springframework.transaction.PlatformTransactionManager} implementation
* for a single JDO {@link javax.jdo.PersistenceManagerFactory}. Binds a JDO
* PersistenceManager from the specified factory to the thread, potentially allowing
* for one thread-bound PersistenceManager per factory.
* {@link PersistenceManagerFactoryUtils} and {@link JdoTemplate} are aware of
* thread-bound persistence managers and participate in such transactions automatically.
* Using either of those (or going through a {@link TransactionAwarePersistenceManagerFactoryProxy}
* is required for JDO access code supporting this transaction management mechanism.
*
* <p>This transaction manager is appropriate for applications that use a single
* JDO PersistenceManagerFactory for transactional data access. JTA (usually through
* {@link org.springframework.transaction.jta.JtaTransactionManager}) is necessary
* for accessing multiple transactional resources within the same transaction.
* Note that you need to configure your JDO provider accordingly in order to make
* it participate in JTA transactions.
*
* <p>This transaction manager also supports direct DataSource access within a
* transaction (i.e. plain JDBC code working with the same DataSource).
* This allows for mixing services which access JDO and services which use plain
* JDBC (without being aware of JDO)! Application code needs to stick to the
* same simple Connection lookup pattern as with
* {@link org.springframework.jdbc.datasource.DataSourceTransactionManager}
* (i.e. {@link org.springframework.jdbc.datasource.DataSourceUtils#getConnection}
* or going through a
* {@link org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy}).
*
* <p>Note: To be able to register a DataSource's Connection for plain JDBC code,
* this instance needs to be aware of the DataSource ({@link #setDataSource}).
* The given DataSource should obviously match the one used by the given
* PersistenceManagerFactory. This transaction manager will autodetect the DataSource
* that acts as "connectionFactory" of the PersistenceManagerFactory, so you usually
* don't need to explicitly specify the "dataSource" property.
*
* <p>On JDBC 3.0, this transaction manager supports nested transactions via JDBC 3.0
* Savepoints. The {@link #setNestedTransactionAllowed} "nestedTransactionAllowed"}
* flag defaults to "false", though, as nested transactions will just apply to the
* JDBC Connection, not to the JDO PersistenceManager and its cached objects.
* You can manually set the flag to "true" if you want to use nested transactions
* for JDBC access code which participates in JDO transactions (provided that your
* JDBC driver supports Savepoints). <i>Note that JDO itself does not support
* nested transactions! Hence, do not expect JDO access code to semantically
* participate in a nested transaction.</i>
*
* @author Juergen Hoeller
* @since 03.06.2003
* @see #setPersistenceManagerFactory
* @see #setDataSource
* @see javax.jdo.PersistenceManagerFactory#getConnectionFactory
* @see LocalPersistenceManagerFactoryBean
* @see PersistenceManagerFactoryUtils#getPersistenceManager
* @see PersistenceManagerFactoryUtils#releasePersistenceManager
* @see JdoTemplate
* @see TransactionAwarePersistenceManagerFactoryProxy
* @see org.springframework.jdbc.datasource.DataSourceUtils#getConnection
* @see org.springframework.jdbc.datasource.DataSourceUtils#releaseConnection
* @see org.springframework.jdbc.core.JdbcTemplate
* @see org.springframework.jdbc.datasource.DataSourceTransactionManager
* @see org.springframework.transaction.jta.JtaTransactionManager
*/
public class JdoTransactionManager extends AbstractPlatformTransactionManager
implements ResourceTransactionManager, InitializingBean {
private PersistenceManagerFactory persistenceManagerFactory;
private DataSource dataSource;
private boolean autodetectDataSource = true;
private JdoDialect jdoDialect;
/**
* Create a new JdoTransactionManager instance.
* A PersistenceManagerFactory has to be set to be able to use it.
* @see #setPersistenceManagerFactory
*/
public JdoTransactionManager() {
}
/**
* Create a new JdoTransactionManager instance.
* @param pmf PersistenceManagerFactory to manage transactions for
*/
public JdoTransactionManager(PersistenceManagerFactory pmf) {
this.persistenceManagerFactory = pmf;
afterPropertiesSet();
}
/**
* Set the PersistenceManagerFactory that this instance should manage transactions for.
* <p>The PersistenceManagerFactory specified here should be the target
* PersistenceManagerFactory to manage transactions for, not a
* TransactionAwarePersistenceManagerFactoryProxy. Only data access
* code may work with TransactionAwarePersistenceManagerFactoryProxy, while the
* transaction manager needs to work on the underlying target PersistenceManagerFactory.
* @see TransactionAwarePersistenceManagerFactoryProxy
*/
public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) {
this.persistenceManagerFactory = pmf;
}
/**
* Return the PersistenceManagerFactory that this instance should manage transactions for.
*/
public PersistenceManagerFactory getPersistenceManagerFactory() {
return this.persistenceManagerFactory;
}
/**
* Set the JDBC DataSource that this instance should manage transactions for.
* The DataSource should match the one used by the JDO PersistenceManagerFactory:
* for example, you could specify the same JNDI DataSource for both.
* <p>If the PersistenceManagerFactory uses a DataSource as connection factory,
* the DataSource will be autodetected: You can still explictly specify the
* DataSource, but you don't need to in this case.
* <p>A transactional JDBC Connection for this DataSource will be provided to
* application code accessing this DataSource directly via DataSourceUtils
* or JdbcTemplate. The Connection will be taken from the JDO PersistenceManager.
* <p>Note that you need to use a JDO dialect for a specific JDO provider to
* allow for exposing JDO transactions as JDBC transactions.
* <p>The DataSource specified here should be the target DataSource to manage
* transactions for, not a TransactionAwareDataSourceProxy. Only data access
* code may work with TransactionAwareDataSourceProxy, while the transaction
* manager needs to work on the underlying target DataSource. If there's
* nevertheless a TransactionAwareDataSourceProxy passed in, it will be
* unwrapped to extract its target DataSource.
* @see #setAutodetectDataSource
* @see #setJdoDialect
* @see javax.jdo.PersistenceManagerFactory#getConnectionFactory
* @see org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy
* @see org.springframework.jdbc.datasource.DataSourceUtils
* @see org.springframework.jdbc.core.JdbcTemplate
*/
public void setDataSource(DataSource dataSource) {
if (dataSource instanceof TransactionAwareDataSourceProxy) {
// If we got a TransactionAwareDataSourceProxy, we need to perform transactions
// for its underlying target DataSource, else data access code won't see
// properly exposed transactions (i.e. transactions for the target DataSource).
this.dataSource = ((TransactionAwareDataSourceProxy) dataSource).getTargetDataSource();
}
else {
this.dataSource = dataSource;
}
}
/**
* Return the JDBC DataSource that this instance manages transactions for.
*/
public DataSource getDataSource() {
return this.dataSource;
}
/**
* Set whether to autodetect a JDBC DataSource used by the JDO PersistenceManagerFactory,
* as returned by the <code>getConnectionFactory()</code> method. Default is "true".
* <p>Can be turned off to deliberately ignore an available DataSource,
* to not expose JDO transactions as JDBC transactions for that DataSource.
* @see #setDataSource
* @see javax.jdo.PersistenceManagerFactory#getConnectionFactory
*/
public void setAutodetectDataSource(boolean autodetectDataSource) {
this.autodetectDataSource = autodetectDataSource;
}
/**
* Set the JDO dialect to use for this transaction manager.
* <p>The dialect object can be used to retrieve the underlying JDBC connection
* and thus allows for exposing JDO transactions as JDBC transactions.
* @see JdoDialect#getJdbcConnection
*/
public void setJdoDialect(JdoDialect jdoDialect) {
this.jdoDialect = jdoDialect;
}
/**
* Return the JDO dialect to use for this transaction manager.
* <p>Creates a default one for the specified PersistenceManagerFactory if none set.
*/
public JdoDialect getJdoDialect() {
if (this.jdoDialect == null) {
this.jdoDialect = new DefaultJdoDialect();
}
return this.jdoDialect;
}
/**
* Eagerly initialize the JDO dialect, creating a default one
* for the specified PersistenceManagerFactory if none set.
* Auto-detect the PersistenceManagerFactory's DataSource, if any.
*/
public void afterPropertiesSet() {
if (getPersistenceManagerFactory() == null) {
throw new IllegalArgumentException("Property 'persistenceManagerFactory' is required");
}
// Build default JdoDialect if none explicitly specified.
if (this.jdoDialect == null) {
this.jdoDialect = new DefaultJdoDialect(getPersistenceManagerFactory().getConnectionFactory());
}
// Check for DataSource as connection factory.
if (this.autodetectDataSource && getDataSource() == null) {
Object pmfcf = getPersistenceManagerFactory().getConnectionFactory();
if (pmfcf instanceof DataSource) {
// Use the PersistenceManagerFactory's DataSource for exposing transactions to JDBC code.
this.dataSource = (DataSource) pmfcf;
if (logger.isInfoEnabled()) {
logger.info("Using DataSource [" + this.dataSource +
"] of JDO PersistenceManagerFactory for JdoTransactionManager");
}
}
}
}
public Object getResourceFactory() {
return getPersistenceManagerFactory();
}
@Override
protected Object doGetTransaction() {
JdoTransactionObject txObject = new JdoTransactionObject();
txObject.setSavepointAllowed(isNestedTransactionAllowed());
PersistenceManagerHolder pmHolder = (PersistenceManagerHolder)
TransactionSynchronizationManager.getResource(getPersistenceManagerFactory());
if (pmHolder != null) {
if (logger.isDebugEnabled()) {
logger.debug("Found thread-bound PersistenceManager [" +
pmHolder.getPersistenceManager() + "] for JDO transaction");
}
txObject.setPersistenceManagerHolder(pmHolder, false);
}
if (getDataSource() != null) {
ConnectionHolder conHolder = (ConnectionHolder)
TransactionSynchronizationManager.getResource(getDataSource());
txObject.setConnectionHolder(conHolder);
}
return txObject;
}
@Override
protected boolean isExistingTransaction(Object transaction) {
return ((JdoTransactionObject) transaction).hasTransaction();
}
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
JdoTransactionObject txObject = (JdoTransactionObject) transaction;
if (txObject.hasConnectionHolder() && !txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
throw new IllegalTransactionStateException(
"Pre-bound JDBC Connection found! JdoTransactionManager does not support " +
"running within DataSourceTransactionManager if told to manage the DataSource itself. " +
"It is recommended to use a single JdoTransactionManager for all transactions " +
"on a single DataSource, no matter whether JDO or JDBC access.");
}
PersistenceManager pm;
try {
if (txObject.getPersistenceManagerHolder() == null ||
txObject.getPersistenceManagerHolder().isSynchronizedWithTransaction()) {
PersistenceManager newPm = getPersistenceManagerFactory().getPersistenceManager();
if (logger.isDebugEnabled()) {
logger.debug("Opened new PersistenceManager [" + newPm + "] for JDO transaction");
}
txObject.setPersistenceManagerHolder(new PersistenceManagerHolder(newPm), true);
}
pm = txObject.getPersistenceManagerHolder().getPersistenceManager();
// Delegate to JdoDialect for actual transaction begin.
final int timeoutToUse = determineTimeout(definition);
Object transactionData = getJdoDialect().beginTransaction(pm.currentTransaction(),
new DelegatingTransactionDefinition(definition) {
@Override
public int getTimeout() {
return timeoutToUse;
}
});
txObject.setTransactionData(transactionData);
// Register transaction timeout.
if (timeoutToUse != TransactionDefinition.TIMEOUT_DEFAULT) {
txObject.getPersistenceManagerHolder().setTimeoutInSeconds(timeoutToUse);
}
// Register the JDO PersistenceManager's JDBC Connection for the DataSource, if set.
if (getDataSource() != null) {
ConnectionHandle conHandle = getJdoDialect().getJdbcConnection(pm, definition.isReadOnly());
if (conHandle != null) {
ConnectionHolder conHolder = new ConnectionHolder(conHandle);
if (timeoutToUse != TransactionDefinition.TIMEOUT_DEFAULT) {
conHolder.setTimeoutInSeconds(timeoutToUse);
}
if (logger.isDebugEnabled()) {
logger.debug("Exposing JDO transaction as JDBC transaction [" + conHolder.getConnectionHandle() + "]");
}
TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);
txObject.setConnectionHolder(conHolder);
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Not exposing JDO transaction [" + pm + "] as JDBC transaction because JdoDialect [" +
getJdoDialect() + "] does not support JDBC Connection retrieval");
}
}
}
// Bind the persistence manager holder to the thread.
if (txObject.isNewPersistenceManagerHolder()) {
TransactionSynchronizationManager.bindResource(
getPersistenceManagerFactory(), txObject.getPersistenceManagerHolder());
}
txObject.getPersistenceManagerHolder().setSynchronizedWithTransaction(true);
}
catch (TransactionException ex) {
closePersistenceManagerAfterFailedBegin(txObject);
throw ex;
}
catch (Exception ex) {
closePersistenceManagerAfterFailedBegin(txObject);
throw new CannotCreateTransactionException("Could not open JDO PersistenceManager for transaction", ex);
}
}
/**
* Close the current transaction's EntityManager.
* Called after a transaction begin attempt failed.
* @param txObject the current transaction
*/
protected void closePersistenceManagerAfterFailedBegin(JdoTransactionObject txObject) {
if (txObject.isNewPersistenceManagerHolder()) {
PersistenceManager pm = txObject.getPersistenceManagerHolder().getPersistenceManager();
try {
if (pm.currentTransaction().isActive()) {
pm.currentTransaction().rollback();
}
}
catch (Throwable ex) {
logger.debug("Could not rollback PersistenceManager after failed transaction begin", ex);
}
finally {
PersistenceManagerFactoryUtils.releasePersistenceManager(pm, getPersistenceManagerFactory());
}
}
}
@Override
protected Object doSuspend(Object transaction) {
JdoTransactionObject txObject = (JdoTransactionObject) transaction;
txObject.setPersistenceManagerHolder(null, false);
PersistenceManagerHolder persistenceManagerHolder = (PersistenceManagerHolder)
TransactionSynchronizationManager.unbindResource(getPersistenceManagerFactory());
txObject.setConnectionHolder(null);
ConnectionHolder connectionHolder = null;
if (getDataSource() != null && TransactionSynchronizationManager.hasResource(getDataSource())) {
connectionHolder = (ConnectionHolder) TransactionSynchronizationManager.unbindResource(getDataSource());
}
return new SuspendedResourcesHolder(persistenceManagerHolder, connectionHolder);
}
@Override
protected void doResume(Object transaction, Object suspendedResources) {
SuspendedResourcesHolder resourcesHolder = (SuspendedResourcesHolder) suspendedResources;
TransactionSynchronizationManager.bindResource(
getPersistenceManagerFactory(), resourcesHolder.getPersistenceManagerHolder());
if (getDataSource() != null && resourcesHolder.getConnectionHolder() != null) {
TransactionSynchronizationManager.bindResource(getDataSource(), resourcesHolder.getConnectionHolder());
}
}
/**
* This implementation returns "true": a JDO2 commit will properly handle
* transactions that have been marked rollback-only at a global level.
*/
@Override
protected boolean shouldCommitOnGlobalRollbackOnly() {
return true;
}
@Override
protected void doCommit(DefaultTransactionStatus status) {
JdoTransactionObject txObject = (JdoTransactionObject) status.getTransaction();
if (status.isDebug()) {
logger.debug("Committing JDO transaction on PersistenceManager [" +
txObject.getPersistenceManagerHolder().getPersistenceManager() + "]");
}
try {
Transaction tx = txObject.getPersistenceManagerHolder().getPersistenceManager().currentTransaction();
tx.commit();
}
catch (JDOException ex) {
// Assumably failed to flush changes to database.
throw convertJdoAccessException(ex);
}
}
@Override
protected void doRollback(DefaultTransactionStatus status) {
JdoTransactionObject txObject = (JdoTransactionObject) status.getTransaction();
if (status.isDebug()) {
logger.debug("Rolling back JDO transaction on PersistenceManager [" +
txObject.getPersistenceManagerHolder().getPersistenceManager() + "]");
}
try {
Transaction tx = txObject.getPersistenceManagerHolder().getPersistenceManager().currentTransaction();
if (tx.isActive()) {
tx.rollback();
}
}
catch (JDOException ex) {
throw new TransactionSystemException("Could not roll back JDO transaction", ex);
}
}
@Override
protected void doSetRollbackOnly(DefaultTransactionStatus status) {
JdoTransactionObject txObject = (JdoTransactionObject) status.getTransaction();
if (status.isDebug()) {
logger.debug("Setting JDO transaction on PersistenceManager [" +
txObject.getPersistenceManagerHolder().getPersistenceManager() + "] rollback-only");
}
txObject.setRollbackOnly();
}
@Override
protected void doCleanupAfterCompletion(Object transaction) {
JdoTransactionObject txObject = (JdoTransactionObject) transaction;
// Remove the persistence manager holder from the thread.
if (txObject.isNewPersistenceManagerHolder()) {
TransactionSynchronizationManager.unbindResource(getPersistenceManagerFactory());
}
txObject.getPersistenceManagerHolder().clear();
// Remove the JDBC connection holder from the thread, if exposed.
if (txObject.hasConnectionHolder()) {
TransactionSynchronizationManager.unbindResource(getDataSource());
try {
getJdoDialect().releaseJdbcConnection(txObject.getConnectionHolder().getConnectionHandle(),
txObject.getPersistenceManagerHolder().getPersistenceManager());
}
catch (Throwable ex) {
// Just log it, to keep a transaction-related exception.
logger.debug("Could not release JDBC connection after transaction", ex);
}
}
getJdoDialect().cleanupTransaction(txObject.getTransactionData());
if (txObject.isNewPersistenceManagerHolder()) {
PersistenceManager pm = txObject.getPersistenceManagerHolder().getPersistenceManager();
if (logger.isDebugEnabled()) {
logger.debug("Closing JDO PersistenceManager [" + pm + "] after transaction");
}
PersistenceManagerFactoryUtils.releasePersistenceManager(pm, getPersistenceManagerFactory());
}
else {
logger.debug("Not closing pre-bound JDO PersistenceManager after transaction");
}
}
/**
* Convert the given JDOException to an appropriate exception from the
* <code>org.springframework.dao</code> hierarchy.
* <p>The default implementation delegates to the JdoDialect.
* May be overridden in subclasses.
* @param ex JDOException that occured
* @return the corresponding DataAccessException instance
* @see JdoDialect#translateException
*/
protected DataAccessException convertJdoAccessException(JDOException ex) {
return getJdoDialect().translateException(ex);
}
/**
* JDO transaction object, representing a PersistenceManagerHolder.
* Used as transaction object by JdoTransactionManager.
*/
private class JdoTransactionObject extends JdbcTransactionObjectSupport {
private PersistenceManagerHolder persistenceManagerHolder;
private boolean newPersistenceManagerHolder;
private Object transactionData;
public void setPersistenceManagerHolder(
PersistenceManagerHolder persistenceManagerHolder, boolean newPersistenceManagerHolder) {
this.persistenceManagerHolder = persistenceManagerHolder;
this.newPersistenceManagerHolder = newPersistenceManagerHolder;
}
public PersistenceManagerHolder getPersistenceManagerHolder() {
return this.persistenceManagerHolder;
}
public boolean isNewPersistenceManagerHolder() {
return this.newPersistenceManagerHolder;
}
public boolean hasTransaction() {
return (this.persistenceManagerHolder != null && this.persistenceManagerHolder.isTransactionActive());
}
public void setTransactionData(Object transactionData) {
this.transactionData = transactionData;
this.persistenceManagerHolder.setTransactionActive(true);
}
public Object getTransactionData() {
return this.transactionData;
}
public void setRollbackOnly() {
Transaction tx = this.persistenceManagerHolder.getPersistenceManager().currentTransaction();
if (tx.isActive()) {
tx.setRollbackOnly();
}
if (hasConnectionHolder()) {
getConnectionHolder().setRollbackOnly();
}
}
public boolean isRollbackOnly() {
Transaction tx = this.persistenceManagerHolder.getPersistenceManager().currentTransaction();
return tx.getRollbackOnly();
}
public void flush() {
try {
this.persistenceManagerHolder.getPersistenceManager().flush();
}
catch (JDOException ex) {
throw convertJdoAccessException(ex);
}
}
}
/**
* Holder for suspended resources.
* Used internally by <code>doSuspend</code> and <code>doResume</code>.
*/
private static class SuspendedResourcesHolder {
private final PersistenceManagerHolder persistenceManagerHolder;
private final ConnectionHolder connectionHolder;
private SuspendedResourcesHolder(PersistenceManagerHolder pmHolder, ConnectionHolder conHolder) {
this.persistenceManagerHolder = pmHolder;
this.connectionHolder = conHolder;
}
private PersistenceManagerHolder getPersistenceManagerHolder() {
return this.persistenceManagerHolder;
}
private ConnectionHolder getConnectionHolder() {
return this.connectionHolder;
}
}
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2002-2005 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jdo;
import javax.jdo.JDOFatalUserException;
import javax.jdo.JDOUserException;
import org.springframework.dao.InvalidDataAccessApiUsageException;
/**
* JDO-specific subclass of InvalidDataAccessApiUsageException.
* Converts JDO's JDOUserException and JDOFatalUserException.
*
* @author Juergen Hoeller
* @since 03.06.2003
* @see PersistenceManagerFactoryUtils#convertJdoAccessException
*/
public class JdoUsageException extends InvalidDataAccessApiUsageException {
public JdoUsageException(JDOUserException ex) {
super(ex.getMessage(), ex);
}
public JdoUsageException(JDOFatalUserException ex) {
super(ex.getMessage(), ex);
}
}

View File

@@ -0,0 +1,322 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jdo;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.jdo.JDOException;
import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.util.CollectionUtils;
/**
* {@link org.springframework.beans.factory.FactoryBean} that creates a
* JDO {@link javax.jdo.PersistenceManagerFactory}. This is the usual way to
* set up a shared JDO PersistenceManagerFactory in a Spring application context;
* the PersistenceManagerFactory can then be passed to JDO-based DAOs via
* dependency injection. Note that switching to a JNDI lookup or to a bean-style
* PersistenceManagerFactory instance is just a matter of configuration!
*
* <p><b>NOTE: This class requires JDO 2.0 or higher, as of Spring 2.5.</b>
* Since JDO 2.1, it will also expose the JPA {@link javax.persistence.EntityManagerFactory}
* as long as the JDO provider creates a {@link javax.jdo.JDOEntityManagerFactory} reference
* underneath, which means that this class can be used as a replacement for
* {@link org.springframework.orm.jpa.LocalEntityManagerFactoryBean} in such a scenario.
*
* <p>Configuration settings can either be read from a properties file,
* specified as "configLocation", or locally specified. Properties
* specified as "jdoProperties" here will override any settings in a file.
* On JDO 2.1, you may alternatively specify a "persistenceManagerFactoryName",
* referring to a PMF definition in "META-INF/jdoconfig.xml"
* (see {@link #setPersistenceManagerFactoryName}).
*
* <p>This class also implements the
* {@link org.springframework.dao.support.PersistenceExceptionTranslator}
* interface, as autodetected by Spring's
* {@link org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor},
* for AOP-based translation of native exceptions to Spring DataAccessExceptions.
* Hence, the presence of a LocalPersistenceManagerFactoryBean automatically enables
* a PersistenceExceptionTranslationPostProcessor to translate JDO exceptions.
*
* <p><b>Alternative: Configuration of a PersistenceManagerFactory provider bean</b>
*
* <p>As alternative to the properties-driven approach that this FactoryBean offers
* (which is analogous to using the standard JDOHelper class with a Properties
* object that is populated with standard JDO properties), you can set up an
* instance of your PersistenceManagerFactory implementation class directly.
*
* <p>Like a DataSource, a PersistenceManagerFactory is encouraged to
* support bean-style configuration, which makes it very easy to set up as
* Spring-managed bean. The implementation class becomes the bean class;
* the remaining properties are applied as bean properties (starting with
* lower-case characters, in contrast to the corresponding JDO properties).
*
* <p>For example, in case of <a href="http://www.jpox.org">JPOX</a>:
*
* <p><pre>
* &lt;bean id="persistenceManagerFactory" class="org.jpox.PersistenceManagerFactoryImpl" destroy-method="close"&gt;
* &lt;property name="connectionFactory" ref="dataSource"/&gt;
* &lt;property name="nontransactionalRead" value="true"/&gt;
* &lt;/bean&gt;
* </pre>
*
* <p>Note that such direct setup of a PersistenceManagerFactory implementation
* is the only way to pass an external connection factory (i.e. a JDBC DataSource)
* into a JDO PersistenceManagerFactory. With the standard properties-driven approach,
* you can only use an internal connection pool or a JNDI DataSource.
*
* <p>The <code>close()</code> method is standardized in JDO; don't forget to
* specify it as "destroy-method" for any PersistenceManagerFactory instance.
* Note that this FactoryBean will automatically invoke <code>close()</code> for
* the PersistenceManagerFactory that it creates, without any special configuration.
*
* @author Juergen Hoeller
* @since 03.06.2003
* @see JdoTemplate#setPersistenceManagerFactory
* @see JdoTransactionManager#setPersistenceManagerFactory
* @see org.springframework.jndi.JndiObjectFactoryBean
* @see javax.jdo.JDOHelper#getPersistenceManagerFactory
* @see javax.jdo.PersistenceManagerFactory#setConnectionFactory
* @see javax.jdo.PersistenceManagerFactory#close()
* @see org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor
*/
public class LocalPersistenceManagerFactoryBean implements FactoryBean<PersistenceManagerFactory>,
BeanClassLoaderAware, InitializingBean, DisposableBean, PersistenceExceptionTranslator {
protected final Log logger = LogFactory.getLog(getClass());
private String persistenceManagerFactoryName;
private Resource configLocation;
private final Map<String, Object> jdoPropertyMap = new HashMap<String, Object>();
private ClassLoader beanClassLoader;
private PersistenceManagerFactory persistenceManagerFactory;
private JdoDialect jdoDialect;
/**
* Specify the name of the desired PersistenceManagerFactory.
* <p>This may either be a properties resource in the classpath if such a resource exists
* (JDO 2.0), or a PMF definition with that name from "META-INF/jdoconfig.xml" (JDO 2.1),
* or a JPA EntityManagerFactory cast to a PersistenceManagerFactory based on the
* persistence-unit name from "META-INF/persistence.xml" (JDO 2.1 / JPA 1.0).
* <p>Default is none: Either 'persistenceManagerFactoryName' or 'configLocation'
* or 'jdoProperties' needs to be specified.
* @see #setConfigLocation
* @see #setJdoProperties
*/
public void setPersistenceManagerFactoryName(String persistenceManagerFactoryName) {
this.persistenceManagerFactoryName = persistenceManagerFactoryName;
}
/**
* Set the location of the JDO properties config file, for example
* as classpath resource "classpath:kodo.properties".
* <p>Note: Can be omitted when all necessary properties are
* specified locally via this bean.
*/
public void setConfigLocation(Resource configLocation) {
this.configLocation = configLocation;
}
/**
* Set JDO properties, such as"javax.jdo.PersistenceManagerFactoryClass".
* <p>Can be used to override values in a JDO properties config file,
* or to specify all necessary properties locally.
* <p>Can be populated with a String "value" (parsed via PropertiesEditor)
* or a "props" element in XML bean definitions.
*/
public void setJdoProperties(Properties jdoProperties) {
CollectionUtils.mergePropertiesIntoMap(jdoProperties, this.jdoPropertyMap);
}
/**
* Specify JDO properties as a Map, to be passed into
* <code>JDOHelper.getPersistenceManagerFactory</code> (if any).
* <p>Can be populated with a "map" or "props" element in XML bean definitions.
* @see javax.jdo.JDOHelper#getPersistenceManagerFactory(java.util.Map)
*/
public void setJdoPropertyMap(Map<String, Object> jdoProperties) {
if (jdoProperties != null) {
this.jdoPropertyMap.putAll(jdoProperties);
}
}
/**
* Allow Map access to the JDO properties to be passed to the JDOHelper,
* with the option to add or override specific entries.
* <p>Useful for specifying entries directly, for example via
* "jdoPropertyMap[myKey]".
*/
public Map<String, Object> getJdoPropertyMap() {
return this.jdoPropertyMap;
}
/**
* Set the JDO dialect to use for the PersistenceExceptionTranslator
* functionality of this factory.
* <p>Default is a DefaultJdoDialect based on the PersistenceManagerFactory's
* underlying DataSource, if any.
* @see JdoDialect#translateException
* @see #translateExceptionIfPossible
* @see org.springframework.dao.support.PersistenceExceptionTranslator
*/
public void setJdoDialect(JdoDialect jdoDialect) {
this.jdoDialect = jdoDialect;
}
public void setBeanClassLoader(ClassLoader beanClassLoader) {
this.beanClassLoader = beanClassLoader;
}
/**
* Initialize the PersistenceManagerFactory for the given location.
* @throws IllegalArgumentException in case of illegal property values
* @throws IOException if the properties could not be loaded from the given location
* @throws JDOException in case of JDO initialization errors
*/
public void afterPropertiesSet() throws IllegalArgumentException, IOException, JDOException {
if (this.persistenceManagerFactoryName != null) {
if (this.configLocation != null || !this.jdoPropertyMap.isEmpty()) {
throw new IllegalStateException("'configLocation'/'jdoProperties' not supported in " +
"combination with 'persistenceManagerFactoryName' - specify one or the other, not both");
}
if (logger.isInfoEnabled()) {
logger.info("Building new JDO PersistenceManagerFactory for name '" +
this.persistenceManagerFactoryName + "'");
}
this.persistenceManagerFactory = newPersistenceManagerFactory(this.persistenceManagerFactoryName);
}
else {
Map<String, Object> mergedProps = new HashMap<String, Object>();
if (this.configLocation != null) {
if (logger.isInfoEnabled()) {
logger.info("Loading JDO config from [" + this.configLocation + "]");
}
CollectionUtils.mergePropertiesIntoMap(
PropertiesLoaderUtils.loadProperties(this.configLocation), mergedProps);
}
mergedProps.putAll(this.jdoPropertyMap);
logger.info("Building new JDO PersistenceManagerFactory");
this.persistenceManagerFactory = newPersistenceManagerFactory(mergedProps);
}
// Build default JdoDialect if none explicitly specified.
if (this.jdoDialect == null) {
this.jdoDialect = new DefaultJdoDialect(this.persistenceManagerFactory.getConnectionFactory());
}
}
/**
* Subclasses can override this to perform custom initialization of the
* PersistenceManagerFactory instance, creating it for the specified name.
* <p>The default implementation invokes JDOHelper's
* <code>getPersistenceManagerFactory(String)</code> method.
* A custom implementation could prepare the instance in a specific way,
* or use a custom PersistenceManagerFactory implementation.
* @param name the name of the desired PersistenceManagerFactory
* @return the PersistenceManagerFactory instance
* @see javax.jdo.JDOHelper#getPersistenceManagerFactory(String)
*/
protected PersistenceManagerFactory newPersistenceManagerFactory(String name) {
return JDOHelper.getPersistenceManagerFactory(name, this.beanClassLoader);
}
/**
* Subclasses can override this to perform custom initialization of the
* PersistenceManagerFactory instance, creating it via the given Properties
* that got prepared by this LocalPersistenceManagerFactoryBean.
* <p>The default implementation invokes JDOHelper's
* <code>getPersistenceManagerFactory(Map)</code> method.
* A custom implementation could prepare the instance in a specific way,
* or use a custom PersistenceManagerFactory implementation.
* @param props the merged properties prepared by this LocalPersistenceManagerFactoryBean
* @return the PersistenceManagerFactory instance
* @see javax.jdo.JDOHelper#getPersistenceManagerFactory(java.util.Map)
*/
protected PersistenceManagerFactory newPersistenceManagerFactory(Map props) {
return JDOHelper.getPersistenceManagerFactory(props, this.beanClassLoader);
}
/**
* Return the singleton PersistenceManagerFactory.
*/
public PersistenceManagerFactory getObject() {
return this.persistenceManagerFactory;
}
public Class<? extends PersistenceManagerFactory> getObjectType() {
return (this.persistenceManagerFactory != null ?
this.persistenceManagerFactory.getClass() : PersistenceManagerFactory.class);
}
public boolean isSingleton() {
return true;
}
/**
* Implementation of the PersistenceExceptionTranslator interface,
* as autodetected by Spring's PersistenceExceptionTranslationPostProcessor.
* <p>Converts the exception if it is a JDOException, preferably using a specified
* JdoDialect. Else returns <code>null</code> to indicate an unknown exception.
* @see org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor
* @see JdoDialect#translateException
* @see PersistenceManagerFactoryUtils#convertJdoAccessException
*/
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
if (ex instanceof JDOException) {
if (this.jdoDialect != null) {
return this.jdoDialect.translateException((JDOException) ex);
}
else {
return PersistenceManagerFactoryUtils.convertJdoAccessException((JDOException) ex);
}
}
return null;
}
/**
* Close the PersistenceManagerFactory on bean factory shutdown.
*/
public void destroy() {
logger.info("Closing JDO PersistenceManagerFactory");
this.persistenceManagerFactory.close();
}
}

View File

@@ -0,0 +1,337 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jdo;
import javax.jdo.JDODataStoreException;
import javax.jdo.JDOException;
import javax.jdo.JDOFatalDataStoreException;
import javax.jdo.JDOFatalUserException;
import javax.jdo.JDOObjectNotFoundException;
import javax.jdo.JDOOptimisticVerificationException;
import javax.jdo.JDOUserException;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Query;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.core.Ordered;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.jdbc.datasource.DataSourceUtils;
import org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator;
import org.springframework.jdbc.support.SQLExceptionTranslator;
import org.springframework.jdbc.support.SQLStateSQLExceptionTranslator;
import org.springframework.transaction.support.ResourceHolderSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
/**
* Helper class featuring methods for JDO PersistenceManager handling,
* allowing for reuse of PersistenceManager instances within transactions.
* Also provides support for exception translation.
*
* <p>Used internally by {@link JdoTemplate}, {@link JdoInterceptor} and
* {@link JdoTransactionManager}. Can also be used directly in application code.
*
* @author Juergen Hoeller
* @since 03.06.2003
* @see JdoTransactionManager
* @see org.springframework.transaction.jta.JtaTransactionManager
* @see org.springframework.transaction.support.TransactionSynchronizationManager
*/
public abstract class PersistenceManagerFactoryUtils {
/**
* Order value for TransactionSynchronization objects that clean up JDO
* PersistenceManagers. Return DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 100
* to execute PersistenceManager cleanup before JDBC Connection cleanup, if any.
* @see org.springframework.jdbc.datasource.DataSourceUtils#CONNECTION_SYNCHRONIZATION_ORDER
*/
public static final int PERSISTENCE_MANAGER_SYNCHRONIZATION_ORDER =
DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 100;
private static final Log logger = LogFactory.getLog(PersistenceManagerFactoryUtils.class);
/**
* Create an appropriate SQLExceptionTranslator for the given PersistenceManagerFactory.
* <p>If a DataSource is found, creates a SQLErrorCodeSQLExceptionTranslator for the
* DataSource; else, falls back to a SQLStateSQLExceptionTranslator.
* @param connectionFactory the connection factory of the PersistenceManagerFactory
* (may be <code>null</code>)
* @return the SQLExceptionTranslator (never <code>null</code>)
* @see javax.jdo.PersistenceManagerFactory#getConnectionFactory()
* @see org.springframework.jdbc.support.SQLErrorCodeSQLExceptionTranslator
* @see org.springframework.jdbc.support.SQLStateSQLExceptionTranslator
*/
static SQLExceptionTranslator newJdbcExceptionTranslator(Object connectionFactory) {
// Check for PersistenceManagerFactory's DataSource.
if (connectionFactory instanceof DataSource) {
return new SQLErrorCodeSQLExceptionTranslator((DataSource) connectionFactory);
}
else {
return new SQLStateSQLExceptionTranslator();
}
}
/**
* Obtain a JDO PersistenceManager via the given factory. Is aware of a
* corresponding PersistenceManager bound to the current thread,
* for example when using JdoTransactionManager. Will create a new
* PersistenceManager else, if "allowCreate" is <code>true</code>.
* @param pmf PersistenceManagerFactory to create the PersistenceManager with
* @param allowCreate if a non-transactional PersistenceManager should be created
* when no transactional PersistenceManager can be found for the current thread
* @return the PersistenceManager
* @throws DataAccessResourceFailureException if the PersistenceManager couldn't be obtained
* @throws IllegalStateException if no thread-bound PersistenceManager found and
* "allowCreate" is <code>false</code>
* @see JdoTransactionManager
*/
public static PersistenceManager getPersistenceManager(PersistenceManagerFactory pmf, boolean allowCreate)
throws DataAccessResourceFailureException, IllegalStateException {
try {
return doGetPersistenceManager(pmf, allowCreate);
}
catch (JDOException ex) {
throw new DataAccessResourceFailureException("Could not obtain JDO PersistenceManager", ex);
}
}
/**
* Obtain a JDO PersistenceManager via the given factory. Is aware of a
* corresponding PersistenceManager bound to the current thread,
* for example when using JdoTransactionManager. Will create a new
* PersistenceManager else, if "allowCreate" is <code>true</code>.
* <p>Same as <code>getPersistenceManager</code>, but throwing the original JDOException.
* @param pmf PersistenceManagerFactory to create the PersistenceManager with
* @param allowCreate if a non-transactional PersistenceManager should be created
* when no transactional PersistenceManager can be found for the current thread
* @return the PersistenceManager
* @throws JDOException if the PersistenceManager couldn't be created
* @throws IllegalStateException if no thread-bound PersistenceManager found and
* "allowCreate" is <code>false</code>
* @see #getPersistenceManager(javax.jdo.PersistenceManagerFactory, boolean)
* @see JdoTransactionManager
*/
public static PersistenceManager doGetPersistenceManager(PersistenceManagerFactory pmf, boolean allowCreate)
throws JDOException, IllegalStateException {
Assert.notNull(pmf, "No PersistenceManagerFactory specified");
PersistenceManagerHolder pmHolder =
(PersistenceManagerHolder) TransactionSynchronizationManager.getResource(pmf);
if (pmHolder != null) {
if (!pmHolder.isSynchronizedWithTransaction() &&
TransactionSynchronizationManager.isSynchronizationActive()) {
pmHolder.setSynchronizedWithTransaction(true);
TransactionSynchronizationManager.registerSynchronization(
new PersistenceManagerSynchronization(pmHolder, pmf, false));
}
return pmHolder.getPersistenceManager();
}
if (!allowCreate && !TransactionSynchronizationManager.isSynchronizationActive()) {
throw new IllegalStateException("No JDO PersistenceManager bound to thread, " +
"and configuration does not allow creation of non-transactional one here");
}
logger.debug("Opening JDO PersistenceManager");
PersistenceManager pm = pmf.getPersistenceManager();
if (TransactionSynchronizationManager.isSynchronizationActive()) {
logger.debug("Registering transaction synchronization for JDO PersistenceManager");
// Use same PersistenceManager for further JDO actions within the transaction.
// Thread object will get removed by synchronization at transaction completion.
pmHolder = new PersistenceManagerHolder(pm);
pmHolder.setSynchronizedWithTransaction(true);
TransactionSynchronizationManager.registerSynchronization(
new PersistenceManagerSynchronization(pmHolder, pmf, true));
TransactionSynchronizationManager.bindResource(pmf, pmHolder);
}
return pm;
}
/**
* Return whether the given JDO PersistenceManager is transactional, that is,
* bound to the current thread by Spring's transaction facilities.
* @param pm the JDO PersistenceManager to check
* @param pmf JDO PersistenceManagerFactory that the PersistenceManager
* was created with (can be <code>null</code>)
* @return whether the PersistenceManager is transactional
*/
public static boolean isPersistenceManagerTransactional(
PersistenceManager pm, PersistenceManagerFactory pmf) {
if (pmf == null) {
return false;
}
PersistenceManagerHolder pmHolder =
(PersistenceManagerHolder) TransactionSynchronizationManager.getResource(pmf);
return (pmHolder != null && pm == pmHolder.getPersistenceManager());
}
/**
* Apply the current transaction timeout, if any, to the given JDO Query object.
* @param query the JDO Query object
* @param pmf JDO PersistenceManagerFactory that the Query was created for
* @param jdoDialect the JdoDialect to use for applying a query timeout
* (must not be <code>null</code>)
* @throws JDOException if thrown by JDO methods
* @see JdoDialect#applyQueryTimeout
*/
public static void applyTransactionTimeout(
Query query, PersistenceManagerFactory pmf, JdoDialect jdoDialect) throws JDOException {
Assert.notNull(query, "No Query object specified");
PersistenceManagerHolder pmHolder =
(PersistenceManagerHolder) TransactionSynchronizationManager.getResource(pmf);
if (pmHolder != null && pmHolder.hasTimeout()) {
jdoDialect.applyQueryTimeout(query, pmHolder.getTimeToLiveInSeconds());
}
}
/**
* Convert the given JDOException to an appropriate exception from the
* <code>org.springframework.dao</code> hierarchy.
* <p>The most important cases like object not found or optimistic locking
* failure are covered here. For more fine-granular conversion, JdoAccessor and
* JdoTransactionManager support sophisticated translation of exceptions via a
* JdoDialect.
* @param ex JDOException that occured
* @return the corresponding DataAccessException instance
* @see JdoAccessor#convertJdoAccessException
* @see JdoTransactionManager#convertJdoAccessException
* @see JdoDialect#translateException
*/
public static DataAccessException convertJdoAccessException(JDOException ex) {
if (ex instanceof JDOObjectNotFoundException) {
throw new JdoObjectRetrievalFailureException((JDOObjectNotFoundException) ex);
}
if (ex instanceof JDOOptimisticVerificationException) {
throw new JdoOptimisticLockingFailureException((JDOOptimisticVerificationException) ex);
}
if (ex instanceof JDODataStoreException) {
return new JdoResourceFailureException((JDODataStoreException) ex);
}
if (ex instanceof JDOFatalDataStoreException) {
return new JdoResourceFailureException((JDOFatalDataStoreException) ex);
}
if (ex instanceof JDOUserException) {
return new JdoUsageException((JDOUserException) ex);
}
if (ex instanceof JDOFatalUserException) {
return new JdoUsageException((JDOFatalUserException) ex);
}
// fallback
return new JdoSystemException(ex);
}
/**
* Close the given PersistenceManager, created via the given factory,
* if it is not managed externally (i.e. not bound to the thread).
* @param pm PersistenceManager to close
* @param pmf PersistenceManagerFactory that the PersistenceManager was created with
* (can be <code>null</code>)
*/
public static void releasePersistenceManager(PersistenceManager pm, PersistenceManagerFactory pmf) {
try {
doReleasePersistenceManager(pm, pmf);
}
catch (JDOException ex) {
logger.debug("Could not close JDO PersistenceManager", ex);
}
catch (Throwable ex) {
logger.debug("Unexpected exception on closing JDO PersistenceManager", ex);
}
}
/**
* Actually release a PersistenceManager for the given factory.
* Same as <code>releasePersistenceManager</code>, but throwing the original JDOException.
* @param pm PersistenceManager to close
* @param pmf PersistenceManagerFactory that the PersistenceManager was created with
* (can be <code>null</code>)
* @throws JDOException if thrown by JDO methods
*/
public static void doReleasePersistenceManager(PersistenceManager pm, PersistenceManagerFactory pmf)
throws JDOException {
if (pm == null) {
return;
}
// Only release non-transactional PersistenceManagers.
if (!isPersistenceManagerTransactional(pm, pmf)) {
logger.debug("Closing JDO PersistenceManager");
pm.close();
}
}
/**
* Callback for resource cleanup at the end of a non-JDO transaction
* (e.g. when participating in a JtaTransactionManager transaction).
* @see org.springframework.transaction.jta.JtaTransactionManager
*/
private static class PersistenceManagerSynchronization
extends ResourceHolderSynchronization<PersistenceManagerHolder, PersistenceManagerFactory>
implements Ordered {
private final boolean newPersistenceManager;
public PersistenceManagerSynchronization(
PersistenceManagerHolder pmHolder, PersistenceManagerFactory pmf, boolean newPersistenceManager) {
super(pmHolder, pmf);
this.newPersistenceManager = newPersistenceManager;
}
public int getOrder() {
return PERSISTENCE_MANAGER_SYNCHRONIZATION_ORDER;
}
@Override
public void flushResource(PersistenceManagerHolder resourceHolder) {
try {
resourceHolder.getPersistenceManager().flush();
}
catch (JDOException ex) {
throw convertJdoAccessException(ex);
}
}
@Override
protected boolean shouldUnbindAtCompletion() {
return this.newPersistenceManager;
}
@Override
protected boolean shouldReleaseAfterCompletion(PersistenceManagerHolder resourceHolder) {
return !resourceHolder.getPersistenceManager().isClosed();
}
@Override
protected void releaseResource(PersistenceManagerHolder resourceHolder, PersistenceManagerFactory resourceKey) {
releasePersistenceManager(resourceHolder.getPersistenceManager(), resourceKey);
}
}
}

View File

@@ -0,0 +1,67 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jdo;
import javax.jdo.PersistenceManager;
import org.springframework.transaction.support.ResourceHolderSupport;
import org.springframework.util.Assert;
/**
* Holder wrapping a JDO PersistenceManager.
* JdoTransactionManager binds instances of this class
* to the thread, for a given PersistenceManagerFactory.
*
* <p>Note: This is an SPI class, not intended to be used by applications.
*
* @author Juergen Hoeller
* @since 03.06.2003
* @see JdoTransactionManager
* @see PersistenceManagerFactoryUtils
*/
public class PersistenceManagerHolder extends ResourceHolderSupport {
private final PersistenceManager persistenceManager;
private boolean transactionActive;
public PersistenceManagerHolder(PersistenceManager persistenceManager) {
Assert.notNull(persistenceManager, "PersistenceManager must not be null");
this.persistenceManager = persistenceManager;
}
public PersistenceManager getPersistenceManager() {
return this.persistenceManager;
}
protected void setTransactionActive(boolean transactionActive) {
this.transactionActive = transactionActive;
}
protected boolean isTransactionActive() {
return this.transactionActive;
}
@Override
public void clear() {
super.clear();
this.transactionActive = false;
}
}

View File

@@ -0,0 +1,219 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jdo;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
/**
* Proxy for a target JDO {@link javax.jdo.PersistenceManagerFactory},
* returning the current thread-bound PersistenceManager (the Spring-managed
* transactional PersistenceManager or the single OpenPersistenceManagerInView
* PersistenceManager) on <code>getPersistenceManager()</code>, if any.
*
* <p>Essentially, <code>getPersistenceManager()</code> calls get seamlessly
* forwarded to {@link PersistenceManagerFactoryUtils#getPersistenceManager}.
* Furthermore, <code>PersistenceManager.close</code> calls get forwarded to
* {@link PersistenceManagerFactoryUtils#releasePersistenceManager}.
*
* <p>The main advantage of this proxy is that it allows DAOs to work with a
* plain JDO PersistenceManagerFactory reference, while still participating in
* Spring's (or a J2EE server's) resource and transaction management. DAOs will
* only rely on the JDO API in such a scenario, without any Spring dependencies.
*
* <p>Note that the behavior of this proxy matches the behavior that the JDO spec
* defines for a PersistenceManagerFactory as exposed by a JCA connector, when
* deployed in a J2EE server. Hence, DAOs could seamlessly switch between a JNDI
* PersistenceManagerFactory and this proxy for a local PersistenceManagerFactory,
* receiving the reference through Dependency Injection. This will work without
* any Spring API dependencies in the DAO code!
*
* <p>It is usually preferable to write your JDO-based DAOs with Spring's
* {@link JdoTemplate}, offering benefits such as consistent data access
* exceptions instead of JDOExceptions at the DAO layer. However, Spring's
* resource and transaction management (and Dependency Injection) will work
* for DAOs written against the plain JDO API as well.
*
* <p>Of course, you can still access the target PersistenceManagerFactory
* even when your DAOs go through this proxy, by defining a bean reference
* that points directly at your target PersistenceManagerFactory bean.
*
* @author Juergen Hoeller
* @since 1.2
* @see javax.jdo.PersistenceManagerFactory#getPersistenceManager()
* @see javax.jdo.PersistenceManager#close()
* @see PersistenceManagerFactoryUtils#getPersistenceManager
* @see PersistenceManagerFactoryUtils#releasePersistenceManager
*/
public class TransactionAwarePersistenceManagerFactoryProxy implements FactoryBean<PersistenceManagerFactory> {
private PersistenceManagerFactory target;
private boolean allowCreate = true;
private PersistenceManagerFactory proxy;
/**
* Set the target JDO PersistenceManagerFactory that this proxy should
* delegate to. This should be the raw PersistenceManagerFactory, as
* accessed by JdoTransactionManager.
* @see org.springframework.orm.jdo.JdoTransactionManager
*/
public void setTargetPersistenceManagerFactory(PersistenceManagerFactory target) {
Assert.notNull(target, "Target PersistenceManagerFactory must not be null");
this.target = target;
Class[] ifcs = ClassUtils.getAllInterfacesForClass(target.getClass(), target.getClass().getClassLoader());
this.proxy = (PersistenceManagerFactory) Proxy.newProxyInstance(
target.getClass().getClassLoader(), ifcs, new PersistenceManagerFactoryInvocationHandler());
}
/**
* Return the target JDO PersistenceManagerFactory that this proxy delegates to.
*/
public PersistenceManagerFactory getTargetPersistenceManagerFactory() {
return this.target;
}
/**
* Set whether the PersistenceManagerFactory proxy is allowed to create
* a non-transactional PersistenceManager when no transactional
* PersistenceManager can be found for the current thread.
* <p>Default is "true". Can be turned off to enforce access to
* transactional PersistenceManagers, which safely allows for DAOs
* written to get a PersistenceManager without explicit closing
* (i.e. a <code>PersistenceManagerFactory.getPersistenceManager()</code>
* call without corresponding <code>PersistenceManager.close()</code> call).
* @see PersistenceManagerFactoryUtils#getPersistenceManager(javax.jdo.PersistenceManagerFactory, boolean)
*/
public void setAllowCreate(boolean allowCreate) {
this.allowCreate = allowCreate;
}
/**
* Return whether the PersistenceManagerFactory proxy is allowed to create
* a non-transactional PersistenceManager when no transactional
* PersistenceManager can be found for the current thread.
*/
protected boolean isAllowCreate() {
return this.allowCreate;
}
public PersistenceManagerFactory getObject() {
return this.proxy;
}
public Class<? extends PersistenceManagerFactory> getObjectType() {
return PersistenceManagerFactory.class;
}
public boolean isSingleton() {
return true;
}
/**
* Invocation handler that delegates getPersistenceManager calls on the
* PersistenceManagerFactory proxy to PersistenceManagerFactoryUtils
* for being aware of thread-bound transactions.
*/
private class PersistenceManagerFactoryInvocationHandler implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Invocation on PersistenceManagerFactory interface coming in...
if (method.getName().equals("equals")) {
// Only consider equal when proxies are identical.
return (proxy == args[0]);
}
else if (method.getName().equals("hashCode")) {
// Use hashCode of PersistenceManagerFactory proxy.
return System.identityHashCode(proxy);
}
else if (method.getName().equals("getPersistenceManager")) {
PersistenceManagerFactory target = getTargetPersistenceManagerFactory();
PersistenceManager pm =
PersistenceManagerFactoryUtils.doGetPersistenceManager(target, isAllowCreate());
Class[] ifcs = ClassUtils.getAllInterfacesForClass(pm.getClass(), pm.getClass().getClassLoader());
return Proxy.newProxyInstance(
pm.getClass().getClassLoader(), ifcs, new PersistenceManagerInvocationHandler(pm, target));
}
// Invoke method on target PersistenceManagerFactory.
try {
return method.invoke(getTargetPersistenceManagerFactory(), args);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
/**
* Invocation handler that delegates close calls on PersistenceManagers to
* PersistenceManagerFactoryUtils for being aware of thread-bound transactions.
*/
private static class PersistenceManagerInvocationHandler implements InvocationHandler {
private final PersistenceManager target;
private final PersistenceManagerFactory persistenceManagerFactory;
public PersistenceManagerInvocationHandler(PersistenceManager target, PersistenceManagerFactory pmf) {
this.target = target;
this.persistenceManagerFactory = pmf;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Invocation on PersistenceManager interface coming in...
if (method.getName().equals("equals")) {
// Only consider equal when proxies are identical.
return (proxy == args[0]);
}
else if (method.getName().equals("hashCode")) {
// Use hashCode of PersistenceManager proxy.
return System.identityHashCode(proxy);
}
else if (method.getName().equals("close")) {
// Handle close method: only close if not within a transaction.
PersistenceManagerFactoryUtils.doReleasePersistenceManager(
this.target, this.persistenceManagerFactory);
return null;
}
// Invoke method on target PersistenceManager.
try {
return method.invoke(this.target, args);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
}

View File

@@ -0,0 +1,10 @@
/**
*
* Package providing integration of JDO (Java Date Objects) with Spring concepts.
* Contains PersistenceManagerFactory helper classes, a template plus callback for JDO
* access, and an implementation of Spring's transaction SPI for local JDO transactions.
*
*/
package org.springframework.orm.jdo;

View File

@@ -0,0 +1,175 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jdo.support;
import javax.jdo.JDOException;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataAccessResourceFailureException;
import org.springframework.dao.support.DaoSupport;
import org.springframework.orm.jdo.JdoTemplate;
import org.springframework.orm.jdo.PersistenceManagerFactoryUtils;
/**
* Convenient super class for JDO data access objects.
*
* <p>Requires a PersistenceManagerFactory to be set, providing a JdoTemplate
* based on it to subclasses. Can alternatively be initialized directly with a
* JdoTemplate, to reuse the latter's settings such as the PersistenceManagerFactory,
* JdoDialect, flush mode, etc.
*
* <p>This base class is mainly intended for JdoTemplate usage but can also
* be used when working with PersistenceManagerFactoryUtils directly, for example
* in combination with JdoInterceptor-managed PersistenceManagers. Convenience
* <code>getPersistenceManager</code> and <code>releasePersistenceManager</code>
* methods are provided for that usage style.
*
* <p>This class will create its own JdoTemplate if only a PersistenceManagerFactory
* is passed in. The "allowCreate" flag on that JdoTemplate will be "true" by default.
* A custom JdoTemplate instance can be used through overriding <code>createJdoTemplate</code>.
*
* @author Juergen Hoeller
* @since 28.07.2003
* @see #setPersistenceManagerFactory
* @see #setJdoTemplate
* @see #createJdoTemplate
* @see #getPersistenceManager
* @see #releasePersistenceManager
* @see org.springframework.orm.jdo.JdoTemplate
* @see org.springframework.orm.jdo.JdoInterceptor
* @deprecated as of Spring 3.1, in favor of native PersistenceManager usage
* (see {@link org.springframework.orm.jdo.TransactionAwarePersistenceManagerFactoryProxy}
* and {@link SpringPersistenceManagerProxyBean})
*/
@Deprecated
public abstract class JdoDaoSupport extends DaoSupport {
private JdoTemplate jdoTemplate;
/**
* Set the JDO PersistenceManagerFactory to be used by this DAO.
* Will automatically create a JdoTemplate for the given PersistenceManagerFactory.
* @see #createJdoTemplate
* @see #setJdoTemplate
*/
public final void setPersistenceManagerFactory(PersistenceManagerFactory persistenceManagerFactory) {
if (this.jdoTemplate == null || persistenceManagerFactory != this.jdoTemplate.getPersistenceManagerFactory()) {
this.jdoTemplate = createJdoTemplate(persistenceManagerFactory);
}
}
/**
* Create a JdoTemplate for the given PersistenceManagerFactory.
* Only invoked if populating the DAO with a PersistenceManagerFactory reference!
* <p>Can be overridden in subclasses to provide a JdoTemplate instance
* with different configuration, or a custom JdoTemplate subclass.
* @param persistenceManagerFactory the JDO PersistenceManagerFactoryto create a JdoTemplate for
* @return the new JdoTemplate instance
* @see #setPersistenceManagerFactory
*/
protected JdoTemplate createJdoTemplate(PersistenceManagerFactory persistenceManagerFactory) {
return new JdoTemplate(persistenceManagerFactory);
}
/**
* Return the JDO PersistenceManagerFactory used by this DAO.
*/
public final PersistenceManagerFactory getPersistenceManagerFactory() {
return (this.jdoTemplate != null ? this.jdoTemplate.getPersistenceManagerFactory() : null);
}
/**
* Set the JdoTemplate for this DAO explicitly,
* as an alternative to specifying a PersistenceManagerFactory.
* @see #setPersistenceManagerFactory
*/
public final void setJdoTemplate(JdoTemplate jdoTemplate) {
this.jdoTemplate = jdoTemplate;
}
/**
* Return the JdoTemplate for this DAO, pre-initialized
* with the PersistenceManagerFactory or set explicitly.
*/
public final JdoTemplate getJdoTemplate() {
return jdoTemplate;
}
@Override
protected final void checkDaoConfig() {
if (this.jdoTemplate == null) {
throw new IllegalArgumentException("persistenceManagerFactory or jdoTemplate is required");
}
}
/**
* Get a JDO PersistenceManager, either from the current transaction or
* a new one. The latter is only allowed if the "allowCreate" setting
* of this bean's JdoTemplate is true.
* @return the JDO PersistenceManager
* @throws DataAccessResourceFailureException if the PersistenceManager couldn't be created
* @throws IllegalStateException if no thread-bound PersistenceManager found and allowCreate false
* @see org.springframework.orm.jdo.PersistenceManagerFactoryUtils#getPersistenceManager
*/
protected final PersistenceManager getPersistenceManager() {
return getPersistenceManager(this.jdoTemplate.isAllowCreate());
}
/**
* Get a JDO PersistenceManager, either from the current transaction or
* a new one. The latter is only allowed if "allowCreate" is true.
* @param allowCreate if a non-transactional PersistenceManager should be created
* when no transactional PersistenceManager can be found for the current thread
* @return the JDO PersistenceManager
* @throws DataAccessResourceFailureException if the PersistenceManager couldn't be created
* @throws IllegalStateException if no thread-bound PersistenceManager found and allowCreate false
* @see org.springframework.orm.jdo.PersistenceManagerFactoryUtils#getPersistenceManager
*/
protected final PersistenceManager getPersistenceManager(boolean allowCreate)
throws DataAccessResourceFailureException, IllegalStateException {
return PersistenceManagerFactoryUtils.getPersistenceManager(getPersistenceManagerFactory(), allowCreate);
}
/**
* Convert the given JDOException to an appropriate exception from the
* org.springframework.dao hierarchy.
* <p>Delegates to the convertJdoAccessException method of this DAO's JdoTemplate.
* @param ex JDOException that occured
* @return the corresponding DataAccessException instance
* @see #setJdoTemplate
* @see org.springframework.orm.jdo.JdoTemplate#convertJdoAccessException
*/
protected final DataAccessException convertJdoAccessException(JDOException ex) {
return this.jdoTemplate.convertJdoAccessException(ex);
}
/**
* Close the given JDO PersistenceManager, created via this DAO's
* PersistenceManagerFactory, if it isn't bound to the thread.
* @param pm PersistenceManager to close
* @see org.springframework.orm.jdo.PersistenceManagerFactoryUtils#releasePersistenceManager
*/
protected final void releasePersistenceManager(PersistenceManager pm) {
PersistenceManagerFactoryUtils.releasePersistenceManager(pm, getPersistenceManagerFactory());
}
}

View File

@@ -0,0 +1,146 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jdo.support;
import java.io.IOException;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.orm.jdo.PersistenceManagerFactoryUtils;
import org.springframework.orm.jdo.PersistenceManagerHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.filter.OncePerRequestFilter;
/**
* Servlet 2.3 Filter that binds a JDO PersistenceManager to the thread for the
* entire processing of the request. Intended for the "Open PersistenceManager in
* View" pattern, i.e. to allow for lazy loading in web views despite the
* original transactions already being completed.
*
* <p>This filter makes JDO PersistenceManagers available via the current thread,
* which will be autodetected by transaction managers. It is suitable for service
* layer transactions via {@link org.springframework.orm.jdo.JdoTransactionManager}
* or {@link org.springframework.transaction.jta.JtaTransactionManager} as well
* as for non-transactional read-only execution.
*
* <p>Looks up the PersistenceManagerFactory in Spring's root web application context.
* Supports a "persistenceManagerFactoryBeanName" filter init-param in <code>web.xml</code>;
* the default bean name is "persistenceManagerFactory". Looks up the PersistenceManagerFactory
* on each request, to avoid initialization order issues (when using ContextLoaderServlet,
* the root application context will get initialized <i>after</i> this filter).
*
* @author Juergen Hoeller
* @since 1.1
* @see OpenPersistenceManagerInViewInterceptor
* @see org.springframework.orm.jdo.JdoInterceptor
* @see org.springframework.orm.jdo.JdoTransactionManager
* @see org.springframework.orm.jdo.PersistenceManagerFactoryUtils#getPersistenceManager
* @see org.springframework.transaction.support.TransactionSynchronizationManager
*/
public class OpenPersistenceManagerInViewFilter extends OncePerRequestFilter {
public static final String DEFAULT_PERSISTENCE_MANAGER_FACTORY_BEAN_NAME = "persistenceManagerFactory";
private String persistenceManagerFactoryBeanName = DEFAULT_PERSISTENCE_MANAGER_FACTORY_BEAN_NAME;
/**
* Set the bean name of the PersistenceManagerFactory to fetch from Spring's
* root application context. Default is "persistenceManagerFactory".
* @see #DEFAULT_PERSISTENCE_MANAGER_FACTORY_BEAN_NAME
*/
public void setPersistenceManagerFactoryBeanName(String persistenceManagerFactoryBeanName) {
this.persistenceManagerFactoryBeanName = persistenceManagerFactoryBeanName;
}
/**
* Return the bean name of the PersistenceManagerFactory to fetch from Spring's
* root application context.
*/
protected String getPersistenceManagerFactoryBeanName() {
return this.persistenceManagerFactoryBeanName;
}
@Override
protected void doFilterInternal(
HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
PersistenceManagerFactory pmf = lookupPersistenceManagerFactory(request);
boolean participate = false;
if (TransactionSynchronizationManager.hasResource(pmf)) {
// Do not modify the PersistenceManager: just set the participate flag.
participate = true;
}
else {
logger.debug("Opening JDO PersistenceManager in OpenPersistenceManagerInViewFilter");
PersistenceManager pm = PersistenceManagerFactoryUtils.getPersistenceManager(pmf, true);
TransactionSynchronizationManager.bindResource(pmf, new PersistenceManagerHolder(pm));
}
try {
filterChain.doFilter(request, response);
}
finally {
if (!participate) {
PersistenceManagerHolder pmHolder = (PersistenceManagerHolder)
TransactionSynchronizationManager.unbindResource(pmf);
logger.debug("Closing JDO PersistenceManager in OpenPersistenceManagerInViewFilter");
PersistenceManagerFactoryUtils.releasePersistenceManager(pmHolder.getPersistenceManager(), pmf);
}
}
}
/**
* Look up the PersistenceManagerFactory that this filter should use,
* taking the current HTTP request as argument.
* <p>Default implementation delegates to the <code>lookupPersistenceManagerFactory</code>
* without arguments.
* @return the PersistenceManagerFactory to use
* @see #lookupPersistenceManagerFactory()
*/
protected PersistenceManagerFactory lookupPersistenceManagerFactory(HttpServletRequest request) {
return lookupPersistenceManagerFactory();
}
/**
* Look up the PersistenceManagerFactory that this filter should use.
* The default implementation looks for a bean with the specified name
* in Spring's root application context.
* @return the PersistenceManagerFactory to use
* @see #getPersistenceManagerFactoryBeanName
*/
protected PersistenceManagerFactory lookupPersistenceManagerFactory() {
if (logger.isDebugEnabled()) {
logger.debug("Using PersistenceManagerFactory '" + getPersistenceManagerFactoryBeanName() +
"' for OpenPersistenceManagerInViewFilter");
}
WebApplicationContext wac = WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());
return wac.getBean(getPersistenceManagerFactoryBeanName(), PersistenceManagerFactory.class);
}
}

View File

@@ -0,0 +1,143 @@
/*
* Copyright 2002-2008 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jdo.support;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.jdo.PersistenceManagerFactoryUtils;
import org.springframework.orm.jdo.PersistenceManagerHolder;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.ui.ModelMap;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.context.request.WebRequestInterceptor;
/**
* Spring web request interceptor that binds a JDO PersistenceManager to the
* thread for the entire processing of the request. Intended for the "Open
* PersistenceManager in View" pattern, i.e. to allow for lazy loading in
* web views despite the original transactions already being completed.
*
* <p>This interceptor makes JDO PersistenceManagers available via the current thread,
* which will be autodetected by transaction managers. It is suitable for service
* layer transactions via {@link org.springframework.orm.jdo.JdoTransactionManager}
* or {@link org.springframework.transaction.jta.JtaTransactionManager} as well
* as for non-transactional read-only execution.
*
* <p>In contrast to {@link OpenPersistenceManagerInViewFilter}, this interceptor
* is set up in a Spring application context and can thus take advantage of
* bean wiring. It inherits common JDO configuration properties from
* {@link org.springframework.orm.jdo.JdoAccessor}, to be configured in a
* bean definition.
*
* @author Juergen Hoeller
* @since 1.1
* @see OpenPersistenceManagerInViewFilter
* @see org.springframework.orm.jdo.JdoInterceptor
* @see org.springframework.orm.jdo.JdoTransactionManager
* @see org.springframework.orm.jdo.PersistenceManagerFactoryUtils#getPersistenceManager
* @see org.springframework.transaction.support.TransactionSynchronizationManager
*/
public class OpenPersistenceManagerInViewInterceptor implements WebRequestInterceptor {
/**
* Suffix that gets appended to the PersistenceManagerFactory toString
* representation for the "participate in existing persistence manager
* handling" request attribute.
* @see #getParticipateAttributeName
*/
public static final String PARTICIPATE_SUFFIX = ".PARTICIPATE";
protected final Log logger = LogFactory.getLog(getClass());
private PersistenceManagerFactory persistenceManagerFactory;
/**
* Set the JDO PersistenceManagerFactory that should be used to create
* PersistenceManagers.
*/
public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) {
this.persistenceManagerFactory = pmf;
}
/**
* Return the JDO PersistenceManagerFactory that should be used to create
* PersistenceManagers.
*/
public PersistenceManagerFactory getPersistenceManagerFactory() {
return persistenceManagerFactory;
}
public void preHandle(WebRequest request) throws DataAccessException {
if (TransactionSynchronizationManager.hasResource(getPersistenceManagerFactory())) {
// Do not modify the PersistenceManager: just mark the request accordingly.
String participateAttributeName = getParticipateAttributeName();
Integer count = (Integer) request.getAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
int newCount = (count != null ? count + 1 : 1);
request.setAttribute(getParticipateAttributeName(), newCount, WebRequest.SCOPE_REQUEST);
}
else {
logger.debug("Opening JDO PersistenceManager in OpenPersistenceManagerInViewInterceptor");
PersistenceManager pm =
PersistenceManagerFactoryUtils.getPersistenceManager(getPersistenceManagerFactory(), true);
TransactionSynchronizationManager.bindResource(
getPersistenceManagerFactory(), new PersistenceManagerHolder(pm));
}
}
public void postHandle(WebRequest request, ModelMap model) {
}
public void afterCompletion(WebRequest request, Exception ex) throws DataAccessException {
String participateAttributeName = getParticipateAttributeName();
Integer count = (Integer) request.getAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
if (count != null) {
// Do not modify the PersistenceManager: just clear the marker.
if (count > 1) {
request.setAttribute(participateAttributeName, count - 1, WebRequest.SCOPE_REQUEST);
}
else {
request.removeAttribute(participateAttributeName, WebRequest.SCOPE_REQUEST);
}
}
else {
PersistenceManagerHolder pmHolder = (PersistenceManagerHolder)
TransactionSynchronizationManager.unbindResource(getPersistenceManagerFactory());
logger.debug("Closing JDO PersistenceManager in OpenPersistenceManagerInViewInterceptor");
PersistenceManagerFactoryUtils.releasePersistenceManager(
pmHolder.getPersistenceManager(), getPersistenceManagerFactory());
}
}
/**
* Return the name of the request attribute that identifies that a request is
* already filtered. Default implementation takes the toString representation
* of the PersistenceManagerFactory instance and appends ".FILTERED".
* @see #PARTICIPATE_SUFFIX
*/
protected String getParticipateAttributeName() {
return getPersistenceManagerFactory().toString() + PARTICIPATE_SUFFIX;
}
}

View File

@@ -0,0 +1,228 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jdo.support;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.Query;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.orm.jdo.DefaultJdoDialect;
import org.springframework.orm.jdo.JdoDialect;
import org.springframework.orm.jdo.PersistenceManagerFactoryUtils;
import org.springframework.util.Assert;
/**
* Proxy that implements the {@link javax.jdo.PersistenceManager} interface,
* delegating to the current thread-bound PersistenceManager (the Spring-managed
* transactional PersistenceManager or the single OpenPersistenceManagerInView
* PersistenceManager, if any) on each invocation. This class makes such a
* Spring-style PersistenceManager proxy available for bean references.
*
* <p>The main advantage of this proxy is that it allows DAOs to work with a
* plain JDO PersistenceManager reference in JDO 2.1 style
* (see {@link javax.jdo.PersistenceManagerFactory#getPersistenceManagerProxy()}),
* while still participating in Spring's resource and transaction management.
*
* <p>The behavior of this proxy matches the behavior that the JDO 2.1 spec
* defines for a PersistenceManager proxy. Hence, DAOs could seamlessly switch
* between {@link StandardPersistenceManagerProxyBean} and this Spring-style proxy,
* receiving the reference through Dependency Injection. This will work without
* any Spring API dependencies in the DAO code!
*
* <p>Note: In contrast to {@link StandardPersistenceManagerProxyBean}, this proxy
* works with JDO 2.0 and higher. It does not require JDO 2.1.
*
* @author Juergen Hoeller
* @since 3.0
* @see StandardPersistenceManagerProxyBean
* @see javax.jdo.PersistenceManagerFactory#getPersistenceManagerProxy()
* @see org.springframework.orm.jdo.PersistenceManagerFactoryUtils#getPersistenceManager
* @see org.springframework.orm.jdo.PersistenceManagerFactoryUtils#releasePersistenceManager
*/
public class SpringPersistenceManagerProxyBean implements FactoryBean<PersistenceManager>, InitializingBean {
private PersistenceManagerFactory persistenceManagerFactory;
private JdoDialect jdoDialect;
private Class<? extends PersistenceManager> persistenceManagerInterface = PersistenceManager.class;
private boolean allowCreate = true;
private PersistenceManager proxy;
/**
* Set the target PersistenceManagerFactory for this proxy.
*/
public void setPersistenceManagerFactory(PersistenceManagerFactory persistenceManagerFactory) {
this.persistenceManagerFactory = persistenceManagerFactory;
}
/**
* Return the target PersistenceManagerFactory for this proxy.
*/
protected PersistenceManagerFactory getPersistenceManagerFactory() {
return this.persistenceManagerFactory;
}
/**
* Set the JDO dialect to use for this proxy.
* <p>Default is a DefaultJdoDialect based on the PersistenceManagerFactory's
* underlying DataSource, if any.
*/
public void setJdoDialect(JdoDialect jdoDialect) {
this.jdoDialect = jdoDialect;
}
/**
* Return the JDO dialect to use for this proxy.
*/
protected JdoDialect getJdoDialect() {
return this.jdoDialect;
}
/**
* Specify the PersistenceManager interface to expose,
* possibly including vendor extensions.
* <p>Default is the standard <code>javax.jdo.PersistenceManager</code> interface.
*/
public void setPersistenceManagerInterface(Class<? extends PersistenceManager> persistenceManagerInterface) {
this.persistenceManagerInterface = persistenceManagerInterface;
Assert.notNull(persistenceManagerInterface, "persistenceManagerInterface must not be null");
Assert.isAssignable(PersistenceManager.class, persistenceManagerInterface);
}
/**
* Return the PersistenceManager interface to expose.
*/
protected Class<? extends PersistenceManager> getPersistenceManagerInterface() {
return this.persistenceManagerInterface;
}
/**
* Set whether the PersistenceManagerFactory proxy is allowed to create
* a non-transactional PersistenceManager when no transactional
* PersistenceManager can be found for the current thread.
* <p>Default is "true". Can be turned off to enforce access to
* transactional PersistenceManagers, which safely allows for DAOs
* written to get a PersistenceManager without explicit closing
* (i.e. a <code>PersistenceManagerFactory.getPersistenceManager()</code>
* call without corresponding <code>PersistenceManager.close()</code> call).
* @see org.springframework.orm.jdo.PersistenceManagerFactoryUtils#getPersistenceManager(javax.jdo.PersistenceManagerFactory, boolean)
*/
public void setAllowCreate(boolean allowCreate) {
this.allowCreate = allowCreate;
}
/**
* Return whether the PersistenceManagerFactory proxy is allowed to create
* a non-transactional PersistenceManager when no transactional
* PersistenceManager can be found for the current thread.
*/
protected boolean isAllowCreate() {
return this.allowCreate;
}
public void afterPropertiesSet() {
if (getPersistenceManagerFactory() == null) {
throw new IllegalArgumentException("Property 'persistenceManagerFactory' is required");
}
// Build default JdoDialect if none explicitly specified.
if (this.jdoDialect == null) {
this.jdoDialect = new DefaultJdoDialect(getPersistenceManagerFactory().getConnectionFactory());
}
this.proxy = (PersistenceManager) Proxy.newProxyInstance(
getPersistenceManagerFactory().getClass().getClassLoader(),
new Class[] {getPersistenceManagerInterface()}, new PersistenceManagerInvocationHandler());
}
public PersistenceManager getObject() {
return this.proxy;
}
public Class<? extends PersistenceManager> getObjectType() {
return getPersistenceManagerInterface();
}
public boolean isSingleton() {
return true;
}
/**
* Invocation handler that delegates close calls on PersistenceManagers to
* PersistenceManagerFactoryUtils for being aware of thread-bound transactions.
*/
private class PersistenceManagerInvocationHandler implements InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// Invocation on PersistenceManager interface coming in...
if (method.getName().equals("equals")) {
// Only consider equal when proxies are identical.
return (proxy == args[0]);
}
else if (method.getName().equals("hashCode")) {
// Use hashCode of PersistenceManager proxy.
return System.identityHashCode(proxy);
}
else if (method.getName().equals("toString")) {
// Deliver toString without touching a target EntityManager.
return "Spring PersistenceManager proxy for target factory [" + getPersistenceManagerFactory() + "]";
}
else if (method.getName().equals("getPersistenceManagerFactory")) {
// Return PersistenceManagerFactory without creating a PersistenceManager.
return getPersistenceManagerFactory();
}
else if (method.getName().equals("isClosed")) {
// Proxy is always usable.
return false;
}
else if (method.getName().equals("close")) {
// Suppress close method.
return null;
}
// Invoke method on target PersistenceManager.
PersistenceManager pm = PersistenceManagerFactoryUtils.doGetPersistenceManager(
getPersistenceManagerFactory(), isAllowCreate());
try {
Object retVal = method.invoke(pm, args);
if (retVal instanceof Query) {
PersistenceManagerFactoryUtils.applyTransactionTimeout(
(Query) retVal, getPersistenceManagerFactory(), getJdoDialect());
}
return retVal;
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
finally {
PersistenceManagerFactoryUtils.doReleasePersistenceManager(pm, getPersistenceManagerFactory());
}
}
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2002-2009 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either exprShess or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jdo.support;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.util.Assert;
/**
* Proxy that implements the {@link javax.jdo.PersistenceManager} interface,
* delegating to a thread-bound PersistenceManager on each invocation -
* as defined by the JDO 2.1 specification. This class makes such a standard
* JDO PersistenceManager proxy available for bean references.
*
* <p>The main advantage of this proxy is that it allows DAOs to work with a
* plain JDO PersistenceManager reference in JDO 2.1 style
* (see {@link javax.jdo.PersistenceManagerFactory#getPersistenceManagerProxy()}),
* exposing the exact behavior that the target JDO provider implements.
*
* <p>Note: This proxy requires JDO 2.1 or higher.
*
* @author Juergen Hoeller
* @since 3.0
* @see SpringPersistenceManagerProxyBean
* @see javax.jdo.PersistenceManagerFactory#getPersistenceManagerProxy()
*/
public class StandardPersistenceManagerProxyBean implements FactoryBean<PersistenceManager> {
private PersistenceManager proxy;
/**
* Set the target JDO PersistenceManagerFactory that this proxy should
* delegate to. This should be the raw PersistenceManagerFactory, as
* accessed by JdoTransactionManager.
* @see org.springframework.orm.jdo.JdoTransactionManager
*/
public void setPersistenceManagerFactory(PersistenceManagerFactory pmf) {
Assert.notNull(pmf, "PersistenceManagerFactory must not be null");
this.proxy = pmf.getPersistenceManagerProxy();
}
public PersistenceManager getObject() {
return this.proxy;
}
public Class<? extends PersistenceManager> getObjectType() {
return (this.proxy != null ? this.proxy.getClass() : PersistenceManager.class);
}
public boolean isSingleton() {
return true;
}
}

View File

@@ -0,0 +1,9 @@
/**
*
* Classes supporting the <code>org.springframework.orm.jdo</code> package.
* Contains a DAO base class for JdoTemplate usage.
*
*/
package org.springframework.orm.jdo.support;

View File

@@ -0,0 +1,525 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jpa;
import java.io.IOException;
import java.io.NotSerializableException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceException;
import javax.persistence.spi.PersistenceProvider;
import javax.persistence.spi.PersistenceUnitInfo;
import javax.sql.DataSource;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.support.PersistenceExceptionTranslator;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
/**
* Abstract {@link org.springframework.beans.factory.FactoryBean} that
* creates a local JPA {@link javax.persistence.EntityManagerFactory}
* instance within a Spring application context.
*
* <p>Encapsulates the common functionality between the different JPA
* bootstrap contracts (standalone as well as container).
*
* <p>Implements support for standard JPA configuration as well as
* Spring's {@link JpaVendorAdapter} abstraction, and controls the
* EntityManagerFactory's lifecycle.
*
* <p>This class also implements the
* {@link org.springframework.dao.support.PersistenceExceptionTranslator}
* interface, as autodetected by Spring's
* {@link org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor},
* for AOP-based translation of native exceptions to Spring DataAccessExceptions.
* Hence, the presence of e.g. LocalEntityManagerFactoryBean automatically enables
* a PersistenceExceptionTranslationPostProcessor to translate JPA exceptions.
*
* @author Juergen Hoeller
* @author Rod Johnson
* @since 2.0
* @see LocalEntityManagerFactoryBean
* @see LocalContainerEntityManagerFactoryBean
*/
public abstract class AbstractEntityManagerFactoryBean implements
FactoryBean<EntityManagerFactory>, BeanClassLoaderAware, BeanFactoryAware, BeanNameAware,
InitializingBean, DisposableBean, EntityManagerFactoryInfo, PersistenceExceptionTranslator, Serializable {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
private PersistenceProvider persistenceProvider;
private String persistenceUnitName;
private final Map<String, Object> jpaPropertyMap = new HashMap<String, Object>();
private Class<? extends EntityManagerFactory> entityManagerFactoryInterface;
private Class<? extends EntityManager> entityManagerInterface;
private JpaDialect jpaDialect;
private JpaVendorAdapter jpaVendorAdapter;
private ClassLoader beanClassLoader = getClass().getClassLoader();
private BeanFactory beanFactory;
private String beanName;
/** Raw EntityManagerFactory as returned by the PersistenceProvider */
public EntityManagerFactory nativeEntityManagerFactory;
private EntityManagerFactoryPlusOperations plusOperations;
private EntityManagerFactory entityManagerFactory;
/**
* Set the PersistenceProvider implementation class to use for creating the
* EntityManagerFactory. If not specified, the persistence provider will be
* taken from the JpaVendorAdapter (if any) or retrieved through scanning
* (as far as possible).
* @see JpaVendorAdapter#getPersistenceProvider()
* @see javax.persistence.spi.PersistenceProvider
* @see javax.persistence.Persistence
*/
public void setPersistenceProviderClass(Class<? extends PersistenceProvider> persistenceProviderClass) {
Assert.isAssignable(PersistenceProvider.class, persistenceProviderClass);
this.persistenceProvider = BeanUtils.instantiateClass(persistenceProviderClass);
}
/**
* Set the PersistenceProvider instance to use for creating the
* EntityManagerFactory. If not specified, the persistence provider
* will be taken from the JpaVendorAdapter (if any) or determined
* by the persistence unit deployment descriptor (as far as possible).
* @see JpaVendorAdapter#getPersistenceProvider()
* @see javax.persistence.spi.PersistenceProvider
* @see javax.persistence.Persistence
*/
public void setPersistenceProvider(PersistenceProvider persistenceProvider) {
this.persistenceProvider = persistenceProvider;
}
public PersistenceProvider getPersistenceProvider() {
return this.persistenceProvider;
}
/**
* Specify the name of the EntityManagerFactory configuration.
* <p>Default is none, indicating the default EntityManagerFactory
* configuration. The persistence provider will throw an exception if
* ambiguous EntityManager configurations are found.
* @see javax.persistence.Persistence#createEntityManagerFactory(String)
*/
public void setPersistenceUnitName(String persistenceUnitName) {
this.persistenceUnitName = persistenceUnitName;
}
public String getPersistenceUnitName() {
return this.persistenceUnitName;
}
/**
* Specify JPA properties, to be passed into
* <code>Persistence.createEntityManagerFactory</code> (if any).
* <p>Can be populated with a String "value" (parsed via PropertiesEditor) or a
* "props" element in XML bean definitions.
* @see javax.persistence.Persistence#createEntityManagerFactory(String, java.util.Map)
* @see javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory(javax.persistence.spi.PersistenceUnitInfo, java.util.Map)
*/
public void setJpaProperties(Properties jpaProperties) {
CollectionUtils.mergePropertiesIntoMap(jpaProperties, this.jpaPropertyMap);
}
/**
* Specify JPA properties as a Map, to be passed into
* <code>Persistence.createEntityManagerFactory</code> (if any).
* <p>Can be populated with a "map" or "props" element in XML bean definitions.
* @see javax.persistence.Persistence#createEntityManagerFactory(String, java.util.Map)
* @see javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory(javax.persistence.spi.PersistenceUnitInfo, java.util.Map)
*/
public void setJpaPropertyMap(Map<String, ?> jpaProperties) {
if (jpaProperties != null) {
this.jpaPropertyMap.putAll(jpaProperties);
}
}
/**
* Allow Map access to the JPA properties to be passed to the persistence
* provider, with the option to add or override specific entries.
* <p>Useful for specifying entries directly, for example via
* "jpaPropertyMap[myKey]".
*/
public Map<String, Object> getJpaPropertyMap() {
return this.jpaPropertyMap;
}
/**
* Specify the (potentially vendor-specific) EntityManagerFactory interface
* that this EntityManagerFactory proxy is supposed to implement.
* <p>The default will be taken from the specific JpaVendorAdapter, if any,
* or set to the standard <code>javax.persistence.EntityManagerFactory</code>
* interface else.
* @see JpaVendorAdapter#getEntityManagerFactoryInterface()
*/
public void setEntityManagerFactoryInterface(Class<? extends EntityManagerFactory> emfInterface) {
Assert.isAssignable(EntityManagerFactory.class, emfInterface);
this.entityManagerFactoryInterface = emfInterface;
}
/**
* Specify the (potentially vendor-specific) EntityManager interface
* that this factory's EntityManagers are supposed to implement.
* <p>The default will be taken from the specific JpaVendorAdapter, if any,
* or set to the standard <code>javax.persistence.EntityManager</code>
* interface else.
* @see JpaVendorAdapter#getEntityManagerInterface()
* @see EntityManagerFactoryInfo#getEntityManagerInterface()
*/
public void setEntityManagerInterface(Class<? extends EntityManager> emInterface) {
Assert.isAssignable(EntityManager.class, emInterface);
this.entityManagerInterface = emInterface;
}
public Class<? extends EntityManager> getEntityManagerInterface() {
return this.entityManagerInterface;
}
/**
* Specify the vendor-specific JpaDialect implementation to associate with
* this EntityManagerFactory. This will be exposed through the
* EntityManagerFactoryInfo interface, to be picked up as default dialect by
* accessors that intend to use JpaDialect functionality.
* @see EntityManagerFactoryInfo#getJpaDialect()
*/
public void setJpaDialect(JpaDialect jpaDialect) {
this.jpaDialect = jpaDialect;
}
public JpaDialect getJpaDialect() {
return this.jpaDialect;
}
/**
* Specify the JpaVendorAdapter implementation for the desired JPA provider,
* if any. This will initialize appropriate defaults for the given provider,
* such as persistence provider class and JpaDialect, unless locally
* overridden in this FactoryBean.
*/
public void setJpaVendorAdapter(JpaVendorAdapter jpaVendorAdapter) {
this.jpaVendorAdapter = jpaVendorAdapter;
}
/**
* Return the JpaVendorAdapter implementation for this
* EntityManagerFactory, or <code>null</code> if not known.
*/
public JpaVendorAdapter getJpaVendorAdapter() {
return this.jpaVendorAdapter;
}
public void setBeanClassLoader(ClassLoader classLoader) {
this.beanClassLoader = classLoader;
}
public ClassLoader getBeanClassLoader() {
return this.beanClassLoader;
}
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public void setBeanName(String name) {
this.beanName = name;
}
public final void afterPropertiesSet() throws PersistenceException {
if (this.jpaVendorAdapter != null) {
if (this.persistenceProvider == null) {
this.persistenceProvider = this.jpaVendorAdapter.getPersistenceProvider();
}
Map<String, ?> vendorPropertyMap = this.jpaVendorAdapter.getJpaPropertyMap();
if (vendorPropertyMap != null) {
for (Map.Entry<String, ?> entry : vendorPropertyMap.entrySet()) {
if (!this.jpaPropertyMap.containsKey(entry.getKey())) {
this.jpaPropertyMap.put(entry.getKey(), entry.getValue());
}
}
}
if (this.entityManagerFactoryInterface == null) {
this.entityManagerFactoryInterface = this.jpaVendorAdapter.getEntityManagerFactoryInterface();
if (!ClassUtils.isVisible(this.entityManagerFactoryInterface, this.beanClassLoader)) {
this.entityManagerFactoryInterface = EntityManagerFactory.class;
}
}
if (this.entityManagerInterface == null) {
this.entityManagerInterface = this.jpaVendorAdapter.getEntityManagerInterface();
if (!ClassUtils.isVisible(this.entityManagerInterface, this.beanClassLoader)) {
this.entityManagerInterface = EntityManager.class;
}
}
if (this.jpaDialect == null) {
this.jpaDialect = this.jpaVendorAdapter.getJpaDialect();
}
}
this.nativeEntityManagerFactory = createNativeEntityManagerFactory();
if (this.nativeEntityManagerFactory == null) {
throw new IllegalStateException(
"JPA PersistenceProvider returned null EntityManagerFactory - check your JPA provider setup!");
}
if (this.jpaVendorAdapter != null) {
this.jpaVendorAdapter.postProcessEntityManagerFactory(this.nativeEntityManagerFactory);
}
// Wrap the EntityManagerFactory in a factory implementing all its interfaces.
// This allows interception of createEntityManager methods to return an
// application-managed EntityManager proxy that automatically joins
// existing transactions.
this.entityManagerFactory = createEntityManagerFactoryProxy(this.nativeEntityManagerFactory);
}
/**
* Create a proxy of the given EntityManagerFactory. We do this to be able
* to return transaction-aware proxies for application-managed
* EntityManagers, and to introduce the NamedEntityManagerFactory interface
* @param emf EntityManagerFactory as returned by the persistence provider
* @return proxy entity manager
*/
protected EntityManagerFactory createEntityManagerFactoryProxy(EntityManagerFactory emf) {
Set<Class> ifcs = new LinkedHashSet<Class>();
if (this.entityManagerFactoryInterface != null) {
ifcs.add(this.entityManagerFactoryInterface);
}
else {
ifcs.addAll(ClassUtils.getAllInterfacesForClassAsSet(emf.getClass(), this.beanClassLoader));
}
ifcs.add(EntityManagerFactoryInfo.class);
if (getJpaDialect() != null && getJpaDialect().supportsEntityManagerFactoryPlusOperations()) {
this.plusOperations = getJpaDialect().getEntityManagerFactoryPlusOperations(emf);
ifcs.add(EntityManagerFactoryPlusOperations.class);
}
try {
return (EntityManagerFactory) Proxy.newProxyInstance(
this.beanClassLoader, ifcs.toArray(new Class[ifcs.size()]),
new ManagedEntityManagerFactoryInvocationHandler(this));
}
catch (IllegalArgumentException ex) {
if (this.entityManagerFactoryInterface != null) {
throw new IllegalStateException("EntityManagerFactory interface [" + this.entityManagerFactoryInterface +
"] seems to conflict with Spring's EntityManagerFactoryInfo mixin - consider resetting the "+
"'entityManagerFactoryInterface' property to plain [javax.persistence.EntityManagerFactory]", ex);
}
else {
throw new IllegalStateException("Conflicting EntityManagerFactory interfaces - " +
"consider specifying the 'jpaVendorAdapter' or 'entityManagerFactoryInterface' property " +
"to select a specific EntityManagerFactory interface to proceed with", ex);
}
}
}
/**
* Delegate an incoming invocation from the proxy, dispatching to EntityManagerFactoryInfo /
* EntityManagerFactoryPlusOperations / the native EntityManagerFactory accordingly.
*/
Object invokeProxyMethod(Method method, Object[] args) throws Throwable {
if (method.getDeclaringClass().isAssignableFrom(EntityManagerFactoryInfo.class)) {
return method.invoke(this, args);
}
else if (method.getDeclaringClass().equals(EntityManagerFactoryPlusOperations.class)) {
return method.invoke(this.plusOperations, args);
}
Object retVal = method.invoke(this.nativeEntityManagerFactory, args);
if (retVal instanceof EntityManager) {
EntityManager rawEntityManager = (EntityManager) retVal;
retVal = ExtendedEntityManagerCreator.createApplicationManagedEntityManager(rawEntityManager, this);
}
return retVal;
}
/**
* Subclasses must implement this method to create the EntityManagerFactory
* that will be returned by the <code>getObject()</code> method.
* @return EntityManagerFactory instance returned by this FactoryBean
* @throws PersistenceException if the EntityManager cannot be created
*/
protected abstract EntityManagerFactory createNativeEntityManagerFactory() throws PersistenceException;
/**
* Implementation of the PersistenceExceptionTranslator interface, as
* autodetected by Spring's PersistenceExceptionTranslationPostProcessor.
* <p>Uses the dialect's conversion if possible; otherwise falls back to
* standard JPA exception conversion.
* @see org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor
* @see JpaDialect#translateExceptionIfPossible
* @see EntityManagerFactoryUtils#convertJpaAccessExceptionIfPossible
*/
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
return (this.jpaDialect != null ? this.jpaDialect.translateExceptionIfPossible(ex) :
EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(ex));
}
public EntityManagerFactory getNativeEntityManagerFactory() {
return this.nativeEntityManagerFactory;
}
public PersistenceUnitInfo getPersistenceUnitInfo() {
return null;
}
public DataSource getDataSource() {
return null;
}
/**
* Return the singleton EntityManagerFactory.
*/
public EntityManagerFactory getObject() {
return this.entityManagerFactory;
}
public Class<? extends EntityManagerFactory> getObjectType() {
return (this.entityManagerFactory != null ? this.entityManagerFactory.getClass() : EntityManagerFactory.class);
}
public boolean isSingleton() {
return true;
}
/**
* Close the EntityManagerFactory on bean factory shutdown.
*/
public void destroy() {
if (logger.isInfoEnabled()) {
logger.info("Closing JPA EntityManagerFactory for persistence unit '" + getPersistenceUnitName() + "'");
}
this.entityManagerFactory.close();
}
//---------------------------------------------------------------------
// Serialization support
//---------------------------------------------------------------------
private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
throw new NotSerializableException("An EntityManagerFactoryBean itself is not deserializable - " +
"just a SerializedEntityManagerFactoryBeanReference is");
}
protected Object writeReplace() throws ObjectStreamException {
if (this.beanFactory != null && this.beanName != null) {
return new SerializedEntityManagerFactoryBeanReference(this.beanFactory, this.beanName);
}
else {
throw new NotSerializableException("EntityManagerFactoryBean does not run within a BeanFactory");
}
}
/**
* Minimal bean reference to the surrounding AbstractEntityManagerFactoryBean.
* Resolved to the actual AbstractEntityManagerFactoryBean instance on deserialization.
*/
private static class SerializedEntityManagerFactoryBeanReference implements Serializable {
private final BeanFactory beanFactory;
private final String lookupName;
public SerializedEntityManagerFactoryBeanReference(BeanFactory beanFactory, String beanName) {
this.beanFactory = beanFactory;
this.lookupName = BeanFactory.FACTORY_BEAN_PREFIX + beanName;
}
private Object readResolve() {
return this.beanFactory.getBean(this.lookupName, AbstractEntityManagerFactoryBean.class);
}
}
/**
* Dynamic proxy invocation handler proxying an EntityManagerFactory to
* return a proxy EntityManager if necessary from createEntityManager()
* methods.
*/
private static class ManagedEntityManagerFactoryInvocationHandler implements InvocationHandler, Serializable {
private final AbstractEntityManagerFactoryBean entityManagerFactoryBean;
public ManagedEntityManagerFactoryInvocationHandler(AbstractEntityManagerFactoryBean emfb) {
this.entityManagerFactoryBean = emfb;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (method.getName().equals("equals")) {
// Only consider equal when proxies are identical.
return (proxy == args[0]);
}
else if (method.getName().equals("hashCode")) {
// Use hashCode of EntityManagerFactory proxy.
return System.identityHashCode(proxy);
}
else if (method.getName().equals("unwrap")) {
// Handle JPA 2.1 unwrap method - could be a proxy match.
Class targetClass = (Class) args[0];
if (targetClass == null || targetClass.isInstance(proxy)) {
return proxy;
}
}
return this.entityManagerFactoryBean.invokeProxyMethod(method, args);
}
catch (InvocationTargetException ex) {
throw ex.getTargetException();
}
}
}
}

View File

@@ -0,0 +1,140 @@
/*
* Copyright 2002-2007 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jpa;
import java.io.Serializable;
import java.sql.SQLException;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.PersistenceException;
import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.datasource.ConnectionHandle;
import org.springframework.transaction.InvalidIsolationLevelException;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionException;
/**
* Default implementation of the {@link JpaDialect} interface.
* Used as default dialect by {@link JpaAccessor} and {@link JpaTransactionManager}.
*
* <p>Simply begins a standard JPA transaction in {@link #beginTransaction}
* and performs standard exception translation through {@link EntityManagerFactoryUtils}.
*
* @author Juergen Hoeller
* @since 2.0
* @see JpaAccessor#setJpaDialect
* @see JpaTransactionManager#setJpaDialect
*/
public class DefaultJpaDialect implements JpaDialect, Serializable {
//-------------------------------------------------------------------------
// Hooks for transaction management (used by JpaTransactionManager)
//-------------------------------------------------------------------------
/**
* This implementation invokes the standard JPA <code>Transaction.begin</code>
* method. Throws an InvalidIsolationLevelException if a non-default isolation
* level is set.
* <p>This implementation does not return any transaction data Object, since there
* is no state to be kept for a standard JPA transaction. Hence, subclasses do not
* have to care about the return value (<code>null</code>) of this implementation
* and are free to return their own transaction data Object.
* @see javax.persistence.EntityTransaction#begin
* @see org.springframework.transaction.InvalidIsolationLevelException
* @see #cleanupTransaction
*/
public Object beginTransaction(EntityManager entityManager, TransactionDefinition definition)
throws PersistenceException, SQLException, TransactionException {
if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT) {
throw new InvalidIsolationLevelException(
"Standard JPA does not support custom isolation levels - " +
"use a special JpaDialect for your JPA implementation");
}
entityManager.getTransaction().begin();
return null;
}
public Object prepareTransaction(EntityManager entityManager, boolean readOnly, String name)
throws PersistenceException {
return null;
}
/**
* This implementation does nothing, since the default <code>beginTransaction</code>
* implementation does not require any cleanup.
* @see #beginTransaction
*/
public void cleanupTransaction(Object transactionData) {
}
/**
* This implementation always returns <code>null</code>,
* indicating that no JDBC Connection can be provided.
*/
public ConnectionHandle getJdbcConnection(EntityManager entityManager, boolean readOnly)
throws PersistenceException, SQLException {
return null;
}
/**
* This implementation does nothing, assuming that the Connection
* will implicitly be closed with the EntityManager.
* <p>If the JPA implementation returns a Connection handle that it expects
* the application to close after use, the dialect implementation needs to invoke
* <code>Connection.close()</code> (or some other method with similar effect) here.
* @see java.sql.Connection#close()
*/
public void releaseJdbcConnection(ConnectionHandle conHandle, EntityManager em)
throws PersistenceException, SQLException {
}
//-----------------------------------------------------------------------------------
// Hook for exception translation (used by JpaTransactionManager and JpaTemplate)
//-----------------------------------------------------------------------------------
/**
* This implementation delegates to EntityManagerFactoryUtils.
* @see EntityManagerFactoryUtils#convertJpaAccessExceptionIfPossible
*/
public DataAccessException translateExceptionIfPossible(RuntimeException ex) {
return EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(ex);
}
public boolean supportsEntityManagerFactoryPlusOperations() {
return false;
}
public boolean supportsEntityManagerPlusOperations() {
return false;
}
public EntityManagerFactoryPlusOperations getEntityManagerFactoryPlusOperations(EntityManagerFactory rawEntityManager) {
throw new UnsupportedOperationException(getClass().getName() + " does not support EntityManagerFactoryPlusOperations");
}
public EntityManagerPlusOperations getEntityManagerPlusOperations(EntityManager rawEntityManager) {
throw new UnsupportedOperationException(getClass().getName() + " does not support EntityManagerPlusOperations");
}
}

View File

@@ -0,0 +1,172 @@
/*
* Copyright 2002-2011 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.orm.jpa;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
/**
* Base class for any class that needs to access an EntityManagerFactory,
* usually in order to obtain an EntityManager. Defines common properties.
*
* <p>Not intended to be used directly. See {@link JpaAccessor}.
*
* @author Juergen Hoeller
* @since 2.0
* @see JpaAccessor
* @see EntityManagerFactoryUtils
*/
public abstract class EntityManagerFactoryAccessor implements BeanFactoryAware {
/** Logger available to subclasses */
protected final Log logger = LogFactory.getLog(getClass());
private EntityManagerFactory entityManagerFactory;
private String persistenceUnitName;
private final Map<String, Object> jpaPropertyMap = new HashMap<String, Object>();
/**
* Set the JPA EntityManagerFactory that should be used to create
* EntityManagers.
* @see javax.persistence.EntityManagerFactory#createEntityManager()
* @see javax.persistence.EntityManagerFactory#createEntityManager(java.util.Map)
*/
public void setEntityManagerFactory(EntityManagerFactory emf) {
this.entityManagerFactory = emf;
}
/**
* Return the JPA EntityManagerFactory that should be used to create
* EntityManagers.
*/
public EntityManagerFactory getEntityManagerFactory() {
return this.entityManagerFactory;
}
/**
* Set the name of the persistence unit to access the EntityManagerFactory for.
* <p>This is an alternative to specifying the EntityManagerFactory by direct reference,
* resolving it by its persistence unit name instead. If no EntityManagerFactory and
* no persistence unit name have been specified, a default EntityManagerFactory will
* be retrieved through finding a single unique bean of type EntityManagerFactory.
* @see #setEntityManagerFactory
*/
public void setPersistenceUnitName(String persistenceUnitName) {
this.persistenceUnitName = persistenceUnitName;
}
/**
* Return the name of the persistence unit to access the EntityManagerFactory for, if any.
*/
public String getPersistenceUnitName() {
return this.persistenceUnitName;
}
/**
* Specify JPA properties, to be passed into
* <code>EntityManagerFactory.createEntityManager(Map)</code> (if any).
* <p>Can be populated with a String "value" (parsed via PropertiesEditor)
* or a "props" element in XML bean definitions.
* @see javax.persistence.EntityManagerFactory#createEntityManager(java.util.Map)
*/
public void setJpaProperties(Properties jpaProperties) {
CollectionUtils.mergePropertiesIntoMap(jpaProperties, this.jpaPropertyMap);
}
/**
* Specify JPA properties as a Map, to be passed into
* <code>EntityManagerFactory.createEntityManager(Map)</code> (if any).
* <p>Can be populated with a "map" or "props" element in XML bean definitions.
* @see javax.persistence.EntityManagerFactory#createEntityManager(java.util.Map)
*/
public void setJpaPropertyMap(Map<String, Object> jpaProperties) {
if (jpaProperties != null) {
this.jpaPropertyMap.putAll(jpaProperties);
}
}
/**
* Allow Map access to the JPA properties to be passed to the persistence
* provider, with the option to add or override specific entries.
* <p>Useful for specifying entries directly, for example via "jpaPropertyMap[myKey]".
*/
public Map<String, Object> getJpaPropertyMap() {
return this.jpaPropertyMap;
}
/**
* Retrieves an EntityManagerFactory by persistence unit name, if none set explicitly.
* Falls back to a default EntityManagerFactory bean if no persistence unit specified.
* @see #setPersistenceUnitName
*/
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (getEntityManagerFactory() == null) {
if (!(beanFactory instanceof ListableBeanFactory)) {
throw new IllegalStateException("Cannot retrieve EntityManagerFactory by persistence unit name " +
"in a non-listable BeanFactory: " + beanFactory);
}
ListableBeanFactory lbf = (ListableBeanFactory) beanFactory;
setEntityManagerFactory(EntityManagerFactoryUtils.findEntityManagerFactory(lbf, getPersistenceUnitName()));
}
}
/**
* Obtain a new EntityManager from this accessor's EntityManagerFactory.
* <p>Can be overridden in subclasses to create specific EntityManager variants.
* @return a new EntityManager
* @throws IllegalStateException if this accessor is not configured with an EntityManagerFactory
* @see javax.persistence.EntityManagerFactory#createEntityManager()
* @see javax.persistence.EntityManagerFactory#createEntityManager(java.util.Map)
*/
protected EntityManager createEntityManager() throws IllegalStateException {
EntityManagerFactory emf = getEntityManagerFactory();
Assert.state(emf != null, "No EntityManagerFactory specified");
Map<String, Object> properties = getJpaPropertyMap();
return (!CollectionUtils.isEmpty(properties) ? emf.createEntityManager(properties) : emf.createEntityManager());
}
/**
* Obtain the transactional EntityManager for this accessor's EntityManagerFactory, if any.
* @return the transactional EntityManager, or <code>null</code> if none
* @throws IllegalStateException if this accessor is not configured with an EntityManagerFactory
* @see EntityManagerFactoryUtils#getTransactionalEntityManager(javax.persistence.EntityManagerFactory)
* @see EntityManagerFactoryUtils#getTransactionalEntityManager(javax.persistence.EntityManagerFactory, java.util.Map)
*/
protected EntityManager getTransactionalEntityManager() throws IllegalStateException{
EntityManagerFactory emf = getEntityManagerFactory();
Assert.state(emf != null, "No EntityManagerFactory specified");
return EntityManagerFactoryUtils.getTransactionalEntityManager(emf, getJpaPropertyMap());
}
}

Some files were not shown because too many files have changed in this diff Show More