lucee.runtime.orm.hibernate.HibernateORMSession.java Source code

Java tutorial

Introduction

Here is the source code for lucee.runtime.orm.hibernate.HibernateORMSession.java

Source

/**
 *
 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved.
 *
 * 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; without 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, see <http://www.gnu.org/licenses/>.
 * 
 **/
package lucee.runtime.orm.hibernate;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import lucee.commons.lang.types.RefBoolean;
import lucee.loader.util.Util;
import lucee.runtime.Component;
import lucee.runtime.ComponentScope;
import lucee.runtime.PageContext;
import lucee.runtime.db.DataSource;
import lucee.runtime.db.DatasourceConnection;
import lucee.runtime.db.SQLItem;
import lucee.runtime.exp.PageException;
import lucee.runtime.op.Caster;
import lucee.runtime.orm.ORMEngine;
import lucee.runtime.orm.ORMSession;
import lucee.runtime.orm.ORMTransaction;
import lucee.runtime.orm.ORMUtil;
import lucee.runtime.type.Array;
import lucee.runtime.type.Collection.Key;
import lucee.runtime.type.KeyImpl;
import lucee.runtime.type.Struct;
import lucee.runtime.type.scope.Argument;

import org.hibernate.Criteria;
import org.hibernate.FlushMode;
import org.hibernate.NonUniqueResultException;
import org.hibernate.QueryException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.criterion.Example;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
import org.hibernate.engine.query.HQLQueryPlan;
import org.hibernate.engine.query.ParameterMetadata;
import org.hibernate.engine.query.QueryPlanCache;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.type.Type;

public class HibernateORMSession implements ORMSession {

    private SessionFactoryData data;
    //private DataSource[] sources;
    private DatasourceConnection[] connections;
    //private Map<DataSource,Session> _sessions=new HashMap<DataSource, Session>();

    //private Map<Key,DataSource> _sources=new HashMap<Key, DataSource>();
    private Map<Key, Session> _sessions = new HashMap<Key, Session>();

    public HibernateORMSession(PageContext pc, SessionFactoryData data) throws PageException {
        this.data = data;
        //this.dc=dc;
        DataSource[] sources = data.getDataSources();
        connections = new DatasourceConnection[sources.length];

        for (int i = 0; i < sources.length; i++) {
            connections[i] = CommonUtil.getDatasourceConnection(pc, sources[i]);
            createSession(data.getFactory(KeyImpl.init(sources[i].getName())), connections[i]);
        }
    }

    /*private Session session(){
       return _session;
    }*/

    private Session getSession(Key datasSourceName) throws PageException {
        Session s = _sessions.get(datasSourceName);
        if (s != null)
            return s;

        lucee.commons.lang.ExceptionUtil.similarKeyMessage(_sessions.keySet().toArray(new Key[_sessions.size()]),
                datasSourceName.getString(), "datasource", "datasources", true);

        throw ExceptionUtil.createException(data, null,
                "there is no Session for the datasource [" + datasSourceName + "]", null);
    }

    public SessionFactoryData getSessionFactoryData() {
        return data;
    }

    SessionFactory getSessionFactory(Key datasSourceName) throws PageException {
        Session s = getSession(datasSourceName);
        return s.getSessionFactory();
    }

    void resetSession(PageContext pc, SessionFactory factory, Key dataSourceName, SessionFactoryData data)
            throws PageException {

        for (int i = 0; i < connections.length; i++) {
            if (dataSourceName.equals(connections[i].getDatasource().getName())) {
                createSession(factory, connections[i]);
                return;
            }
        }
        DataSource ds = data.getDataSource(dataSourceName);
        DatasourceConnection dc = CommonUtil.getDatasourceConnection(pc, ds);
        try {
            createSession(factory, dc);
        } finally {
            CommonUtil.releaseDatasourceConnection(pc, dc);
        }
    }

