blob: d9899a5ec6645a7a37eb029349131e562201ef40 [file] [log] [blame]
/**
* <copyright>
*
* Copyright (c) 2005, 2006, 2007, 2008, 2021 Springsite BV (The Netherlands) and others
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Martin Taal
* </copyright>
*
* $Id: LazyCollectionUtils.java,v 1.9 2010/08/18 11:50:38 mtaal Exp $
*/
package org.eclipse.emf.teneo.hibernate;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import org.eclipse.emf.teneo.hibernate.mapping.eav.EAVDelegatingList;
import org.eclipse.emf.teneo.hibernate.mapping.eav.EAVValueHolder;
import org.eclipse.emf.teneo.mapping.elist.PersistableDelegateList;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.collection.internal.AbstractPersistentCollection;
import org.hibernate.engine.spi.CollectionEntry;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.hql.internal.ast.util.SessionFactoryHelper;
import org.hibernate.persister.collection.AbstractCollectionPersister;
import org.hibernate.persister.collection.QueryableCollection;
/**
* A utility class providing methods related to lazy loading of collections.
*
* @author <a href="mailto:mtaal@elver.org">Martin Taal</a>
* @version $Revision: 1.9 $
*/
public class LazyCollectionUtils {
public final static int DEFAULT_PAGE_SIZE = 100;
/**
* Returns an iterator which loads the underlying data from the collection in pages (controlled by
* the pageSize parameter). Note if the collection is not lazy loadable then a normal iterator is
* returned. This is checked using the {@link #isLazyLoadableCollection(Collection)} method.
*
* Note: this method can only handle collections of EObjects so not collections of primitive typed
* objects. Paged iteration of these collections is not supported by Hibernate.
*
* @param coll
* the collection to iterate lazily over
* @param pageSize
* the pageSize, this determines the pagesize of the page of data read each time from the
* database
* @return a paging iterator or if not a lazy loadable collection a normal iterator
* @see PagingIterator
*/
public static <E> Iterator<E> getPagedLoadingIterator(Collection<E> coll, int pageSize) {
if (!isLazyLoadableCollection(coll)) {
return coll.iterator();
}
final PersistableDelegateList<?> persistableList = (PersistableDelegateList<?>) coll;
final AbstractPersistentCollection persistentCollection = (AbstractPersistentCollection) persistableList
.getDelegate();
final SharedSessionContractImplementor session = persistentCollection.getSession();
final PagingIterator<E> pagingIterator = new PagingIterator<E>();
pagingIterator.setCollection(persistentCollection);
pagingIterator.setPageSize(pageSize);
pagingIterator.setSession((Session) session);
final CollectionEntry entry = session.getPersistenceContext().getCollectionEntry(
persistentCollection);
final AbstractCollectionPersister persister = (AbstractCollectionPersister) entry
.getLoadedPersister();
pagingIterator.setEavCollection(coll instanceof EAVDelegatingList);
pagingIterator.setIndexColumnNames(persister.getIndexColumnNames());
return pagingIterator;
}
/**
* Reads the size of a collection in a lazy manner, i.e. will try to not load the collection from
* the database. The size is cached in the object, so subsequent calls to this method will not
* result in additional database queries. This until the collection changes then the cache is
* cleared.
*
* Note if the collection can not be lazy loaded (see the
* {@link #isLazyLoadableCollection(Collection)}) then the size method is called on the
* collection. This method call is probably not lazy.
*
* @param coll
* the collection to get the size from
* @return the size of the collection
*/
public static int size(Collection<?> coll) {
if (!isLazyLoadableCollection(coll)) {
return coll.size();
}
final PersistableDelegateList<?> persistableList = (PersistableDelegateList<?>) coll;
final AbstractPersistentCollection persistentCollection = (AbstractPersistentCollection) persistableList
.getDelegate();
final SharedSessionContractImplementor session = persistentCollection.getSession();
final QueryableCollection persister = new SessionFactoryHelper(session.getFactory())
.getCollectionPersister(persistentCollection.getRole());
return persister.getSize(persistentCollection.getKey(), session);
}
/**
* Determines if a collection can be lazy loaded. A lazy loadable collection must be a
* {@link PersistableDelegateList} which has a {@link AbstractPersistentCollection} as the
* delegate list which also must have an open Hibernate session.
*
* @param coll
* the collection for which to determine if it can be lazy loaded
* @return false if not lazy loadable, true otherwise
*/
public static <E> boolean isLazyLoadableCollection(Collection<E> coll) {
boolean lazyLoadable = coll instanceof PersistableDelegateList<?>;
if (!lazyLoadable) {
return false;
}
final PersistableDelegateList<?> persistableList = (PersistableDelegateList<?>) coll;
lazyLoadable &= persistableList.getDelegate() instanceof AbstractPersistentCollection;
if (!lazyLoadable) {
return false;
}
final AbstractPersistentCollection persistentCollection = (AbstractPersistentCollection) persistableList
.getDelegate();
final SharedSessionContractImplementor session = persistentCollection.getSession();
// if not a valid session then go away
if (session == null || !session.isOpen()
|| !session.getPersistenceContext().containsCollection(persistentCollection)) {
return false;
}
return true;
}
/**
* A paging iterator reads the underlying data from the database in pages. Everytime the iterator
* reaches the end of a page it reads the next page from the database.
*
* @author mtaal
*/
public static class PagingIterator<E> implements Iterator<E> {
private Session session;
private int pageSize;
private Boolean hasNext = null;
private int currentIteratorIndex = 0;
private int nextPageStart = 0;
private List<E> content;
private Object collection;
private String[] indexColumnNames;
private String orderBy = "";
private boolean eavCollection;
public boolean hasNext() {
if (content == null) {
setPageInformation();
}
if (currentIteratorIndex < content.size()) {
return true;
}
if (hasNext != null) {
return hasNext;
}
if (content.size() < pageSize) {
hasNext = false;
return hasNext;
}
// load the next page to see if there is any content
hasNext = !loadNextPage().isEmpty();
return hasNext;
}
public E next() {
if (currentIteratorIndex < content.size()) {
return convert(content.get(currentIteratorIndex++));
}
// load the next page
setPageInformation();
if (content.isEmpty()) {
throw new NoSuchElementException();
}
return convert(content.get(currentIteratorIndex++));
}
@SuppressWarnings("unchecked")
private E convert(E value) {
if (value instanceof EAVValueHolder) {
return (E) ((EAVValueHolder) value).getValue();
}
return value;
}
private void setPageInformation() {
content = loadNextPage();
currentIteratorIndex = 0;
hasNext = null;
nextPageStart += content.size();
}
@SuppressWarnings("unchecked")
private List<E> loadNextPage() {
final Query query;
if (isEavCollection()) {
query = session.createFilter(collection, " order by this.listIndex ");
} else {
query = session.createFilter(collection, orderBy);
}
query.setMaxResults(pageSize);
query.setFirstResult(nextPageStart);
return (List<E>) query.list();
}
public void remove() {
throw new UnsupportedOperationException("Removal is not supported by the paging iterator");
}
public Session getSession() {
return session;
}
public void setSession(Session session) {
this.session = session;
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public Object getCollection() {
return collection;
}
public void setCollection(Object collection) {
this.collection = collection;
}
public String[] getIndexColumnNames() {
return indexColumnNames;
}
public void setIndexColumnNames(String[] indexColumnNames) {
this.indexColumnNames = indexColumnNames;
final StringBuilder sb = new StringBuilder();
if (indexColumnNames != null) {
for (String indexColumnName : indexColumnNames) {
if (sb.length() == 0) {
sb.append(" order by ");
} else {
sb.append(", ");
}
sb.append(indexColumnName.replaceAll("`", "").replaceAll("\"", ""));
}
}
orderBy = sb.toString();
}
public String getOrderBy() {
return orderBy;
}
/**
* Note the parameter must include the term: order by. Refer to properties of the collection
* content using the this keyword.
*
* @param orderBy
* the order by clause including the order by keyword, for example: order by this.name
*/
public void setOrderBy(String orderBy) {
this.orderBy = orderBy;
}
public boolean isEavCollection() {
return eavCollection;
}
public void setEavCollection(boolean eavCollection) {
this.eavCollection = eavCollection;
}
}
}