001/** 002 * 003 * Copyright (c) 2014, the Railo Company Ltd. All rights reserved. 004 * 005 * This library is free software; you can redistribute it and/or 006 * modify it under the terms of the GNU Lesser General Public 007 * License as published by the Free Software Foundation; either 008 * version 2.1 of the License, or (at your option) any later version. 009 * 010 * This library is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 013 * Lesser General Public License for more details. 014 * 015 * You should have received a copy of the GNU Lesser General Public 016 * License along with this library. If not, see <http://www.gnu.org/licenses/>. 017 * 018 **/ 019package lucee.runtime.orm.hibernate; 020 021import java.sql.DatabaseMetaData; 022import java.sql.ResultSet; 023import java.sql.SQLException; 024import java.util.ArrayList; 025import java.util.HashSet; 026import java.util.Iterator; 027import java.util.Set; 028 029import lucee.commons.io.res.Resource; 030import lucee.loader.util.Util; 031import lucee.runtime.Component; 032import lucee.runtime.PageContext; 033import lucee.runtime.PageContextImpl; 034import lucee.runtime.PageSource; 035import lucee.runtime.component.Property; 036import lucee.runtime.config.ConfigImpl; 037import lucee.runtime.db.DataSourceUtil; 038import lucee.runtime.db.DatasourceConnection; 039import lucee.runtime.exp.PageException; 040import lucee.runtime.orm.ORMConfiguration; 041import lucee.runtime.orm.ORMSession; 042import lucee.runtime.type.CastableStruct; 043import lucee.runtime.type.Collection; 044import lucee.runtime.type.Struct; 045import lucee.runtime.type.util.ComponentUtil; 046 047import org.hibernate.HibernateException; 048import org.hibernate.metadata.ClassMetadata; 049import org.hibernate.type.ComponentType; 050import org.hibernate.type.Type; 051 052 053public class HibernateUtil { 054 055 public static final short FIELDTYPE_ID=0; 056 public static final short FIELDTYPE_COLUMN=1; 057 public static final short FIELDTYPE_TIMESTAMP=2; 058 public static final short FIELDTYPE_RELATION=4; 059 public static final short FIELDTYPE_VERSION=8; 060 public static final short FIELDTYPE_COLLECTION=16; 061 062 private static final String[] KEYWORDS=new String[]{"absolute","access","accessible","action","add","after","alias","all","allocate","allow","alter","analyze","and","any","application","are","array","as","asc","asensitive","assertion","associate","asutime","asymmetric","at","atomic","audit","authorization","aux","auxiliary","avg","backup","before","begin","between","bigint","binary","bit","bit_length","blob","boolean","both","breadth","break","browse","bufferpool","bulk","by","cache","call","called","capture","cardinality","cascade","cascaded","case","cast","catalog","ccsid","change","char","char_length","character","character_length","check","checkpoint","clob","close","cluster","clustered","coalesce","collate","collation","collection","collid","column","comment","commit","compress","compute","concat","condition","connect","connection","constraint","constraints","constructor","contains","containstable","continue","convert","corresponding","count","count_big","create","cross","cube","current","current_date","current_default_transform_group","current_lc_ctype","current_path","current_role","current_server","current_time","current_timestamp","current_timezone","current_transform_group_for_type","current_user","cursor","cycle","data","database","databases","date","day","day_hour","day_microsecond","day_minute","day_second","days","db2general","db2genrl","db2sql","dbcc","dbinfo","deallocate","dec","decimal","declare","default","defaults","deferrable","deferred","delayed","delete","deny","depth","deref","desc","describe","descriptor","deterministic","diagnostics","disallow","disconnect","disk","distinct","distinctrow","distributed","div","do","domain","double","drop","dsnhattr","dssize","dual","dummy","dump","dynamic","each","editproc","else","elseif","enclosed","encoding","end","end-exec","end-exec1","endexec","equals","erase","errlvl","escape","escaped","except","exception","excluding","exclusive","exec","execute","exists","exit","explain","external","extract","false","fenced","fetch","fieldproc","file","fillfactor","filter","final","first","float","float4","float8","for","force","foreign","found","free","freetext","freetexttable","from","full","fulltext","function","general","generated","get","get_current_connection","global","go","goto","grant","graphic","group","grouping","handler","having","high_priority","hold","holdlock","hour","hour_microsecond","hour_minute","hour_second","hours","identified","identity","identity_insert","identitycol","if","ignore","immediate","in","including","increment","index","indicator","infile","inherit","initial","initially","inner","inout","input","insensitive","insert","int","int1","int2","int3","int4","int8","integer","integrity","intersect","interval","into","is","isobid","isolation","iterate","jar","java","join","key","keys","kill","language","large","last","lateral","leading","leave","left","level","like","limit","linear","lineno","lines","linktype","load","local","locale","localtime","localtimestamp","locator","locators","lock","lockmax","locksize","long","longblob","longint","longtext","loop","low_priority","lower","ltrim","map","master_ssl_verify_server_cert","match","max","maxextents","maxvalue","mediumblob","mediumint","mediumtext","method","microsecond","microseconds","middleint","min","minus","minute","minute_microsecond","minute_second","minutes","minvalue","mlslabel","mod","mode","modifies","modify","module","month","months","names","national","natural","nchar","nclob","new","new_table","next","no","no_write_to_binlog","noaudit","nocache","nocheck","nocompress","nocycle","nodename","nodenumber","nomaxvalue","nominvalue","nonclustered","none","noorder","not","nowait","null","nullif","nulls","number","numeric","numparts","nvarchar","obid","object","octet_length","of","off","offline","offsets","old","old_table","on","online","only","open","opendatasource","openquery","openrowset","openxml","optimization","optimize","option","optionally","or","order","ordinality","out","outer","outfile","output","over","overlaps","overriding","package","pad","parameter","part","partial","partition","path","pctfree","percent","piecesize","plan","position","precision","prepare","preserve","primary","print","prior","priqty","privileges","proc","procedure","program","psid","public","purge","queryno","raiserror","range","raw","read","read_write","reads","readtext","real","reconfigure","recovery","recursive","ref","references","referencing","regexp","relative","release","rename","repeat","replace","replication","require","resignal","resource","restart","restore","restrict","result","result_set_locator","return","returns","revoke","right","rlike","role","rollback","rollup","routine","row","rowcount","rowguidcol","rowid","rownum","rows","rrn","rtrim","rule","run","runtimestatistics","save","savepoint","schema","schemas","scope","scratchpad","scroll","search","second","second_microsecond","seconds","secqty","section","security","select","sensitive","separator","session","session_user","set","sets","setuser","share","show","shutdown","signal","similar","simple","size","smallint","some","source","space","spatial","specific","specifictype","sql","sql_big_result","sql_calc_found_rows","sql_small_result","sqlcode","sqlerror","sqlexception","sqlid","sqlstate","sqlwarning","ssl","standard","start","starting","state","static","statistics","stay","stogroup","stores","straight_join","style","subpages","substr","substring","successful","sum","symmetric","synonym","sysdate","sysfun","sysibm","sysproc","system","system_user","table","tablespace","temporary","terminated","textsize","then","time","timestamp","timezone_hour","timezone_minute","tinyblob","tinyint","tinytext","to","top","trailing","tran","transaction","translate","translation","treat","trigger","trim","true","truncate","tsequal","type","uid","under","undo","union","unique","unknown","unlock","unnest","unsigned","until","update","updatetext","upper","usage","use","user","using","utc_date","utc_time","utc_timestamp","validate","validproc","value","values","varbinary","varchar","varchar2","varcharacter","variable","variant","varying","vcat","view","volumes","waitfor","when","whenever","where","while","window","with","within","without","wlm","work","write","writetext","xor","year","year_month","zerofill","zone"}; 063 private static final Set<String> keywords=new HashSet<String>(); 064 static { 065 for(int i=0;i<KEYWORDS.length;i++){ 066 keywords.add(KEYWORDS[i]); 067 } 068 } 069 070 071 public static boolean isKeyword(String word){ 072 if(word==null) return false; 073 return keywords.contains(word.trim().toLowerCase()); 074 } 075 076 077 public static Type getPropertyType(ClassMetadata metaData, String name) throws HibernateException { 078 try{ 079 return metaData.getPropertyType(name); 080 } 081 catch(HibernateException he){ 082 if(name.equalsIgnoreCase(metaData.getIdentifierPropertyName())) 083 return metaData.getIdentifierType(); 084 085 String[] names = metaData.getPropertyNames(); 086 for(int i=0;i<names.length;i++){ 087 if(names[i].equalsIgnoreCase(name)) 088 return metaData.getPropertyType(names[i]); 089 } 090 throw he; 091 } 092 } 093 public static Type getPropertyType(ClassMetadata metaData, String name, Type defaultValue) { 094 try{ 095 return metaData.getPropertyType(name); 096 } 097 catch(HibernateException he){ 098 if(name.equalsIgnoreCase(metaData.getIdentifierPropertyName())) 099 return metaData.getIdentifierType(); 100 101 String[] names = metaData.getPropertyNames(); 102 for(int i=0;i<names.length;i++){ 103 if(names[i].equalsIgnoreCase(name)) 104 return metaData.getPropertyType(names[i]); 105 } 106 return defaultValue; 107 } 108 } 109 110 public static String validateColumnName(ClassMetadata metaData, String name) throws PageException { 111 String res = validateColumnName(metaData, name,null); 112 if(res!=null) return res; 113 throw ExceptionUtil.createException((ORMSession)null,null,"invalid name, there is no property with name ["+name+"] in the entity ["+metaData.getEntityName()+"]", 114 "valid properties names are ["+CommonUtil.toList(metaData.getPropertyNames(), ", ")+"]"); 115 116 } 117 118 119 public static String validateColumnName(ClassMetadata metaData, String name, String defaultValue) { 120 Type type = metaData.getIdentifierType(); 121 // composite id 122 if(type.isComponentType()) { 123 String res=_validateColumnName(((ComponentType) type).getPropertyNames(),name); 124 if(res!=null) return res; 125 } 126 // regular id 127 String id = metaData.getIdentifierPropertyName(); 128 if(id!=null && name.equalsIgnoreCase(id)) 129 return metaData.getIdentifierPropertyName(); 130 131 String res=_validateColumnName(metaData.getPropertyNames(),name); 132 if(res!=null) return res; 133 return defaultValue; 134 } 135 136 private static String _validateColumnName(String[] names, String name) { 137 if(names==null) return null; 138 for(int i=0;i<names.length;i++){ 139 if(names[i].equalsIgnoreCase(name)) 140 return names[i]; 141 } 142 return null; 143 } 144 145 146 // 147 148 public static Property[] createPropertiesFromTable(DatasourceConnection dc, String tableName) { 149 Struct properties = CommonUtil.createStruct(); 150 try { 151 DatabaseMetaData md = dc.getConnection().getMetaData(); 152 String dbName=DataSourceUtil.getDatabaseName(dc); 153 Collection.Key name; 154 155 156 // get all columns 157 ResultSet res = md.getColumns(dbName, null, tableName, null); 158 while(res.next()) { 159 name=CommonUtil.createKey(res.getString("COLUMN_NAME")); 160 properties.setEL( 161 name, 162 CommonUtil.createProperty(name.getString(),res.getString("TYPE_NAME"))); 163 } 164 165 // ids 166 res = md.getPrimaryKeys(null, null, tableName); 167 Property p; 168 while(res.next()) { 169 name=CommonUtil.createKey(res.getString("COLUMN_NAME")); 170 p=(Property) properties.get(name,null); 171 if(p!=null) p.getDynamicAttributes().setEL(CommonUtil.FIELDTYPE, "id"); 172 } 173 174 // MUST foreign-key relation 175 176 } 177 catch(Throwable t){ 178 lucee.commons.lang.ExceptionUtil.rethrowIfNecessary(t); 179 return new Property[0]; 180 } 181 182 Iterator<Object> it = properties.valueIterator(); 183 Property[] rtn=new Property[properties.size()]; 184 for(int i=0;i<rtn.length;i++){ 185 rtn[i]=(Property) it.next(); 186 } 187 188 return rtn; 189 } 190 191 192 public static Property[] getProperties(Component component,int fieldType, Property[] defaultValue) { 193 Property[] props = component.getProperties(true); 194 java.util.List<Property> rtn=new ArrayList<Property>(); 195 196 if(props!=null) { 197 for(int i=0;i<props.length;i++){ 198 if(fieldType==getFieldType(props[i],FIELDTYPE_COLUMN)) 199 rtn.add(props[i]); 200 } 201 } 202 return rtn.toArray(new Property[rtn.size()]); 203 } 204 205 206 private static int getFieldType(Property property, int defaultValue) { 207 return getFieldType(CommonUtil.toString(property.getDynamicAttributes().get(CommonUtil.FIELDTYPE, null),null),defaultValue); 208 209 } 210 211 private static int getFieldType(String fieldType, int defaultValue) { 212 if(Util.isEmpty(fieldType,true)) return defaultValue; 213 fieldType=fieldType.trim().toLowerCase(); 214 215 216 if("id".equals(fieldType)) return FIELDTYPE_ID; 217 if("column".equals(fieldType)) return FIELDTYPE_COLUMN; 218 if("timestamp".equals(fieldType)) return FIELDTYPE_TIMESTAMP; 219 if("relation".equals(fieldType)) return FIELDTYPE_RELATION; 220 if("version".equals(fieldType)) return FIELDTYPE_VERSION; 221 if("collection".equals(fieldType)) return FIELDTYPE_COLLECTION; 222 return defaultValue; 223 } 224 225 226 227 public static String convertTableName(SessionFactoryData data,String tableName) throws PageException { 228 if(tableName==null) return null; 229 return data.getNamingStrategy().convertTableName(tableName); 230 } 231 232 public static String convertColumnName(SessionFactoryData data,String columnName) throws PageException { 233 if(columnName==null) return null; 234 return data.getNamingStrategy().convertColumnName(columnName); 235 } 236 237 public static boolean isEntity(ORMConfiguration ormConf,Component cfc, String cfcName, String name) { 238 if(!Util.isEmpty(cfcName)) { 239 if(cfc.equalTo(cfcName)) return true; 240 241 if(cfcName.indexOf('.')!=-1) { 242 String path=cfcName.replace('.', '/')+".cfc"; 243 Resource[] locations = ormConf.getCfcLocations(); 244 for(int i=0;i<locations.length;i++){ 245 if(locations[i].getRealResource(path).equals(cfc.getPageSource().getResource())) 246 return true; 247 } 248 return false; 249 } 250 } 251 252 if(cfc.equalTo(name)) return true; 253 return name.equalsIgnoreCase(HibernateCaster.getEntityName(cfc)); 254 } 255 256 public static String id(String id) { 257 return id.toLowerCase().trim(); 258 } 259 260 public static Struct checkTable(DatasourceConnection dc, String tableName, SessionFactoryData data) throws PageException { 261 262 try { 263 String dbName=DataSourceUtil.getDatabaseName(dc); 264 DatabaseMetaData md = dc.getConnection().getMetaData(); 265 Struct rows=checkTableFill(md,dbName,tableName); 266 if(rows.size()==0) { 267 String tableName2 = checkTableValidate(md,dbName,tableName); 268 if(tableName2!=null)rows=checkTableFill(md,dbName,tableName2); 269 } 270 271 if(rows.size()==0) { 272 return null; 273 } 274 return rows; 275 } catch (SQLException e) { 276 throw CommonUtil.toPageException(e); 277 } 278 } 279 280 281 282 private static Struct checkTableFill(DatabaseMetaData md, String dbName, String tableName) throws SQLException, PageException { 283 Struct rows=new CastableStruct(tableName,Struct.TYPE_LINKED); 284 ResultSet columns = md.getColumns(dbName, null, tableName, null); 285 //print.o(new QueryImpl(columns,"")); 286 try{ 287 String name; 288 Object nullable; 289 while(columns.next()) { 290 name=columns.getString("COLUMN_NAME"); 291 292 nullable=columns.getObject("IS_NULLABLE"); 293 rows.setEL(CommonUtil.createKey(name),new ColumnInfo( 294 name, 295 columns.getInt("DATA_TYPE"), 296 columns.getString("TYPE_NAME"), 297 columns.getInt("COLUMN_SIZE"), 298 CommonUtil.toBooleanValue(nullable) 299 )); 300 } 301 } 302 finally { 303 CommonUtil.closeEL(columns); 304 }// Table susid defined for cfc susid does not exist. 305 306 return rows; 307 } 308 309 private static String checkTableValidate(DatabaseMetaData md, String dbName,String tableName) { 310 311 ResultSet tables=null; 312 try{ 313 tables = md.getTables(dbName, null, null, null); 314 String name; 315 while(tables.next()) { 316 name=tables.getString("TABLE_NAME"); 317 if(name.equalsIgnoreCase(tableName) && tables.getString("TABLE_TYPE").toUpperCase().indexOf("SYSTEM")==-1) 318 return name; 319 } 320 } 321 catch(Throwable t){ 322 lucee.commons.lang.ExceptionUtil.rethrowIfNecessary(t); 323 } 324 finally { 325 CommonUtil.closeEL(tables); 326 } 327 return null; 328 329 330 331 } 332 333 334 public static HibernateORMEngine getORMEngine(PageContext pc) throws PageException { 335 if(pc==null) pc=CommonUtil.pc(); 336 ConfigImpl config = (ConfigImpl) pc.getConfig(); 337 return (HibernateORMEngine) config.getORMEngine(pc);// TODO add this method to the public interface 338 } 339 340 341 public static HibernateORMSession getORMSession(PageContext pc, boolean create) throws PageException { 342 return (HibernateORMSession) ((PageContextImpl)pc).getORMSession(create);// TODO add this method to the public interface 343 } 344 345 346 347 public static Property[] getIDProperties(Component c,boolean onlyPeristent, boolean includeBaseProperties) { 348 Property[] props = CommonUtil.getProperties(c,onlyPeristent,includeBaseProperties,false,false); 349 java.util.List<Property> tmp=new ArrayList<Property>(); 350 for(int i=0;i<props.length;i++){ 351 if("id".equalsIgnoreCase(CommonUtil.toString(props[i].getDynamicAttributes().get(CommonUtil.FIELDTYPE,null),""))) 352 tmp.add(props[i]); 353 } 354 return tmp.toArray(new Property[tmp.size()]); 355 } 356 357 public static long getCompileTime(PageContext pc, PageSource ps) throws PageException { 358 return ComponentUtil.getCompileTime(pc, ps); 359 } 360 361 public static Object getMetaStructItem(Component cfc,Collection.Key name) { 362 return CommonUtil.getMetaStructItem(cfc,name); 363 } 364}