    void createSession(SessionFactory factory, DatasourceConnection dc) {
        Session session;
        _sessions.put(KeyImpl.init(dc.getDatasource().getName()),
                session = factory.openSession(dc.getConnection()));
        session.setFlushMode(FlushMode.MANUAL);
    }

    @Override
    public ORMEngine getEngine() {
        return data.getEngine();
    }

    @Override
    public void flushAll(PageContext pc) throws PageException {
        // release all connections
        for (int i = 0; i < connections.length; i++) {
            flush(pc, connections[i].getDatasource());
        }
    }

    @Override
    public void flush(PageContext pc) throws PageException {
        flush(pc, (String) null);// not simply null then it get ambgious for some compiler
    }

    @Override
    public void flush(PageContext pc, String datasource) throws PageException {
        flush(pc, ORMUtil.getDataSource(pc, datasource));
    }

    private void flush(PageContext pc, DataSource datasource) throws PageException {
        Key dsn = KeyImpl.init(datasource.getName());

        try {
            getSession(dsn).flush();

        } catch (Throwable t) {
            throw CommonUtil.toPageException(t);
        }

    }

    @Override
    public void delete(PageContext pc, Object obj) throws PageException {
        if (CommonUtil.isArray(obj)) {

            // convert to a usable structure
            Map<Key, List<Component>> cfcs = new HashMap<Key, List<Component>>();
            {
                Array arr = CommonUtil.toArray(obj);
                Iterator<?> it = arr.valueIterator();
                Component cfc;

                Key dsn;
                List<Component> list;
                while (it.hasNext()) {
                    cfc = HibernateCaster.toComponent(it.next());
                    dsn = KeyImpl.init(ORMUtil.getDataSourceName(pc, cfc));
                    list = cfcs.get(dsn);
                    if (list == null)
                        cfcs.put(dsn, list = new ArrayList<Component>());
                    list.add(cfc);
                }
            }

            Iterator<Entry<Key, List<Component>>> it = cfcs.entrySet().iterator();
            while (it.hasNext()) {
                Entry<Key, List<Component>> e = it.next();
                Transaction trans = getSession(e.getKey()).getTransaction();
                if (trans.isActive())
                    trans.begin();
                else
                    trans = null;

                try {
                    Iterator<Component> _it = e.getValue().iterator();
                    while (_it.hasNext()) {
                        _delete(pc, _it.next(), e.getKey());
                    }
                } catch (Throwable t) {
                    if (trans != null)
                        trans.rollback();
                    throw CommonUtil.toPageException(t);
                }
                if (trans != null)
                    trans.commit();
            }
        } else
            _delete(pc, HibernateCaster.toComponent(obj), null);
    }

    public void _delete(PageContext pc, Component cfc, Key dsn) throws PageException {
        if (dsn == null)
            dsn = KeyImpl.init(ORMUtil.getDataSourceName(pc, cfc));
        data.checkExistent(pc, cfc);
        try {
            getSession(dsn).delete(HibernateCaster.getEntityName(cfc), cfc);
        } catch (Throwable t) {
            throw CommonUtil.toPageException(t);
        }
    }

    @Override
    public void save(PageContext pc, Object obj, boolean forceInsert) throws PageException {
        Component cfc = HibernateCaster.toComponent(obj);
        String name = HibernateCaster.getEntityName(cfc);
        Key dsn = KeyImpl.init(ORMUtil.getDataSourceName(pc, cfc));

        try {
            Session session = getSession(dsn);
            if (forceInsert)
                session.save(name, cfc);
            else
                session.saveOrUpdate(name, cfc);
        } catch (Throwable t) {
            throw ExceptionUtil.createException(this, null, t);
        }
    }

    @Override
    public void reload(PageContext pc, Object obj) throws PageException {
        Component cfc = HibernateCaster.toComponent(obj);
        Key dsn = KeyImpl.init(ORMUtil.getDataSourceName(pc, cfc));
        data.checkExistent(pc, cfc);
        getSession(dsn).refresh(cfc);
    }

