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:
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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() {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
* <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
|
||||
* ...
|
||||
* <property name="filterDefinitions">
|
||||
* <list>
|
||||
* <bean class="org.springframework.orm.hibernate3.FilterDefinitionFactoryBean">
|
||||
* <property name="filterName" value="myFilter"/>
|
||||
* <property name="parameterTypes">
|
||||
* <map>
|
||||
* <entry key="myParam" value="string"/>
|
||||
* <entry key="myOtherParam" value="long"/>
|
||||
* </map>
|
||||
* </property>
|
||||
* </bean>
|
||||
* </list>
|
||||
* </property>
|
||||
* ...
|
||||
* </bean></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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
* <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
|
||||
* ...
|
||||
* <property name="typeDefinitions">
|
||||
* <list>
|
||||
* <bean class="org.springframework.orm.hibernate3.TypeDefinitionBean">
|
||||
* <property name="typeName" value="myType"/>
|
||||
* <property name="typeClass" value="mypackage.MyTypeClass"/>
|
||||
* </bean>
|
||||
* </list>
|
||||
* </property>
|
||||
* ...
|
||||
* </bean></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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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">
|
||||
* <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
|
||||
* <property name="dataSource" ref="dataSource"/>
|
||||
* <property name="annotatedClasses">
|
||||
* <list>
|
||||
* <value>test.package.Foo</value>
|
||||
* <value>test.package.Bar</value>
|
||||
* </list>
|
||||
* </property>
|
||||
* </bean></pre>
|
||||
*
|
||||
* Or when using classpath scanning for autodetection of entity classes:
|
||||
*
|
||||
* <pre class="code">
|
||||
* <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
|
||||
* <property name="dataSource" ref="dataSource"/>
|
||||
* <property name="packagesToScan" value="test.package"/>
|
||||
* </bean></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 {
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
* <bean id="sessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
|
||||
* ...
|
||||
* <property name="entityInterceptor">
|
||||
* <bean class="org.springframework.orm.hibernate3.support.ScopedBeanInterceptor"/>
|
||||
* </property>
|
||||
* </bean></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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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 "<sqlMap>" 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><properties></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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
* <bean id="persistenceManagerFactory" class="org.jpox.PersistenceManagerFactoryImpl" destroy-method="close">
|
||||
* <property name="connectionFactory" ref="dataSource"/>
|
||||
* <property name="nontransactionalRead" value="true"/>
|
||||
* </bean>
|
||||
* </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();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
Reference in New Issue
Block a user