package railo.runtime.orm.hibernate; import java.io.Serializable; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; 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.exception.ConstraintViolationException; import org.hibernate.metadata.ClassMetadata; import org.hibernate.type.Type; import railo.commons.lang.types.RefBoolean; import railo.loader.util.Util; import railo.runtime.Component; import railo.runtime.ComponentScope; import railo.runtime.PageContext; import railo.runtime.db.DataSource; import railo.runtime.db.DatasourceConnection; import railo.runtime.db.SQLItem; import railo.runtime.exp.PageException; import railo.runtime.orm.ORMEngine; import railo.runtime.orm.ORMSession; import railo.runtime.orm.ORMTransaction; import railo.runtime.type.Array; import railo.runtime.type.Collection.Key; import railo.runtime.type.Struct; import railo.runtime.type.scope.Argument; public class HibernateORMSession implements ORMSession{ private Session _session; private DatasourceConnection dc; private SessionFactoryData data; public HibernateORMSession(SessionFactoryData data, DatasourceConnection dc){ this.data=data; this.dc=dc; resetSession(data.getFactory()); } private Session session(){ return _session; } public SessionFactoryData getSessionFactoryData(){ return data; } private SessionFactory getSessionFactory(PageContext pc){ // engine.getSessionFactory(pc); return _session.getSessionFactory(); } SessionFactory getSessionFactory(){ // engine.getSessionFactory(pc); return _session.getSessionFactory(); } void resetSession(SessionFactory factory) { _session = factory.openSession(dc.getConnection()); _session.setFlushMode(FlushMode.MANUAL); } @Override public ORMEngine getEngine() { return data.getEngine(); } @Override public void flush(PageContext pc) throws PageException { try { session().flush(); } catch(ConstraintViolationException cve){ PageException pe = ExceptionUtil.createException(this,null,cve); if(!Util.isEmpty(cve.getConstraintName())) { ExceptionUtil.setAdditional(pe, CommonUtil.createKey("constraint name"), cve.getConstraintName()); } throw pe; } catch(Throwable t) { throw CommonUtil.toPageException(t); } } @Override public void delete(PageContext pc, Object obj) throws PageException { if(CommonUtil.isArray(obj)){ Transaction trans = session().getTransaction(); if(trans.isActive()) trans.begin(); else trans=null; try{ Iterator it = CommonUtil.toArray(obj).valueIterator(); while(it.hasNext()){ _delete(pc,HibernateCaster.toComponent(it.next())); } } catch(Throwable t){ if(trans!=null)trans.rollback(); throw CommonUtil.toPageException(t); } if(trans!=null)trans.commit(); } else _delete(pc,HibernateCaster.toComponent(obj)); } public void _delete(PageContext pc, Component cfc) throws PageException { data.checkExistent(pc,cfc); //Session session = getSession(pc,cfc); try{ session().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); //Session session = getSession(pc, cfc); String name = HibernateCaster.getEntityName(cfc); try { 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); data.checkExistent(pc,cfc); //Session session = getSession(pc,cfc); session().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 { session().clear(); } @Override public void evictQueries(PageContext pc) throws PageException { evictQueries(pc, null); } @Override public void evictQueries(PageContext pc,String cacheName) throws PageException { SessionFactory f = getSessionFactory(pc); 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 { SessionFactory f = getSessionFactory(pc); 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 { SessionFactory f = getSessionFactory(pc); String role=entityName+"."+collectionName; if(id==null) { f.evictCollection(role); } else { f.evictCollection(role,CommonUtil.toSerializable(id)); } } @Override public Object executeQuery(PageContext pc,String hql, Array params, boolean unique,Struct queryOptions) throws PageException { return _executeQuery(pc, hql, params, unique, queryOptions); } @Override public Object executeQuery(PageContext pc,String hql, Struct params, boolean unique,Struct queryOptions) throws PageException { return _executeQuery(pc, hql, params, unique, queryOptions); } public Object _executeQuery(PageContext pc,String hql, Object params, boolean unique,Struct queryOptions) throws PageException { try{ return __executeQuery(pc, 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, hql, CommonUtil.toArray((Argument)params), unique, queryOptions); } catch(Throwable t){t.printStackTrace();} } throw qe; } } private Object __executeQuery(PageContext pc,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(); 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()) query.setParameterList(name, (Object[])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 railo.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 { session().close(); CommonUtil.releaseDatasourceConnection(pc, dc); dc=null; } @Override public Component merge(PageContext pc, Object obj) throws PageException { Component cfc = HibernateCaster.toComponent(obj); data.checkExistent(pc,cfc); String name=HibernateCaster.getEntityName(cfc); //Session session = getSession(pc, cfc); return CommonUtil.toComponent(session().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); String name = HibernateCaster.getEntityName(cfc); Object obj=null; try{ ClassMetadata metaData = getSessionFactory(pc).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=session().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); ComponentScope scope = cfc.getComponentScope(); String name=HibernateCaster.getEntityName(cfc); //Session session=getSession(pc, cfc); Object rtn=null; try{ //trans.begin(); ClassMetadata metaData = getSessionFactory(pc).getClassMetadata(name); String idName = metaData.getIdentifierPropertyName(); Type idType = metaData.getIdentifierType(); Criteria criteria=session().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); String name = HibernateCaster.getEntityName(cfc); ClassMetadata metaData = null; Object rtn; try{ //trans.begin(); Criteria criteria = session().createCriteria(name); // filter if(filter!=null && !filter.isEmpty()){ metaData = getSessionFactory(pc).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=HibernateCaster.toSQL(type,entry.getValue(),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 = getSessionFactory(pc).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() { return session(); } @Override public boolean isValid() { return session()!=null && session().isOpen(); } @Override public ORMTransaction getTransaction(boolean autoManage) { return new HibernateORMTransaction(session(),autoManage); } @Override public DataSource getDataSource(){ if(dc==null) { return data.getDataSource(); } return dc.getDatasource(); } @Override public String[] getEntityNames() { return data.getEntityNames(); } }