    @Override
    public Component create(PageContext pc, String entityName) throws PageException {
        return data.getEngine().create(pc, this, entityName, true);
    }

    @Override
    public void clear(PageContext pc) throws PageException {
        clear(pc, null);
    }

    @Override
    public void clear(PageContext pc, String datasource) throws PageException {
        Key dsn = KeyImpl.init(ORMUtil.getDataSource(pc, datasource).getName());

        getSession(dsn).clear();
        /*Iterator<Session> it = _sessions.values().iterator();
        while(it.hasNext()){
           it.next().clear();
        }*/
    }

    @Override
    public void evictQueries(PageContext pc) throws PageException {
        evictQueries(pc, null, null);
    }

    @Override
    public void evictQueries(PageContext pc, String cacheName) throws PageException {
        evictQueries(pc, cacheName, null);
    }

    @Override
    public void evictQueries(PageContext pc, String cacheName, String datasource) throws PageException {
        Key dsn = KeyImpl.init(ORMUtil.getDataSource(pc, datasource).getName());
        SessionFactory factory = getSession(dsn).getSessionFactory();

        if (Util.isEmpty(cacheName))
            factory.evictQueries();
        else
            factory.evictQueries(cacheName);

        /*Iterator<Session> it = _sessions.values().iterator();
        while(it.hasNext()){
           SessionFactory f = it.next().getSessionFactory();
           if(Util.isEmpty(cacheName))f.evictQueries();
           else f.evictQueries(cacheName);
        }*/
    }

    @Override
    public void evictEntity(PageContext pc, String entityName) throws PageException {
        evictEntity(pc, entityName, null);
    }

    @Override
    public void evictEntity(PageContext pc, String entityName, String id) throws PageException {
        Iterator<Session> it = _sessions.values().iterator();
        while (it.hasNext()) {
            SessionFactory f = it.next().getSessionFactory();
            if (id == null)
                f.evictEntity(entityName);
            else
                f.evictEntity(entityName, CommonUtil.toSerializable(id));
        }
    }

    @Override
    public void evictCollection(PageContext pc, String entityName, String collectionName) throws PageException {
        evictCollection(pc, entityName, collectionName, null);
    }

    @Override
    public void evictCollection(PageContext pc, String entityName, String collectionName, String id)
            throws PageException {
        String role = entityName + "." + collectionName;

        Iterator<Session> it = _sessions.values().iterator();
        while (it.hasNext()) {
            SessionFactory f = it.next().getSessionFactory();
            if (id == null)
                f.evictCollection(role);
            else
                f.evictCollection(role, CommonUtil.toSerializable(id));
        }
    }

    @Override
    public Object executeQuery(PageContext pc, String dataSourceName, String hql, Array params, boolean unique,
            Struct queryOptions) throws PageException {
        return _executeQuery(pc, dataSourceName, hql, params, unique, queryOptions);
    }

    @Override
    public Object executeQuery(PageContext pc, String dataSourceName, String hql, Struct params, boolean unique,
            Struct queryOptions) throws PageException {
        return _executeQuery(pc, dataSourceName, hql, params, unique, queryOptions);
    }

    private Object _executeQuery(PageContext pc, String dataSourceName, String hql, Object params, boolean unique,
            Struct queryOptions) throws PageException {
        Key dsn;
        if (dataSourceName == null)
            dsn = KeyImpl.init(ORMUtil.getDefaultDataSource(pc).getName());
        else
            dsn = KeyImpl.init(dataSourceName);

        Session s = getSession(dsn);
        try {
            return __executeQuery(pc, s, dsn, hql, params, unique, queryOptions);
        } catch (QueryException qe) {
            // argument scope is array and struct at the same time, by default it is handled as struct, if this fails try it as array
            if (params instanceof Argument) {
                try {
                    return __executeQuery(pc, s, dsn, hql, CommonUtil.toArray((Argument) params), unique,
                            queryOptions);
                } catch (Throwable t) {
                    t.printStackTrace();
                }
            }
            throw qe;
        }

    }

