/**
* Copyright (c) 2003 held jointly by the individual authors.
*
* This library is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published
* by the Free Software Foundation; either version 2.1 of the License, or
* (at your option) any later version.
*
* This library is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; with out even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library; if not, write to the Free Software Foundation,
* Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*
* > http://www.gnu.org/copyleft/lesser.html
* > http://www.opensource.org/licenses/lgpl-license.php
*/
package net.mlw.vlh.adapter.hibernate3;
import java.lang.reflect.InvocationTargetException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import net.mlw.vlh.DefaultListBackedValueList;
import net.mlw.vlh.ValueList;
import net.mlw.vlh.ValueListInfo;
import net.mlw.vlh.adapter.AbstractValueListAdapter;
import net.mlw.vlh.adapter.hibernate3.util.ScrollableResultsDecorator;
import net.mlw.vlh.adapter.hibernate3.util.StatementBuilder;
import net.mlw.vlh.adapter.util.ObjectValidator;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.ScrollableResults;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
/**
* This adapter wraps the functionality of Hibernate.
* Add extra functionality such as paging, focusing
* and validating of current result set.
* <i>
* "Hibernate is a powerful, ultra-high performance
* object/relational persistence and query service
* for Java. Hibernate lets you develop persistent
* classes following common Java idiom - including
* association, inheritance, polymorphism, composition
* and the Java collections framework. The Hibernate
* Query Language, designed as a "minimal"
* object-oriented extension to SQL, provides an
* elegant bridge between the object and relational
* worlds. Hibernate also allows you to express queries
* using native SQL or Java-based Criteria and Example
* queries. Hibernate is now the most popular
* object/relational mapping solution for Java."
* </i> -http://www.hibernate.org/
*
* @author Matthew L. Wilson, Andrej Zachar
* @version $Revision: 1.2 $ $Date: 2006/03/29 19:47:49 $
*/
public class HibernateAdapter extends AbstractValueListAdapter
{
/** The Hibernate SessionFactory. */
private SessionFactory sessionFactory;
/**
* <p>
* If is set, it use special ScrollableResultsDecorator, that enable or
* disable to add object in final list.
* </p>
* <h4>NOTE:</h4>
* <p>
* Also, it respects the total count of entries that overlap your paged
* list.
* </p>
*/
private ObjectValidator _validator = null;
/** Commons logger. */
private static final Log LOGGER = LogFactory.getLog(HibernateAdapter.class);
/** If a new Session should be created if no thread-bound found. */
private boolean allowCreate = true;
/** The hibernate query. */
private String hql;
private String namedQuery;
/** The max rows in ResulSet to doFocus
* @author Andrej Zachar
*/
private long maxRowsForFocus = Long.MAX_VALUE;
/** The name of object use to get focus property in hibernate sql syntax
* SELECT defaultFocusPropertyObjectAlias.getFocusProperty ...
*
* @author Andrej Zachar
*/
private String defaultFocusPropertyObjectAlias = "";
/**
* Enable or Disable String length checking of given filters values. If
* filter value is null or empty is removed from query.
* @author Andrej Zachar
*/
private boolean _isRemoveEmptyStrings = false;
private StatementBuilder statementBuilder;
/**
* Enable or disable optimalization of the query for focus property.
*/
private boolean _focusOptimalization = true;
/**
* @return Returns the focusOptimalization.
*/
public boolean isFocusOptimalization()
{
return _focusOptimalization;
}
/**
* Enable or disable optimalization of the query for focus property.
* @param focusOptimalization true - enable query with short select, false - query with full select
*/
public void setFocusOptimalization(boolean focusOptimalization)
{
_focusOptimalization = focusOptimalization;
}
/**
* <p>
* If is set, it use special ScrollableResultsDecorator, that enable or
* disable to add object in final list.
* </p>
* <h4>NOTE:</h4>
* <p>
* Also, it respects the total count of entries that overlap your paged
* list.
* </p>
* @param validator The validator to set.
*/
public void setValidator(ObjectValidator validator)
{
_validator = validator;
}
/**
* @return Returns the isPrefilterEmpty.
*/
public boolean isRemoveEmptyStrings()
{
return _isRemoveEmptyStrings;
}
/**
* Enable or Disable String length checking of given filters values. If
* filter value is null or empty is removed from query.
*
* @param isPrefilterEmpty
* true-remove null and empty, false - remove only null filters.
*/
public void setRemoveEmptyStrings(boolean isPrefilterEmpty)
{
_isRemoveEmptyStrings = isPrefilterEmpty;
}
/**
* @see net.mlw.vlh.ValueListAdapter#getValueList(java.lang.String,
* net.mlw.vlh.ValueListInfo)
*/
public ValueList getValueList(String name, ValueListInfo info)
{
LOGGER.debug("getValueList(String, ValueListInfo) - start");
if (info.getSortingColumn() == null)
{
info.setPrimarySortColumn(getDefaultSortColumn());
info.setPrimarySortDirection(getDefaultSortDirectionInteger());
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("The default sort column '" + getDefaultSortColumn() + "' with direction '" + getDefaultSortDirectionInteger()
+ "' was set.");
}
}
int numberPerPage = info.getPagingNumberPer();
if (numberPerPage == Integer.MAX_VALUE)
{
numberPerPage = getDefaultNumberPerPage();
info.setPagingNumberPer(numberPerPage);
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("The paging number per page '" + numberPerPage + "' was set.");
}
}
Session session = SessionFactoryUtils.getSession(getSessionFactory(), allowCreate);
try
{
Query query;
boolean doFocus = ((getAdapterType() & DO_FOCUS) == 0) && info.isFocusEnabled() && info.isDoFocus() && (namedQuery == null);
if (doFocus)
{
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Start to focusing adapterName '" + name + "', ValueListInfo info = " + info + "'");
}
ScrollableResults results = getScrollableResults(getQueryForFocus(info, session), info);
results.beforeFirst();
doFocusFor(info, results);
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Focusing finished for adapterName '" + name + "', ValueListInfo info '" + info + "'");
}
}
query = getQuery(info, session);
boolean doPaging = ((getAdapterType() & DO_PAGE) == 0);
List list;
if (doPaging)
{
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("getValueList(String adapterName = " + name + ", ValueListInfo info = " + info
+ ") - Start to paging result set");
}
list = new ArrayList(numberPerPage);
ScrollableResults results = getScrollableResults(query, info);
results.last();
int lastRowNumber = results.getRowNumber();
info.setTotalNumberOfEntries(lastRowNumber + 1);
if (numberPerPage == 0)
{
numberPerPage = getDefaultNumberPerPage();
}
int pageNumber = info.getPagingPage();
boolean isResult;
if (pageNumber > 1)
{
if ((pageNumber - 1) * numberPerPage > lastRowNumber)
{
pageNumber = (lastRowNumber / numberPerPage) + 1;
info.setPagingPage(pageNumber);
}
}
if (pageNumber > 1)
{
isResult = results.scroll((pageNumber - 1) * numberPerPage - lastRowNumber);
}
else
{
isResult = results.first();
}
for (int i = 0; i < numberPerPage && isResult; i++)
{
list.add(results.get(0));
isResult = results.next();
}
LOGGER.debug("Sorting finished.");
}
else
{
LOGGER.debug("Retrieving a list directly from the query.");
list = query.list();
info.setTotalNumberOfEntries(list.size());
}
ValueList returnValueList = getListBackedValueList(info, list);
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Retrieved list was wrapped in valuelist, info=" + info);
}
return returnValueList;
}
catch (HibernateException e)
{
LOGGER.error("Error getting data in adapater '" + name + "' with info = '" + info + "'", e);
throw SessionFactoryUtils.convertHibernateAccessException(e);
}
catch (Exception e)
{
LOGGER.fatal("Fatal error getting data in adapater '" + name + "' with info = '" + info + "'", e);
return null;
}
finally
{
SessionFactoryUtils.releaseSession(session, getSessionFactory());
}
}
/**
* @param info
* @param list
* @return DefaultListBackValueList instance
*/
protected ValueList getListBackedValueList(ValueListInfo info, List list)
{
return new DefaultListBackedValueList(list, info);
}
/**
* @param info
* @param results
* @throws HibernateException
* @throws NoSuchMethodException
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
private void doFocusFor(ValueListInfo info, ScrollableResults results) throws HibernateException
{
info.setFocusStatus(ValueListInfo.FOCUS_NOT_FOUND);
int currentRow;
if (isFocusOptimalization())
{
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Focusing only property '" + info.getFocusProperty() + "' == '" + info.getFocusValue() + "'.");
}
for (currentRow = 0; ((results.next()) && (currentRow < maxRowsForFocus)); currentRow++)
{
String value = results.get(0).toString();
if (value.equalsIgnoreCase(info.getFocusValue()))
{
if (LOGGER.isInfoEnabled())
{
LOGGER.info("Focus property '" + info.getFocusProperty() + "' in row '" + currentRow + "'.");
}
info.setPagingPageFromRowNumber(results.getRowNumber());
info.setFocusedRowNumberInTable(results.getRowNumber());
info.setFocusStatus(ValueListInfo.FOCUS_FOUND);
break;
}
}
}
else
{
if (LOGGER.isDebugEnabled())
{
LOGGER.debug("Focusing object with the property '" + info.getFocusProperty() + "' == '" + info.getFocusValue() + "'.");
}
for (currentRow = 0; ((results.next()) && (currentRow < maxRowsForFocus)); currentRow++)
{
Object value;
try
{
value = PropertyUtils.getProperty(results.get(0), info.getFocusProperty());
}
catch (HibernateException e)
{
LOGGER.error("Error getting focus property '" + info.getFocusProperty() + "'", e);
throw e;
}
catch (Exception e)
{
LOGGER.warn("Ingoring error while getting focus property '" + info.getFocusProperty() + "'", e);
continue;
}
if (value.toString().equalsIgnoreCase(info.getFocusValue()))
{
if (LOGGER.isInfoEnabled())
{
LOGGER.info("Focus object's property '" + info.getFocusProperty() + "' was found in the row '" + currentRow + "'.");
}
info.setPagingPageFromRowNumber(results.getRowNumber());
info.setFocusedRowNumberInTable(results.getRowNumber());
info.setFocusStatus(ValueListInfo.FOCUS_FOUND);
break;
}
}
}
if (currentRow == maxRowsForFocus)
{
if (LOGGER.isInfoEnabled())
{
LOGGER.info("Focus for property '" + info.getFocusProperty() + "' exceded maximum rows for focus '" + maxRowsForFocus + "'.");
}
info.setFocusStatus(ValueListInfo.FOCUS_TOO_MANY_ITEMS);
}
}
/**
* @param query
* @param info ValueListInfo This info will be set to validator.
* @return ScrollableResults, if is set non null _validator, it returns the
* ScrollableResultsDecorator.
* @throws HibernateException
*/
private ScrollableResults getScrollableResults(Query query, ValueListInfo info) throws HibernateException
{
ScrollableResults results;
if (_validator == null)
{
LOGGER.debug("Validator is null, using normal ScrollableResults");
results = query.scroll();
}
else
{
LOGGER.info("Using decorator of the ScrollableResults with your validator.");
_validator.setValueListInfo(info);
results = new ScrollableResultsDecorator(query.scroll(), _validator);
}
return results;
}
/**
* @param info
* @param session
* @return @throws
* HibernateException
*/
private Query getQuery(ValueListInfo info, Session session) throws HibernateException, ParseException
{
if (getHql() != null)
{
return getStatementBuilder().generate(session, new StringBuffer(getHql()), info.getFilters(), _isRemoveEmptyStrings);
}
else
{
if (namedQuery != null)
{
return session.getNamedQuery(getNamedQuery());
}
else
{
throw new HibernateException("Please define any QUERY in value list retrieve adpater!");
}
}
}
/**
* If focus optimalization is true, it select only focus property. For
* validator is recommended to set it to false, while you want to validate
* properties of retrieved objects.
*
* @param info
* @param session
* @return
* @throws HibernateException
*/
private Query getQueryForFocus(ValueListInfo info, Session session) throws HibernateException, ParseException
{
if (isFocusOptimalization())
{
LOGGER.info("Focus will use optimalizated query.");
return getOptimizedQuery(info, session);
}
else
{
LOGGER.info("Focus will use normal (full) query.");
return getQuery(info, session);
}
}
/**
*
* @param info
* @param session
* @return query that select only focus property.
* @throws HibernateException
* @throws ParseException
*/
private Query getOptimizedQuery(ValueListInfo info, Session session) throws HibernateException, ParseException
{
if (getHql() != null)
{
return getStatementBuilder().generateForFocus(session, new StringBuffer(getHql()), info.getFilters(), _isRemoveEmptyStrings,
defaultFocusPropertyObjectAlias, info.getFocusProperty());
}
else
{
throw new HibernateException(
"Please define any HQL QUERY in value list retrieve adpater, function is not implemented for NamedQuery!");
}
}
/** Set the Hibernate SessionFactory to be used by this DAO.
*
* @param sessionFactory The Hibernate SessionFactory to be used by this DAO.
*/
public final void setSessionFactory(SessionFactory sessionFactory)
{
this.sessionFactory = sessionFactory;
}
/** Return the Hibernate SessionFactory used by this DAO.
*
* @return The Hibernate SessionFactory used by this DAO.
*/
protected final SessionFactory getSessionFactory()
{
return this.sessionFactory;
}
/** Sets the hql used to retrieve the data.
* @param hql The hql to set.
* @deprecated use setHql(String)
*/
public void setHsql(String hql)
{
this.hql = hql;
}
/** Sets the hql used to retrieve the data.
* @param hql The hql to set.
*/
public void setHql(String hql)
{
this.hql = hql;
}
/** Returns the namedQuery.
* @return Returns the namedQuery.
*/
public String getNamedQuery()
{
return namedQuery;
}
/** Sets the namedQuery.
* <p>
* NOTE: by using this you can not enable sorting, filtering,
* paging of the data, and focusing of rows.
* </p>
* @param namedQuery
* The namedQuery to set.
*/
public void setNamedQuery(String namedQuery)
{
this.namedQuery = namedQuery;
}
/** Gets the hql used to retrieve the data.
* @return Returns the hql.
*/
public String getHql()
{
return hql;
}
/** Sets: If a new Session should be created if no thread-bound found.
*
* @param allowCreate
* The allowCreate to set.
*/
public void setAllowCreate(boolean allowCreate)
{
this.allowCreate = allowCreate;
}
/**
* Maximum rows to search with Focus
*
* @return Returns the maxRowsForFocus.
*/
public long getMaxRowsForFocus()
{
return maxRowsForFocus;
}
/**
* Maximum rows to search with Focus
*
* @param maxRowsForFocus
* The maxRowsForFocus to set.
*/
public void setMaxRowsForFocus(long maxRowsForFocus)
{
this.maxRowsForFocus = maxRowsForFocus;
}
/**
* @return Returns the defaultFocusPropertyObject.
*/
public String getDefaultFocusPropertyObjectAlias()
{
return defaultFocusPropertyObjectAlias;
}
/**
* The name of object use to get focus property in hibernate hql syntax
* SELECT defaultFocusPropertyObjectAlias.getFocusProperty ...
*
* @param defaultFocusPropertyObjectAlias
* The defaultFocusPropertyObjectAlias to set.
*/
public void setDefaultFocusPropertyObjectAlias(String defaultFocusPropertyObjectAlias)
{
this.defaultFocusPropertyObjectAlias = defaultFocusPropertyObjectAlias + ".";
}
/**
* @return Returns the statementBuilder.
*/
public StatementBuilder getStatementBuilder()
{
if (statementBuilder == null)
{
statementBuilder = new StatementBuilder();
}
return statementBuilder;
}
/**
* @param statementBuilder The statementBuilder to set.
*/
public void setStatementBuilder(StatementBuilder statementBuilder)
{
this.statementBuilder = statementBuilder;
}
}