package org.oddjob.sql; import java.util.ArrayList; import java.util.List; import javax.inject.Inject; import org.apache.log4j.Logger; import org.oddjob.arooa.deploy.annotations.ArooaHidden; import org.oddjob.beanbus.AbstractFilter; import org.oddjob.beanbus.BusConductor; import org.oddjob.beanbus.BusCrashException; import org.oddjob.beanbus.BusEvent; import org.oddjob.beanbus.TrackingBusListener; /** * @oddjob.description Captures SQL results in a bean that * has properties to provide those results to other jobs. * <p> * The properties {@code row}, {@code rows}, {@code rowSets} properties * in turn expose the results as beans so the colums can be accessed as * properties. * <p> * If a single query result set consisted of a single row: * * <code><pre> * NAME AGE * John 47 * </pre></code> * * then: * * <code><pre> * row.NAME == rows[0].NAME == rowSets[0][0].NAME == 'John' * row.AGE == rows[0].AGE == rowSets[0][0].AGE == 47 * </pre></code> * * If a single query result set consisted of more than a single row: * * <code><pre> * NAME AGE * John 47 * Jane 72 * </pre></code> * * then the {@code row} property is unavailable and any attempt to access * it would result in an exception, and: * * <code><pre> * rows[1].NAME == rowSets[0][1].NAME == 'Jane' * rows[1].AGE == rowSets[0][1].AGE == '72' * </pre></code> * * If the query results in a multiple query result set: * * <code><pre> * NAME AGE * John 47 * Jane 72 * </pre></code> * * <code><pre> * FRUIT COLOUR * Apple Green * </pre></code> * * then the {@code row} property and the {@code rows} properties are * unavailable and any attempt to access either would result in an exception. * The rowSets property can be used as follows: * * <code><pre> * rowSets[0][1].NAME == 'Jane' * rowSets[0][1].AGE == '72' * rowSets[1][0].FRUIT == 'Apple' * rowSets[1][0].COLOUR == 'Green' * </pre></code> * * The case of the properties depends on the database used. * <p> * * Any attempt to access a row or row set that doesn't exist will result * in an exception. * <p> * * @oddjob.example * * See {@link SQLJob} for an example. * * @author rob * */ public class SQLResultsBean extends AbstractFilter<Object, Object>{ private static final Logger logger = Logger.getLogger(SQLResultsBean.class); /** * @oddjob.property * @oddjob.description The results of the query. This property allows * indexed access to the rows in the result. * @oddjob.required Read only. */ private final List<List<?>> rowSets = new ArrayList<List<?>>(); /** * @oddjob.property * @oddjob.description The update count of any insert/update/delete statement. * @oddjob.required Read only. */ private final List<Integer> updateCounts = new ArrayList<Integer>(); private int rowCount; private int updateCount; private List<Object> beans; private final TrackingBusListener busListener = new TrackingBusListener() { @Override public void busStarting(BusEvent event) throws BusCrashException { rowSets.clear(); updateCounts.clear(); rowCount = 0; updateCount = 0; } @Override public void tripBeginning(BusEvent event) throws BusCrashException { beans = new ArrayList<Object>(); } @Override public void tripEnding(BusEvent event) throws BusCrashException { addBeans(beans); } }; @ArooaHidden @Inject public void setBusConductor(BusConductor busConductor) { busListener.setBusConductor(busConductor); } @Override protected Object filter(Object bean) { if (bean instanceof UpdateCount) { UpdateCount updateCount = (UpdateCount) bean; updateCounts.add(new Integer(updateCount.getCount())); this.updateCount += updateCount.getCount(); this.beans = null; } else { beans.add(bean); } return bean; } public void addBeans(List<?> beans) { if (beans != null) { logger.info("Adding rowSets " + rowSets.size() + " of " + beans.size() + " beans."); rowSets.add(beans); rowCount += beans.size(); } } /** * @oddjob.property rowCount * @oddjob.description The total number of rows returned by all the * queries. * @oddjob.required Read only. */ public int getRowCount() { return rowCount; } /** * @oddjob.property rowSetCount * @oddjob.description The number of rows sets, which will be the same * as the number of queries that returned results. * * @oddjob.required Read only. */ public int getRowSetCount() { return rowSets.size(); } /** * @oddjob.property rowSets * @oddjob.description A two dimensional array of all of the rows * that each individual query returned. * * @oddjob.required Read only. */ public Object[][] getRowSets() { Object[][] allSets = new Object[rowSets.size()][]; int i = 0; for (List<?> rows : rowSets ) { if (rows == null) { allSets[i++] = null; } else { allSets[i++] = rows.toArray(new Object[rows.size()]); } } return allSets; } /** * @oddjob.property rows * @oddjob.description An array of the rows when the query set contains only * one result returning query. * If no results were returned by the queries this property is null. If * there are more than one result sets an exception will occur. * @oddjob.required Read only. */ public Object[] getRows() { if (rowSets.size() > 1) { throw new UnsupportedOperationException( "Properties [row/rows] are not available when there are multiple (" + rowSets.size() + ") row sets. Use rowSets[0] instead."); } if (rowSets.size() == 0) { return null; } List<?> rows = rowSets.get(0); if (rows == null) { return null; } return rows.toArray(new Object[rows.size()]); } /** * @oddjob.property row * @oddjob.description The result of a query when only one result is expected. * If no results were returned by the queries this property is null. If * there are more than one row an exception will occur. * @oddjob.required Read only. */ public Object getRow() { Object[] rows = getRows(); if (rows == null) { return null; } if (rows.length > 1) { throw new UnsupportedOperationException( "Property [row] is not available when there are multiple (" + rows.length + ") rows. Use rows[0] instead."); } if (rows.length == 0) { return null; } return rows[0]; } /** * @oddjob.property updateCounts * @oddjob.description An Array of the update counts, one element per * data modification statement. * @oddjob.required Read only. */ public Integer[] getUpdateCounts() { return updateCounts.toArray(new Integer[updateCounts.size()]); } /** * @oddjob.property updateCount * @oddjob.description The total update count for all queries. * @oddjob.required Read only. */ public int getUpdateCount() { return updateCount; } }