    private Object __executeQuery(PageContext pc, Session session, Key dsn, String hql, Object params,
            boolean unique, Struct options) throws PageException {
        //Session session = getSession(pc,null);
        hql = hql.trim();
        org.hibernate.Query query = session.createQuery(hql);
        // options
        if (options != null) {
            // maxresults
            Object obj = options.get("maxresults", null);
            if (obj != null) {
                int max = CommonUtil.toIntValue(obj, -1);
                if (max < 0)
                    throw ExceptionUtil.createException(this, null, "option [maxresults] has an invalid value ["
                            + obj + "], value should be a number bigger or equal to 0", null);
                query.setMaxResults(max);
            }
            // offset
            obj = options.get("offset", null);
            if (obj != null) {
                int off = CommonUtil.toIntValue(obj, -1);
                if (off < 0)
                    throw ExceptionUtil.createException(this, null, "option [offset] has an invalid value [" + obj
                            + "], value should be a number bigger or equal to 0", null);
                query.setFirstResult(off);
            }
            // readonly
            obj = options.get("readonly", null);
            if (obj != null) {
                Boolean ro = CommonUtil.toBoolean(obj, null);
                if (ro == null)
                    throw ExceptionUtil.createException(this, null,
                            "option [readonly] has an invalid value [" + obj + "], value should be a boolean value",
                            null);
                query.setReadOnly(ro.booleanValue());
            }
            // timeout
            obj = options.get("timeout", null);
            if (obj != null) {
                int to = CommonUtil.toIntValue(obj, -1);
                if (to < 0)
                    throw ExceptionUtil.createException(this, null, "option [timeout] has an invalid value [" + obj
                            + "], value should be a number bigger or equal to 0", null);
                query.setTimeout(to);
            }
        }

        // params
        if (params != null) {
            QueryPlanCache cache = data.getQueryPlanCache(dsn);
            HQLQueryPlan plan = cache.getHQLQueryPlan(hql, false, java.util.Collections.EMPTY_MAP);
            ParameterMetadata meta = plan.getParameterMetadata();
            Type type;
            Object obj;

            // struct
            if (CommonUtil.isStruct(params)) {
                Struct sct = CommonUtil.toStruct(params);
                Key[] keys = CommonUtil.keys(sct);
                String name;
                // fix case-senstive
                Struct names = CommonUtil.createStruct();
                if (meta != null) {
                    Iterator<String> it = meta.getNamedParameterNames().iterator();
                    while (it.hasNext()) {
                        name = it.next();
                        names.setEL(name, name);
                    }
                }

                RefBoolean isArray = CommonUtil.createRefBoolean();
                for (int i = 0; i < keys.length; i++) {
                    obj = sct.get(keys[i], null);
                    if (meta != null) {
                        name = (String) names.get(keys[i], null);
                        if (name == null)
                            continue; // param not needed will be ignored
                        type = meta.getNamedParameterExpectedType(name);
                        obj = HibernateCaster.toSQL(type, obj, isArray);
                        if (isArray.toBooleanValue()) {
                            if (obj instanceof Object[])
                                query.setParameterList(name, (Object[]) obj, type);
                            else if (obj instanceof List)
                                query.setParameterList(name, (List) obj, type);
                            else
                                query.setParameterList(name, Caster.toList(obj), type);
                        } else
                            query.setParameter(name, obj, type);

                    } else
                        query.setParameter(keys[i].getString(), obj);
                }
            }

            // array
            else if (CommonUtil.isArray(params)) {
                Array arr = CommonUtil.toArray(params);
                Iterator it = arr.valueIterator();
                int index = 0;
                SQLItem item;
                RefBoolean isArray = null;
                while (it.hasNext()) {
                    obj = it.next();
                    if (obj instanceof SQLItem) {
                        item = (SQLItem) obj;
                        obj = item.getValue();
                        //HibernateCaster.toHibernateType(item.getType(), null); MUST
                        //query.setParameter(index, item.getValue(),type);
                    }
                    if (meta != null) {
                        type = meta.getOrdinalParameterExpectedType(index + 1);
                        obj = HibernateCaster.toSQL(type, obj, isArray);
                        // TOOD can the following be done somehow
                        //if(isArray.toBooleanValue())
                        //   query.setParameterList(index, (Object[])obj,type);
                        //else
                        query.setParameter(index, obj, type);
                    } else
                        query.setParameter(index, obj);
                    index++;
                }
                if (meta.getOrdinalParameterCount() > index)
                    throw ExceptionUtil.createException(this, null, "parameter array is to small [" + arr.size()
                            + "], need [" + meta.getOrdinalParameterCount() + "] elements", null);
            }
        }

        // select
        String lcHQL = hql.toLowerCase();
        if (lcHQL.startsWith("select") || lcHQL.startsWith("from")) {
            if (unique) {
                return uniqueResult(query);
            }

            return query.list();
        }
        // update
        return new Double(query.executeUpdate());
    }

    private Object uniqueResult(org.hibernate.Query query) throws PageException {
        try {
            return query.uniqueResult();
        } catch (NonUniqueResultException e) {
            List list = query.list();
            if (list.size() > 0)
                return list.iterator().next();
            throw CommonUtil.toPageException(e);
        } catch (Throwable t) {
            throw CommonUtil.toPageException(t);
        }
    }

    @Override
    public lucee.runtime.type.Query toQuery(PageContext pc, Object obj, String name) throws PageException {
        return HibernateCaster.toQuery(pc, this, obj, name);
    }

    @Override
    public void close(PageContext pc) throws PageException {
        close(pc, null);
    }

    @Override
    public void close(PageContext pc, String datasource) throws PageException {
        DataSource ds = ORMUtil.getDataSource(pc, datasource);
        Key dsn = KeyImpl.init(ds.getName());

        // close Session
        getSession(dsn).close();

        // release connection
        List<DatasourceConnection> list = new ArrayList<DatasourceConnection>();
        for (int i = 0; i < connections.length; i++) {
            if (connections[i].getDatasource().equals(ds)) {
                CommonUtil.releaseDatasourceConnection(pc, connections[i]);
            } else
                list.add(connections[i]);
        }
        connections = list.toArray(new DatasourceConnection[list.size()]);
    }

    @Override
    public void closeAll(PageContext pc) throws PageException {

        Iterator<Session> it = _sessions.values().iterator();
        while (it.hasNext()) {
            Session s = it.next();
            s.close();
        }

        // release all connections
        for (int i = 0; i < connections.length; i++) {
            CommonUtil.releaseDatasourceConnection(pc, connections[i]);
        }
        connections = null;
    }

    @Override
    public Component merge(PageContext pc, Object obj) throws PageException {
        Component cfc = HibernateCaster.toComponent(obj);
        CFCInfo info = data.checkExistent(pc, cfc);

        String name = HibernateCaster.getEntityName(cfc);

        return CommonUtil.toComponent(getSession(KeyImpl.init(info.getDataSource().getName())).merge(name, cfc));
    }

    @Override
    public Component load(PageContext pc, String name, Struct filter) throws PageException {
        return (Component) load(pc, name, filter, null, null, true);
    }

    @Override
    public Array loadAsArray(PageContext pc, String name, Struct filter) throws PageException {
        return loadAsArray(pc, name, filter, null, null);
    }

    @Override
    public Array loadAsArray(PageContext pc, String name, String id, String order) throws PageException {
        return loadAsArray(pc, name, id);// order is ignored in this case ACF compatibility
    }

    @Override
    public Array loadAsArray(PageContext pc, String name, String id) throws PageException {
        Array arr = CommonUtil.createArray();
        Component c = load(pc, name, id);
        if (c != null)
            arr.append(c);
        return arr;
    }

    @Override
    public Array loadAsArray(PageContext pc, String name, Struct filter, Struct options) throws PageException {
        return loadAsArray(pc, name, filter, options, null);
    }

