Initial import of ORM
This commit is contained in:
parent
c236f9fac7
commit
d1061e7e9f
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project name="org.springframework.orm">
|
||||
<property file="${basedir}/../build.properties"/>
|
||||
<import file="${basedir}/../build-spring-framework/package-bundle.xml"/>
|
||||
<import file="${basedir}/../spring-build/standard/default.xml"/>
|
||||
</project>
|
|
@ -0,0 +1,49 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xml-stylesheet type="text/xsl" href="http://ivyrep.jayasoft.org/ivy-doc.xsl"?>
|
||||
<ivy-module
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="http://incubator.apache.org/ivy/schemas/ivy.xsd"
|
||||
version="1.3">
|
||||
|
||||
<info organisation="org.springframework" module="${ant.project.name}">
|
||||
<license name="Apache 2.0" url="http://www.apache.org/licenses/LICENSE-2.0"/>
|
||||
</info>
|
||||
|
||||
<configurations>
|
||||
<include file="${spring.build.dir}/common/default-ivy-configurations.xml"/>
|
||||
</configurations>
|
||||
|
||||
<publications>
|
||||
<artifact name="${ant.project.name}"/>
|
||||
<artifact name="${ant.project.name}-sources" type="src" ext="jar"/>
|
||||
</publications>
|
||||
|
||||
<dependencies>
|
||||
<!-- compile dependencies -->
|
||||
<dependency org="org.apache.commons" name="com.springsource.org.apache.commons.logging" rev="1.1.1" conf="compile->runtime" />
|
||||
<dependency org="org.springframework" name="org.springframework.core" rev="latest.integration" conf="compile->compile" />
|
||||
<dependency org="org.springframework" name="org.springframework.beans" rev="latest.integration" conf="compile->compile" />
|
||||
<dependency org="org.springframework" name="org.springframework.aop" rev="latest.integration" conf="compile->compile" />
|
||||
<dependency org="org.springframework" name="org.springframework.transaction" rev="latest.integration" conf="compile->compile" />
|
||||
<dependency org="org.springframework" name="org.springframework.jdbc" rev="latest.integration" conf="compile->compile" />
|
||||
<!-- optional dependencies -->
|
||||
<dependency org="org.springframework" name="org.springframework.context" rev="latest.integration" conf="optional->compile" />
|
||||
<dependency org="org.springframework" name="org.springframework.web" rev="latest.integration" conf="optional->compile" />
|
||||
<dependency org="org.hibernate" name="com.springsource.org.hibernate" rev="3.2.6.ga" conf="optional->compile" />
|
||||
<dependency org="org.hibernate" name="com.springsource.org.hibernate.annotations.common" rev="3.3.0.ga" conf="optional->compile" />
|
||||
<dependency org="com.oracle.toplink" name="com.springsource.oracle.toplink" rev="10.1.3" conf="optional->compile" />
|
||||
<dependency org="javax.persistence" name="com.springsource.javax.persistence" rev="1.0.0" conf="optional->compile" />
|
||||
<dependency org="com.oracle.toplink.essentials" name="com.springsource.oracle.toplink.essentials" rev="2.0.0.b41-beta2" conf="optional->compile" />
|
||||
<dependency org="org.apache.openjpa" name="com.springsource.org.apache.openjpa.persistence" rev="1.0.2" conf="optional->compile" />
|
||||
<dependency org="org.hibernate" name="com.springsource.org.hibernate.ejb" rev="3.3.2.GA" conf="optional->compile" />
|
||||
<dependency org="org.eclipse.persistence" name="com.springsource.org.eclipse.persistence.jpa" rev="1.0.1" conf="optional->compile" />
|
||||
<dependency org="javax.jdo" name="com.springsource.javax.jdo" rev="2.1.0" conf="optional->compile" />
|
||||
<dependency org="org.apache.ibatis" name="com.springsource.com.ibatis" rev="2.3.0.677" conf="optional->compile" />
|
||||
<dependency org="javax.servlet" name="com.springsource.javax.servlet" rev="2.5.0" conf="optional->compile" />
|
||||
<dependency org="javax.transaction" name="com.springsource.javax.transaction" rev="1.1.0" conf="optional->compile" />
|
||||
<!-- test dependencies -->
|
||||
<dependency org="org.junit" name="com.springsource.org.junit" rev="4.4.0" conf="test->runtime" />
|
||||
|
||||
</dependencies>
|
||||
|
||||
</ivy-module>
|
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>org.springframework.core</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
<name>Spring Core Abstractions and Utilities</name>
|
||||
<version>3.0.0.M1</version>
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>com.springsource.repository.bundles.external</id>
|
||||
<name>SpringSource Enterprise Bundle Repository - External Bundle Releases</name>
|
||||
<url>http://repository.springsource.com/maven/bundles/external</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-compiler-plugin</artifactId>
|
||||
<configuration>
|
||||
<source>1.5</source>
|
||||
<target>1.5</target>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>com.springsource.org.apache.commons.logging</artifactId>
|
||||
<version>1.1.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.log4j</groupId>
|
||||
<artifactId>com.springsource.org.apache.log4j</artifactId>
|
||||
<version>1.2.15</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>com.springsource.org.apache.commons.collections</artifactId>
|
||||
<version>3.2.0</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.aspectj</groupId>
|
||||
<artifactId>com.springsource.org.aspectj.weaver</artifactId>
|
||||
<version>1.6.2.RELEASE</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.objectweb.asm</groupId>
|
||||
<artifactId>com.springsource.org.objectweb.asm.commons</artifactId>
|
||||
<version>2.2.3</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -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,336 @@
|
|||
/*
|
||||
* 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 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.SessionFactory;
|
||||
|
||||
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.jdbc.support.SQLExceptionTranslator;
|
||||
|
||||
/**
|
||||
* 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
|
||||
implements FactoryBean, InitializingBean, DisposableBean, PersistenceExceptionTranslator {
|
||||
|
||||
/** Logger available to subclasses */
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private DataSource dataSource;
|
||||
|
||||
private boolean useTransactionAwareDataSource = false;
|
||||
|
||||
private boolean exposeTransactionAwareSessionFactory = true;
|
||||
|
||||
private SQLExceptionTranslator jdbcExceptionTranslator;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 Object getObject() {
|
||||
return this.sessionFactory;
|
||||
}
|
||||
|
||||
public Class getObjectType() {
|
||||
return (this.sessionFactory != null) ? this.sessionFactory.getClass() : SessionFactory.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 HibernateException;
|
||||
* else returns <code>null</code> to indicate an unknown exception.
|
||||
* @see org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor
|
||||
* @see #convertHibernateAccessException
|
||||
*/
|
||||
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</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 (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);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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,139 @@
|
|||
/*
|
||||
* 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.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.hibernate.engine.FilterDefinition;
|
||||
import org.hibernate.type.TypeFactory;
|
||||
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
|
||||
/**
|
||||
* 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">
|
||||
* <props>
|
||||
* <prop key="myParam">string</prop>
|
||||
* <prop key="myOtherParam">long</prop>
|
||||
* </props>
|
||||
* </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, BeanNameAware, InitializingBean {
|
||||
|
||||
private String filterName;
|
||||
|
||||
private Map parameterTypeMap = new HashMap();
|
||||
|
||||
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(Properties parameterTypes) {
|
||||
if (parameterTypes != null) {
|
||||
this.parameterTypeMap = new HashMap(parameterTypes.size());
|
||||
for (Enumeration names = parameterTypes.propertyNames(); names.hasMoreElements();) {
|
||||
String paramName = (String) names.nextElement();
|
||||
String typeName = parameterTypes.getProperty(paramName);
|
||||
this.parameterTypeMap.put(paramName, TypeFactory.heuristicType(typeName));
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.parameterTypeMap = new HashMap();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Object getObject() {
|
||||
return this.filterDefinition;
|
||||
}
|
||||
|
||||
public Class getObjectType() {
|
||||
return FilterDefinition.class;
|
||||
}
|
||||
|
||||
public boolean isSingleton() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,489 @@
|
|||
/*
|
||||
* 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.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.NEVER);
|
||||
return previousFlushMode;
|
||||
}
|
||||
}
|
||||
else {
|
||||
session.setFlushMode(FlushMode.NEVER);
|
||||
}
|
||||
}
|
||||
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-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 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 {
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
Object doInHibernate(Session session) throws HibernateException, SQLException;
|
||||
|
||||
}
|
|
@ -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,937 @@
|
|||
/*
|
||||
* 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.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
|
||||
*/
|
||||
Object execute(HibernateCallback 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)
|
||||
*/
|
||||
Object get(Class 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)
|
||||
*/
|
||||
Object get(Class 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)
|
||||
*/
|
||||
Object load(Class 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)
|
||||
*/
|
||||
Object load(Class 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
|
||||
*/
|
||||
List loadAll(Class 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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
Object merge(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 {@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
|
||||
*/
|
||||
Object merge(String entityName, Object 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,911 @@
|
|||
/*
|
||||
* 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.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.1 or later, as of Spring 2.5.
|
||||
*
|
||||
* @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.
|
||||
* <p>It is recommended to turn this flag off if running against Hibernate 3.1
|
||||
* and a connection pool that does not reset connection settings (for example,
|
||||
* Jakarta Commons DBCP). To keep this flag turned on, you can set the
|
||||
* "hibernate.connection.release_mode" property to "on_close" instead,
|
||||
* or consider using a smarter connection pool (for example, C3P0).
|
||||
* @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 flush before the before-commit synchronization phase, making
|
||||
* flushed state visible to <code>beforeCommit</code> callbacks of registered
|
||||
* {@link org.springframework.transaction.support.TransactionSynchronization}
|
||||
* objects.
|
||||
* <p>Such explicit flush behavior is also consistent with Spring-driven
|
||||
* flushing in a JTA transaction environment, so may also be 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 (Interceptor) 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();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
protected boolean isExistingTransaction(Object transaction) {
|
||||
HibernateTransactionObject txObject = (HibernateTransactionObject) transaction;
|
||||
return (txObject.hasSpringManagedTransaction() ||
|
||||
(this.hibernateManagedSession && txObject.hasHibernateManagedTransaction()));
|
||||
}
|
||||
|
||||
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.NEVER);
|
||||
}
|
||||
|
||||
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 = null;
|
||||
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
protected void prepareForCommit(DefaultTransactionStatus status) {
|
||||
if (this.earlyFlushBeforeCommit) {
|
||||
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.NEVER);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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 static 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() {
|
||||
getSessionHolder().setRollbackOnly();
|
||||
if (hasConnectionHolder()) {
|
||||
getConnectionHolder().setRollbackOnly();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRollbackOnly() {
|
||||
return getSessionHolder().isRollbackOnly() ||
|
||||
(hasConnectionHolder() && getConnectionHolder().isRollbackOnly());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 org.springframework.orm.hibernate3.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,120 @@
|
|||
/*
|
||||
* 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.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 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,37 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
public boolean supportsAggressiveRelease() {
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
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,802 @@
|
|||
/*
|
||||
* 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.HashMap;
|
||||
import java.util.Iterator;
|
||||
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.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.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 deferredCloseHolder =
|
||||
new NamedThreadLocal("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");
|
||||
|
||||
SessionHolder sessionHolder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
|
||||
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.NEVER, 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.NEVER);
|
||||
}
|
||||
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) {
|
||||
return new InvalidDataAccessResourceUsageException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof LockAcquisitionException) {
|
||||
return new CannotAcquireLockException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof ConstraintViolationException) {
|
||||
return new DataIntegrityViolationException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof DataException) {
|
||||
return new DataIntegrityViolationException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof JDBCException) {
|
||||
return new HibernateJdbcException((JDBCException) 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 QueryException) {
|
||||
return new HibernateQueryException((QueryException) ex);
|
||||
}
|
||||
if (ex instanceof UnresolvableObjectException) {
|
||||
return new HibernateObjectRetrievalFailureException((UnresolvableObjectException) ex);
|
||||
}
|
||||
if (ex instanceof WrongClassException) {
|
||||
return new HibernateObjectRetrievalFailureException((WrongClassException) ex);
|
||||
}
|
||||
if (ex instanceof NonUniqueResultException) {
|
||||
return new IncorrectResultSizeDataAccessException(ex.getMessage(), 1);
|
||||
}
|
||||
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 holderMap = (Map) 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 holderMap = (Map) deferredCloseHolder.get();
|
||||
if (holderMap == null) {
|
||||
holderMap = new HashMap();
|
||||
deferredCloseHolder.set(holderMap);
|
||||
}
|
||||
holderMap.put(sessionFactory, new LinkedHashSet(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 holderMap = (Map) 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 sessions = (Set) holderMap.remove(sessionFactory);
|
||||
for (Iterator it = sessions.iterator(); it.hasNext();) {
|
||||
closeSession((Session) it.next());
|
||||
}
|
||||
|
||||
if (holderMap.isEmpty()) {
|
||||
deferredCloseHolder.set(null);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 holderMap = (Map) deferredCloseHolder.get();
|
||||
if (holderMap != null && sessionFactory != null && holderMap.containsKey(sessionFactory)) {
|
||||
logger.debug("Registering Hibernate Session for deferred close");
|
||||
// Switch Session to FlushMode.NEVER for remaining lifetime.
|
||||
session.setFlushMode(FlushMode.NEVER);
|
||||
Set sessions = (Set) 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,153 @@
|
|||
/*
|
||||
* 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.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 sessionMap = Collections.synchronizedMap(new HashMap(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 (Session) this.sessionMap.get(key);
|
||||
}
|
||||
|
||||
public Session getValidatedSession() {
|
||||
return getValidatedSession(DEFAULT_KEY);
|
||||
}
|
||||
|
||||
public Session getValidatedSession(Object key) {
|
||||
Session 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 (Session) 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 (Session) 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;
|
||||
}
|
||||
|
||||
|
||||
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,236 @@
|
|||
/*
|
||||
* 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 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 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) {
|
||||
if (this.jdbcExceptionTranslator != null && ex instanceof JDBCException) {
|
||||
JDBCException jdbcEx = (JDBCException) ex;
|
||||
throw this.jdbcExceptionTranslator.translate(
|
||||
"Hibernate flushing: " + jdbcEx.getMessage(), jdbcEx.getSQL(), jdbcEx.getSQLException());
|
||||
}
|
||||
throw 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) {
|
||||
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.
|
||||
if (session instanceof SessionImplementor) {
|
||||
((SessionImplementor) session).afterTransactionCompletion(status == STATUS_COMMITTED, null);
|
||||
}
|
||||
// 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();
|
||||
}
|
||||
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,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 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.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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,243 @@
|
|||
/*
|
||||
* 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.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 higher, 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)};
|
||||
|
||||
private ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
|
||||
|
||||
|
||||
public AnnotationSessionFactoryBean() {
|
||||
setConfigurationClass(AnnotationConfiguration.class);
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set whether to use Spring-based scanning for entity classes in the classpath
|
||||
* instead of 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>.
|
||||
* @see #setPackagesToScan
|
||||
*/
|
||||
public void setEntityTypeFilters(TypeFilter[] entityTypeFilters) {
|
||||
this.entityTypeFilters = entityTypeFilters;
|
||||
}
|
||||
|
||||
public void setResourceLoader(ResourceLoader resourceLoader) {
|
||||
this.resourcePatternResolver = (resourceLoader != null ?
|
||||
ResourcePatternUtils.getResourcePatternResolver(resourceLoader) :
|
||||
new PathMatchingResourcePatternResolver());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads metadata from annotated classes and packages into the
|
||||
* AnnotationConfiguration instance.
|
||||
*/
|
||||
protected void postProcessMappings(Configuration config) throws HibernateException {
|
||||
AnnotationConfiguration annConfig = (AnnotationConfiguration) config;
|
||||
if (this.annotatedClasses != null) {
|
||||
for (int i = 0; i < this.annotatedClasses.length; i++) {
|
||||
annConfig.addAnnotatedClass(this.annotatedClasses[i]);
|
||||
}
|
||||
}
|
||||
if (this.annotatedPackages != null) {
|
||||
for (int i = 0; i < this.annotatedPackages.length; i++) {
|
||||
annConfig.addPackage(this.annotatedPackages[i]);
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Delegates to {@link #postProcessAnnotationConfiguration}.
|
||||
*/
|
||||
protected final void postProcessConfiguration(Configuration config) throws HibernateException {
|
||||
postProcessAnnotationConfiguration((AnnotationConfiguration) config);
|
||||
}
|
||||
|
||||
/**
|
||||
* To be implemented by subclasses that want to to perform custom
|
||||
* post-processing of the AnnotationConfiguration object after this
|
||||
* FactoryBean performed its default initialization.
|
||||
* @param config the current AnnotationConfiguration object
|
||||
* @throws HibernateException in case of Hibernate initialization errors
|
||||
*/
|
||||
protected void postProcessAnnotationConfiguration(AnnotationConfiguration config)
|
||||
throws HibernateException {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
Support package for the Hibernate3 Annotation add-on,
|
||||
which supports EJB3-compliant JDK 1.5+ annotations for mappings.
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,16 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
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.
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -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,104 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
public boolean isMutable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean equals(Object x, Object y) {
|
||||
return (x == y) ||
|
||||
(x instanceof byte[] && y instanceof byte[] && Arrays.equals((byte[]) x, (byte[]) y));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
protected Object nullSafeGetInternal(
|
||||
ResultSet rs, String[] names, Object owner, LobHandler lobHandler)
|
||||
throws SQLException {
|
||||
|
||||
return lobHandler.getBlobAsBytes(rs, names[0]);
|
||||
}
|
||||
|
||||
protected void nullSafeSetInternal(
|
||||
PreparedStatement ps, int index, Object value, LobCreator lobCreator)
|
||||
throws SQLException {
|
||||
|
||||
lobCreator.setBlobAsBytes(ps, index, (byte[]) value);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
public boolean isMutable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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,121 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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,87 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
protected Object nullSafeGetInternal(
|
||||
ResultSet rs, String[] names, Object owner, LobHandler lobHandler)
|
||||
throws SQLException {
|
||||
|
||||
return lobHandler.getClobAsString(rs, names[0]);
|
||||
}
|
||||
|
||||
protected void nullSafeSetInternal(
|
||||
PreparedStatement ps, int index, Object value, LobCreator lobCreator)
|
||||
throws SQLException {
|
||||
|
||||
lobCreator.setClobAsString(ps, index, (String) value);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,204 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
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 occured
|
||||
* @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,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.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.
|
||||
*/
|
||||
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,283 @@
|
|||
/*
|
||||
* 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.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.NEVER;
|
||||
|
||||
|
||||
/**
|
||||
* 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 "NEVER". Specify "AUTO" if you intend to use
|
||||
* this filter without service layer transactions.
|
||||
* @see org.hibernate.Session#setFlushMode
|
||||
* @see org.hibernate.FlushMode#NEVER
|
||||
* @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;
|
||||
}
|
||||
|
||||
|
||||
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 (SessionFactory) 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 "NEVER".
|
||||
* <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#NEVER
|
||||
*/
|
||||
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-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.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.intValue() + 1 : 1;
|
||||
request.setAttribute(getParticipateAttributeName(), new Integer(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.intValue() > 1) {
|
||||
request.setAttribute(participateAttributeName, new Integer(count.intValue() - 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,58 @@
|
|||
/*
|
||||
* 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 {
|
||||
|
||||
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,8 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
Classes supporting the <code>org.springframework.orm.hibernate3</code> package.
|
||||
Contains a DAO base class for HibernateTemplate usage.
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* 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.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 {
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException;
|
||||
|
||||
}
|
|
@ -0,0 +1,419 @@
|
|||
/*
|
||||
* 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.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, InitializingBean {
|
||||
|
||||
private static final ThreadLocal configTimeLobHandlerHolder = new ThreadLocal();
|
||||
|
||||
/**
|
||||
* 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 (LobHandler) 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.set(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 (int i = 0; i < configLocations.length; i++) {
|
||||
InputStream is = configLocations[i].getInputStream();
|
||||
try {
|
||||
client = configParser.parse(is, properties);
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
throw new NestedIOException("Failed to parse config resource: " + configLocations[i], ex.getCause());
|
||||
}
|
||||
}
|
||||
|
||||
if (mappingLocations != null) {
|
||||
SqlMapParser mapParser = SqlMapParserFactory.createSqlMapParser(configParser);
|
||||
for (int i = 0; i < mappingLocations.length; i++) {
|
||||
try {
|
||||
mapParser.parse(mappingLocations[i].getInputStream());
|
||||
}
|
||||
catch (NodeletException ex) {
|
||||
throw new NestedIOException("Failed to parse mapping resource: " + mappingLocations[i], 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 Object getObject() {
|
||||
return this.sqlMapClient;
|
||||
}
|
||||
|
||||
public Class 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,198 @@
|
|||
/*
|
||||
* 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.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#queryForPaginatedList(String, int)
|
||||
* @deprecated as of iBATIS 2.3.0
|
||||
* @throws org.springframework.dao.DataAccessException in case of errors
|
||||
*/
|
||||
PaginatedList queryForPaginatedList(String statementName, int pageSize)
|
||||
throws DataAccessException;
|
||||
|
||||
/**
|
||||
* @see com.ibatis.sqlmap.client.SqlMapExecutor#queryForPaginatedList(String, Object, int)
|
||||
* @deprecated as of iBATIS 2.3.0
|
||||
* @throws org.springframework.dao.DataAccessException in case of errors
|
||||
*/
|
||||
PaginatedList queryForPaginatedList(String statementName, Object parameterObject, int pageSize)
|
||||
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,454 @@
|
|||
/*
|
||||
* 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.ibatis;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import com.ibatis.common.util.PaginatedList;
|
||||
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 com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
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;
|
||||
|
||||
private boolean lazyLoadingAvailable = true;
|
||||
|
||||
|
||||
/**
|
||||
* 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()
|
||||
*/
|
||||
public DataSource getDataSource() {
|
||||
DataSource ds = super.getDataSource();
|
||||
return (ds != null ? ds : this.sqlMapClient.getDataSource());
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
if (this.sqlMapClient == null) {
|
||||
throw new IllegalArgumentException("Property 'sqlMapClient' is required");
|
||||
}
|
||||
if (this.sqlMapClient instanceof ExtendedSqlMapClient) {
|
||||
// Check whether iBATIS lazy loading is available, that is,
|
||||
// whether a DataSource was specified on the SqlMapClient itself.
|
||||
this.lazyLoadingAvailable = (((ExtendedSqlMapClient) this.sqlMapClient).getDelegate().getTxManager() != null);
|
||||
}
|
||||
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 Object execute(SqlMapClientCallback action) throws DataAccessException {
|
||||
Assert.notNull(action, "Callback object must not be null");
|
||||
Assert.notNull(this.sqlMapClient, "No SqlMapClient specified");
|
||||
|
||||
// We always needs 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
|
||||
*/
|
||||
public List executeWithListResult(SqlMapClientCallback action) throws DataAccessException {
|
||||
return (List) 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
|
||||
*/
|
||||
public Map executeWithMapResult(SqlMapClientCallback action) throws DataAccessException {
|
||||
return (Map) 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() {
|
||||
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() {
|
||||
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 executeWithListResult(new SqlMapClientCallback() {
|
||||
public Object 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 executeWithListResult(new SqlMapClientCallback() {
|
||||
public Object 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() {
|
||||
public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
|
||||
executor.queryWithRowHandler(statementName, parameterObject, rowHandler);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated as of iBATIS 2.3.0
|
||||
*/
|
||||
public PaginatedList queryForPaginatedList(String statementName, int pageSize)
|
||||
throws DataAccessException {
|
||||
|
||||
return queryForPaginatedList(statementName, null, pageSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated as of iBATIS 2.3.0
|
||||
*/
|
||||
public PaginatedList queryForPaginatedList(
|
||||
final String statementName, final Object parameterObject, final int pageSize)
|
||||
throws DataAccessException {
|
||||
|
||||
// Throw exception if lazy loading will not work.
|
||||
if (!this.lazyLoadingAvailable) {
|
||||
throw new InvalidDataAccessApiUsageException(
|
||||
"SqlMapClient needs to have DataSource to allow for lazy loading" +
|
||||
" - specify SqlMapClientFactoryBean's 'dataSource' property");
|
||||
}
|
||||
|
||||
return (PaginatedList) execute(new SqlMapClientCallback() {
|
||||
public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
|
||||
return executor.queryForPaginatedList(statementName, parameterObject, pageSize);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public Map queryForMap(
|
||||
final String statementName, final Object parameterObject, final String keyProperty)
|
||||
throws DataAccessException {
|
||||
|
||||
return executeWithMapResult(new SqlMapClientCallback() {
|
||||
public Object 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 executeWithMapResult(new SqlMapClientCallback() {
|
||||
public Object 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() {
|
||||
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 {
|
||||
|
||||
Integer result = (Integer) execute(new SqlMapClientCallback() {
|
||||
public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
|
||||
return new Integer(executor.update(statementName, parameterObject));
|
||||
}
|
||||
});
|
||||
return result.intValue();
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
Integer result = (Integer) execute(new SqlMapClientCallback() {
|
||||
public Object doInSqlMapClient(SqlMapExecutor executor) throws SQLException {
|
||||
return new Integer(executor.delete(statementName, parameterObject));
|
||||
}
|
||||
});
|
||||
return result.intValue();
|
||||
}
|
||||
|
||||
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,12 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
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.
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,193 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return LOB_CREATOR_SYNCHRONIZATION_ORDER;
|
||||
}
|
||||
|
||||
public void beforeCompletion() {
|
||||
this.lobCreator.close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
protected void setParameterInternal(
|
||||
PreparedStatement ps, int index, Object value, String jdbcType, LobCreator lobCreator)
|
||||
throws SQLException {
|
||||
lobCreator.setBlobAsBytes(ps, index, (byte[]) value);
|
||||
}
|
||||
|
||||
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,108 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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,75 @@
|
|||
/*
|
||||
* 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);
|
||||
}
|
||||
|
||||
protected void setParameterInternal(
|
||||
PreparedStatement ps, int index, Object value, String jdbcType, LobCreator lobCreator)
|
||||
throws SQLException {
|
||||
lobCreator.setClobAsString(ps, index, (String) value);
|
||||
}
|
||||
|
||||
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,112 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
protected final void checkDaoConfig() {
|
||||
if (!this.externalTemplate) {
|
||||
this.sqlMapClientTemplate.afterPropertiesSet();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
Classes supporting the <code>org.springframework.orm.ibatis</code> package.
|
||||
Contains a DAO base class for SqlMapClientTemplate usage.
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
* 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 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;
|
||||
|
||||
/**
|
||||
* 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 {
|
||||
|
||||
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)
|
||||
*/
|
||||
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 delegates to JDO 2.0's <code>flush</code> method.
|
||||
* <p>To be overridden for pre-JDO2 implementations, using the corresponding
|
||||
* vendor-specific mechanism there.
|
||||
* @see javax.jdo.PersistenceManager#flush()
|
||||
*/
|
||||
public void flush(PersistenceManager pm) throws JDOException {
|
||||
pm.flush();
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation logs a warning that it cannot apply a query timeout.
|
||||
*/
|
||||
public void applyQueryTimeout(Query query, int remainingTimeInSeconds) throws JDOException {
|
||||
logger.info("DefaultJdoDialect does not support query timeouts: ignoring remaining transaction time");
|
||||
}
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------------
|
||||
// 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,168 @@
|
|||
/*
|
||||
* 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.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
|
||||
*/
|
||||
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 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 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("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");
|
||||
getJdoDialect().flush(pm);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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,69 @@
|
|||
/*
|
||||
* 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.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
|
||||
*/
|
||||
public interface JdoCallback {
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
Object doInJdo(PersistenceManager pm) throws JDOException;
|
||||
|
||||
}
|
|
@ -0,0 +1,194 @@
|
|||
/*
|
||||
* 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 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;
|
||||
|
||||
/**
|
||||
* Flush the given PersistenceManager, i.e. flush all changes (that have been
|
||||
* applied to persistent objects) to the underlying database. This method will
|
||||
* just get invoked when eager flushing is actually necessary, for example when
|
||||
* JDBC access code needs to see changes within the same transaction.
|
||||
* @param pm the current JDO PersistenceManager
|
||||
* @throws JDOException in case of errors
|
||||
* @see JdoAccessor#setFlushEager
|
||||
*/
|
||||
void flush(PersistenceManager pm) throws JDOException;
|
||||
|
||||
/**
|
||||
* 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 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,124 @@
|
|||
/*
|
||||
* 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.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
|
||||
*/
|
||||
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,442 @@
|
|||
/*
|
||||
* 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 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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
Object execute(JdoCallback 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)
|
||||
*/
|
||||
Object getObjectById(Class 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)
|
||||
*/
|
||||
Object makePersistent(Object 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)
|
||||
*/
|
||||
Collection makePersistentAll(Collection 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)
|
||||
*/
|
||||
Object detachCopy(Object 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)
|
||||
*/
|
||||
Collection detachCopyAll(Collection entities);
|
||||
|
||||
/**
|
||||
* Reattach the given detached instance (for example, a web form object) with
|
||||
* the current JDO transaction, merging its changes into the current persistence
|
||||
* instance that represents the corresponding entity.
|
||||
* <p>Note that as of JDO 2.0 final, this operation is equivalent to a
|
||||
* <code>makePersistent</code> call, with the latter method returning the
|
||||
* persistence instance.
|
||||
* @param detachedEntity the detached instance to attach
|
||||
* @return the corresponding persistent instance
|
||||
* @deprecated in favor of {@link #makePersistent(Object)}.
|
||||
* To be removed in Spring 3.0.
|
||||
*/
|
||||
Object attachCopy(Object detachedEntity);
|
||||
|
||||
/**
|
||||
* Reattach the given detached instances (for example, web form objects) with
|
||||
* the current JDO transaction, merging their changes into the current persistence
|
||||
* instances that represent the corresponding entities.
|
||||
* <p>Note that as of JDO 2.0 final, this operation is equivalent to a
|
||||
* <code>makePersistentAll</code> call, with the latter method returning the
|
||||
* persistence instance.
|
||||
* @param detachedEntities the detached instances to reattach
|
||||
* @return the corresponding persistent instances
|
||||
* @deprecated in favor of {@link #makePersistentAll(java.util.Collection)}.
|
||||
* To be removed in Spring 3.0.
|
||||
*/
|
||||
Collection attachCopyAll(Collection detachedEntities);
|
||||
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
Collection find(Class 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)
|
||||
*/
|
||||
Collection find(Class 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
|
||||
*/
|
||||
Collection find(Class 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
|
||||
*/
|
||||
Collection find(Class 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
|
||||
*/
|
||||
Collection find(Class 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
|
||||
*/
|
||||
Collection find(Class entityClass, String filter, String parameters, Map 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
|
||||
*/
|
||||
Collection find(Class entityClass, String filter, String parameters, Map 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 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)
|
||||
*/
|
||||
Collection findByNamedQuery(Class 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)
|
||||
*/
|
||||
Collection findByNamedQuery(Class 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)
|
||||
*/
|
||||
Collection findByNamedQuery(Class entityClass, String queryName, Map 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,621 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
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>
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
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 Object execute(JdoCallback 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 Object execute(JdoCallback 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));
|
||||
Object 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 Object postProcessResult(Object 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() {
|
||||
public Object doInJdo(PersistenceManager pm) throws JDOException {
|
||||
return pm.getObjectById(objectId, true);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public Object getObjectById(final Class entityClass, final Object idValue) throws DataAccessException {
|
||||
return execute(new JdoCallback() {
|
||||
public Object doInJdo(PersistenceManager pm) throws JDOException {
|
||||
return pm.getObjectById(entityClass, idValue);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public void evict(final Object entity) throws DataAccessException {
|
||||
execute(new JdoCallback() {
|
||||
public Object doInJdo(PersistenceManager pm) throws JDOException {
|
||||
pm.evict(entity);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public void evictAll(final Collection entities) throws DataAccessException {
|
||||
execute(new JdoCallback() {
|
||||
public Object doInJdo(PersistenceManager pm) throws JDOException {
|
||||
pm.evictAll(entities);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public void evictAll() throws DataAccessException {
|
||||
execute(new JdoCallback() {
|
||||
public Object doInJdo(PersistenceManager pm) throws JDOException {
|
||||
pm.evictAll();
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public void refresh(final Object entity) throws DataAccessException {
|
||||
execute(new JdoCallback() {
|
||||
public Object doInJdo(PersistenceManager pm) throws JDOException {
|
||||
pm.refresh(entity);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public void refreshAll(final Collection entities) throws DataAccessException {
|
||||
execute(new JdoCallback() {
|
||||
public Object doInJdo(PersistenceManager pm) throws JDOException {
|
||||
pm.refreshAll(entities);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public void refreshAll() throws DataAccessException {
|
||||
execute(new JdoCallback() {
|
||||
public Object doInJdo(PersistenceManager pm) throws JDOException {
|
||||
pm.refreshAll();
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public Object makePersistent(final Object entity) throws DataAccessException {
|
||||
return execute(new JdoCallback() {
|
||||
public Object doInJdo(PersistenceManager pm) throws JDOException {
|
||||
return pm.makePersistent(entity);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public Collection makePersistentAll(final Collection entities) throws DataAccessException {
|
||||
return (Collection) execute(new JdoCallback() {
|
||||
public Object doInJdo(PersistenceManager pm) throws JDOException {
|
||||
return pm.makePersistentAll(entities);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public void deletePersistent(final Object entity) throws DataAccessException {
|
||||
execute(new JdoCallback() {
|
||||
public Object doInJdo(PersistenceManager pm) throws JDOException {
|
||||
pm.deletePersistent(entity);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public void deletePersistentAll(final Collection entities) throws DataAccessException {
|
||||
execute(new JdoCallback() {
|
||||
public Object doInJdo(PersistenceManager pm) throws JDOException {
|
||||
pm.deletePersistentAll(entities);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public Object detachCopy(final Object entity) {
|
||||
return execute(new JdoCallback() {
|
||||
public Object doInJdo(PersistenceManager pm) throws JDOException {
|
||||
return pm.detachCopy(entity);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public Collection detachCopyAll(final Collection entities) {
|
||||
return (Collection) execute(new JdoCallback() {
|
||||
public Object doInJdo(PersistenceManager pm) throws JDOException {
|
||||
return pm.detachCopyAll(entities);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated in favor of {@link #makePersistent(Object)}.
|
||||
* To be removed in Spring 3.0.
|
||||
*/
|
||||
public Object attachCopy(Object detachedEntity) {
|
||||
return makePersistent(detachedEntity);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated in favor of {@link #makePersistentAll(java.util.Collection)}.
|
||||
* To be removed in Spring 3.0.
|
||||
*/
|
||||
public Collection attachCopyAll(Collection detachedEntities) {
|
||||
return makePersistentAll(detachedEntities);
|
||||
}
|
||||
|
||||
public void flush() throws DataAccessException {
|
||||
execute(new JdoCallback() {
|
||||
public Object doInJdo(PersistenceManager pm) throws JDOException {
|
||||
getJdoDialect().flush(pm);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Convenience finder methods
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
public Collection find(Class entityClass) throws DataAccessException {
|
||||
return find(entityClass, null, null);
|
||||
}
|
||||
|
||||
public Collection find(Class entityClass, String filter) throws DataAccessException {
|
||||
return find(entityClass, filter, null);
|
||||
}
|
||||
|
||||
public Collection find(final Class entityClass, final String filter, final String ordering)
|
||||
throws DataAccessException {
|
||||
|
||||
return (Collection) execute(new JdoCallback() {
|
||||
public Object 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 query.execute();
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public Collection find(Class entityClass, String filter, String parameters, Object[] values)
|
||||
throws DataAccessException {
|
||||
|
||||
return find(entityClass, filter, parameters, values, null);
|
||||
}
|
||||
|
||||
public Collection find(
|
||||
final Class entityClass, final String filter, final String parameters, final Object[] values,
|
||||
final String ordering) throws DataAccessException {
|
||||
|
||||
return (Collection) execute(new JdoCallback() {
|
||||
public Object doInJdo(PersistenceManager pm) throws JDOException {
|
||||
Query query = pm.newQuery(entityClass, filter);
|
||||
prepareQuery(query);
|
||||
query.declareParameters(parameters);
|
||||
if (ordering != null) {
|
||||
query.setOrdering(ordering);
|
||||
}
|
||||
return query.executeWithArray(values);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public Collection find(Class entityClass, String filter, String parameters, Map values)
|
||||
throws DataAccessException {
|
||||
|
||||
return find(entityClass, filter, parameters, values, null);
|
||||
}
|
||||
|
||||
public Collection find(
|
||||
final Class entityClass, final String filter, final String parameters, final Map values,
|
||||
final String ordering) throws DataAccessException {
|
||||
|
||||
return (Collection) execute(new JdoCallback() {
|
||||
public Object doInJdo(PersistenceManager pm) throws JDOException {
|
||||
Query query = pm.newQuery(entityClass, filter);
|
||||
prepareQuery(query);
|
||||
query.declareParameters(parameters);
|
||||
if (ordering != null) {
|
||||
query.setOrdering(ordering);
|
||||
}
|
||||
return query.executeWithMap(values);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public Collection find(final String language, final Object queryObject) throws DataAccessException {
|
||||
return (Collection) execute(new JdoCallback() {
|
||||
public Object doInJdo(PersistenceManager pm) throws JDOException {
|
||||
Query query = pm.newQuery(language, queryObject);
|
||||
prepareQuery(query);
|
||||
return query.execute();
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public Collection find(final String queryString) throws DataAccessException {
|
||||
return (Collection) execute(new JdoCallback() {
|
||||
public Object doInJdo(PersistenceManager pm) throws JDOException {
|
||||
Query query = pm.newQuery(queryString);
|
||||
prepareQuery(query);
|
||||
return query.execute();
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public Collection find(final String queryString, final Object[] values) throws DataAccessException {
|
||||
return (Collection) execute(new JdoCallback() {
|
||||
public Object doInJdo(PersistenceManager pm) throws JDOException {
|
||||
Query query = pm.newQuery(queryString);
|
||||
prepareQuery(query);
|
||||
return query.executeWithArray(values);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public Collection find(final String queryString, final Map values) throws DataAccessException {
|
||||
return (Collection) execute(new JdoCallback() {
|
||||
public Object doInJdo(PersistenceManager pm) throws JDOException {
|
||||
Query query = pm.newQuery(queryString);
|
||||
prepareQuery(query);
|
||||
return query.executeWithMap(values);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public Collection findByNamedQuery(final Class entityClass, final String queryName) throws DataAccessException {
|
||||
return (Collection) execute(new JdoCallback() {
|
||||
public Object doInJdo(PersistenceManager pm) throws JDOException {
|
||||
Query query = pm.newNamedQuery(entityClass, queryName);
|
||||
prepareQuery(query);
|
||||
return query.execute();
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public Collection findByNamedQuery(final Class entityClass, final String queryName, final Object[] values)
|
||||
throws DataAccessException {
|
||||
|
||||
return (Collection) execute(new JdoCallback() {
|
||||
public Object doInJdo(PersistenceManager pm) throws JDOException {
|
||||
Query query = pm.newNamedQuery(entityClass, queryName);
|
||||
prepareQuery(query);
|
||||
return query.executeWithArray(values);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public Collection findByNamedQuery(final Class entityClass, final String queryName, final Map values)
|
||||
throws DataAccessException {
|
||||
|
||||
return (Collection) execute(new JdoCallback() {
|
||||
public Object doInJdo(PersistenceManager pm) throws JDOException {
|
||||
Query query = pm.newNamedQuery(entityClass, queryName);
|
||||
prepareQuery(query);
|
||||
return query.executeWithMap(values);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prepare the given JDO query object. To be used within a JdoCallback.
|
||||
* 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] ? Boolean.TRUE : Boolean.FALSE);
|
||||
}
|
||||
else if (method.getName().equals("hashCode")) {
|
||||
// Use hashCode of PersistenceManager proxy.
|
||||
return new Integer(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,587 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
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.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();
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
protected boolean isExistingTransaction(Object transaction) {
|
||||
return ((JdoTransactionObject) transaction).hasTransaction();
|
||||
}
|
||||
|
||||
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 = null;
|
||||
|
||||
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.
|
||||
Object transactionData = getJdoDialect().beginTransaction(pm.currentTransaction(), definition);
|
||||
txObject.setTransactionData(transactionData);
|
||||
|
||||
// Register transaction timeout.
|
||||
int timeout = determineTimeout(definition);
|
||||
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
|
||||
txObject.getPersistenceManagerHolder().setTimeoutInSeconds(timeout);
|
||||
}
|
||||
|
||||
// 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 (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
|
||||
conHolder.setTimeoutInSeconds(timeout);
|
||||
}
|
||||
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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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.
|
||||
*/
|
||||
protected boolean shouldCommitOnGlobalRollbackOnly() {
|
||||
return true;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
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>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 static 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 persistenceManagerHolder;
|
||||
}
|
||||
|
||||
public boolean isNewPersistenceManagerHolder() {
|
||||
return 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 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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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-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 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>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><b>NOTE: This class requires JDO 2.0 or higher, as of Spring 2.5.</b>
|
||||
*
|
||||
* <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, BeanClassLoaderAware, InitializingBean, DisposableBean, PersistenceExceptionTranslator {
|
||||
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private String persistenceManagerFactoryName;
|
||||
|
||||
private Resource configLocation;
|
||||
|
||||
private final Map jdoPropertyMap = new HashMap();
|
||||
|
||||
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 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 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 mergedProps = new HashMap();
|
||||
|
||||
if (this.configLocation != null) {
|
||||
if (logger.isInfoEnabled()) {
|
||||
logger.info("Loading JDO config from [" + this.configLocation + "]");
|
||||
}
|
||||
mergedProps.putAll(PropertiesLoaderUtils.loadProperties(this.configLocation));
|
||||
}
|
||||
|
||||
mergedProps.putAll(this.jdoPropertyMap);
|
||||
|
||||
// Build PersistenceManagerFactory instance.
|
||||
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 Object getObject() {
|
||||
return this.persistenceManagerFactory;
|
||||
}
|
||||
|
||||
public Class 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,321 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
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.ResourceHolder;
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
protected boolean shouldUnbindAtCompletion() {
|
||||
return this.newPersistenceManager;
|
||||
}
|
||||
|
||||
protected void releaseResource(ResourceHolder resourceHolder, Object resourceKey) {
|
||||
releasePersistenceManager(((PersistenceManagerHolder) resourceHolder).getPersistenceManager(),
|
||||
(PersistenceManagerFactory) resourceKey);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* 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;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
super.clear();
|
||||
this.transactionActive = false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,222 @@
|
|||
/*
|
||||
* 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;
|
||||
|
||||
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 a 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 {
|
||||
|
||||
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(), getClass().getClassLoader());
|
||||
this.proxy = (PersistenceManagerFactory) Proxy.newProxyInstance(
|
||||
target.getClass().getClassLoader(), ifcs, new TransactionAwareFactoryInvocationHandler());
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 Object getObject() {
|
||||
return this.proxy;
|
||||
}
|
||||
|
||||
public Class 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 TransactionAwareFactoryInvocationHandler 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] ? Boolean.TRUE : Boolean.FALSE);
|
||||
}
|
||||
else if (method.getName().equals("hashCode")) {
|
||||
// Use hashCode of PersistenceManagerFactory proxy.
|
||||
return new Integer(System.identityHashCode(proxy));
|
||||
}
|
||||
else if (method.getName().equals("getPersistenceManager")) {
|
||||
PersistenceManagerFactory target = getTargetPersistenceManagerFactory();
|
||||
PersistenceManager pm =
|
||||
PersistenceManagerFactoryUtils.doGetPersistenceManager(target, isAllowCreate());
|
||||
Class[] ifcs = ClassUtils.getAllInterfacesForClass(pm.getClass(), getClass().getClassLoader());
|
||||
return (PersistenceManager) Proxy.newProxyInstance(
|
||||
pm.getClass().getClassLoader(), ifcs, new TransactionAwareInvocationHandler(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 TransactionAwareInvocationHandler implements InvocationHandler {
|
||||
|
||||
private final PersistenceManager target;
|
||||
|
||||
private final PersistenceManagerFactory persistenceManagerFactory;
|
||||
|
||||
public TransactionAwareInvocationHandler(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] ? Boolean.TRUE : Boolean.FALSE);
|
||||
}
|
||||
else if (method.getName().equals("hashCode")) {
|
||||
// Use hashCode of PersistenceManager proxy.
|
||||
return new Integer(System.identityHashCode(proxy));
|
||||
}
|
||||
else if (method.getName().equals("close")) {
|
||||
// Handle close method: only close if not within a transaction.
|
||||
if (this.persistenceManagerFactory != null) {
|
||||
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,9 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
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.
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,170 @@
|
|||
/*
|
||||
* 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.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
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
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,147 @@
|
|||
/*
|
||||
* 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.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;
|
||||
}
|
||||
|
||||
|
||||
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 (PersistenceManagerFactory)
|
||||
wac.getBean(getPersistenceManagerFactoryBeanName(), PersistenceManagerFactory.class);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
/*
|
||||
* 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.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.intValue() + 1 : 1;
|
||||
request.setAttribute(getParticipateAttributeName(), new Integer(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.intValue() > 1) {
|
||||
request.setAttribute(participateAttributeName, new Integer(count.intValue() - 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,8 @@
|
|||
<html>
|
||||
<body>
|
||||
|
||||
Classes supporting the <code>org.springframework.orm.jdo</code> package.
|
||||
Contains a DAO base class for JdoTemplate usage.
|
||||
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,448 @@
|
|||
/*
|
||||
* 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.jpa;
|
||||
|
||||
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.Iterator;
|
||||
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.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, BeanClassLoaderAware, InitializingBean, DisposableBean,
|
||||
EntityManagerFactoryInfo, PersistenceExceptionTranslator {
|
||||
|
||||
/** Logger available to subclasses */
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private PersistenceProvider persistenceProvider;
|
||||
|
||||
private String persistenceUnitName;
|
||||
|
||||
private final Map jpaPropertyMap = new HashMap();
|
||||
|
||||
private Class<? extends EntityManagerFactory> entityManagerFactoryInterface;
|
||||
|
||||
private Class<? extends EntityManager> entityManagerInterface;
|
||||
|
||||
private JpaDialect jpaDialect;
|
||||
|
||||
private JpaVendorAdapter jpaVendorAdapter;
|
||||
|
||||
private ClassLoader beanClassLoader = getClass().getClassLoader();
|
||||
|
||||
/** Raw EntityManagerFactory as returned by the PersistenceProvider */
|
||||
public EntityManagerFactory nativeEntityManagerFactory;
|
||||
|
||||
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 = (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 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 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 final void afterPropertiesSet() throws PersistenceException {
|
||||
if (this.jpaVendorAdapter != null) {
|
||||
if (this.persistenceProvider == null) {
|
||||
this.persistenceProvider = this.jpaVendorAdapter.getPersistenceProvider();
|
||||
}
|
||||
Map vendorPropertyMap = this.jpaVendorAdapter.getJpaPropertyMap();
|
||||
if (vendorPropertyMap != null) {
|
||||
for (Iterator it = vendorPropertyMap.entrySet().iterator(); it.hasNext();) {
|
||||
Map.Entry entry = (Map.Entry) it.next();
|
||||
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);
|
||||
EntityManagerFactoryPlusOperations plusOperations = null;
|
||||
if (getJpaDialect() != null && getJpaDialect().supportsEntityManagerFactoryPlusOperations()) {
|
||||
plusOperations = getJpaDialect().getEntityManagerFactoryPlusOperations(emf);
|
||||
ifcs.add(EntityManagerFactoryPlusOperations.class);
|
||||
}
|
||||
return (EntityManagerFactory) Proxy.newProxyInstance(
|
||||
this.beanClassLoader, ifcs.toArray(new Class[ifcs.size()]),
|
||||
new ManagedEntityManagerFactoryInvocationHandler(emf, this, plusOperations));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Dynamic proxy invocation handler proxying an EntityManagerFactory to
|
||||
* return a proxy EntityManager if necessary from createEntityManager()
|
||||
* methods.
|
||||
*/
|
||||
private static class ManagedEntityManagerFactoryInvocationHandler implements InvocationHandler {
|
||||
|
||||
private final EntityManagerFactory targetEntityManagerFactory;
|
||||
|
||||
private final EntityManagerFactoryInfo entityManagerFactoryInfo;
|
||||
|
||||
private final EntityManagerFactoryPlusOperations entityManagerFactoryPlusOperations;
|
||||
|
||||
public ManagedEntityManagerFactoryInvocationHandler(EntityManagerFactory targetEmf,
|
||||
EntityManagerFactoryInfo emfInfo, EntityManagerFactoryPlusOperations entityManagerFactoryPlusOperations) {
|
||||
|
||||
this.targetEntityManagerFactory = targetEmf;
|
||||
this.entityManagerFactoryInfo = emfInfo;
|
||||
this.entityManagerFactoryPlusOperations = entityManagerFactoryPlusOperations;
|
||||
}
|
||||
|
||||
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] ? Boolean.TRUE : Boolean.FALSE);
|
||||
}
|
||||
else if (method.getName().equals("hashCode")) {
|
||||
// Use hashCode of EntityManagerFactory proxy.
|
||||
return new Integer(System.identityHashCode(proxy));
|
||||
}
|
||||
else if (method.getDeclaringClass().isAssignableFrom(EntityManagerFactoryInfo.class)) {
|
||||
return method.invoke(this.entityManagerFactoryInfo, args);
|
||||
}
|
||||
else if (method.getDeclaringClass().equals(EntityManagerFactoryPlusOperations.class)) {
|
||||
return method.invoke(this.entityManagerFactoryPlusOperations, args);
|
||||
}
|
||||
|
||||
Object retVal = method.invoke(this.targetEntityManagerFactory, args);
|
||||
if (retVal instanceof EntityManager) {
|
||||
EntityManager rawEntityManager = (EntityManager) retVal;
|
||||
retVal = ExtendedEntityManagerCreator.createApplicationManagedEntityManager(
|
||||
rawEntityManager, this.entityManagerFactoryInfo);
|
||||
}
|
||||
return retVal;
|
||||
}
|
||||
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,132 @@
|
|||
/*
|
||||
* 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.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.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 {
|
||||
|
||||
/** Logger available to subclasses */
|
||||
protected final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private EntityManagerFactory entityManagerFactory;
|
||||
|
||||
private final Map jpaPropertyMap = new HashMap();
|
||||
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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 getJpaPropertyMap() {
|
||||
return this.jpaPropertyMap;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 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());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,101 @@
|
|||
/*
|
||||
* 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.jpa;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.persistence.spi.PersistenceProvider;
|
||||
import javax.persistence.spi.PersistenceUnitInfo;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
/**
|
||||
* Metadata interface for a Spring-managed JPA {@link EntityManagerFactory}.
|
||||
*
|
||||
* <p>This facility can be obtained from Spring-managed EntityManagerFactory
|
||||
* proxies through casting the EntityManagerFactory handle to this interface.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public interface EntityManagerFactoryInfo {
|
||||
|
||||
/**
|
||||
* Return the raw underlying EntityManagerFactory.
|
||||
* @return the unadorned EntityManagerFactory (never <code>null</code>)
|
||||
*/
|
||||
EntityManagerFactory getNativeEntityManagerFactory();
|
||||
|
||||
/**
|
||||
* Return the underlying PersistenceProvider that the underlying
|
||||
* EntityManagerFactory was created with.
|
||||
* @return the PersistenceProvider used to create this EntityManagerFactory,
|
||||
* or <code>null</code> if the standard JPA provider autodetection process
|
||||
* was used to configure the EntityManagerFactory
|
||||
*/
|
||||
PersistenceProvider getPersistenceProvider();
|
||||
|
||||
/**
|
||||
* Return the PersistenceUnitInfo used to create this
|
||||
* EntityManagerFactory, if the in-container API was used.
|
||||
* @return the PersistenceUnitInfo used to create this EntityManagerFactory,
|
||||
* or <code>null</code> if the in-container contract was not used to
|
||||
* configure the EntityManagerFactory
|
||||
*/
|
||||
PersistenceUnitInfo getPersistenceUnitInfo();
|
||||
|
||||
/**
|
||||
* Return the name of the persistence unit used to create this
|
||||
* EntityManagerFactory, or <code>null</code> if
|
||||
* it is an unnamed default. If <code>getPersistenceUnitInfo()</code>
|
||||
* returns non-null, the return type of <code>getPersistenceUnitName()</code>
|
||||
* must be equal to the value returned by
|
||||
* <code>PersistenceUnitInfo.getPersistenceUnitName()</code>.
|
||||
* @see #getPersistenceUnitInfo()
|
||||
* @see javax.persistence.spi.PersistenceUnitInfo#getPersistenceUnitName()
|
||||
*/
|
||||
String getPersistenceUnitName();
|
||||
|
||||
/**
|
||||
* Return the JDBC DataSource that this EntityManagerFactory
|
||||
* obtains its JDBC Connections from.
|
||||
* @return the JDBC DataSource, or <code>null</code> if not known
|
||||
*/
|
||||
DataSource getDataSource();
|
||||
|
||||
/**
|
||||
* Return the (potentially vendor-specific) EntityManager interface
|
||||
* that this factory's EntityManagers will implement.
|
||||
* <p>A <code>null</code> return value suggests that autodetection is supposed
|
||||
* to happen: either based on a target <code>EntityManager</code> instance
|
||||
* or simply defaulting to <code>javax.persistence.EntityManager</code>.
|
||||
*/
|
||||
Class<? extends EntityManager> getEntityManagerInterface();
|
||||
|
||||
/**
|
||||
* Return the vendor-specific JpaDialect implementation for this
|
||||
* EntityManagerFactory, or <code>null</code> if not known.
|
||||
*/
|
||||
JpaDialect getJpaDialect();
|
||||
|
||||
/**
|
||||
* Return the ClassLoader that the application's beans are loaded with.
|
||||
* <p>Proxies will be generated in this ClassLoader.
|
||||
*/
|
||||
ClassLoader getBeanClassLoader();
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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.jpa;
|
||||
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
|
||||
/**
|
||||
* Extension of the standard JPA EntityManagerFactory interface, linking in
|
||||
* Spring's EntityManagerFactoryPlusOperations interface which defines
|
||||
* additional operations (beyond JPA 1.0) in a vendor-independent fashion.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @since 2.0
|
||||
* @see javax.persistence.EntityManager
|
||||
*/
|
||||
public interface EntityManagerFactoryPlus extends EntityManagerFactory, EntityManagerFactoryPlusOperations {
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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.jpa;
|
||||
|
||||
/**
|
||||
* Interface that defines common operations beyond the standard
|
||||
* JPA EntityManagerFactory interface, in a vendor-independent fashion.
|
||||
* To be adapted to specific JPA providers through a JpaDialect.
|
||||
*
|
||||
* <p>As of Spring 2.0, this interface does not define any operations yet.
|
||||
* The pass-through mechanism to the underlying JpaDialect is already in
|
||||
* place. Concrete operations will be added in the Spring 2.5 timeframe.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @since 2.0
|
||||
* @see JpaDialect#getEntityManagerPlusOperations
|
||||
* @see javax.persistence.EntityManagerFactory
|
||||
*/
|
||||
public interface EntityManagerFactoryPlusOperations {
|
||||
|
||||
}
|
|
@ -0,0 +1,367 @@
|
|||
/*
|
||||
* 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.jpa;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.EntityExistsException;
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.persistence.EntityNotFoundException;
|
||||
import javax.persistence.NoResultException;
|
||||
import javax.persistence.NonUniqueResultException;
|
||||
import javax.persistence.OptimisticLockException;
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.persistence.TransactionRequiredException;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.ListableBeanFactory;
|
||||
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.dao.EmptyResultDataAccessException;
|
||||
import org.springframework.dao.IncorrectResultSizeDataAccessException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.jdbc.datasource.DataSourceUtils;
|
||||
import org.springframework.transaction.support.ResourceHolder;
|
||||
import org.springframework.transaction.support.ResourceHolderSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Helper class featuring methods for JPA EntityManager handling,
|
||||
* allowing for reuse of EntityManager instances within transactions.
|
||||
* Also provides support for exception translation.
|
||||
*
|
||||
* <p>Mainly intended for internal use within the framework.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public abstract class EntityManagerFactoryUtils {
|
||||
|
||||
/**
|
||||
* Order value for TransactionSynchronization objects that clean up JPA
|
||||
* EntityManagers. Return DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 100
|
||||
* to execute EntityManager cleanup before JDBC Connection cleanup, if any.
|
||||
* @see org.springframework.jdbc.datasource.DataSourceUtils#CONNECTION_SYNCHRONIZATION_ORDER
|
||||
*/
|
||||
public static final int ENTITY_MANAGER_SYNCHRONIZATION_ORDER =
|
||||
DataSourceUtils.CONNECTION_SYNCHRONIZATION_ORDER - 100;
|
||||
|
||||
private static final Log logger = LogFactory.getLog(EntityManagerFactoryUtils.class);
|
||||
|
||||
|
||||
/**
|
||||
* Find an EntityManagerFactory with the given name in the given
|
||||
* Spring application context (represented as ListableBeanFactory).
|
||||
* <p>The specified unit name will be matched against the configured
|
||||
* peristence unit, provided that a discovered EntityManagerFactory
|
||||
* implements the {@link EntityManagerFactoryInfo} interface. If not,
|
||||
* the persistence unit name will be matched against the Spring bean name,
|
||||
* assuming that the EntityManagerFactory bean names follow that convention.
|
||||
* @param beanFactory the ListableBeanFactory to search
|
||||
* @param unitName the name of the persistence unit (never empty)
|
||||
* @return the EntityManagerFactory
|
||||
* @throws NoSuchBeanDefinitionException if there is no such EntityManagerFactory in the context
|
||||
* @see EntityManagerFactoryInfo#getPersistenceUnitName()
|
||||
*/
|
||||
public static EntityManagerFactory findEntityManagerFactory(
|
||||
ListableBeanFactory beanFactory, String unitName) throws NoSuchBeanDefinitionException {
|
||||
|
||||
Assert.notNull(beanFactory, "ListableBeanFactory must not be null");
|
||||
Assert.hasLength(unitName, "Unit name must not be empty");
|
||||
|
||||
// See whether we can find an EntityManagerFactory with matching persistence unit name.
|
||||
String[] candidateNames =
|
||||
BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, EntityManagerFactory.class);
|
||||
for (String candidateName : candidateNames) {
|
||||
EntityManagerFactory emf = (EntityManagerFactory) beanFactory.getBean(candidateName);
|
||||
if (emf instanceof EntityManagerFactoryInfo) {
|
||||
if (unitName.equals(((EntityManagerFactoryInfo) emf).getPersistenceUnitName())) {
|
||||
return emf;
|
||||
}
|
||||
}
|
||||
}
|
||||
// No matching persistence unit found - simply take the EntityManagerFactory
|
||||
// with the persistence unit name as bean name (by convention).
|
||||
return (EntityManagerFactory) beanFactory.getBean(unitName, EntityManagerFactory.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a JPA EntityManager from the given factory. Is aware of a
|
||||
* corresponding EntityManager bound to the current thread,
|
||||
* for example when using JpaTransactionManager.
|
||||
* <p>Note: Will return <code>null</code> if no thread-bound EntityManager found!
|
||||
* @param emf EntityManagerFactory to create the EntityManager with
|
||||
* @return the EntityManager, or <code>null</code> if none found
|
||||
* @throws DataAccessResourceFailureException if the EntityManager couldn't be obtained
|
||||
* @see JpaTransactionManager
|
||||
*/
|
||||
public static EntityManager getTransactionalEntityManager(EntityManagerFactory emf)
|
||||
throws DataAccessResourceFailureException {
|
||||
|
||||
return getTransactionalEntityManager(emf, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a JPA EntityManager from the given factory. Is aware of a
|
||||
* corresponding EntityManager bound to the current thread,
|
||||
* for example when using JpaTransactionManager.
|
||||
* <p>Note: Will return <code>null</code> if no thread-bound EntityManager found!
|
||||
* @param emf EntityManagerFactory to create the EntityManager with
|
||||
* @param properties the properties to be passed into the <code>createEntityManager</code>
|
||||
* call (may be <code>null</code>)
|
||||
* @return the EntityManager, or <code>null</code> if none found
|
||||
* @throws DataAccessResourceFailureException if the EntityManager couldn't be obtained
|
||||
* @see JpaTransactionManager
|
||||
*/
|
||||
public static EntityManager getTransactionalEntityManager(EntityManagerFactory emf, Map properties)
|
||||
throws DataAccessResourceFailureException {
|
||||
try {
|
||||
return doGetTransactionalEntityManager(emf, properties);
|
||||
}
|
||||
catch (PersistenceException ex) {
|
||||
throw new DataAccessResourceFailureException("Could not obtain JPA EntityManager", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Obtain a JPA EntityManager from the given factory. Is aware of a
|
||||
* corresponding EntityManager bound to the current thread,
|
||||
* for example when using JpaTransactionManager.
|
||||
* <p>Same as <code>getEntityManager</code>, but throwing the original PersistenceException.
|
||||
* @param emf EntityManagerFactory to create the EntityManager with
|
||||
* @param properties the properties to be passed into the <code>createEntityManager</code>
|
||||
* call (may be <code>null</code>)
|
||||
* @return the EntityManager, or <code>null</code> if none found
|
||||
* @throws javax.persistence.PersistenceException if the EntityManager couldn't be created
|
||||
* @see #getTransactionalEntityManager(javax.persistence.EntityManagerFactory)
|
||||
* @see JpaTransactionManager
|
||||
*/
|
||||
public static EntityManager doGetTransactionalEntityManager(
|
||||
EntityManagerFactory emf, Map properties) throws PersistenceException {
|
||||
|
||||
Assert.notNull(emf, "No EntityManagerFactory specified");
|
||||
|
||||
EntityManagerHolder emHolder =
|
||||
(EntityManagerHolder) TransactionSynchronizationManager.getResource(emf);
|
||||
if (emHolder != null) {
|
||||
if (!emHolder.isSynchronizedWithTransaction() &&
|
||||
TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||
// Try to explicitly synchronize the EntityManager itself
|
||||
// with an ongoing JTA transaction, if any.
|
||||
try {
|
||||
emHolder.getEntityManager().joinTransaction();
|
||||
}
|
||||
catch (TransactionRequiredException ex) {
|
||||
logger.debug("Could not join JTA transaction because none was active", ex);
|
||||
}
|
||||
Object transactionData = prepareTransaction(emHolder.getEntityManager(), emf);
|
||||
TransactionSynchronizationManager.registerSynchronization(
|
||||
new EntityManagerSynchronization(emHolder, emf, transactionData, false));
|
||||
emHolder.setSynchronizedWithTransaction(true);
|
||||
}
|
||||
return emHolder.getEntityManager();
|
||||
}
|
||||
|
||||
if (!TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||
// Indicate that we can't obtain a transactional EntityManager.
|
||||
return null;
|
||||
}
|
||||
|
||||
// Create a new EntityManager for use within the current transaction.
|
||||
logger.debug("Opening JPA EntityManager");
|
||||
EntityManager em =
|
||||
(!CollectionUtils.isEmpty(properties) ? emf.createEntityManager(properties) : emf.createEntityManager());
|
||||
|
||||
if (TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||
logger.debug("Registering transaction synchronization for JPA EntityManager");
|
||||
// Use same EntityManager for further JPA actions within the transaction.
|
||||
// Thread object will get removed by synchronization at transaction completion.
|
||||
emHolder = new EntityManagerHolder(em);
|
||||
Object transactionData = prepareTransaction(em, emf);
|
||||
TransactionSynchronizationManager.registerSynchronization(
|
||||
new EntityManagerSynchronization(emHolder, emf, transactionData, true));
|
||||
emHolder.setSynchronizedWithTransaction(true);
|
||||
TransactionSynchronizationManager.bindResource(emf, emHolder);
|
||||
}
|
||||
|
||||
return em;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a transaction on the given EntityManager, if possible.
|
||||
* @param em the EntityManager to prepare
|
||||
* @param emf the EntityManagerFactory that the EntityManager has been created with
|
||||
* @return an arbitrary object that holds transaction data, if any
|
||||
* (to be passed into cleanupTransaction)
|
||||
* @see JpaDialect#prepareTransaction
|
||||
*/
|
||||
private static Object prepareTransaction(EntityManager em, EntityManagerFactory emf) {
|
||||
if (emf instanceof EntityManagerFactoryInfo) {
|
||||
EntityManagerFactoryInfo emfInfo = (EntityManagerFactoryInfo) emf;
|
||||
JpaDialect jpaDialect = emfInfo.getJpaDialect();
|
||||
if (jpaDialect != null) {
|
||||
return jpaDialect.prepareTransaction(em,
|
||||
TransactionSynchronizationManager.isCurrentTransactionReadOnly(),
|
||||
TransactionSynchronizationManager.getCurrentTransactionName());
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepare a transaction on the given EntityManager, if possible.
|
||||
* @param transactionData arbitrary object that holds transaction data, if any
|
||||
* (as returned by prepareTransaction)
|
||||
* @param emf the EntityManagerFactory that the EntityManager has been created with
|
||||
* @see JpaDialect#cleanupTransaction
|
||||
*/
|
||||
private static void cleanupTransaction(Object transactionData, EntityManagerFactory emf) {
|
||||
if (emf instanceof EntityManagerFactoryInfo) {
|
||||
EntityManagerFactoryInfo emfInfo = (EntityManagerFactoryInfo) emf;
|
||||
JpaDialect jpaDialect = emfInfo.getJpaDialect();
|
||||
if (jpaDialect != null) {
|
||||
jpaDialect.cleanupTransaction(transactionData);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given runtime exception to an appropriate exception from the
|
||||
* <code>org.springframework.dao</code> hierarchy.
|
||||
* Return null if no translation is appropriate: any other exception may
|
||||
* have resulted from user code, and should not be translated.
|
||||
* <p>The most important cases like object not found or optimistic locking
|
||||
* failure are covered here. For more fine-granular conversion, JpaAccessor and
|
||||
* JpaTransactionManager support sophisticated translation of exceptions via a
|
||||
* JpaDialect.
|
||||
* @param ex runtime exception that occured
|
||||
* @return the corresponding DataAccessException instance,
|
||||
* or <code>null</code> if the exception should not be translated
|
||||
*/
|
||||
public static DataAccessException convertJpaAccessExceptionIfPossible(RuntimeException ex) {
|
||||
// Following the JPA specification, a persistence provider can also
|
||||
// throw these two exceptions, besides PersistenceException.
|
||||
if (ex instanceof IllegalStateException) {
|
||||
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof IllegalArgumentException) {
|
||||
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
// Check for well-known PersistenceException subclasses.
|
||||
if (ex instanceof EntityNotFoundException) {
|
||||
return new JpaObjectRetrievalFailureException((EntityNotFoundException) ex);
|
||||
}
|
||||
if (ex instanceof NoResultException) {
|
||||
return new EmptyResultDataAccessException(ex.getMessage(), 1);
|
||||
}
|
||||
if (ex instanceof NonUniqueResultException) {
|
||||
return new IncorrectResultSizeDataAccessException(ex.getMessage(), 1);
|
||||
}
|
||||
if (ex instanceof OptimisticLockException) {
|
||||
return new JpaOptimisticLockingFailureException((OptimisticLockException) ex);
|
||||
}
|
||||
if (ex instanceof EntityExistsException) {
|
||||
return new DataIntegrityViolationException(ex.getMessage(), ex);
|
||||
}
|
||||
if (ex instanceof TransactionRequiredException) {
|
||||
return new InvalidDataAccessApiUsageException(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
// If we have another kind of PersistenceException, throw it.
|
||||
if (ex instanceof PersistenceException) {
|
||||
return new JpaSystemException((PersistenceException) ex);
|
||||
}
|
||||
|
||||
// If we get here, we have an exception that resulted from user code,
|
||||
// rather than the persistence provider, so we return null to indicate
|
||||
// that translation should not occur.
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the given JPA EntityManager,
|
||||
* catching and logging any cleanup exceptions thrown.
|
||||
* @param em the JPA EntityManager to close (may be <code>null</code>)
|
||||
* @see javax.persistence.EntityManager#close()
|
||||
*/
|
||||
public static void closeEntityManager(EntityManager em) {
|
||||
if (em != null) {
|
||||
logger.debug("Closing JPA EntityManager");
|
||||
try {
|
||||
em.close();
|
||||
}
|
||||
catch (PersistenceException ex) {
|
||||
logger.debug("Could not close JPA EntityManager", ex);
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.debug("Unexpected exception on closing JPA EntityManager", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Callback for resource cleanup at the end of a non-JPA transaction
|
||||
* (e.g. when participating in a JtaTransactionManager transaction).
|
||||
* @see org.springframework.transaction.jta.JtaTransactionManager
|
||||
*/
|
||||
private static class EntityManagerSynchronization extends ResourceHolderSynchronization implements Ordered {
|
||||
|
||||
private final Object transactionData;
|
||||
|
||||
private final boolean newEntityManager;
|
||||
|
||||
public EntityManagerSynchronization(
|
||||
EntityManagerHolder emHolder, EntityManagerFactory emf, Object transactionData, boolean newEntityManager) {
|
||||
super(emHolder, emf);
|
||||
this.transactionData = transactionData;
|
||||
this.newEntityManager = newEntityManager;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return ENTITY_MANAGER_SYNCHRONIZATION_ORDER;
|
||||
}
|
||||
|
||||
protected boolean shouldUnbindAtCompletion() {
|
||||
return this.newEntityManager;
|
||||
}
|
||||
|
||||
protected void releaseResource(ResourceHolder resourceHolder, Object resourceKey) {
|
||||
closeEntityManager(((EntityManagerHolder) resourceHolder).getEntityManager());
|
||||
}
|
||||
|
||||
protected void cleanupResource(ResourceHolder resourceHolder, Object resourceKey, boolean committed) {
|
||||
if (!committed) {
|
||||
// Clear all pending inserts/updates/deletes in the EntityManager.
|
||||
// Necessary for pre-bound EntityManagers, to avoid inconsistent state.
|
||||
((EntityManagerHolder) resourceHolder).getEntityManager().clear();
|
||||
}
|
||||
cleanupTransaction(this.transactionData, (EntityManagerFactory) resourceKey);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* 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 javax.persistence.EntityManager;
|
||||
|
||||
import org.springframework.transaction.SavepointManager;
|
||||
import org.springframework.transaction.support.ResourceHolderSupport;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Holder wrapping a JPA EntityManager.
|
||||
* JpaTransactionManager binds instances of this class to the thread,
|
||||
* for a given EntityManagerFactory.
|
||||
*
|
||||
* <p>Note: This is an SPI class, not intended to be used by applications.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see JpaTransactionManager
|
||||
* @see EntityManagerFactoryUtils
|
||||
*/
|
||||
public class EntityManagerHolder extends ResourceHolderSupport {
|
||||
|
||||
private final EntityManager entityManager;
|
||||
|
||||
private boolean transactionActive;
|
||||
|
||||
private SavepointManager savepointManager;
|
||||
|
||||
|
||||
public EntityManagerHolder(EntityManager entityManager) {
|
||||
Assert.notNull(entityManager, "EntityManager must not be null");
|
||||
this.entityManager = entityManager;
|
||||
}
|
||||
|
||||
|
||||
public EntityManager getEntityManager() {
|
||||
return this.entityManager;
|
||||
}
|
||||
|
||||
protected void setTransactionActive(boolean transactionActive) {
|
||||
this.transactionActive = transactionActive;
|
||||
}
|
||||
|
||||
protected boolean isTransactionActive() {
|
||||
return this.transactionActive;
|
||||
}
|
||||
|
||||
protected void setSavepointManager(SavepointManager savepointManager) {
|
||||
this.savepointManager = savepointManager;
|
||||
}
|
||||
|
||||
protected SavepointManager getSavepointManager() {
|
||||
return this.savepointManager;
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
super.clear();
|
||||
this.transactionActive = false;
|
||||
this.savepointManager = null;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
* 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.jpa;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
/**
|
||||
* Extension of the standard JPA EntityManager interface, linking in
|
||||
* Spring's EntityManagerPlusOperations interface which defines additional
|
||||
* operations (beyond JPA 1.0) in a vendor-independent fashion.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @since 2.0
|
||||
* @see javax.persistence.EntityManager
|
||||
*/
|
||||
public interface EntityManagerPlus extends EntityManager, EntityManagerPlusOperations {
|
||||
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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.jpa;
|
||||
|
||||
/**
|
||||
* Interface that defines common operations beyond the standard
|
||||
* JPA EntityManager interface, in a vendor-independent fashion.
|
||||
* To be adapted to specific JPA providers through a JpaDialect.
|
||||
*
|
||||
* <p>As of Spring 2.0, this interface does not define any operations yet.
|
||||
* The pass-through mechanism to the underlying JpaDialect is already in
|
||||
* place. Concrete operations will be added in the Spring 2.5 timeframe.
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @since 2.0
|
||||
* @see JpaDialect#getEntityManagerPlusOperations
|
||||
* @see javax.persistence.EntityManager
|
||||
*/
|
||||
public interface EntityManagerPlusOperations {
|
||||
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* 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 javax.persistence.EntityManager;
|
||||
|
||||
/**
|
||||
* Subinterface of {@link javax.persistence.EntityManager} to be implemented by
|
||||
* EntityManager proxies. Allows access to the underlying target EntityManager.
|
||||
*
|
||||
* <p>This interface is mainly intended for framework usage. Application code
|
||||
* should prefer the use of the {@link javax.persistence.EntityManager#getDelegate()}
|
||||
* method to access native functionality of the underlying resource.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.5
|
||||
*/
|
||||
public interface EntityManagerProxy extends EntityManager {
|
||||
|
||||
/**
|
||||
* Return the underlying EntityManager that this proxy will delegate to.
|
||||
* <p>In case of an extended EntityManager, this will be the associated
|
||||
* raw EntityManager.
|
||||
* <p>In case of a shared ("transactional") EntityManager, this will be
|
||||
* the raw EntityManager that is currently associated with the transaction.
|
||||
* Outside of a transaction, an IllegalStateException will be thrown.
|
||||
* @return the underlying raw EntityManager (never <code>null</code>)
|
||||
* @throws IllegalStateException if no underlying EntityManager is available
|
||||
*/
|
||||
EntityManager getTargetEntityManager() throws IllegalStateException;
|
||||
|
||||
}
|
|
@ -0,0 +1,486 @@
|
|||
/*
|
||||
* 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.jpa;
|
||||
|
||||
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.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.persistence.EntityTransaction;
|
||||
import javax.persistence.TransactionRequiredException;
|
||||
import javax.persistence.spi.PersistenceUnitInfo;
|
||||
import javax.persistence.spi.PersistenceUnitTransactionType;
|
||||
|
||||
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.support.PersistenceExceptionTranslator;
|
||||
import org.springframework.transaction.support.ResourceHolderSynchronization;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* Factory for dynamic EntityManager proxies that follow the JPA spec's
|
||||
* semantics for "extended" EntityManagers.
|
||||
*
|
||||
* <p>Supports explicit joining of a transaction through the
|
||||
* <code>joinTransaction()</code> method ("application-managed extended
|
||||
* EntityManager") as well as automatic joining on each operation
|
||||
* ("container-managed extended EntityManager").
|
||||
*
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
*/
|
||||
public abstract class ExtendedEntityManagerCreator {
|
||||
|
||||
/**
|
||||
* Create an EntityManager that can join transactions with the
|
||||
* <code>joinTransaction()</code> method, but is not automatically
|
||||
* managed by the container.
|
||||
* @param rawEntityManager raw EntityManager
|
||||
* @param plusOperations an implementation of the EntityManagerPlusOperations
|
||||
* interface, if those operations should be exposed (may be <code>null</code>)
|
||||
* @return an application-managed EntityManager that can join transactions
|
||||
* but does not participate in them automatically
|
||||
*/
|
||||
public static EntityManager createApplicationManagedEntityManager(
|
||||
EntityManager rawEntityManager, EntityManagerPlusOperations plusOperations) {
|
||||
|
||||
return createProxy(rawEntityManager, null, null, plusOperations, null, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an EntityManager that can join transactions with the
|
||||
* <code>joinTransaction()</code> method, but is not automatically
|
||||
* managed by the container.
|
||||
* @param rawEntityManager raw EntityManager
|
||||
* @param plusOperations an implementation of the EntityManagerPlusOperations
|
||||
* interface, if those operations should be exposed (may be <code>null</code>)
|
||||
* @param exceptionTranslator the exception translator to use for translating
|
||||
* JPA commit/rollback exceptions during transaction synchronization
|
||||
* (may be <code>null</code>)
|
||||
* @return an application-managed EntityManager that can join transactions
|
||||
* but does not participate in them automatically
|
||||
*/
|
||||
public static EntityManager createApplicationManagedEntityManager(
|
||||
EntityManager rawEntityManager, EntityManagerPlusOperations plusOperations,
|
||||
PersistenceExceptionTranslator exceptionTranslator) {
|
||||
|
||||
return createProxy(rawEntityManager, null, null, plusOperations, exceptionTranslator, null, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an EntityManager that can join transactions with the
|
||||
* <code>joinTransaction()</code> method, but is not automatically
|
||||
* managed by the container.
|
||||
* @param rawEntityManager raw EntityManager
|
||||
* @param emfInfo the EntityManagerFactoryInfo to obtain the
|
||||
* EntityManagerPlusOperations and PersistenceUnitInfo from
|
||||
* @return an application-managed EntityManager that can join transactions
|
||||
* but does not participate in them automatically
|
||||
*/
|
||||
public static EntityManager createApplicationManagedEntityManager(
|
||||
EntityManager rawEntityManager, EntityManagerFactoryInfo emfInfo) {
|
||||
|
||||
return createProxy(rawEntityManager, emfInfo, false);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create an EntityManager that automatically joins transactions on each
|
||||
* operation in a transaction.
|
||||
* @param rawEntityManager raw EntityManager
|
||||
* @param plusOperations an implementation of the EntityManagerPlusOperations
|
||||
* interface, if those operations should be exposed (may be <code>null</code>)
|
||||
* @return a container-managed EntityManager that will automatically participate
|
||||
* in any managed transaction
|
||||
*/
|
||||
public static EntityManager createContainerManagedEntityManager(
|
||||
EntityManager rawEntityManager, EntityManagerPlusOperations plusOperations) {
|
||||
|
||||
return createProxy(rawEntityManager, null, null, plusOperations, null, null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an EntityManager that automatically joins transactions on each
|
||||
* operation in a transaction.
|
||||
* @param rawEntityManager raw EntityManager
|
||||
* @param plusOperations an implementation of the EntityManagerPlusOperations
|
||||
* interface, if those operations should be exposed (may be <code>null</code>)
|
||||
* @param exceptionTranslator the exception translator to use for translating
|
||||
* JPA commit/rollback exceptions during transaction synchronization
|
||||
* (may be <code>null</code>)
|
||||
* @return a container-managed EntityManager that will automatically participate
|
||||
* in any managed transaction
|
||||
*/
|
||||
public static EntityManager createContainerManagedEntityManager(
|
||||
EntityManager rawEntityManager, EntityManagerPlusOperations plusOperations,
|
||||
PersistenceExceptionTranslator exceptionTranslator) {
|
||||
|
||||
return createProxy(rawEntityManager, null, null, plusOperations, exceptionTranslator, null, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an EntityManager that automatically joins transactions on each
|
||||
* operation in a transaction.
|
||||
* @param rawEntityManager raw EntityManager
|
||||
* @param emfInfo the EntityManagerFactoryInfo to obtain the
|
||||
* EntityManagerPlusOperations and PersistenceUnitInfo from
|
||||
* @return a container-managed EntityManager that will automatically participate
|
||||
* in any managed transaction
|
||||
*/
|
||||
public static EntityManager createContainerManagedEntityManager(
|
||||
EntityManager rawEntityManager, EntityManagerFactoryInfo emfInfo) {
|
||||
|
||||
return createProxy(rawEntityManager, emfInfo, true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Create an EntityManager that automatically joins transactions on each
|
||||
* operation in a transaction.
|
||||
* @param emf the EntityManagerFactory to create the EntityManager with.
|
||||
* If this implements the EntityManagerFactoryInfo interface, appropriate handling
|
||||
* of the native EntityManagerFactory and available EntityManagerPlusOperations
|
||||
* will automatically apply.
|
||||
* @return a container-managed EntityManager that will automatically participate
|
||||
* in any managed transaction
|
||||
* @see javax.persistence.EntityManagerFactory#createEntityManager()
|
||||
*/
|
||||
public static EntityManager createContainerManagedEntityManager(EntityManagerFactory emf) {
|
||||
return createContainerManagedEntityManager(emf, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an EntityManager that automatically joins transactions on each
|
||||
* operation in a transaction.
|
||||
* @param emf the EntityManagerFactory to create the EntityManager with.
|
||||
* If this implements the EntityManagerFactoryInfo interface, appropriate handling
|
||||
* of the native EntityManagerFactory and available EntityManagerPlusOperations
|
||||
* will automatically apply.
|
||||
* @param properties the properties to be passed into the <code>createEntityManager</code>
|
||||
* call (may be <code>null</code>)
|
||||
* @return a container-managed EntityManager that will automatically participate
|
||||
* in any managed transaction
|
||||
* @see javax.persistence.EntityManagerFactory#createEntityManager(java.util.Map)
|
||||
*/
|
||||
public static EntityManager createContainerManagedEntityManager(EntityManagerFactory emf, Map properties) {
|
||||
Assert.notNull(emf, "EntityManagerFactory must not be null");
|
||||
if (emf instanceof EntityManagerFactoryInfo) {
|
||||
EntityManagerFactoryInfo emfInfo = (EntityManagerFactoryInfo) emf;
|
||||
EntityManagerFactory nativeEmf = emfInfo.getNativeEntityManagerFactory();
|
||||
EntityManager rawEntityManager = (!CollectionUtils.isEmpty(properties) ?
|
||||
nativeEmf.createEntityManager(properties) : nativeEmf.createEntityManager());
|
||||
return createProxy(rawEntityManager, emfInfo, true);
|
||||
}
|
||||
else {
|
||||
EntityManager rawEntityManager = (!CollectionUtils.isEmpty(properties) ?
|
||||
emf.createEntityManager(properties) : emf.createEntityManager());
|
||||
return createProxy(rawEntityManager, null, null, null, null, null, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Actually create the EntityManager proxy.
|
||||
* @param rawEntityManager raw EntityManager
|
||||
* @param emfInfo the EntityManagerFactoryInfo to obtain the
|
||||
* EntityManagerPlusOperations and PersistenceUnitInfo from
|
||||
* @param containerManaged whether to follow container-managed EntityManager
|
||||
* or application-managed EntityManager semantics
|
||||
* @return the EntityManager proxy
|
||||
*/
|
||||
private static EntityManager createProxy(
|
||||
EntityManager rawEntityManager, EntityManagerFactoryInfo emfInfo, boolean containerManaged) {
|
||||
|
||||
Assert.notNull(emfInfo, "EntityManagerFactoryInfo must not be null");
|
||||
JpaDialect jpaDialect = emfInfo.getJpaDialect();
|
||||
EntityManagerPlusOperations plusOperations = null;
|
||||
if (jpaDialect != null && jpaDialect.supportsEntityManagerPlusOperations()) {
|
||||
plusOperations = jpaDialect.getEntityManagerPlusOperations(rawEntityManager);
|
||||
}
|
||||
PersistenceUnitInfo pui = emfInfo.getPersistenceUnitInfo();
|
||||
Boolean jta = (pui != null ? pui.getTransactionType() == PersistenceUnitTransactionType.JTA : null);
|
||||
return createProxy(rawEntityManager, emfInfo.getEntityManagerInterface(),
|
||||
emfInfo.getBeanClassLoader(), plusOperations, jpaDialect, jta, containerManaged);
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually create the EntityManager proxy.
|
||||
* @param rawEm raw EntityManager
|
||||
* @param emIfc the (potentially vendor-specific) EntityManager
|
||||
* interface to proxy, or <code>null</code> for default detection of all interfaces
|
||||
* @param plusOperations an implementation of the EntityManagerPlusOperations
|
||||
* interface, if those operations should be exposed (may be <code>null</code>)
|
||||
* @param exceptionTranslator the PersistenceException translator to use
|
||||
* @param jta whether to create a JTA-aware EntityManager
|
||||
* (or <code>null</code> if not known in advance)
|
||||
* @param containerManaged whether to follow container-managed EntityManager
|
||||
* or application-managed EntityManager semantics
|
||||
* @return the EntityManager proxy
|
||||
*/
|
||||
private static EntityManager createProxy(
|
||||
EntityManager rawEm, Class<? extends EntityManager> emIfc, ClassLoader cl,
|
||||
EntityManagerPlusOperations plusOperations, PersistenceExceptionTranslator exceptionTranslator,
|
||||
Boolean jta, boolean containerManaged) {
|
||||
|
||||
Assert.notNull(rawEm, "EntityManager must not be null");
|
||||
Set<Class> ifcs = new LinkedHashSet<Class>();
|
||||
if (emIfc != null) {
|
||||
ifcs.add(emIfc);
|
||||
}
|
||||
else {
|
||||
ifcs.addAll(ClassUtils.getAllInterfacesForClassAsSet(rawEm.getClass(), cl));
|
||||
}
|
||||
ifcs.add(EntityManagerProxy.class);
|
||||
if (plusOperations != null) {
|
||||
ifcs.add(EntityManagerPlusOperations.class);
|
||||
}
|
||||
return (EntityManager) Proxy.newProxyInstance(
|
||||
(cl != null ? cl : ExtendedEntityManagerCreator.class.getClassLoader()),
|
||||
ifcs.toArray(new Class[ifcs.size()]),
|
||||
new ExtendedEntityManagerInvocationHandler(
|
||||
rawEm, plusOperations, exceptionTranslator, jta, containerManaged));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* InvocationHandler for extended EntityManagers as defined in the JPA spec.
|
||||
*/
|
||||
private static class ExtendedEntityManagerInvocationHandler implements InvocationHandler, Serializable {
|
||||
|
||||
private static final Log logger = LogFactory.getLog(ExtendedEntityManagerInvocationHandler.class);
|
||||
|
||||
private final EntityManager target;
|
||||
|
||||
private final EntityManagerPlusOperations plusOperations;
|
||||
|
||||
private final PersistenceExceptionTranslator exceptionTranslator;
|
||||
|
||||
private final boolean containerManaged;
|
||||
|
||||
private boolean jta;
|
||||
|
||||
private ExtendedEntityManagerInvocationHandler(
|
||||
EntityManager target, EntityManagerPlusOperations plusOperations,
|
||||
PersistenceExceptionTranslator exceptionTranslator, Boolean jta, boolean containerManaged) {
|
||||
|
||||
this.target = target;
|
||||
this.plusOperations = plusOperations;
|
||||
this.exceptionTranslator = exceptionTranslator;
|
||||
this.jta = (jta != null ? jta.booleanValue() : isJtaEntityManager());
|
||||
this.containerManaged = containerManaged;
|
||||
}
|
||||
|
||||
private boolean isJtaEntityManager() {
|
||||
try {
|
||||
this.target.getTransaction();
|
||||
return false;
|
||||
}
|
||||
catch (IllegalStateException ex) {
|
||||
logger.debug("Cannot access EntityTransaction handle - assuming we're in a JTA environment");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
// Invocation on EntityManager 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 EntityManager proxy.
|
||||
return hashCode();
|
||||
}
|
||||
else if (method.getName().equals("getTargetEntityManager")) {
|
||||
return this.target;
|
||||
}
|
||||
else if (method.getName().equals("isOpen")) {
|
||||
if (this.containerManaged) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (method.getName().equals("close")) {
|
||||
if (this.containerManaged) {
|
||||
throw new IllegalStateException("Invalid usage: Cannot close a container-managed EntityManager");
|
||||
}
|
||||
}
|
||||
else if (method.getName().equals("getTransaction")) {
|
||||
if (this.containerManaged) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot execute getTransaction() on a container-managed EntityManager");
|
||||
}
|
||||
}
|
||||
else if (method.getName().equals("joinTransaction")) {
|
||||
doJoinTransaction(true);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Do automatic joining if required.
|
||||
if (this.containerManaged && method.getDeclaringClass().isInterface()) {
|
||||
doJoinTransaction(false);
|
||||
}
|
||||
|
||||
// Invoke method on current EntityManager.
|
||||
try {
|
||||
if (method.getDeclaringClass().equals(EntityManagerPlusOperations.class)) {
|
||||
return method.invoke(this.plusOperations, args);
|
||||
}
|
||||
else {
|
||||
return method.invoke(this.target, args);
|
||||
}
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
throw ex.getTargetException();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Join an existing transaction, if not already joined.
|
||||
* @param enforce whether to enforce the transaction
|
||||
* (i.e. whether failure to join is considered fatal)
|
||||
*/
|
||||
private void doJoinTransaction(boolean enforce) {
|
||||
if (this.jta) {
|
||||
// Let's try whether we're in a JTA transaction.
|
||||
try {
|
||||
this.target.joinTransaction();
|
||||
logger.debug("Joined JTA transaction");
|
||||
}
|
||||
catch (TransactionRequiredException ex) {
|
||||
if (!enforce) {
|
||||
logger.debug("No JTA transaction to join: " + ex);
|
||||
}
|
||||
else {
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (TransactionSynchronizationManager.isSynchronizationActive()) {
|
||||
if (!TransactionSynchronizationManager.hasResource(this.target) &&
|
||||
!this.target.getTransaction().isActive()) {
|
||||
enlistInCurrentTransaction();
|
||||
}
|
||||
logger.debug("Joined local transaction");
|
||||
}
|
||||
else {
|
||||
if (!enforce) {
|
||||
logger.debug("No local transaction to join");
|
||||
}
|
||||
else {
|
||||
throw new TransactionRequiredException("No local transaction to join");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enlist this application-managed EntityManager in the current transaction.
|
||||
*/
|
||||
private void enlistInCurrentTransaction() {
|
||||
// Resource local transaction, need to acquire the EntityTransaction,
|
||||
// start a transaction now and enlist a synchronization for
|
||||
// commit or rollback later.
|
||||
EntityTransaction et = this.target.getTransaction();
|
||||
et.begin();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Starting resource local transaction on application-managed " +
|
||||
"EntityManager [" + this.target + "]");
|
||||
}
|
||||
ExtendedEntityManagerSynchronization extendedEntityManagerSynchronization =
|
||||
new ExtendedEntityManagerSynchronization(this.target, this.exceptionTranslator);
|
||||
TransactionSynchronizationManager.bindResource(this.target,
|
||||
extendedEntityManagerSynchronization);
|
||||
TransactionSynchronizationManager.registerSynchronization(extendedEntityManagerSynchronization);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* TransactionSynchronization enlisting an extended EntityManager
|
||||
* with a current Spring transaction.
|
||||
*/
|
||||
private static class ExtendedEntityManagerSynchronization extends ResourceHolderSynchronization
|
||||
implements Ordered {
|
||||
|
||||
private final EntityManager entityManager;
|
||||
|
||||
private final PersistenceExceptionTranslator exceptionTranslator;
|
||||
|
||||
public ExtendedEntityManagerSynchronization(
|
||||
EntityManager em, PersistenceExceptionTranslator exceptionTranslator) {
|
||||
super(new EntityManagerHolder(em), em);
|
||||
this.entityManager = em;
|
||||
this.exceptionTranslator = exceptionTranslator;
|
||||
}
|
||||
|
||||
public int getOrder() {
|
||||
return EntityManagerFactoryUtils.ENTITY_MANAGER_SYNCHRONIZATION_ORDER + 1;
|
||||
}
|
||||
|
||||
protected boolean shouldReleaseBeforeCompletion() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public void afterCommit() {
|
||||
super.afterCommit();
|
||||
// Trigger commit here to let exceptions propagate to the caller.
|
||||
try {
|
||||
this.entityManager.getTransaction().commit();
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
throw convertCompletionException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public void afterCompletion(int status) {
|
||||
super.afterCompletion(status);
|
||||
if (status != STATUS_COMMITTED) {
|
||||
// Haven't had an afterCommit call: trigger a rollback.
|
||||
try {
|
||||
this.entityManager.getTransaction().rollback();
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
throw convertCompletionException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private RuntimeException convertCompletionException(RuntimeException ex) {
|
||||
DataAccessException daex = (this.exceptionTranslator != null) ?
|
||||
this.exceptionTranslator.translateExceptionIfPossible(ex) :
|
||||
EntityManagerFactoryUtils.convertJpaAccessExceptionIfPossible(ex);
|
||||
return (daex != null ? daex : ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* 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 javax.persistence.EntityManager;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.dao.support.DataAccessUtils;
|
||||
|
||||
/**
|
||||
* Base class for JpaTemplate and JpaInterceptor, defining common
|
||||
* properties such as EntityManagerFactory and flushing behavior.
|
||||
*
|
||||
* <p>Not intended to be used directly.
|
||||
* See {@link JpaTemplate} and {@link JpaInterceptor}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see #setEntityManagerFactory
|
||||
* @see #setEntityManager
|
||||
* @see #setJpaDialect
|
||||
* @see #setFlushEager
|
||||
* @see JpaTemplate
|
||||
* @see JpaInterceptor
|
||||
* @see JpaDialect
|
||||
*/
|
||||
public abstract class JpaAccessor extends EntityManagerFactoryAccessor implements InitializingBean {
|
||||
|
||||
private EntityManager entityManager;
|
||||
|
||||
private JpaDialect jpaDialect = new DefaultJpaDialect();
|
||||
|
||||
private boolean flushEager = false;
|
||||
|
||||
|
||||
/**
|
||||
* Set the JPA EntityManager to use.
|
||||
*/
|
||||
public void setEntityManager(EntityManager entityManager) {
|
||||
this.entityManager = entityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JPA EntityManager to use.
|
||||
*/
|
||||
public EntityManager getEntityManager() {
|
||||
return entityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JPA dialect to use for this accessor.
|
||||
* <p>The dialect object can be used to retrieve the underlying JDBC
|
||||
* connection, for example.
|
||||
*/
|
||||
public void setJpaDialect(JpaDialect jpaDialect) {
|
||||
this.jpaDialect = (jpaDialect != null ? jpaDialect : new DefaultJpaDialect());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JPA dialect to use for this accessor.
|
||||
* <p>Creates a default one for the specified EntityManagerFactory if none set.
|
||||
*/
|
||||
public JpaDialect getJpaDialect() {
|
||||
return this.jpaDialect;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 JPA
|
||||
* 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 JPA dialect, creating a default one
|
||||
* for the specified EntityManagerFactory if none set.
|
||||
*/
|
||||
public void afterPropertiesSet() {
|
||||
EntityManagerFactory emf = getEntityManagerFactory();
|
||||
if (emf == null && getEntityManager() == null) {
|
||||
throw new IllegalArgumentException("entityManagerFactory or entityManager is required");
|
||||
}
|
||||
if (emf instanceof EntityManagerFactoryInfo) {
|
||||
JpaDialect jpaDialect = ((EntityManagerFactoryInfo) emf).getJpaDialect();
|
||||
if (jpaDialect != null) {
|
||||
setJpaDialect(jpaDialect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Flush the given JPA entity manager if necessary.
|
||||
* @param em the current JPA PersistenceManage
|
||||
* @param existingTransaction if executing within an existing transaction
|
||||
* @throws javax.persistence.PersistenceException in case of JPA flushing errors
|
||||
*/
|
||||
protected void flushIfNecessary(EntityManager em, boolean existingTransaction) throws PersistenceException {
|
||||
if (isFlushEager()) {
|
||||
logger.debug("Eagerly flushing JPA entity manager");
|
||||
em.flush();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert the given runtime exception to an appropriate exception from the
|
||||
* <code>org.springframework.dao</code> hierarchy if necessary, or
|
||||
* return the exception itself if it is not persistence related
|
||||
* <p>Default implementation delegates to the JpaDialect.
|
||||
* May be overridden in subclasses.
|
||||
* @param ex runtime exception that occured, which may or may not
|
||||
* be JPA-related
|
||||
* @return the corresponding DataAccessException instance if
|
||||
* wrapping should occur, otherwise the raw exception
|
||||
* @see org.springframework.dao.support.DataAccessUtils#translateIfNecessary
|
||||
*/
|
||||
public RuntimeException translateIfNecessary(RuntimeException ex) {
|
||||
return DataAccessUtils.translateIfNecessary(ex, getJpaDialect());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* 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.jpa;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
/**
|
||||
* Callback interface for JPA code. To be used with {@link JpaTemplate}'s
|
||||
* execution method, often as anonymous classes within a method implementation.
|
||||
* A typical implementation will call <code>EntityManager.find/merge</code>
|
||||
* to perform some operations on persistent objects.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see org.springframework.orm.jpa.JpaTemplate
|
||||
* @see org.springframework.orm.jpa.JpaTransactionManager
|
||||
*/
|
||||
public interface JpaCallback {
|
||||
|
||||
/**
|
||||
* Gets called by <code>JpaTemplate.execute</code> with an active
|
||||
* JPA <code>EntityManager</code>. Does not need to care about activating
|
||||
* or closing the <code>EntityManager</code>, or handling transactions.
|
||||
*
|
||||
* <p>Note that JPA callback code will not flush any modifications to the
|
||||
* database if not executed within a transaction. Thus, you need to make
|
||||
* sure that JpaTransactionManager has initiated a JPA 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 em active EntityManager
|
||||
* @return a result object, or <code>null</code> if none
|
||||
* @throws PersistenceException if thrown by the JPA API
|
||||
* @see org.springframework.orm.jpa.JpaTemplate#execute
|
||||
* @see org.springframework.orm.jpa.JpaTemplate#executeFind
|
||||
*/
|
||||
Object doInJpa(EntityManager em) throws PersistenceException;
|
||||
|
||||
}
|
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* 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.sql.SQLException;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import org.springframework.dao.support.PersistenceExceptionTranslator;
|
||||
import org.springframework.jdbc.datasource.ConnectionHandle;
|
||||
import org.springframework.transaction.TransactionDefinition;
|
||||
import org.springframework.transaction.TransactionException;
|
||||
|
||||
/**
|
||||
* SPI strategy that encapsulates certain functionality that standard JPA 1.0
|
||||
* does not offer, such as access to the underlying JDBC Connection. This
|
||||
* strategy is mainly intended for standalone usage of a JPA provider; most
|
||||
* of its functionality is not relevant when running with JTA transactions.
|
||||
*
|
||||
* <p>Also allows for the provision of value-added methods for portable yet
|
||||
* more capable EntityManager and EntityManagerFactory subinterfaces offered
|
||||
* by Spring.
|
||||
*
|
||||
* <p>In general, it is recommended to derive from DefaultJpaDialect instead of
|
||||
* implementing this interface directly. This allows for inheriting common
|
||||
* behavior (present and future) from DefaultJpaDialect, only overriding
|
||||
* specific hooks to plug in concrete vendor-specific behavior.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rod Johnson
|
||||
* @since 2.0
|
||||
* @see DefaultJpaDialect
|
||||
* @see JpaAccessor#setJpaDialect
|
||||
* @see JpaTransactionManager#setJpaDialect
|
||||
* @see JpaVendorAdapter#getJpaDialect()
|
||||
* @see AbstractEntityManagerFactoryBean#setJpaDialect
|
||||
* @see AbstractEntityManagerFactoryBean#setJpaVendorAdapter
|
||||
*/
|
||||
public interface JpaDialect extends PersistenceExceptionTranslator {
|
||||
|
||||
//-----------------------------------------------------------------------------------
|
||||
// Hooks for non-standard persistence operations (used by EntityManagerFactory beans)
|
||||
//-----------------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Return whether the EntityManagerFactoryPlus(Operations) interface is
|
||||
* supported by this provider.
|
||||
* @see EntityManagerFactoryPlusOperations
|
||||
* @see EntityManagerFactoryPlus
|
||||
*/
|
||||
boolean supportsEntityManagerFactoryPlusOperations();
|
||||
|
||||
/**
|
||||
* Return whether the EntityManagerPlus(Operations) interface is
|
||||
* supported by this provider.
|
||||
* @see EntityManagerPlusOperations
|
||||
* @see EntityManagerPlus
|
||||
*/
|
||||
boolean supportsEntityManagerPlusOperations();
|
||||
|
||||
/**
|
||||
* Return an EntityManagerFactoryPlusOperations implementation for
|
||||
* the given raw EntityManagerFactory. This operations object can be
|
||||
* used to serve the additional operations behind a proxy that
|
||||
* implements the EntityManagerFactoryPlus interface.
|
||||
* @param rawEntityManager the raw provider-specific EntityManagerFactory
|
||||
* @return the EntityManagerFactoryPlusOperations implementation
|
||||
*/
|
||||
EntityManagerFactoryPlusOperations getEntityManagerFactoryPlusOperations(EntityManagerFactory rawEntityManager);
|
||||
|
||||
/**
|
||||
* Return an EntityManagerPlusOperations implementation for
|
||||
* the given raw EntityManager. This operations object can be
|
||||
* used to serve the additional operations behind a proxy that
|
||||
* implements the EntityManagerPlus interface.
|
||||
* @param rawEntityManager the raw provider-specific EntityManagerFactory
|
||||
* @return the EntityManagerFactoryPlusOperations implementation
|
||||
*/
|
||||
EntityManagerPlusOperations getEntityManagerPlusOperations(EntityManager rawEntityManager);
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Hooks for transaction management (used by JpaTransactionManager)
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Begin the given JPA transaction, applying the semantics specified by the
|
||||
* given Spring transaction definition (in particular, an isolation level
|
||||
* and a timeout). Called by JpaTransactionManager on transaction begin.
|
||||
* <p>An implementation can configure the JPA 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 apply the read-only flag as flush mode. In that case,
|
||||
* a transaction data object can be returned that holds the previous flush mode
|
||||
* (and possibly other data), to be reset in <code>cleanupTransaction</code>.
|
||||
* It may also apply the read-only flag and isolation level to the underlying
|
||||
* JDBC Connection before beginning the transaction.
|
||||
* <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).
|
||||
* <p>This method also allows for exposing savepoint capabilities if supported by
|
||||
* the persistence provider, through returning an Object that implements Spring's
|
||||
* {@link org.springframework.transaction.SavepointManager} interface.
|
||||
* {@link JpaTransactionManager} will use this capability if needed.
|
||||
* @param entityManager the EntityManager to begin a JPA transaction on
|
||||
* @param definition the Spring transaction definition that defines semantics
|
||||
* @return an arbitrary object that holds transaction data, if any
|
||||
* (to be passed into {@link #cleanupTransaction}). May implement the
|
||||
* {@link org.springframework.transaction.SavepointManager} interface.
|
||||
* @throws javax.persistence.PersistenceException if thrown by JPA methods
|
||||
* @throws java.sql.SQLException if thrown by JDBC methods
|
||||
* @throws org.springframework.transaction.TransactionException in case of invalid arguments
|
||||
* @see #cleanupTransaction
|
||||
* @see javax.persistence.EntityTransaction#begin
|
||||
* @see org.springframework.jdbc.datasource.DataSourceUtils#prepareConnectionForTransaction
|
||||
*/
|
||||
Object beginTransaction(EntityManager entityManager, TransactionDefinition definition)
|
||||
throws PersistenceException, SQLException, TransactionException;
|
||||
|
||||
/**
|
||||
* Prepare a JPA transaction, applying the specified semantics. Called by
|
||||
* EntityManagerFactoryUtils when enlisting an EntityManager in a JTA transaction.
|
||||
* <p>An implementation can apply the read-only flag as flush mode. In that case,
|
||||
* a transaction data object can be returned that holds the previous flush mode
|
||||
* (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 entityManager the EntityManager to begin a JPA transaction on
|
||||
* @param readOnly whether the transaction is supposed to be read-only
|
||||
* @param name the name of the transaction (if any)
|
||||
* @return an arbitrary object that holds transaction data, if any
|
||||
* (to be passed into cleanupTransaction)
|
||||
* @throws javax.persistence.PersistenceException if thrown by JPA methods
|
||||
* @see #cleanupTransaction
|
||||
*/
|
||||
Object prepareTransaction(EntityManager entityManager, boolean readOnly, String name)
|
||||
throws PersistenceException;
|
||||
|
||||
/**
|
||||
* Clean up the transaction via the given transaction data. Called by
|
||||
* JpaTransactionManager and EntityManagerFactoryUtils 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 or prepareTransaction)
|
||||
* @see #beginTransaction
|
||||
* @see org.springframework.jdbc.datasource.DataSourceUtils#resetConnectionAfterTransaction
|
||||
*/
|
||||
void cleanupTransaction(Object transactionData);
|
||||
|
||||
/**
|
||||
* Retrieve the JDBC Connection that the given JPA EntityManager 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 JPA
|
||||
* transaction (for example, by JpaTransactionManager). The returned handle will
|
||||
* be passed into the <code>releaseJdbcConnection</code> method when not needed anymore.
|
||||
* <p>This strategy is necessary as JPA 1.0 does not provide a standard way to retrieve
|
||||
* the underlying JDBC Connection (due to the fact that a JPA implementation might not
|
||||
* work with a relational database at all).
|
||||
* <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
|
||||
* EntityManager 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 entityManager the current JPA EntityManager
|
||||
* @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 javax.persistence.PersistenceException if thrown by JPA methods
|
||||
* @throws java.sql.SQLException if thrown by JDBC methods
|
||||
* @see #releaseJdbcConnection
|
||||
* @see org.springframework.jdbc.datasource.ConnectionHandle#getConnection
|
||||
* @see org.springframework.jdbc.datasource.SimpleConnectionHandle
|
||||
* @see JpaTransactionManager#setDataSource
|
||||
* @see org.springframework.jdbc.support.nativejdbc.NativeJdbcExtractor
|
||||
*/
|
||||
ConnectionHandle getJdbcConnection(EntityManager entityManager, boolean readOnly)
|
||||
throws PersistenceException, 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 JPA
|
||||
* transaction completes or when the EntityManager is closed.
|
||||
* @param conHandle the JDBC Connection handle to release
|
||||
* @param entityManager the current JPA EntityManager
|
||||
* @throws javax.persistence.PersistenceException if thrown by JPA methods
|
||||
* @throws java.sql.SQLException if thrown by JDBC methods
|
||||
* @see #getJdbcConnection
|
||||
*/
|
||||
void releaseJdbcConnection(ConnectionHandle conHandle, EntityManager entityManager)
|
||||
throws PersistenceException, SQLException;
|
||||
|
||||
}
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* 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.jpa;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
|
||||
/**
|
||||
* This interceptor binds a new JPA EntityManager 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 EntityManager (e.g. from JpaTransactionManager,
|
||||
* or from a surrounding JPA-intercepted method), the interceptor simply participates in it.
|
||||
*
|
||||
* <p>Application code must retrieve a JPA EntityManager via the
|
||||
* <code>EntityManagerFactoryUtils.getEntityManager</code> method or - preferably -
|
||||
* via a shared <code>EntityManager</code> reference, to be able to detect a
|
||||
* thread-bound EntityManager. Typically, the code will look like as follows:
|
||||
*
|
||||
* <pre>
|
||||
* public void doSomeDataAccessAction() {
|
||||
* this.entityManager...
|
||||
* }</pre>
|
||||
*
|
||||
* <p>Note that this interceptor automatically translates PersistenceExceptions,
|
||||
* via delegating to the <code>EntityManagerFactoryUtils.convertJpaAccessException</code>
|
||||
* method that converts them to exceptions that are compatible with the
|
||||
* <code>org.springframework.dao</code> exception hierarchy (like JpaTemplate does).
|
||||
*
|
||||
* <p>This class can be considered a declarative alternative to JpaTemplate'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 EntityManager in the first place, so adding this interceptor
|
||||
* to the configuration just adds value when fine-tuning EntityManager settings
|
||||
* like the flush mode - or when relying on exception translation.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see JpaTransactionManager
|
||||
* @see JpaTemplate
|
||||
*/
|
||||
public class JpaInterceptor extends JpaAccessor implements MethodInterceptor {
|
||||
|
||||
private boolean exceptionConversionEnabled = true;
|
||||
|
||||
|
||||
/**
|
||||
* Set whether to convert any PersistenceException 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 {
|
||||
// Determine current EntityManager: either the transactional one
|
||||
// managed by the factory or a temporary one for the given invocation.
|
||||
EntityManager em = getTransactionalEntityManager();
|
||||
boolean isNewEm = false;
|
||||
if (em == null) {
|
||||
logger.debug("Creating new EntityManager for JpaInterceptor invocation");
|
||||
em = createEntityManager();
|
||||
isNewEm = true;
|
||||
TransactionSynchronizationManager.bindResource(getEntityManagerFactory(), new EntityManagerHolder(em));
|
||||
}
|
||||
|
||||
try {
|
||||
Object retVal = methodInvocation.proceed();
|
||||
flushIfNecessary(em, !isNewEm);
|
||||
return retVal;
|
||||
}
|
||||
catch (RuntimeException rawException) {
|
||||
if (this.exceptionConversionEnabled) {
|
||||
// Translation enabled. Translate if we understand the exception.
|
||||
throw translateIfNecessary(rawException);
|
||||
}
|
||||
else {
|
||||
// Translation not enabled. Don't try to translate.
|
||||
throw rawException;
|
||||
}
|
||||
}
|
||||
finally {
|
||||
if (isNewEm) {
|
||||
TransactionSynchronizationManager.unbindResource(getEntityManagerFactory());
|
||||
EntityManagerFactoryUtils.closeEntityManager(em);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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.jpa;
|
||||
|
||||
import javax.persistence.EntityNotFoundException;
|
||||
|
||||
import org.springframework.orm.ObjectRetrievalFailureException;
|
||||
|
||||
/**
|
||||
* JPA-specific subclass of ObjectRetrievalFailureException.
|
||||
* Converts JPA's EntityNotFoundException.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see EntityManagerFactoryUtils#convertJpaAccessExceptionIfPossible
|
||||
*/
|
||||
public class JpaObjectRetrievalFailureException extends ObjectRetrievalFailureException {
|
||||
|
||||
public JpaObjectRetrievalFailureException(EntityNotFoundException ex) {
|
||||
super(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* 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.jpa;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
|
||||
/**
|
||||
* Interface that specifies a basic set of JPA operations,
|
||||
* implemented by {@link JpaTemplate}. Not often used, but a useful
|
||||
* option to enhance testability, as it can easily be mocked or stubbed.
|
||||
*
|
||||
* <p>Defines <code>JpaTemplate</code>'s data access methods that mirror
|
||||
* various {@link javax.persistence.EntityManager} methods. Users are
|
||||
* strongly encouraged to read the JPA <code>EntityManager</code>
|
||||
* javadocs for details on the semantics of those methods.
|
||||
*
|
||||
* <p>Note that lazy loading will just work with an open JPA
|
||||
* <code>EntityManager</code>, either within a managed transaction or within
|
||||
* {@link org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter}/
|
||||
* {@link org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor}.
|
||||
* Furthermore, some operations just make sense within transactions,
|
||||
* for example: <code>flush</code>, <code>clear</code>.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see JpaTemplate
|
||||
* @see javax.persistence.EntityManager
|
||||
* @see JpaTransactionManager
|
||||
* @see JpaDialect
|
||||
* @see org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter
|
||||
* @see org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor
|
||||
*/
|
||||
public interface JpaOperations {
|
||||
|
||||
Object execute(JpaCallback action) throws DataAccessException;
|
||||
|
||||
List executeFind(JpaCallback action) throws DataAccessException;
|
||||
|
||||
<T> T find(Class<T> entityClass, Object id) throws DataAccessException;
|
||||
|
||||
<T> T getReference(Class<T> entityClass, Object id) throws DataAccessException;
|
||||
|
||||
boolean contains(Object entity) throws DataAccessException;
|
||||
|
||||
void refresh(Object entity) throws DataAccessException;
|
||||
|
||||
void persist(Object entity) throws DataAccessException;
|
||||
|
||||
<T> T merge(T entity) throws DataAccessException;
|
||||
|
||||
void remove(Object entity) throws DataAccessException;
|
||||
|
||||
void flush() throws DataAccessException;
|
||||
|
||||
List find(String queryString) throws DataAccessException;
|
||||
|
||||
List find(String queryString, Object... values) throws DataAccessException;
|
||||
|
||||
List findByNamedParams(String queryString, Map<String,? extends Object> params) throws DataAccessException;
|
||||
|
||||
List findByNamedQuery(String queryName) throws DataAccessException;
|
||||
|
||||
List findByNamedQuery(String queryName, Object... values) throws DataAccessException;
|
||||
|
||||
List findByNamedQueryAndNamedParams(String queryName, Map<String,? extends Object> params) throws DataAccessException;
|
||||
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* 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.jpa;
|
||||
|
||||
import javax.persistence.OptimisticLockException;
|
||||
|
||||
import org.springframework.orm.ObjectOptimisticLockingFailureException;
|
||||
|
||||
/**
|
||||
* JPA-specific subclass of ObjectOptimisticLockingFailureException.
|
||||
* Converts JPA's OptimisticLockException.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see EntityManagerFactoryUtils#convertJpaAccessExceptionIfPossible
|
||||
*/
|
||||
public class JpaOptimisticLockingFailureException extends ObjectOptimisticLockingFailureException {
|
||||
|
||||
public JpaOptimisticLockingFailureException(OptimisticLockException ex) {
|
||||
super(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* 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.jpa;
|
||||
|
||||
import javax.persistence.PersistenceException;
|
||||
|
||||
import org.springframework.dao.UncategorizedDataAccessException;
|
||||
|
||||
/**
|
||||
* JPA-specific subclass of UncategorizedDataAccessException,
|
||||
* for JPA system errors that do not match any concrete
|
||||
* <code>org.springframework.dao</code> exceptions.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see EntityManagerFactoryUtils#convertJpaAccessExceptionIfPossible
|
||||
*/
|
||||
public class JpaSystemException extends UncategorizedDataAccessException {
|
||||
|
||||
public JpaSystemException(PersistenceException ex) {
|
||||
super(ex.getMessage(), ex);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,412 @@
|
|||
/*
|
||||
* 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.jpa;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.persistence.Query;
|
||||
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.InvalidDataAccessApiUsageException;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
|
||||
/**
|
||||
* Helper class that allows for writing JPA data access code in the same style
|
||||
* as with Spring's well-known JdoTemplate and HibernateTemplate classes.
|
||||
* Automatically converts PersistenceExceptions into Spring DataAccessExceptions,
|
||||
* following the <code>org.springframework.dao</code> exception hierarchy.
|
||||
*
|
||||
* <p>The central method is of this template is "execute", supporting JPA access code
|
||||
* implementing the {@link JpaCallback} interface. It provides JPA EntityManager
|
||||
* handling such that neither the JpaCallback implementation nor the calling code
|
||||
* needs to explicitly care about retrieving/closing EntityManagers, or handling
|
||||
* JPA lifecycle exceptions.
|
||||
*
|
||||
* <p>Can be used within a service implementation via direct instantiation with
|
||||
* a EntityManagerFactory reference, or get prepared in an application context
|
||||
* and given to services as bean reference. Note: The EntityManagerFactory 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><b>NOTE: JpaTemplate mainly exists as a sibling of JdoTemplate and
|
||||
* HibernateTemplate, offering the same style for people used to it. For newly
|
||||
* started projects, consider adopting the standard JPA style of coding data
|
||||
* access objects instead, based on a "shared EntityManager" reference injected
|
||||
* via a Spring bean definition or the JPA PersistenceContext annotation.</b>
|
||||
* (Using Spring's SharedEntityManagerBean / PersistenceAnnotationBeanPostProcessor,
|
||||
* or using a direct JNDI lookup for an EntityManager on a Java EE 5 server.)
|
||||
*
|
||||
* <p>JpaTemplate can be considered as direct alternative to working with the
|
||||
* native JPA EntityManager API (through a shared EntityManager reference,
|
||||
* as outlined above). The major advantage is its automatic conversion to
|
||||
* DataAccessExceptions; the major disadvantage is that it introduces
|
||||
* another thin layer on top of the native JPA API. Note that exception
|
||||
* translation can also be achieved through AOP advice; check out
|
||||
* {@link org.springframework.dao.annotation.PersistenceExceptionTranslationPostProcessor}.
|
||||
*
|
||||
* <p>{@link LocalContainerEntityManagerFactoryBean} is the preferred way of
|
||||
* obtaining a reference to an EntityManagerFactory, at least outside of a full
|
||||
* Java EE 5 environment. The Spring application context will manage its lifecycle,
|
||||
* initializing and shutting down the factory as part of the application.
|
||||
* Within a Java EE 5 environment, you will typically work with a server-managed
|
||||
* EntityManagerFactory that is exposed via JNDI, obtained through Spring's
|
||||
* {@link org.springframework.jndi.JndiObjectFactoryBean}.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see #setEntityManagerFactory
|
||||
* @see JpaCallback
|
||||
* @see javax.persistence.EntityManager
|
||||
* @see LocalEntityManagerFactoryBean
|
||||
* @see LocalContainerEntityManagerFactoryBean
|
||||
* @see JpaTransactionManager
|
||||
* @see org.springframework.transaction.jta.JtaTransactionManager
|
||||
* @see org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter
|
||||
* @see org.springframework.orm.jpa.support.OpenEntityManagerInViewInterceptor
|
||||
*/
|
||||
public class JpaTemplate extends JpaAccessor implements JpaOperations {
|
||||
|
||||
private boolean exposeNativeEntityManager = false;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new JpaTemplate instance.
|
||||
*/
|
||||
public JpaTemplate() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new JpaTemplate instance.
|
||||
* @param emf EntityManagerFactory to create EntityManagers
|
||||
*/
|
||||
public JpaTemplate(EntityManagerFactory emf) {
|
||||
setEntityManagerFactory(emf);
|
||||
afterPropertiesSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new JpaTemplate instance.
|
||||
* @param em EntityManager to use
|
||||
*/
|
||||
public JpaTemplate(EntityManager em) {
|
||||
setEntityManager(em);
|
||||
afterPropertiesSet();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set whether to expose the native JPA EntityManager to JpaCallback
|
||||
* code. Default is "false": a EntityManager 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 EntityManager
|
||||
* class in DAOs that use the JPA 1.0 API, for JPA 2.0 previews and other
|
||||
* provider-specific functionality, the exposed proxy implements all interfaces
|
||||
* implemented by the original EntityManager. If this is not sufficient,
|
||||
* turn this flag to "true".
|
||||
* @see JpaCallback
|
||||
* @see javax.persistence.EntityManager
|
||||
*/
|
||||
public void setExposeNativeEntityManager(boolean exposeNativeEntityManager) {
|
||||
this.exposeNativeEntityManager = exposeNativeEntityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether to expose the native JPA EntityManager to JpaCallback
|
||||
* code, or rather an EntityManager proxy.
|
||||
*/
|
||||
public boolean isExposeNativeEntityManager() {
|
||||
return this.exposeNativeEntityManager;
|
||||
}
|
||||
|
||||
|
||||
public Object execute(JpaCallback action) throws DataAccessException {
|
||||
return execute(action, isExposeNativeEntityManager());
|
||||
}
|
||||
|
||||
public List executeFind(JpaCallback action) throws DataAccessException {
|
||||
Object result = execute(action, isExposeNativeEntityManager());
|
||||
if (!(result instanceof List)) {
|
||||
throw new InvalidDataAccessApiUsageException(
|
||||
"Result object returned from JpaCallback isn't a List: [" + result + "]");
|
||||
}
|
||||
return (List) result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the action specified by the given action object within a
|
||||
* EntityManager.
|
||||
* @param action callback object that specifies the JPA action
|
||||
* @param exposeNativeEntityManager whether to expose the native
|
||||
* JPA entity manager to callback code
|
||||
* @return a result object returned by the action, or <code>null</code>
|
||||
* @throws org.springframework.dao.DataAccessException in case of JPA errors
|
||||
*/
|
||||
public Object execute(JpaCallback action, boolean exposeNativeEntityManager) throws DataAccessException {
|
||||
Assert.notNull(action, "Callback object must not be null");
|
||||
|
||||
EntityManager em = getEntityManager();
|
||||
boolean isNewEm = false;
|
||||
if (em == null) {
|
||||
em = getTransactionalEntityManager();
|
||||
if (em == null) {
|
||||
logger.debug("Creating new EntityManager for JpaTemplate execution");
|
||||
em = createEntityManager();
|
||||
isNewEm = true;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
EntityManager emToExpose = (exposeNativeEntityManager ? em : createEntityManagerProxy(em));
|
||||
Object result = action.doInJpa(emToExpose);
|
||||
flushIfNecessary(em, !isNewEm);
|
||||
return result;
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
throw translateIfNecessary(ex);
|
||||
}
|
||||
finally {
|
||||
if (isNewEm) {
|
||||
logger.debug("Closing new EntityManager after JPA template execution");
|
||||
EntityManagerFactoryUtils.closeEntityManager(em);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a close-suppressing proxy for the given JPA EntityManager.
|
||||
* The proxy also prepares returned JPA Query objects.
|
||||
* @param em the JPA EntityManager to create a proxy for
|
||||
* @return the EntityManager proxy, implementing all interfaces
|
||||
* implemented by the passed-in EntityManager object (that is,
|
||||
* also implementing all provider-specific extension interfaces)
|
||||
* @see javax.persistence.EntityManager#close
|
||||
*/
|
||||
protected EntityManager createEntityManagerProxy(EntityManager em) {
|
||||
Class[] ifcs = null;
|
||||
EntityManagerFactory emf = getEntityManagerFactory();
|
||||
if (emf instanceof EntityManagerFactoryInfo) {
|
||||
Class entityManagerInterface = ((EntityManagerFactoryInfo) emf).getEntityManagerInterface();
|
||||
if (entityManagerInterface != null) {
|
||||
ifcs = new Class[] {entityManagerInterface};
|
||||
}
|
||||
}
|
||||
if (ifcs == null) {
|
||||
ifcs = ClassUtils.getAllInterfacesForClass(em.getClass());
|
||||
}
|
||||
return (EntityManager) Proxy.newProxyInstance(
|
||||
em.getClass().getClassLoader(), ifcs, new CloseSuppressingInvocationHandler(em));
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Convenience methods for load, save, delete
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T find(final Class<T> entityClass, final Object id) throws DataAccessException {
|
||||
return (T) execute(new JpaCallback() {
|
||||
public Object doInJpa(EntityManager em) throws PersistenceException {
|
||||
return em.find(entityClass, id);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T getReference(final Class<T> entityClass, final Object id) throws DataAccessException {
|
||||
return (T) execute(new JpaCallback() {
|
||||
public Object doInJpa(EntityManager em) throws PersistenceException {
|
||||
return em.getReference(entityClass, id);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public boolean contains(final Object entity) throws DataAccessException {
|
||||
Boolean result = (Boolean) execute(new JpaCallback() {
|
||||
public Object doInJpa(EntityManager em) throws PersistenceException {
|
||||
return new Boolean(em.contains(entity));
|
||||
}
|
||||
}, true);
|
||||
return result.booleanValue();
|
||||
}
|
||||
|
||||
public void refresh(final Object entity) throws DataAccessException {
|
||||
execute(new JpaCallback() {
|
||||
public Object doInJpa(EntityManager em) throws PersistenceException {
|
||||
em.refresh(entity);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public void persist(final Object entity) throws DataAccessException {
|
||||
execute(new JpaCallback() {
|
||||
public Object doInJpa(EntityManager em) throws PersistenceException {
|
||||
em.persist(entity);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public <T> T merge(final T entity) throws DataAccessException {
|
||||
return (T) execute(new JpaCallback() {
|
||||
public Object doInJpa(EntityManager em) throws PersistenceException {
|
||||
return em.merge(entity);
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public void remove(final Object entity) throws DataAccessException {
|
||||
execute(new JpaCallback() {
|
||||
public Object doInJpa(EntityManager em) throws PersistenceException {
|
||||
em.remove(entity);
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
public void flush() throws DataAccessException {
|
||||
execute(new JpaCallback() {
|
||||
public Object doInJpa(EntityManager em) throws PersistenceException {
|
||||
em.flush();
|
||||
return null;
|
||||
}
|
||||
}, true);
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------------------------
|
||||
// Convenience finder methods
|
||||
//-------------------------------------------------------------------------
|
||||
|
||||
public List find(String queryString) throws DataAccessException {
|
||||
return find(queryString, (Object[]) null);
|
||||
}
|
||||
|
||||
public List find(final String queryString, final Object... values) throws DataAccessException {
|
||||
return executeFind(new JpaCallback() {
|
||||
public Object doInJpa(EntityManager em) throws PersistenceException {
|
||||
Query queryObject = em.createQuery(queryString);
|
||||
if (values != null) {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
queryObject.setParameter(i + 1, values[i]);
|
||||
}
|
||||
}
|
||||
return queryObject.getResultList();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public List findByNamedParams(final String queryString, final Map<String, ?> params) throws DataAccessException {
|
||||
return executeFind(new JpaCallback() {
|
||||
public Object doInJpa(EntityManager em) throws PersistenceException {
|
||||
Query queryObject = em.createQuery(queryString);
|
||||
if (params != null) {
|
||||
for (Map.Entry<String, ?> entry : params.entrySet()) {
|
||||
queryObject.setParameter(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
return queryObject.getResultList();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public List findByNamedQuery(String queryName) throws DataAccessException {
|
||||
return findByNamedQuery(queryName, (Object[]) null);
|
||||
}
|
||||
|
||||
public List findByNamedQuery(final String queryName, final Object... values) throws DataAccessException {
|
||||
return executeFind(new JpaCallback() {
|
||||
public Object doInJpa(EntityManager em) throws PersistenceException {
|
||||
Query queryObject = em.createNamedQuery(queryName);
|
||||
if (values != null) {
|
||||
for (int i = 0; i < values.length; i++) {
|
||||
queryObject.setParameter(i + 1, values[i]);
|
||||
}
|
||||
}
|
||||
return queryObject.getResultList();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public List findByNamedQueryAndNamedParams(final String queryName, final Map<String, ?> params)
|
||||
throws DataAccessException {
|
||||
|
||||
return executeFind(new JpaCallback() {
|
||||
public Object doInJpa(EntityManager em) throws PersistenceException {
|
||||
Query queryObject = em.createNamedQuery(queryName);
|
||||
if (params != null) {
|
||||
for (Map.Entry<String, ?> entry : params.entrySet()) {
|
||||
queryObject.setParameter(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
return queryObject.getResultList();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Invocation handler that suppresses close calls on JPA EntityManagers.
|
||||
* Also prepares returned Query and Criteria objects.
|
||||
* @see javax.persistence.EntityManager#close
|
||||
*/
|
||||
private class CloseSuppressingInvocationHandler implements InvocationHandler {
|
||||
|
||||
private final EntityManager target;
|
||||
|
||||
public CloseSuppressingInvocationHandler(EntityManager target) {
|
||||
this.target = target;
|
||||
}
|
||||
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
// Invocation on EntityManager interface (or provider-specific extension) coming in...
|
||||
|
||||
if (method.getName().equals("equals")) {
|
||||
// Only consider equal when proxies are identical.
|
||||
return (proxy == args[0] ? Boolean.TRUE : Boolean.FALSE);
|
||||
}
|
||||
else if (method.getName().equals("hashCode")) {
|
||||
// Use hashCode of EntityManager proxy.
|
||||
return new Integer(System.identityHashCode(proxy));
|
||||
}
|
||||
else if (method.getName().equals("close")) {
|
||||
// Handle close method: suppress, not valid.
|
||||
return null;
|
||||
}
|
||||
|
||||
// Invoke method on target EntityManager.
|
||||
try {
|
||||
return method.invoke(this.target, args);
|
||||
}
|
||||
catch (InvocationTargetException ex) {
|
||||
throw ex.getTargetException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,652 @@
|
|||
/*
|
||||
* 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.jpa;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.persistence.EntityTransaction;
|
||||
import javax.persistence.PersistenceException;
|
||||
import javax.persistence.RollbackException;
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.dao.DataAccessException;
|
||||
import org.springframework.dao.support.DataAccessUtils;
|
||||
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.NestedTransactionNotSupportedException;
|
||||
import org.springframework.transaction.SavepointManager;
|
||||
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.ResourceTransactionManager;
|
||||
import org.springframework.transaction.support.TransactionSynchronizationManager;
|
||||
import org.springframework.util.CollectionUtils;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.transaction.PlatformTransactionManager} implementation
|
||||
* for a single JPA {@link javax.persistence.EntityManagerFactory}. Binds a JPA
|
||||
* EntityManager from the specified factory to the thread, potentially allowing for
|
||||
* one thread-bound EntityManager per factory. {@link SharedEntityManagerCreator}
|
||||
* and {@link JpaTemplate} are aware of thread-bound entity managers and participate
|
||||
* in such transactions automatically. Using either is required for JPA access code
|
||||
* supporting this transaction management mechanism.
|
||||
*
|
||||
* <p>This transaction manager is appropriate for applications that use a single
|
||||
* JPA EntityManagerFactory 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 JPA 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 JPA and services which use plain
|
||||
* JDBC (without being aware of JPA)! 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}).
|
||||
* Note that this requires a vendor-specific {@link JpaDialect} to be configured.
|
||||
*
|
||||
* <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
|
||||
* EntityManagerFactory. This transaction manager will autodetect the DataSource
|
||||
* used as known connection factory of the EntityManagerFactory, 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 JPA EntityManager 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 JPA transactions (provided that your
|
||||
* JDBC driver supports Savepoints). <i>Note that JPA itself does not support
|
||||
* nested transactions! Hence, do not expect JPA access code to semantically
|
||||
* participate in a nested transaction.</i>
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @since 2.0
|
||||
* @see #setEntityManagerFactory
|
||||
* @see #setDataSource
|
||||
* @see LocalEntityManagerFactoryBean
|
||||
* @see JpaTemplate#execute
|
||||
* @see org.springframework.orm.jpa.support.SharedEntityManagerBean
|
||||
* @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 JpaTransactionManager extends AbstractPlatformTransactionManager
|
||||
implements ResourceTransactionManager, InitializingBean {
|
||||
|
||||
private EntityManagerFactory entityManagerFactory;
|
||||
|
||||
private final Map jpaPropertyMap = new HashMap();
|
||||
|
||||
private DataSource dataSource;
|
||||
|
||||
private JpaDialect jpaDialect = new DefaultJpaDialect();
|
||||
|
||||
|
||||
/**
|
||||
* Create a new JpaTransactionManager instance.
|
||||
* A EntityManagerFactory has to be set to be able to use it.
|
||||
* @see #setEntityManagerFactory
|
||||
*/
|
||||
public JpaTransactionManager() {
|
||||
setNestedTransactionAllowed(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new JpaTransactionManager instance.
|
||||
* @param emf EntityManagerFactory to manage transactions for
|
||||
*/
|
||||
public JpaTransactionManager(EntityManagerFactory emf) {
|
||||
this();
|
||||
this.entityManagerFactory = emf;
|
||||
afterPropertiesSet();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Set the EntityManagerFactory that this instance should manage transactions for.
|
||||
*/
|
||||
public void setEntityManagerFactory(EntityManagerFactory emf) {
|
||||
this.entityManagerFactory = emf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the EntityManagerFactory that this instance should manage transactions for.
|
||||
*/
|
||||
public EntityManagerFactory getEntityManagerFactory() {
|
||||
return this.entityManagerFactory;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 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 getJpaPropertyMap() {
|
||||
return this.jpaPropertyMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the JDBC DataSource that this instance should manage transactions for.
|
||||
* The DataSource should match the one used by the JPA EntityManagerFactory:
|
||||
* for example, you could specify the same JNDI DataSource for both.
|
||||
* <p>If the EntityManagerFactory uses a known 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 JPA EntityManager.
|
||||
* <p>Note that you need to use a JPA dialect for a specific JPA implementation
|
||||
* to allow for exposing JPA 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 EntityManagerFactoryInfo#getDataSource()
|
||||
* @see #setJpaDialect
|
||||
* @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 the JPA dialect to use for this transaction manager.
|
||||
* Used for vendor-specific transaction management and JDBC connection exposure.
|
||||
* <p>If the EntityManagerFactory uses a known JpaDialect, it will be autodetected:
|
||||
* You can still explictly specify the DataSource, but you don't need to in this case.
|
||||
* <p>The dialect object can be used to retrieve the underlying JDBC connection
|
||||
* and thus allows for exposing JPA transactions as JDBC transactions.
|
||||
* @see EntityManagerFactoryInfo#getJpaDialect()
|
||||
* @see JpaDialect#beginTransaction
|
||||
* @see JpaDialect#getJdbcConnection
|
||||
*/
|
||||
public void setJpaDialect(JpaDialect jpaDialect) {
|
||||
this.jpaDialect = (jpaDialect != null ? jpaDialect : new DefaultJpaDialect());
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the JPA dialect to use for this transaction manager.
|
||||
*/
|
||||
public JpaDialect getJpaDialect() {
|
||||
return this.jpaDialect;
|
||||
}
|
||||
|
||||
/**
|
||||
* Eagerly initialize the JPA dialect, creating a default one
|
||||
* for the specified EntityManagerFactory if none set.
|
||||
* Auto-detect the EntityManagerFactory's DataSource, if any.
|
||||
*/
|
||||
public void afterPropertiesSet() {
|
||||
if (getEntityManagerFactory() == null) {
|
||||
throw new IllegalArgumentException("Property 'entityManagerFactory' is required");
|
||||
}
|
||||
if (getEntityManagerFactory() instanceof EntityManagerFactoryInfo) {
|
||||
EntityManagerFactoryInfo emfInfo = (EntityManagerFactoryInfo) getEntityManagerFactory();
|
||||
DataSource dataSource = emfInfo.getDataSource();
|
||||
if (dataSource != null) {
|
||||
setDataSource(dataSource);
|
||||
}
|
||||
JpaDialect jpaDialect = emfInfo.getJpaDialect();
|
||||
if (jpaDialect != null) {
|
||||
setJpaDialect(jpaDialect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public Object getResourceFactory() {
|
||||
return getEntityManagerFactory();
|
||||
}
|
||||
|
||||
protected Object doGetTransaction() {
|
||||
JpaTransactionObject txObject = new JpaTransactionObject();
|
||||
txObject.setSavepointAllowed(isNestedTransactionAllowed());
|
||||
|
||||
EntityManagerHolder emHolder = (EntityManagerHolder)
|
||||
TransactionSynchronizationManager.getResource(getEntityManagerFactory());
|
||||
if (emHolder != null) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Found thread-bound EntityManager [" +
|
||||
emHolder.getEntityManager() + "] for JPA transaction");
|
||||
}
|
||||
txObject.setEntityManagerHolder(emHolder, false);
|
||||
}
|
||||
|
||||
if (getDataSource() != null) {
|
||||
ConnectionHolder conHolder = (ConnectionHolder)
|
||||
TransactionSynchronizationManager.getResource(getDataSource());
|
||||
txObject.setConnectionHolder(conHolder);
|
||||
}
|
||||
|
||||
return txObject;
|
||||
}
|
||||
|
||||
protected boolean isExistingTransaction(Object transaction) {
|
||||
return ((JpaTransactionObject) transaction).hasTransaction();
|
||||
}
|
||||
|
||||
protected void doBegin(Object transaction, TransactionDefinition definition) {
|
||||
JpaTransactionObject txObject = (JpaTransactionObject) transaction;
|
||||
|
||||
if (txObject.hasConnectionHolder() && !txObject.getConnectionHolder().isSynchronizedWithTransaction()) {
|
||||
throw new IllegalTransactionStateException(
|
||||
"Pre-bound JDBC Connection found! JpaTransactionManager does not support " +
|
||||
"running within DataSourceTransactionManager if told to manage the DataSource itself. " +
|
||||
"It is recommended to use a single JpaTransactionManager for all transactions " +
|
||||
"on a single DataSource, no matter whether JPA or JDBC access.");
|
||||
}
|
||||
|
||||
EntityManager em = null;
|
||||
|
||||
try {
|
||||
if (txObject.getEntityManagerHolder() == null ||
|
||||
txObject.getEntityManagerHolder().isSynchronizedWithTransaction()) {
|
||||
EntityManager newEm = createEntityManagerForTransaction();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Opened new EntityManager [" + newEm + "] for JPA transaction");
|
||||
}
|
||||
txObject.setEntityManagerHolder(new EntityManagerHolder(newEm), true);
|
||||
}
|
||||
|
||||
em = txObject.getEntityManagerHolder().getEntityManager();
|
||||
|
||||
// Delegate to JpaDialect for actual transaction begin.
|
||||
Object transactionData = getJpaDialect().beginTransaction(em, definition);
|
||||
txObject.setTransactionData(transactionData);
|
||||
|
||||
// Register transaction timeout.
|
||||
int timeout = determineTimeout(definition);
|
||||
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
|
||||
txObject.getEntityManagerHolder().setTimeoutInSeconds(timeout);
|
||||
}
|
||||
|
||||
// Register the JPA EntityManager's JDBC Connection for the DataSource, if set.
|
||||
if (getDataSource() != null) {
|
||||
ConnectionHandle conHandle = getJpaDialect().getJdbcConnection(em, definition.isReadOnly());
|
||||
if (conHandle != null) {
|
||||
ConnectionHolder conHolder = new ConnectionHolder(conHandle);
|
||||
if (timeout != TransactionDefinition.TIMEOUT_DEFAULT) {
|
||||
conHolder.setTimeoutInSeconds(timeout);
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Exposing JPA transaction as JDBC transaction [" + conHolder.getConnectionHandle() + "]");
|
||||
}
|
||||
TransactionSynchronizationManager.bindResource(getDataSource(), conHolder);
|
||||
txObject.setConnectionHolder(conHolder);
|
||||
}
|
||||
else {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Not exposing JPA transaction [" + em + "] as JDBC transaction because JpaDialect [" +
|
||||
getJpaDialect() + "] does not support JDBC Connection retrieval");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bind the entity manager holder to the thread.
|
||||
if (txObject.isNewEntityManagerHolder()) {
|
||||
TransactionSynchronizationManager.bindResource(
|
||||
getEntityManagerFactory(), txObject.getEntityManagerHolder());
|
||||
}
|
||||
txObject.getEntityManagerHolder().setSynchronizedWithTransaction(true);
|
||||
}
|
||||
|
||||
catch (TransactionException ex) {
|
||||
closeEntityManagerAfterFailedBegin(txObject);
|
||||
throw ex;
|
||||
}
|
||||
catch (Exception ex) {
|
||||
closeEntityManagerAfterFailedBegin(txObject);
|
||||
throw new CannotCreateTransactionException("Could not open JPA EntityManager for transaction", ex);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a JPA EntityManager to be used for a transaction.
|
||||
* <p>The default implementation checks whether the EntityManagerFactory
|
||||
* is a Spring proxy and unwraps it first.
|
||||
* @see javax.persistence.EntityManagerFactory#createEntityManager()
|
||||
* @see EntityManagerFactoryInfo#getNativeEntityManagerFactory()
|
||||
*/
|
||||
protected EntityManager createEntityManagerForTransaction() {
|
||||
EntityManagerFactory emf = getEntityManagerFactory();
|
||||
if (emf instanceof EntityManagerFactoryInfo) {
|
||||
emf = ((EntityManagerFactoryInfo) emf).getNativeEntityManagerFactory();
|
||||
}
|
||||
Map properties = getJpaPropertyMap();
|
||||
return (!CollectionUtils.isEmpty(properties) ?
|
||||
emf.createEntityManager(properties) : emf.createEntityManager());
|
||||
}
|
||||
|
||||
/**
|
||||
* Close the current transaction's EntityManager.
|
||||
* Called after a transaction begin attempt failed.
|
||||
* @param txObject the current transaction
|
||||
*/
|
||||
protected void closeEntityManagerAfterFailedBegin(JpaTransactionObject txObject) {
|
||||
if (txObject.isNewEntityManagerHolder()) {
|
||||
EntityManager em = txObject.getEntityManagerHolder().getEntityManager();
|
||||
try {
|
||||
if (em.getTransaction().isActive()) {
|
||||
em.getTransaction().rollback();
|
||||
}
|
||||
}
|
||||
catch (Throwable ex) {
|
||||
logger.debug("Could not rollback EntityManager after failed transaction begin", ex);
|
||||
}
|
||||
finally {
|
||||
EntityManagerFactoryUtils.closeEntityManager(em);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Object doSuspend(Object transaction) {
|
||||
JpaTransactionObject txObject = (JpaTransactionObject) transaction;
|
||||
txObject.setEntityManagerHolder(null, false);
|
||||
EntityManagerHolder entityManagerHolder = (EntityManagerHolder)
|
||||
TransactionSynchronizationManager.unbindResource(getEntityManagerFactory());
|
||||
txObject.setConnectionHolder(null);
|
||||
ConnectionHolder connectionHolder = null;
|
||||
if (getDataSource() != null && TransactionSynchronizationManager.hasResource(getDataSource())) {
|
||||
connectionHolder = (ConnectionHolder) TransactionSynchronizationManager.unbindResource(getDataSource());
|
||||
}
|
||||
return new SuspendedResourcesHolder(entityManagerHolder, connectionHolder);
|
||||
}
|
||||
|
||||
protected void doResume(Object transaction, Object suspendedResources) {
|
||||
SuspendedResourcesHolder resourcesHolder = (SuspendedResourcesHolder) suspendedResources;
|
||||
TransactionSynchronizationManager.bindResource(
|
||||
getEntityManagerFactory(), resourcesHolder.getEntityManagerHolder());
|
||||
if (getDataSource() != null && resourcesHolder.getConnectionHolder() != null) {
|
||||
TransactionSynchronizationManager.bindResource(getDataSource(), resourcesHolder.getConnectionHolder());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This implementation returns "true": a JPA commit will properly handle
|
||||
* transactions that have been marked rollback-only at a global level.
|
||||
*/
|
||||
protected boolean shouldCommitOnGlobalRollbackOnly() {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void doCommit(DefaultTransactionStatus status) {
|
||||
JpaTransactionObject txObject = (JpaTransactionObject) status.getTransaction();
|
||||
if (status.isDebug()) {
|
||||
logger.debug("Committing JPA transaction on EntityManager [" +
|
||||
txObject.getEntityManagerHolder().getEntityManager() + "]");
|
||||
}
|
||||
try {
|
||||
EntityTransaction tx = txObject.getEntityManagerHolder().getEntityManager().getTransaction();
|
||||
tx.commit();
|
||||
}
|
||||
catch (RollbackException ex) {
|
||||
if (ex.getCause() instanceof RuntimeException) {
|
||||
DataAccessException dex = getJpaDialect().translateExceptionIfPossible((RuntimeException) ex.getCause());
|
||||
if (dex != null) {
|
||||
throw dex;
|
||||
}
|
||||
}
|
||||
throw new TransactionSystemException("Could not commit JPA transaction", ex);
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
// Assumably failed to flush changes to database.
|
||||
throw DataAccessUtils.translateIfNecessary(ex, getJpaDialect());
|
||||
}
|
||||
}
|
||||
|
||||
protected void doRollback(DefaultTransactionStatus status) {
|
||||
JpaTransactionObject txObject = (JpaTransactionObject) status.getTransaction();
|
||||
if (status.isDebug()) {
|
||||
logger.debug("Rolling back JPA transaction on EntityManager [" +
|
||||
txObject.getEntityManagerHolder().getEntityManager() + "]");
|
||||
}
|
||||
try {
|
||||
EntityTransaction tx = txObject.getEntityManagerHolder().getEntityManager().getTransaction();
|
||||
if (tx.isActive()) {
|
||||
tx.rollback();
|
||||
}
|
||||
}
|
||||
catch (PersistenceException ex) {
|
||||
throw new TransactionSystemException("Could not roll back JPA transaction", ex);
|
||||
}
|
||||
finally {
|
||||
if (!txObject.isNewEntityManagerHolder()) {
|
||||
// Clear all pending inserts/updates/deletes in the EntityManager.
|
||||
// Necessary for pre-bound EntityManagers, to avoid inconsistent state.
|
||||
txObject.getEntityManagerHolder().getEntityManager().clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected void doSetRollbackOnly(DefaultTransactionStatus status) {
|
||||
JpaTransactionObject txObject = (JpaTransactionObject) status.getTransaction();
|
||||
if (status.isDebug()) {
|
||||
logger.debug("Setting JPA transaction on EntityManager [" +
|
||||
txObject.getEntityManagerHolder().getEntityManager() + "] rollback-only");
|
||||
}
|
||||
txObject.setRollbackOnly();
|
||||
}
|
||||
|
||||
protected void doCleanupAfterCompletion(Object transaction) {
|
||||
JpaTransactionObject txObject = (JpaTransactionObject) transaction;
|
||||
|
||||
// Remove the entity manager holder from the thread.
|
||||
if (txObject.isNewEntityManagerHolder()) {
|
||||
TransactionSynchronizationManager.unbindResource(getEntityManagerFactory());
|
||||
}
|
||||
txObject.getEntityManagerHolder().clear();
|
||||
|
||||
// Remove the JDBC connection holder from the thread, if exposed.
|
||||
if (txObject.hasConnectionHolder()) {
|
||||
TransactionSynchronizationManager.unbindResource(getDataSource());
|
||||
try {
|
||||
getJpaDialect().releaseJdbcConnection(txObject.getConnectionHolder().getConnectionHandle(),
|
||||
txObject.getEntityManagerHolder().getEntityManager());
|
||||
}
|
||||
catch (Exception ex) {
|
||||
// Just log it, to keep a transaction-related exception.
|
||||
logger.error("Could not close JDBC connection after transaction", ex);
|
||||
}
|
||||
}
|
||||
|
||||
getJpaDialect().cleanupTransaction(txObject.getTransactionData());
|
||||
|
||||
// Remove the entity manager holder from the thread.
|
||||
if (txObject.isNewEntityManagerHolder()) {
|
||||
EntityManager em = txObject.getEntityManagerHolder().getEntityManager();
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Closing JPA EntityManager [" + em + "] after transaction");
|
||||
}
|
||||
EntityManagerFactoryUtils.closeEntityManager(em);
|
||||
}
|
||||
else {
|
||||
logger.debug("Not closing pre-bound JPA EntityManager after transaction");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* JPA transaction object, representing a EntityManagerHolder.
|
||||
* Used as transaction object by JpaTransactionManager.
|
||||
*/
|
||||
private static class JpaTransactionObject extends JdbcTransactionObjectSupport {
|
||||
|
||||
private EntityManagerHolder entityManagerHolder;
|
||||
|
||||
private boolean newEntityManagerHolder;
|
||||
|
||||
private Object transactionData;
|
||||
|
||||
public void setEntityManagerHolder(
|
||||
EntityManagerHolder entityManagerHolder, boolean newEntityManagerHolder) {
|
||||
this.entityManagerHolder = entityManagerHolder;
|
||||
this.newEntityManagerHolder = newEntityManagerHolder;
|
||||
}
|
||||
|
||||
public EntityManagerHolder getEntityManagerHolder() {
|
||||
return this.entityManagerHolder;
|
||||
}
|
||||
|
||||
public boolean isNewEntityManagerHolder() {
|
||||
return this.newEntityManagerHolder;
|
||||
}
|
||||
|
||||
public boolean hasTransaction() {
|
||||
return (this.entityManagerHolder != null && this.entityManagerHolder.isTransactionActive());
|
||||
}
|
||||
|
||||
public void setTransactionData(Object transactionData) {
|
||||
this.transactionData = transactionData;
|
||||
this.entityManagerHolder.setTransactionActive(true);
|
||||
if (transactionData instanceof SavepointManager) {
|
||||
this.entityManagerHolder.setSavepointManager((SavepointManager) transactionData);
|
||||
}
|
||||
}
|
||||
|
||||
public Object getTransactionData() {
|
||||
return this.transactionData;
|
||||
}
|
||||
|
||||
public void setRollbackOnly() {
|
||||
EntityTransaction tx = this.entityManagerHolder.getEntityManager().getTransaction();
|
||||
if (tx.isActive()) {
|
||||
tx.setRollbackOnly();
|
||||
}
|
||||
if (hasConnectionHolder()) {
|
||||
getConnectionHolder().setRollbackOnly();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isRollbackOnly() {
|
||||
EntityTransaction tx = this.entityManagerHolder.getEntityManager().getTransaction();
|
||||
return tx.getRollbackOnly();
|
||||
}
|
||||
|
||||
public Object createSavepoint() throws TransactionException {
|
||||
return getSavepointManager().createSavepoint();
|
||||
}
|
||||
|
||||
public void rollbackToSavepoint(Object savepoint) throws TransactionException {
|
||||
getSavepointManager().rollbackToSavepoint(savepoint);
|
||||
}
|
||||
|
||||
public void releaseSavepoint(Object savepoint) throws TransactionException {
|
||||
getSavepointManager().releaseSavepoint(savepoint);
|
||||
}
|
||||
|
||||
private SavepointManager getSavepointManager() {
|
||||
if (!isSavepointAllowed()) {
|
||||
throw new NestedTransactionNotSupportedException(
|
||||
"Transaction manager does not allow nested transactions");
|
||||
}
|
||||
SavepointManager savepointManager = getEntityManagerHolder().getSavepointManager();
|
||||
if (savepointManager == null) {
|
||||
throw new NestedTransactionNotSupportedException(
|
||||
"JpaDialect does not support savepoints - check your JPA provider's capabilities");
|
||||
}
|
||||
return savepointManager;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Holder for suspended resources.
|
||||
* Used internally by <code>doSuspend</code> and <code>doResume</code>.
|
||||
*/
|
||||
private static class SuspendedResourcesHolder {
|
||||
|
||||
private final EntityManagerHolder entityManagerHolder;
|
||||
|
||||
private final ConnectionHolder connectionHolder;
|
||||
|
||||
private SuspendedResourcesHolder(EntityManagerHolder emHolder, ConnectionHolder conHolder) {
|
||||
this.entityManagerHolder = emHolder;
|
||||
this.connectionHolder = conHolder;
|
||||
}
|
||||
|
||||
private EntityManagerHolder getEntityManagerHolder() {
|
||||
return this.entityManagerHolder;
|
||||
}
|
||||
|
||||
private ConnectionHolder getConnectionHolder() {
|
||||
return this.connectionHolder;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* 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.jpa;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import javax.persistence.EntityManager;
|
||||
import javax.persistence.EntityManagerFactory;
|
||||
import javax.persistence.spi.PersistenceProvider;
|
||||
|
||||
/**
|
||||
* SPI interface that allows to plug in vendor-specific behavior
|
||||
* into Spring's EntityManagerFactory creators. Serves as single
|
||||
* configuration point for all vendor-specific properties.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Rod Johnson
|
||||
* @since 2.0
|
||||
* @see AbstractEntityManagerFactoryBean#setJpaVendorAdapter
|
||||
*/
|
||||
public interface JpaVendorAdapter {
|
||||
|
||||
/**
|
||||
* Return the vendor-specific persistence provider.
|
||||
*/
|
||||
PersistenceProvider getPersistenceProvider();
|
||||
|
||||
/**
|
||||
* Return the name of the persistence provider's root package
|
||||
* (e.g. "oracle.toplink.essentials"). Will be used for
|
||||
* excluding provider classes from temporary class overriding.
|
||||
* @since 2.5.2
|
||||
*/
|
||||
String getPersistenceProviderRootPackage();
|
||||
|
||||
/**
|
||||
* Return a Map of vendor-specific JPA properties,
|
||||
* typically based on settings in this JpaVendorAdapter instance.
|
||||
* <p>Note that there might be further JPA properties defined on
|
||||
* the EntityManagerFactory bean, which might potentially override
|
||||
* individual JPA property values specified here.
|
||||
* @return a Map of JPA properties, as as accepted by the standard
|
||||
* JPA bootstrap facilities, or <code>null</code> or an empty Map
|
||||
* if there are no such properties to expose
|
||||
* @see javax.persistence.Persistence#createEntityManagerFactory(String, java.util.Map)
|
||||
* @see javax.persistence.spi.PersistenceProvider#createContainerEntityManagerFactory(javax.persistence.spi.PersistenceUnitInfo, java.util.Map)
|
||||
*/
|
||||
Map getJpaPropertyMap();
|
||||
|
||||
/**
|
||||
* Return the vendor-specific JpaDialect implementation for this
|
||||
* provider, or <code>null</code> if there is none.
|
||||
*/
|
||||
JpaDialect getJpaDialect();
|
||||
|
||||
/**
|
||||
* Return the vendor-specific EntityManagerFactory interface
|
||||
* that the EntityManagerFactory proxy is supposed to implement.
|
||||
* <p>If the provider does not offer any EntityManagerFactory extensions,
|
||||
* the adapter should simply return the standard
|
||||
* {@link javax.persistence.EntityManagerFactory} class here.
|
||||
* @since 2.5.2
|
||||
*/
|
||||
Class<? extends EntityManagerFactory> getEntityManagerFactoryInterface();
|
||||
|
||||
/**
|
||||
* Return the vendor-specific EntityManager interface
|
||||
* that this provider's EntityManagers will implement.
|
||||
* <p>If the provider does not offer any EntityManager extensions,
|
||||
* the adapter should simply return the standard
|
||||
* {@link javax.persistence.EntityManager} class here.
|
||||
*/
|
||||
Class<? extends EntityManager> getEntityManagerInterface();
|
||||
|
||||
/**
|
||||
* Optional callback for post-processing the native EntityManagerFactory
|
||||
* before active use.
|
||||
* <p>This can be used for triggering vendor-specific initialization processes.
|
||||
* While this is not expected to be used for most providers, it is included
|
||||
* here as a general extension hook.
|
||||
*/
|
||||
void postProcessEntityManagerFactory(EntityManagerFactory emf);
|
||||
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue