001 package railo.runtime.orm.hibernate; 002 003 import java.io.Serializable; 004 import java.util.Iterator; 005 import java.util.List; 006 import java.util.Map; 007 import java.util.Map.Entry; 008 009 import org.hibernate.Criteria; 010 import org.hibernate.FlushMode; 011 import org.hibernate.NonUniqueResultException; 012 import org.hibernate.QueryException; 013 import org.hibernate.Session; 014 import org.hibernate.SessionFactory; 015 import org.hibernate.Transaction; 016 import org.hibernate.criterion.Example; 017 import org.hibernate.criterion.Order; 018 import org.hibernate.criterion.Restrictions; 019 import org.hibernate.engine.query.HQLQueryPlan; 020 import org.hibernate.engine.query.ParameterMetadata; 021 import org.hibernate.engine.query.QueryPlanCache; 022 import org.hibernate.exception.ConstraintViolationException; 023 import org.hibernate.metadata.ClassMetadata; 024 import org.hibernate.type.Type; 025 026 import railo.commons.lang.StringUtil; 027 import railo.commons.lang.types.RefBoolean; 028 import railo.commons.lang.types.RefBooleanImpl; 029 import railo.runtime.Component; 030 import railo.runtime.ComponentScope; 031 import railo.runtime.PageContext; 032 import railo.runtime.config.ConfigWebImpl; 033 import railo.runtime.db.DatasourceConnection; 034 import railo.runtime.db.SQLItem; 035 import railo.runtime.exp.PageException; 036 import railo.runtime.exp.PageExceptionImpl; 037 import railo.runtime.op.Caster; 038 import railo.runtime.op.Decision; 039 import railo.runtime.orm.ORMEngine; 040 import railo.runtime.orm.ORMException; 041 import railo.runtime.orm.ORMSession; 042 import railo.runtime.orm.ORMTransaction; 043 import railo.runtime.type.Array; 044 import railo.runtime.type.ArrayImpl; 045 import railo.runtime.type.Collection.Key; 046 import railo.runtime.type.KeyImpl; 047 import railo.runtime.type.Struct; 048 import railo.runtime.type.StructImpl; 049 import railo.runtime.type.scope.Argument; 050 import railo.runtime.type.scope.ArgumentImpl; 051 import railo.runtime.type.util.CollectionUtil; 052 053 public class HibernateORMSession implements ORMSession{ 054 055 private HibernateORMEngine engine; 056 private Session _session; 057 private DatasourceConnection dc; 058 059 public HibernateORMSession(HibernateORMEngine engine, SessionFactory factory, DatasourceConnection dc){ 060 this.engine=engine; 061 this.dc=dc; 062 resetSession(factory); 063 //this._session=session; 064 } 065 066 private Session session(){ 067 return _session; 068 } 069 070 private SessionFactory getSessionFactory(PageContext pc){ 071 // engine.getSessionFactory(pc); 072 return _session.getSessionFactory(); 073 } 074 SessionFactory getSessionFactory(){ 075 // engine.getSessionFactory(pc); 076 return _session.getSessionFactory(); 077 } 078 079 void resetSession(SessionFactory factory) { 080 _session = factory.openSession(dc.getConnection()); 081 _session.setFlushMode(FlushMode.MANUAL); 082 } 083 084 085 086 @Override 087 public DatasourceConnection getDatasourceConnection() { 088 return dc; 089 } 090 091 092 @Override 093 public ORMEngine getEngine() { 094 return engine; 095 } 096 097 @Override 098 public void flush(PageContext pc) throws PageException { 099 try { 100 session().flush(); 101 } 102 catch(ConstraintViolationException cve){ 103 PageException pe = HibernateException.toPageException(engine, cve); 104 if(pe instanceof PageExceptionImpl && !StringUtil.isEmpty(cve.getConstraintName())) { 105 //print.o(cve.getConstraintName()); 106 ((PageExceptionImpl)pe).setAdditional(KeyImpl.init("constraint name"), cve.getConstraintName() ); 107 } 108 throw pe; 109 } 110 111 } 112 113 @Override 114 public void delete(PageContext pc, Object obj) throws PageException { 115 if(Decision.isArray(obj)){ 116 Transaction trans = session().getTransaction(); 117 if(trans.isActive()) trans.begin(); 118 else trans=null; 119 120 try{ 121 Iterator it = Caster.toArray(obj).valueIterator(); 122 while(it.hasNext()){ 123 _delete(pc,HibernateCaster.toComponent(it.next())); 124 } 125 } 126 catch(Throwable t){ 127 if(trans!=null)trans.rollback(); 128 throw Caster.toPageException(t); 129 } 130 if(trans!=null)trans.commit(); 131 } 132 else _delete(pc,HibernateCaster.toComponent(obj)); 133 } 134 135 public void _delete(PageContext pc, Component cfc) throws PageException { 136 engine.checkExistent(pc,cfc); 137 //Session session = getSession(pc,cfc); 138 139 try{ 140 session().delete(HibernateCaster.getEntityName(cfc), cfc); 141 } 142 catch(Throwable t){ 143 throw Caster.toPageException(t); 144 } 145 } 146 147 148 149 @Override 150 public void save(PageContext pc, Object obj,boolean forceInsert) throws PageException { 151 Component cfc = HibernateCaster.toComponent(obj); 152 //Session session = getSession(pc, cfc); 153 String name = HibernateCaster.getEntityName(cfc); 154 try { 155 if(forceInsert) 156 session().save(name, cfc); 157 else 158 session().saveOrUpdate(name, cfc); 159 } 160 catch(Throwable t){ 161 throw HibernateException.toPageException(getEngine(), t); 162 } 163 } 164 165 @Override 166 public void reload(PageContext pc,Object obj) throws PageException { 167 Component cfc = HibernateCaster.toComponent(obj); 168 engine.checkExistent(pc,cfc); 169 //Session session = getSession(pc,cfc); 170 session().refresh(cfc); 171 } 172 173 174 @Override 175 public Component create(PageContext pc, String entityName)throws PageException { 176 return engine.create(pc,this, entityName,true); 177 } 178 179 @Override 180 public void clear(PageContext pc) throws PageException { 181 session().clear(); 182 } 183 184 @Override 185 public void evictQueries(PageContext pc) throws PageException { 186 evictQueries(pc, null); 187 } 188 189 @Override 190 public void evictQueries(PageContext pc,String cacheName) throws PageException { 191 SessionFactory f = getSessionFactory(pc); 192 if(StringUtil.isEmpty(cacheName))f.evictQueries(); 193 else f.evictQueries(cacheName); 194 } 195 196 @Override 197 public void evictEntity(PageContext pc, String entityName) throws PageException { 198 evictEntity(pc, entityName, null); 199 } 200 201 @Override 202 public void evictEntity(PageContext pc, String entityName, String id) throws PageException { 203 SessionFactory f = getSessionFactory(pc); 204 205 if(id==null) { 206 f.evictEntity(entityName); 207 } 208 else { 209 f.evictEntity(entityName,Caster.toSerializable(id)); 210 } 211 } 212 213 @Override 214 public void evictCollection(PageContext pc, String entityName, String collectionName) throws PageException { 215 evictCollection(pc, entityName, collectionName, null); 216 } 217 218 @Override 219 public void evictCollection(PageContext pc, String entityName, String collectionName, String id) throws PageException { 220 SessionFactory f = getSessionFactory(pc); 221 String role=entityName+"."+collectionName; 222 if(id==null) { 223 f.evictCollection(role); 224 } 225 else { 226 f.evictCollection(role,Caster.toSerializable(id)); 227 } 228 } 229 230 231 232 233 234 235 236 237 @Override 238 public Object executeQuery(PageContext pc,String hql, Array params, boolean unique,Struct queryOptions) throws PageException { 239 return _executeQuery(pc, hql, params, unique, queryOptions); 240 } 241 242 @Override 243 public Object executeQuery(PageContext pc,String hql, Struct params, boolean unique,Struct queryOptions) throws PageException { 244 return _executeQuery(pc, hql, params, unique, queryOptions); 245 } 246 247 public Object _executeQuery(PageContext pc,String hql, Object params, boolean unique,Struct queryOptions) throws PageException { 248 try{ 249 return __executeQuery(pc, hql, params, unique, queryOptions); 250 } 251 catch(QueryException qe) { 252 // argument scope is array and struct at the same time, by default it is handled as struct, if this fails try it as array 253 if(params instanceof Argument) { 254 try{ 255 return __executeQuery(pc, hql, ArgumentImpl.toArray((Argument)params), unique, queryOptions); 256 } 257 catch(Throwable t){t.printStackTrace();} 258 } 259 throw qe; 260 } 261 262 263 } 264 265 private Object __executeQuery(PageContext pc,String hql, Object params, boolean unique,Struct options) throws PageException { 266 //Session session = getSession(pc,null); 267 hql=hql.trim(); 268 org.hibernate.Query query = session().createQuery(hql); 269 // options 270 if(options!=null){ 271 // maxresults 272 Object obj=options.get("maxresults",null); 273 if(obj!=null) { 274 int max=Caster.toIntValue(obj,-1); 275 if(max<0) throw new ORMException(engine,"option [maxresults] has an invalid value ["+obj+"], value should be a number bigger or equal to 0"); 276 query.setMaxResults(max); 277 } 278 // offset 279 obj=options.get("offset",null); 280 if(obj!=null) { 281 int off=Caster.toIntValue(obj,-1); 282 if(off<0) throw new ORMException(engine,"option [offset] has an invalid value ["+obj+"], value should be a number bigger or equal to 0"); 283 query.setFirstResult(off); 284 } 285 // readonly 286 obj=options.get("readonly",null); 287 if(obj!=null) { 288 Boolean ro=Caster.toBoolean(obj,null); 289 if(ro==null) throw new ORMException(engine,"option [readonly] has an invalid value ["+obj+"], value should be a boolean value"); 290 query.setReadOnly(ro.booleanValue()); 291 } 292 // timeout 293 obj=options.get("timeout",null); 294 if(obj!=null) { 295 int to=Caster.toIntValue(obj,-1); 296 if(to<0) throw new ORMException(engine,"option [timeout] has an invalid value ["+obj+"], value should be a number bigger or equal to 0"); 297 query.setTimeout(to); 298 } 299 } 300 301 302 // params 303 if(params!=null){ 304 QueryPlanCache cache=engine.getQueryPlanCache(pc); 305 HQLQueryPlan plan = cache.getHQLQueryPlan(hql, false, java.util.Collections.EMPTY_MAP); 306 ParameterMetadata meta = plan.getParameterMetadata(); 307 Type type; 308 Object obj; 309 310 311 // struct 312 if(Decision.isStruct(params)) { 313 Struct sct=Caster.toStruct(params); 314 Key[] keys = CollectionUtil.keys(sct); 315 String name; 316 // fix case-senstive 317 Struct names=new StructImpl(); 318 if(meta!=null){ 319 Iterator<String> it = meta.getNamedParameterNames().iterator(); 320 while(it.hasNext()){ 321 name=it.next(); 322 names.setEL(name, name); 323 } 324 } 325 326 RefBoolean isArray=new RefBooleanImpl(); 327 for(int i=0;i<keys.length;i++){ 328 obj=sct.get(keys[i],null); 329 if(meta!=null){ 330 name=(String) names.get(keys[i],null); 331 if(name==null) continue; // param not needed will be ignored 332 type = meta.getNamedParameterExpectedType(name); 333 obj=HibernateCaster.toSQL(engine, type, obj,isArray); 334 if(isArray.toBooleanValue()) 335 query.setParameterList(name, (Object[])obj,type); 336 else 337 query.setParameter(name, obj,type); 338 339 340 } 341 else 342 query.setParameter(keys[i].getString(), obj); 343 } 344 } 345 346 // array 347 else if(Decision.isArray(params)){ 348 Array arr=Caster.toArray(params); 349 Iterator it = arr.valueIterator(); 350 int index=0; 351 SQLItem item; 352 RefBoolean isArray=null;//new RefBooleanImpl(); 353 while(it.hasNext()){ 354 obj=it.next(); 355 if(obj instanceof SQLItem) { 356 item=(SQLItem) obj; 357 obj=item.getValue(); 358 //HibernateCaster.toHibernateType(item.getType(), null); MUST 359 //query.setParameter(index, item.getValue(),type); 360 } 361 if(meta!=null){ 362 type = meta.getOrdinalParameterExpectedType(index+1); 363 obj=HibernateCaster.toSQL(engine, type, obj,isArray); 364 // TOOD can the following be done somehow 365 //if(isArray.toBooleanValue()) 366 // query.setParameterList(index, (Object[])obj,type); 367 //else 368 query.setParameter(index, obj,type); 369 } 370 else 371 query.setParameter(index, obj); 372 index++; 373 } 374 if(meta.getOrdinalParameterCount()>index) 375 throw new ORMException(engine,"parameter array is to small ["+arr.size()+"], need ["+meta.getOrdinalParameterCount()+"] elements"); 376 } 377 } 378 379 380 381 // select 382 if(StringUtil.startsWithIgnoreCase(hql,"select") || StringUtil.startsWithIgnoreCase(hql,"from")){ 383 if(unique){ 384 return uniqueResult(query); 385 } 386 387 return query.list(); 388 } 389 // update 390 return Caster.toDouble(query.executeUpdate()); 391 } 392 393 394 395 private Object uniqueResult(org.hibernate.Query query) { 396 try{ 397 return query.uniqueResult(); 398 } 399 catch(NonUniqueResultException e){ 400 List list = query.list(); 401 if(list.size()>0) return list.iterator().next(); 402 throw e; 403 } 404 } 405 406 @Override 407 public railo.runtime.type.Query toQuery(PageContext pc, Object obj, String name) throws PageException { 408 return HibernateCaster.toQuery(pc,this,obj,name); 409 } 410 411 @Override 412 public void close(PageContext pc) throws PageException { 413 session().close(); 414 ((ConfigWebImpl)pc.getConfig()).getDatasourceConnectionPool().releaseDatasourceConnection(dc); 415 } 416 417 @Override 418 public Component merge(PageContext pc, Object obj) throws PageException { 419 Component cfc = HibernateCaster.toComponent(obj); 420 421 engine.checkExistent(pc,cfc); 422 423 String name=HibernateCaster.getEntityName(cfc); 424 425 //Session session = getSession(pc, cfc); 426 try { 427 return Caster.toComponent(session().merge(name, cfc)); 428 } 429 catch(HibernateException e) { 430 throw new ORMException(e); 431 } 432 } 433 434 435 @Override 436 public Component load(PageContext pc, String name, Struct filter) throws PageException { 437 return (Component) load(pc, name, filter, null, null, true); 438 } 439 440 @Override 441 public Array loadAsArray(PageContext pc, String name, Struct filter) throws PageException { 442 return loadAsArray(pc, name, filter,null,null); 443 } 444 445 @Override 446 public Array loadAsArray(PageContext pc, String name, String id, String order) throws PageException{ 447 return loadAsArray(pc, name, id);// order is ignored in this case ACF compatibility 448 } 449 450 @Override 451 public Array loadAsArray(PageContext pc, String name, String id) throws PageException { 452 Array arr=new ArrayImpl(); 453 Component c = load(pc, name, id); 454 if(c!=null)arr.append(c); 455 return arr; 456 } 457 458 @Override 459 public Array loadAsArray(PageContext pc, String name, Struct filter, Struct options) throws PageException { 460 return loadAsArray(pc, name, filter,options,null); 461 } 462 463 @Override 464 public Array loadAsArray(PageContext pc, String name, Struct filter, Struct options, String order) throws PageException { 465 return Caster.toArray(load(pc, name, filter, options, order, false)); 466 } 467 468 @Override 469 public Component load(PageContext pc, String cfcName, String id) throws PageException { 470 //Component cfc = create(pc,cfcName); 471 472 473 Component cfc=engine.create(pc, this,cfcName,false); 474 475 String name = HibernateCaster.getEntityName(cfc); 476 Object obj=null; 477 try{ 478 ClassMetadata metaData = getSessionFactory(pc).getClassMetadata(name); 479 if(metaData==null) throw new ORMException(engine,"could not load meta information for entity ["+name+"]"); 480 Serializable oId = Caster.toSerializable( 481 Caster.castTo(pc, 482 metaData 483 .getIdentifierType() 484 .getReturnedClass(), 485 id)); 486 obj=session().get(name,oId); 487 } 488 catch(Throwable t){ 489 throw Caster.toPageException(t); 490 } 491 492 return (Component) obj; 493 } 494 495 @Override 496 public Component loadByExample(PageContext pc, Object obj) throws PageException { 497 return Caster.toComponent(loadByExample(pc,obj, true)); 498 } 499 500 @Override 501 public Array loadByExampleAsArray(PageContext pc, Object obj) throws PageException { 502 return Caster.toArray(loadByExample(pc,obj, false)); 503 } 504 505 private Object loadByExample(PageContext pc, Object obj, boolean unique) throws PageException { 506 Component cfc=HibernateCaster.toComponent(obj); 507 ComponentScope scope = cfc.getComponentScope(); 508 String name=HibernateCaster.getEntityName(cfc); 509 //Session session=getSession(pc, cfc); 510 511 Object rtn=null; 512 513 try{ 514 //trans.begin(); 515 516 ClassMetadata metaData = getSessionFactory(pc).getClassMetadata(name); 517 String idName = metaData.getIdentifierPropertyName(); 518 Type idType = metaData.getIdentifierType(); 519 520 Criteria criteria=session().createCriteria(name); 521 if(!StringUtil.isEmpty(idName)){ 522 Object idValue = scope.get(KeyImpl.init(idName),null); 523 if(idValue!=null){ 524 criteria.add(Restrictions.eq(idName, HibernateCaster.toSQL(engine, idType, idValue,null))); 525 } 526 } 527 criteria.add(Example.create(cfc)); 528 529 // execute 530 531 if(!unique){ 532 rtn = criteria.list(); 533 } 534 else { 535 //Map map=(Map) criteria.uniqueResult(); 536 rtn= criteria.uniqueResult(); 537 } 538 } 539 catch(Throwable t){ 540 // trans.rollback(); 541 throw Caster.toPageException(t); 542 } 543 //trans.commit(); 544 545 return rtn; 546 } 547 548 549 private Object load(PageContext pc, String cfcName, Struct filter, Struct options, String order, boolean unique) throws PageException { 550 Component cfc=engine.create(pc, this,cfcName,false); 551 552 String name = HibernateCaster.getEntityName(cfc); 553 ClassMetadata metaData = null; 554 555 Object rtn; 556 try{ 557 //trans.begin(); 558 559 Criteria criteria = session().createCriteria(name); 560 561 // filter 562 if(filter!=null && !filter.isEmpty()){ 563 564 metaData = getSessionFactory(pc).getClassMetadata(name); 565 566 567 568 Object value; 569 Map.Entry entry; 570 Iterator it = filter.entrySet().iterator(); 571 String colName; 572 while(it.hasNext()){ 573 entry=(Entry) it.next(); 574 colName=HibernateUtil.validateColumnName(metaData, Caster.toString(entry.getKey())); 575 Type type = HibernateUtil.getPropertyType(metaData,colName,null); 576 value=HibernateCaster.toSQL(engine,type,entry.getValue(),null); 577 if(value!=null) criteria.add(Restrictions.eq(colName, value)); 578 else criteria.add(Restrictions.isNull(colName)); 579 580 581 582 583 } 584 } 585 586 // options 587 boolean ignoreCase=false; 588 if(options!=null && !options.isEmpty()){ 589 // ignorecase 590 Boolean ignorecase=Caster.toBoolean(options.get("ignorecase",null),null); 591 if(ignorecase!=null)ignoreCase=ignorecase.booleanValue(); 592 593 // offset 594 int offset=Caster.toIntValue(options.get("offset",null),0); 595 if(offset>0) criteria.setFirstResult(offset); 596 597 // maxResults 598 int max=Caster.toIntValue(options.get("maxresults",null),-1); 599 if(max>-1) criteria.setMaxResults(max); 600 601 // cacheable 602 Boolean cacheable=Caster.toBoolean(options.get("cacheable",null),null); 603 if(cacheable!=null)criteria.setCacheable(cacheable.booleanValue()); 604 605 // MUST cacheName ? 606 607 // maxResults 608 int timeout=Caster.toIntValue(options.get("timeout",null),-1); 609 if(timeout>-1) criteria.setTimeout(timeout); 610 } 611 612 // order 613 if(!StringUtil.isEmpty(order)){ 614 if(metaData==null)metaData = getSessionFactory(pc).getClassMetadata(name); 615 616 String[] arr = railo.runtime.type.util.ListUtil.listToStringArray(order, ','); 617 railo.runtime.type.util.ListUtil.trimItems(arr); 618 String[] parts; 619 String col; 620 boolean isDesc; 621 Order _order; 622 //ColumnInfo ci; 623 for(int i=0;i<arr.length;i++) { 624 parts=railo.runtime.type.util.ListUtil.toStringArray(railo.runtime.type.util.ListUtil.listToArray(arr[i], " \t\n\b\r")); 625 railo.runtime.type.util.ListUtil.trimItems(parts); 626 col=parts[0]; 627 628 col=HibernateUtil.validateColumnName(metaData, col); 629 isDesc=false; 630 if(parts.length>1){ 631 if(parts[1].equalsIgnoreCase("desc"))isDesc=true; 632 else if(!parts[1].equalsIgnoreCase("asc")){ 633 throw new ORMException("invalid order direction defintion ["+parts[1]+"]","valid values are [asc, desc]"); 634 } 635 636 } 637 _order=isDesc?Order.desc(col):Order.asc(col); 638 if(ignoreCase)_order.ignoreCase(); 639 640 criteria.addOrder(_order); 641 642 } 643 } 644 645 // execute 646 if(!unique){ 647 rtn = HibernateCaster.toCFML(criteria.list()); 648 } 649 else { 650 rtn= HibernateCaster.toCFML(criteria.uniqueResult()); 651 } 652 653 654 } 655 catch(Throwable t){ 656 throw Caster.toPageException(t); 657 } 658 659 return rtn; 660 } 661 662 663 664 665 @Override 666 public Session getRawSession() { 667 return session(); 668 } 669 670 @Override 671 public boolean isValid() { 672 return session()!=null && session().isOpen(); 673 } 674 675 @Override 676 public ORMTransaction getTransaction(boolean autoManage) { 677 return new HibernateORMTransaction(session(),autoManage); 678 } 679 }