    @Override
    public Array loadAsArray(PageContext pc, String name, Struct filter, Struct options, String order)
            throws PageException {
        return CommonUtil.toArray(load(pc, name, filter, options, order, false));
    }

    @Override
    public Component load(PageContext pc, String cfcName, String id) throws PageException {
        //Component cfc = create(pc,cfcName);

        Component cfc = data.getEngine().create(pc, this, cfcName, false);
        Key dsn = KeyImpl.init(ORMUtil.getDataSourceName(pc, cfc));
        Session sess = getSession(dsn);
        String name = HibernateCaster.getEntityName(cfc);
        Object obj = null;
        try {
            ClassMetadata metaData = sess.getSessionFactory().getClassMetadata(name);
            if (metaData == null)
                throw ExceptionUtil.createException(this, null,
                        "could not load meta information for entity [" + name + "]", null);
            Serializable oId = CommonUtil
                    .toSerializable(CommonUtil.castTo(pc, metaData.getIdentifierType().getReturnedClass(), id));
            obj = sess.get(name, oId);
        } catch (Throwable t) {
            throw CommonUtil.toPageException(t);
        }

        return (Component) obj;
    }

    @Override
    public Component loadByExample(PageContext pc, Object obj) throws PageException {
        return CommonUtil.toComponent(loadByExample(pc, obj, true));
    }

    @Override
    public Array loadByExampleAsArray(PageContext pc, Object obj) throws PageException {
        return CommonUtil.toArray(loadByExample(pc, obj, false));
    }

    private Object loadByExample(PageContext pc, Object obj, boolean unique) throws PageException {
        Component cfc = HibernateCaster.toComponent(obj);
        Key dsn = KeyImpl.init(ORMUtil.getDataSourceName(pc, cfc));
        ComponentScope scope = cfc.getComponentScope();
        String name = HibernateCaster.getEntityName(cfc);
        Session sess = getSession(dsn);
        Object rtn = null;

        try {
            //trans.begin();

            ClassMetadata metaData = sess.getSessionFactory().getClassMetadata(name);
            String idName = metaData.getIdentifierPropertyName();
            Type idType = metaData.getIdentifierType();

            Criteria criteria = sess.createCriteria(name);
            if (!Util.isEmpty(idName)) {
                Object idValue = scope.get(CommonUtil.createKey(idName), null);
                if (idValue != null) {
                    criteria.add(Restrictions.eq(idName, HibernateCaster.toSQL(idType, idValue, null)));
                }
            }
            criteria.add(Example.create(cfc));

            // execute

            if (!unique) {
                rtn = criteria.list();
            } else {
                //Map map=(Map) criteria.uniqueResult();
                rtn = criteria.uniqueResult();
            }
        } catch (Throwable t) {
            // trans.rollback();
            throw CommonUtil.toPageException(t);
        }
        //trans.commit();

        return rtn;
    }

