Announcement Announcement Module
Collapse
No announcement yet.
HibernateCursorItemReader arguments Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • HibernateCursorItemReader arguments

    Quick question - this may be obvious but I haven't been able to find a solution - I'm trying to pass some arguments to an hql query in a HibernateCursorItemReader. Specifically, I want to get records from a specific date that's stored in my StepExecution. What's the best way to go about doing that? Should I just extend HibernateCursorItemReader and make some modifications?

    Thanks, and thanks 1,000,000 for the batch framework. It's awesome.

  • #2
    I don't think you need to extend the HibernateCursorItemReader as much as you need to proxy the HQL statement that's passed in. Unfortunately, since the reader won't be invoked until after the statement you passed in has already been wired, it can be a bit trickier than making a FactoryBean (assuming you want to pull the date from JobParameters, which I'm assuming you do) Personally, I would create a composite reader that called the setHql method during StepExecutionListener's beforeStep method(chapter 4 of the reference documentation contains more information about the StepExecutionListener interface) to set the date. Unless there's some other, easier way to do it on the Session itself that I'm unaware of.

    Comment


    • #3
      Thanks for the response. Yes, I do want to pull the date from JobParameters. This is what I had going before you wrote:

      Code:
      public class OneDayItemReader extends HibernateCursorItemReader implements StepExecutionListener {
      
      	private static final String RESTART_DATA_ROW_NUMBER_KEY = "row.number";
      	private SessionFactory sessionFactory;
      	private StatelessSession statelessSession;
      	private Session statefulSession;
      	private ScrollableResults cursor;
      	private String queryString;
      	private boolean useStatelessSession = true;
      
      	/* Current count of processed records. */
      	private int currentProcessedRow = 0;
      	private boolean initialized = false;
      	
      	StepExecution stepExecution;
      	
      	/* (non-Javadoc)
      	 * @see org.springframework.batch.item.database.HibernateCursorItemReader#open(org.springframework.batch.item.ExecutionContext)
      	 */
      	@Override
      	public void open(ExecutionContext executionContext) {
      		
      		String date = stepExecution.getJobParameters().getString("date");
      		DateFormat dateFormat = new SimpleDateFormat("MM-dd-yyyy");
      		Calendar startDate = new GregorianCalendar();
      		Calendar endDate = new GregorianCalendar();
      		try {
      			startDate.setTime(dateFormat.parse(date));
      		}
      		catch(ParseException e) {
      			//what should I do with this exception?
      		}
      		endDate.setTime(startDate.getTime());
      		endDate.add(Calendar.HOUR, 24);
      		
      		Assert.state(!initialized, "Cannot open an already opened ItemReader, call close first");
      
      		if (useStatelessSession) {
      			statelessSession = sessionFactory.openStatelessSession();
      			cursor = statelessSession.createQuery(queryString)
      				.setDate("startDate", startDate.getTime())
      				.setDate("endDate", endDate.getTime())
      				.scroll();
      		}
      		else {
      			statefulSession = sessionFactory.openSession();
      			cursor = statefulSession.createQuery(queryString)
      				.setDate("startDate", startDate.getTime())
      				.setDate("endDate", endDate.getTime())
      				.scroll();
      		}
      		initialized = true;
      
      		if (executionContext.containsKey(getKey(RESTART_DATA_ROW_NUMBER_KEY))) {
      			currentProcessedRow = Integer.parseInt(executionContext.getString(getKey(RESTART_DATA_ROW_NUMBER_KEY)));
      			cursor.setRowNumber(currentProcessedRow - 1);
      		}
      	}
      		
      	/* (non-Javadoc)
      	 * @see org.springframework.batch.core.StepExecutionListener#beforeStep(org.springframework.batch.core.StepExecution)
      	 */
      	public void beforeStep(StepExecution stepExecution) {
      		this.stepExecution = stepExecution;
      	}
      	
      ...
      Then I was going to do

      Code:
      <property name="queryString" value="from Foo where createDate between :startDate and :endDate "/>
      I haven't tested any of this to know if it works but that's the direction I was headed. Think I should double back and try your method?

      Comment


      • #4
        I had a similar requirement and at first I went the direction of extending HibernateCursorItemReader. However, having read Lucas' suggestion, I backtracked and came up with this. Note, in my case, I didn't need to get anything as a JobParameter, I just needed the jobExecutionId (see beforeStep() method). However, it's the same concept and you can of course get the jobParameter information you need from StepExecution object. Also note, this Composite Item Reader needs to be registered as a stream listener in your Step configuration. HTH.

        Code:
        package com.my.company.item.reader;
        
        import org.hibernate.SessionFactory;
        import org.springframework.batch.core.StepExecution;
        import org.springframework.batch.core.listener.StepExecutionListenerSupport;
        import org.springframework.batch.item.ExecutionContext;
        import org.springframework.batch.item.ItemReader;
        import org.springframework.batch.item.ItemStream;
        import org.springframework.batch.item.ItemStreamException;
        import org.springframework.batch.item.NoWorkFoundException;
        import org.springframework.batch.item.ParseException;
        import org.springframework.batch.item.UnexpectedInputException;
        import org.springframework.batch.item.database.HibernateCursorItemReader;
        import org.springframework.beans.factory.InitializingBean;
        import org.springframework.util.Assert;
        
        public final class MyCompositeItemReader extends
        		StepExecutionListenerSupport implements ItemReader, ItemStream,
        		InitializingBean {
        
        	private HibernateCursorItemReader itemReader;
        	private SessionFactory sessionFactory;
        
        	public void beforeStep(StepExecution stepExecution) {
        		Long jobNumber = stepExecution.getJobExecutionId();
        		itemReader = new HibernateCursorItemReader();
        		itemReader
        				.setQueryString("from SomeObject where jobNum='" String.valueOf(jobNumber) + "'");
        		itemReader.setSessionFactory(sessionFactory);
        		callDelegateAfterPropertiesSet(this.itemReader);
        	}
        
        	private void callDelegateAfterPropertiesSet(HibernateCursorItemReader d) {
        		try {
        			d.afterPropertiesSet();
        		} catch (Exception e) {
        			throw new RuntimeException("Error initializing delegate reader.", e);
        		}
        	}
        
        	public void setSessionFactory(SessionFactory sessionFactory) {
        		this.sessionFactory = sessionFactory;
        	}
        
        	public void afterPropertiesSet() throws Exception {
        		Assert.notNull(sessionFactory,
        				"Property [sessionFactory] is required and must be set.");
        	}
        
        	public Object read() throws Exception, UnexpectedInputException,
        			NoWorkFoundException, ParseException {
        		Assert
        				.state(
        						itemReader != null,
        						"ItemReader not initialized.  Make sure to register as a step execution listener.");
        		return itemReader.read();
        	}
        
        	public void mark() {
        		if (itemReader != null) {
        			itemReader.mark();
        		}
        	}
        
        	public void reset() {
        		if (itemReader != null) {
        			itemReader.reset();
        		}
        	}
        
        	public void close(ExecutionContext executionContext)
        			throws ItemStreamException {
        		itemReader.close(executionContext);
        	}
        
        	public void open(ExecutionContext executionContext)
        			throws ItemStreamException {
        		itemReader.open(executionContext);
        	}
        
        	public void update(ExecutionContext executionContext)
        			throws ItemStreamException {
        		itemReader.update(executionContext);
        	}
        
        }

        Comment


        • #5
          Hey joenmoreno, thanks for that. I was still hoping against hope that I could just extend and modify, but because all of the variables are private instead of protected and there are no protected getters for them, there's really no point in doing so. I've been able to do what I wanted to do, but basically had to copy the entire HibernateCursorItemReader to do it. So what I'm doing is pretty stupid, but it works. I wanted to avoid manipulating the hql string directly since I'm using dates instead of ids and things... but I might still try using your approach.

          This is just a thought, but it would be great if there was a version of HibernateCursorItemReader that was 'parameter aware' out of the box, so you could add parameters to your hql string that the HibernateCursorItemReader would then attempt to fetch from the StepExecutionContext, similar, I suppose, to HibernateTemplate.findByNamedParam. Might be kind of messy to implement that, but it could be pretty useful. Just an idea.

          Comment


          • #6
            Best way for this to actually happen is to file a JIRA issue.

            This should be fairly easy to implement for JDBC using named parameters - we could just try to match them to the job parameters. Something similar should be possible for Hibernate as well.

            Comment


            • #7
              Best way for this to actually happen is to file a JIRA issue.
              Yeah, I considered that, but I suppose I wasn't sure that it was such a good idea. But to your point at least people could then vote on it.

              http://jira.springframework.org/browse/BATCH-730

              Comment


              • #8
                Just a quick note, the Jdbc version of this is already in place. The JdbcCursorItemReader accepts a PreparedStatementSetter that can be used to set paramters. There's a stock implementation called StepExecutionPreparedStatementSetter that can be used to pull them from JobParameters. Although, it will probably need to be updated to use the named parameter feature in Spring DA.

                I agree on the extra feature for the hibernate reader though.

                Comment

                Working...
                X