OPEN - issue BATCH-209: standard session option for HibernateCursorInputSource

http://opensource.atlassian.com/projects/spring/browse/BATCH-209

Applied patch from Robert.
This commit is contained in:
dsyer
2007-11-21 10:47:39 +00:00
parent 92cb2ab13b
commit 6d6b8a3005
3 changed files with 105 additions and 15 deletions

View File

@@ -18,6 +18,7 @@ package org.springframework.batch.io.cursor;
import java.util.Properties;
import org.hibernate.ScrollableResults;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.StatelessSession;
import org.springframework.batch.io.InputSource;
@@ -36,6 +37,17 @@ import org.springframework.util.ClassUtils;
/**
* {@link InputSource} for reading database records built on top of Hibernate.
*
* It executes the HQL {@link #queryString} when initialized and iterates over
* the result set as {@link #read()} method is called, returning an object corresponding
* to current row.
*
* Input source can be configured to use either {@link StatelessSession} sufficient
* for simple mappings without the need to cascade to associated objects or standard hibernate
* {@link Session} for more advanced mappings or when caching is desired.
*
* When stateful session is used it will be cleared after successful commit *without* being flushed
* (no inserts or updates are expected).
*
* @author Robert Kasanicky
*/
public class HibernateCursorInputSource implements InputSource, Restartable, InitializingBean, DisposableBean,
@@ -44,13 +56,21 @@ public class HibernateCursorInputSource implements InputSource, Restartable, Ini
private static final String RESTART_DATA_ROW_NUMBER_KEY = ClassUtils.getShortName(HibernateCursorInputSource.class)+".rowNumber";
private SessionFactory sessionFactory;
private StatelessSession session;
private StatelessSession statelessSession;
private Session statefulSession;
private ScrollableResults cursor;
private String queryString;
private boolean useStatelessSession = true;
private int lastCommitRowNumber = 0;
private boolean initialized = false;
private TransactionSynchronization synchronization = new HibernateInputSourceTransactionSynchronization();
@@ -66,25 +86,37 @@ public class HibernateCursorInputSource implements InputSource, Restartable, Ini
}
/**
* Close the resultset cursor and hibernate session.
* Closes the result set cursor and hibernate session.
*/
public void close() {
initialized = false;
cursor.close();
session.close();
if (useStatelessSession) {
statelessSession.close();
} else {
statefulSession.close();
}
}
/**
* Create cursor for the query
* Creates cursor for the query.
*/
public void open() {
session = sessionFactory.openStatelessSession();
cursor = session.createQuery(queryString).scroll();
if (useStatelessSession) {
statelessSession = sessionFactory.openStatelessSession();
cursor = statelessSession.createQuery(queryString).scroll();
} else {
statefulSession = sessionFactory.openSession();
cursor = statefulSession.createQuery(queryString).scroll();
}
BatchTransactionSynchronizationManager.registerSynchronization(synchronization );
initialized = true;
}
/**
* @param sessionFactory hibernate session factory
*/
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
@@ -98,10 +130,24 @@ public class HibernateCursorInputSource implements InputSource, Restartable, Ini
close();
}
/**
* @param queryString HQL query string
*/
public void setQueryString(String queryString) {
this.queryString = queryString;
}
/**
* Can be set only in uninitialized state.
* @param useStatelessSession
* <code>true</code> to use {@link StatelessSession}
* <code>false</code> to use standard hibernate {@link Session}
*/
public void setUseStatelessSession(boolean useStatelessSession) {
Assert.state(!initialized);
this.useStatelessSession = useStatelessSession;
}
/**
* @return the current row number wrapped as <code>RestartData</code>
*/
@@ -113,7 +159,7 @@ public class HibernateCursorInputSource implements InputSource, Restartable, Ini
}
/**
* Set the cursor to the received row number.
* Sets the cursor to the received row number.
*/
public void restoreFrom(RestartData data) {
Assert.state(!initialized,
@@ -138,6 +184,9 @@ public class HibernateCursorInputSource implements InputSource, Restartable, Ini
cursor.setRowNumber(lastCommitRowNumber);
} else if (status == TransactionSynchronization.STATUS_COMMITTED) {
lastCommitRowNumber = cursor.getRowNumber();
if (!useStatelessSession) {
statefulSession.clear();
}
}
}
}

View File

@@ -1,36 +1,61 @@
package org.springframework.batch.io.cursor;
import org.hibernate.SessionFactory;
import org.hibernate.StatelessSession;
import org.springframework.batch.io.InputSource;
import org.springframework.batch.io.cursor.HibernateCursorInputSource;
import org.springframework.batch.io.support.AbstractDataSourceInputSourceIntegrationTests;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.orm.hibernate3.LocalSessionFactoryBean;
/**
* Tests for {@link HibernateCursorInputSource}
* Tests for {@link HibernateCursorInputSource} using {@link StatelessSession}.
*
* @author Robert Kasanicky
*/
public class HibernateInputSourceIntegrationTests extends AbstractDataSourceInputSourceIntegrationTests {
public class HibernateCursorInputSourceIntegrationTests extends AbstractDataSourceInputSourceIntegrationTests {
protected InputSource createInputSource() throws Exception {
LocalSessionFactoryBean factoryBean = new LocalSessionFactoryBean();
factoryBean.setDataSource(super.getJdbcTemplate().getDataSource());
factoryBean.setMappingLocations(new Resource[]{new ClassPathResource("Foo.hbm.xml", getClass())});
factoryBean.setMappingLocations(new Resource[] { new ClassPathResource("Foo.hbm.xml", getClass()) });
factoryBean.afterPropertiesSet();
SessionFactory sessionFactory = (SessionFactory) factoryBean.getObject();
String hsqlQuery = "from Foo";
HibernateCursorInputSource inputSource = new HibernateCursorInputSource();
inputSource.setQueryString(hsqlQuery);
inputSource.setSessionFactory(sessionFactory);
inputSource.setUseStatelessSession(isUseStatelessSession());
inputSource.afterPropertiesSet();
return inputSource;
}
protected boolean isUseStatelessSession() {
return true;
}
/**
* Exception scenario.
*
* {@link HibernateCursorInputSource#setUseStatelessSession(boolean)} can be
* called only in uninitialized state.
*/
public void testSetUseStatelessSession() {
HibernateCursorInputSource inputSource = ((HibernateCursorInputSource) source);
// initialize and call setter => error
inputSource.open();
try {
inputSource.setUseStatelessSession(false);
fail();
}
catch (IllegalStateException e) {
// expected
}
}
}

View File

@@ -0,0 +1,16 @@
package org.springframework.batch.io.cursor;
import org.hibernate.Session;
/**
* Tests for {@link HibernateCursorInputSource} using standard hibernate {@link Session}.
*
* @author Robert Kasanicky
*/
public class HibernateCursorInputSourceStatefulIntegrationTests extends HibernateCursorInputSourceIntegrationTests{
protected boolean isUseStatelessSession() {
return false;
}
}