    private Object load(PageContext pc, String cfcName, Struct filter, Struct options, String order, boolean unique)
            throws PageException {
        Component cfc = data.getEngine().create(pc, this, cfcName, false);
        Key dsn = KeyImpl.init(ORMUtil.getDataSourceName(pc, cfc));
        Session sess = getSession(dsn);

        String name = HibernateCaster.getEntityName(cfc);
        ClassMetadata metaData = null;

        Object rtn;
        try {
            //trans.begin();

            Criteria criteria = sess.createCriteria(name);

            // filter
            if (filter != null && !filter.isEmpty()) {
                metaData = sess.getSessionFactory().getClassMetadata(name);
                Object value;
                Entry<Key, Object> entry;
                Iterator<Entry<Key, Object>> it = filter.entryIterator();
                String colName;
                while (it.hasNext()) {
                    entry = it.next();
                    colName = HibernateUtil.validateColumnName(metaData, CommonUtil.toString(entry.getKey()));
                    Type type = HibernateUtil.getPropertyType(metaData, colName, null);
                    value = entry.getValue();
                    if (!(value instanceof Component))
                        value = HibernateCaster.toSQL(type, value, null);

                    if (value != null)
                        criteria.add(Restrictions.eq(colName, value));
                    else
                        criteria.add(Restrictions.isNull(colName));
                }
            }

            // options
            boolean ignoreCase = false;
            if (options != null && !options.isEmpty()) {
                // ignorecase
                Boolean ignorecase = CommonUtil.toBoolean(options.get("ignorecase", null), null);
                if (ignorecase != null)
                    ignoreCase = ignorecase.booleanValue();

                // offset
                int offset = CommonUtil.toIntValue(options.get("offset", null), 0);
                if (offset > 0)
                    criteria.setFirstResult(offset);

                // maxResults
                int max = CommonUtil.toIntValue(options.get("maxresults", null), -1);
                if (max > -1)
                    criteria.setMaxResults(max);

                // cacheable
                Boolean cacheable = CommonUtil.toBoolean(options.get("cacheable", null), null);
                if (cacheable != null)
                    criteria.setCacheable(cacheable.booleanValue());

                // MUST cacheName ?

                // maxResults
                int timeout = CommonUtil.toIntValue(options.get("timeout", null), -1);
                if (timeout > -1)
                    criteria.setTimeout(timeout);
            }

            // order 
            if (!Util.isEmpty(order)) {
                if (metaData == null)
                    metaData = sess.getSessionFactory().getClassMetadata(name);

                String[] arr = CommonUtil.toStringArray(order, ',');
                CommonUtil.trimItems(arr);
                String[] parts;
                String col;
                boolean isDesc;
                Order _order;
                //ColumnInfo ci;
                for (int i = 0; i < arr.length; i++) {
                    parts = CommonUtil.toStringArray(arr[i], " \t\n\b\r");
                    CommonUtil.trimItems(parts);
                    col = parts[0];

                    col = HibernateUtil.validateColumnName(metaData, col);
                    isDesc = false;
                    if (parts.length > 1) {
                        if (parts[1].equalsIgnoreCase("desc"))
                            isDesc = true;
                        else if (!parts[1].equalsIgnoreCase("asc")) {
                            throw ExceptionUtil.createException((ORMSession) null, null,
                                    "invalid order direction defintion [" + parts[1] + "]",
                                    "valid values are [asc, desc]");
                        }

                    }
                    _order = isDesc ? Order.desc(col) : Order.asc(col);
                    if (ignoreCase)
                        _order.ignoreCase();

                    criteria.addOrder(_order);

                }
            }

            // execute
            if (!unique) {
                rtn = HibernateCaster.toCFML(criteria.list());
            } else {
                rtn = HibernateCaster.toCFML(criteria.uniqueResult());
            }

        } catch (Throwable t) {
            throw CommonUtil.toPageException(t);
        }

        return rtn;
    }

    @Override
    public Session getRawSession(String dsn) throws PageException {
        return getSession(KeyImpl.init(dsn));
    }

    @Override
    public SessionFactory getRawSessionFactory(String dsn) throws PageException {
        return getSession(KeyImpl.init(dsn)).getSessionFactory();
    }

    @Override
    public boolean isValid(DataSource ds) {
        Session sess = _sessions.get(ds);
        return sess != null && sess.isOpen();
    }

    @Override
    public boolean isValid() {
        if (_sessions.size() == 0)
            return false;
        Iterator<Session> it = _sessions.values().iterator();
        while (it.hasNext()) {
            if (!it.next().isOpen())
                return false;
        }
        return true;
    }

    @Override
    public ORMTransaction getTransaction(String dsn, boolean autoManage) throws PageException {
        return new HibernateORMTransaction(getSession(KeyImpl.init(dsn)), autoManage);
    }

    @Override
    public String[] getEntityNames() {
        List<String> names = data.getEntityNames();
        return names.toArray(new String[names.size()]);
    }

    @Override
    public DataSource[] getDataSources() {
        return data.getDataSources();
    }
}