Changeset 5477
- Timestamp:
- Nov 4, 2010, 1:40:34 PM (12 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/core/net/sf/basedb/core/HibernateUtil.java
r5475 r5477 47 47 import net.sf.basedb.core.hibernate.TableExistsWork; 48 48 import net.sf.basedb.core.query.QueryType; 49 import net.sf.basedb.util.FileUtil; 50 import net.sf.basedb.util.RegexpFileFilter; 49 51 import net.sf.basedb.util.XMLUtil; 50 52 51 53 import java.util.ArrayList; 54 import java.util.Enumeration; 52 55 import java.util.LinkedList; 53 56 import java.util.List; 54 57 import java.util.Iterator; 55 58 import java.util.Date; 59 import java.util.jar.JarEntry; 60 import java.util.jar.JarFile; 56 61 import java.sql.Connection; 57 62 import java.sql.Statement; … … 60 65 import java.sql.SQLException; 61 66 import java.net.URL; 62 import java.net.URI;63 67 64 68 import org.jdom.Document; … … 84 88 import org.hibernate.exception.ConstraintViolationException; 85 89 import org.hibernate.mapping.PersistentClass; 86 import org.hibernate.mapping.Property;87 90 import org.hibernate.mapping.Table; 88 91 import org.hibernate.mapping.SimpleValue; … … 170 173 { 171 174 cfg = new Configuration(); 175 // read base.config 172 176 setConfigurationProperties(cfg); 177 // read hibernate.cfg.xml 173 178 cfg.configure(); 179 // Get the database dialect and create a DbEngine 174 180 dialect = Dialect.getDialect(cfg.getProperties()); 175 181 dbEngine = EngineFactory.createEngine(dialect); 182 // Add database-specific functions to the configuration 176 183 addSqlFunctions(cfg, dbEngine); 177 184 } … … 197 204 { 198 205 addStaticMappings(cfg); 206 addRawDataMappings(cfg); 199 207 mappings = cfg.createMappings(); 200 cfg.buildMappings();201 addExtendedPropertiesMappings(cfg, mappings);202 addRawDataMappings(cfg);203 addFilterConditions(cfg);204 fixOtherDbRelatedThings(cfg);205 208 sf = cfg.buildSessionFactory(); 206 209 } … … 253 256 try 254 257 { 255 String classPath = HibernateUtil.class.getResource("HibernateUtil.class").getPath(); 256 int index = classPath.indexOf("!"); 257 if (index == -1) 258 List<URL> mappingFiles = new ArrayList<URL>(); 259 URL coreUrl = HibernateUtil.class.getResource("/base.version"); 260 String corePath = coreUrl.getPath(); 261 if ("jar".equals(coreUrl.getProtocol())) 258 262 { 259 // We are not in a JAR, load configuration from the directory instead 260 cfg.addDirectory((new java.io.File(new URI(classPath).getPath())).getParentFile()); 263 // We are in a JAR file and get a path like: file:/usr/local/.../BASE2.jar!/base.version 264 // Remove "file:" and "!/base.version" to get the path to the JAR 265 String jarPath = corePath.substring("file:".length(), corePath.length()-"/base.version".length()-1); 266 log.info("Loading mappings files from jar: " + jarPath); 267 JarFile jarFile = new JarFile(jarPath); 268 Enumeration<JarEntry> jarEntries = jarFile.entries(); 269 while (jarEntries.hasMoreElements()) 270 { 271 JarEntry entry = jarEntries.nextElement(); 272 if (!entry.isDirectory() && entry.getName().endsWith(".hbm.xml")) 273 { 274 log.debug("Found mapping file: " + entry.getName()); 275 mappingFiles.add(HibernateUtil.class.getResource("/" + entry.getName())); 276 } 277 } 261 278 } 262 279 else 263 280 { 264 // We get a path like: file:/usr/local/.../BASE2.jar!net/sf/basedb.... 265 // Remove everything after the "!" to get the path 266 // to this JAR, which contains all XML mapping files. 267 cfg.addJar(new java.io.File(new URI(classPath.substring(0,index)).getPath())); 281 // We are in a directory and get a path like: /usr/local/.../base.version 282 // Move up one directory to get to the root path 283 java.io.File dir = new java.io.File(corePath).getParentFile(); 284 log.info("Loading mappings files from directory: " + dir); 285 List<java.io.File> files = FileUtil.findFiles(dir, new RegexpFileFilter(".*\\.hbm\\.xml", null)); 286 for (java.io.File hbmFile : files) 287 { 288 log.debug("Found mapping file: " + hbmFile); 289 mappingFiles.add(hbmFile.toURI().toURL()); 290 } 268 291 } 292 293 // Load the XML mapping files and register them with Hibernate 294 final URL dtdFile = HibernateUtil.class.getResource("/org/hibernate/hibernate-mapping-3.0.dtd"); 295 DOMOutputter out = new DOMOutputter(); 296 for (URL url : mappingFiles) 297 { 298 log.info("Loading mapping file: " + url); 299 Document dom = XMLUtil.getValidatedXml(url, dtdFile); 300 manipulateDocument(dom); 301 cfg.addDocument(out.output(dom)); 302 } 269 303 } 270 304 catch (Exception ex) 271 305 { 272 log.error("Exception while adding Static Mappings", ex); 273 throw new BaseException(ex); 274 } 275 } 276 277 /** 278 Generate additional mappings for all {@link ExtendableData} items. 279 */ 280 private static void addExtendedPropertiesMappings(Configuration cfg, Mappings mappings) 281 { 282 List<String> classes = ExtendedProperties.getClasses(); 283 String packageName = BasicData.class.getPackage().getName(); 284 for (String className : classes) 285 { 286 PersistentClass pc = cfg.getClassMapping(packageName+"."+className); 287 if (pc == null) 306 log.error("Exception while loading mapping files", ex); 307 throw new BaseException(ex); 308 } 309 } 310 311 /** 312 Some XML files require manipulation by us before they are registered. 313 <ul> 314 <li>Add extended properties to extendable classes 315 <li>Add filter implementation to almost all classes 316 <li>Other special-case fixes depending on the underlying database 317 </ul> 318 319 */ 320 private static void manipulateDocument(Document dom) 321 { 322 // Get the <class> tag 323 Element classTag = dom.getRootElement().getChild("class"); 324 String className = classTag != null ? classTag.getAttributeValue("name") : null; 325 // We are only interested in <hibernate-mapping> with <class> elements 326 if (className == null) return; 327 328 // Extended properties 329 if (ExtendedProperties.isExtendable(className)) 330 { 331 addExtendedProperties(classTag, className); 332 } 333 334 // Permission filters 335 addFilterConditions(classTag, className); 336 337 // Unit symbol need to be case-sensitive 338 if (className.endsWith("UnitSymbolData")) 339 { 340 fixUnitSymbolColumnDefinition(classTag); 341 } 342 } 343 344 /** 345 Append <property> tags to the given class tag. 346 @param classTag An Element representing a <class> tag 347 @param className The name of the ({@link ExtendableData}) class 348 @since 2.17 349 */ 350 private static void addExtendedProperties(Element classTag, String className) 351 { 352 log.info("Adding extended properties to class: " + className); 353 List<ExtendedProperty> properties = ExtendedProperties.getProperties(className); 354 355 for (ExtendedProperty property : properties) 356 { 357 log.debug("Adding extended property to class '" + className + "': " + property.getName()); 358 classTag.addContent(createPropertyElement(property)); 359 } 360 } 361 362 /** 363 Creates a <property> tag from an extended property 364 definition. This also works for raw data properties since they 365 extend from ExtendedProperty. 366 @since 2.17 367 */ 368 private static Element createPropertyElement(ExtendedProperty property) 369 { 370 Element element = new Element("property"); 371 element.setAttribute("name", property.getName()); 372 element.setAttribute("column", "`"+property.getColumn()+"`"); 373 element.setAttribute("type", property.getType().getStringValue()); 374 element.setAttribute("length", Integer.toString(property.getLength())); 375 element.setAttribute("not-null", Boolean.toString(!property.isNullable())); 376 element.setAttribute("access", "net.sf.basedb.core.ExtendedPropertyAccessor"); 377 return element; 378 } 379 380 /** 381 Add <filter> conditions to the given class tag. 382 @param classTag An Element representing a <class> tag 383 @param className The name of the entity class 384 @since 2.17 385 */ 386 private static void addFilterConditions(Element classTag, String className) 387 { 388 try 389 { 390 Class c = Class.forName(className); 391 log.info("Adding 'denyAll' filter to "+c.getName()); 392 classTag.addContent(createFilterElement("denyAll", "`id` != `id`")); 393 if (NewsData.class.isAssignableFrom(c)) 288 394 { 289 throw new BaseException("Class " + packageName+"."+className + " is not known to Hibernate."); 395 log.info("Adding 'todaysNews' filter to "+c.getName()); 396 classTag.addContent(createFilterElement("todaysNews", 397 ":today >= `start_date` AND (:today <= `end_date` OR `end_date` IS NULL)")); 290 398 } 291 List<ExtendedProperty> properties = ExtendedProperties.getProperties(className); 292 for (ExtendedProperty ep : properties) 399 if (MessageData.class.isAssignableFrom(c)) 293 400 { 294 log.info("Adding extended property " + ep.getName() + " to class " + className);295 pc.addProperty(createExtendedProperty(mappings, pc.getTable(), ep, true));401 log.info("Adding 'ownedBy' filter to " + c.getName()); 402 classTag.addContent(createFilterElement("ownedBy", ":owner = `to_user_id`")); 296 403 } 297 } 298 } 299 300 404 if (RemovableData.class.isAssignableFrom(c)) 405 { 406 log.info("Adding 'isRemoved' filter to "+c.getName()); 407 classTag.addContent(createFilterElement("isRemoved", ":removed = `removed`")); 408 } 409 if (AnnotatableData.class.isAssignableFrom(c)) 410 { 411 log.info("Adding 'isAnnotatated' filter to "+c.getName()); 412 classTag.addContent(createFilterElement("isAnnotated", ":annotated = (`annotationset_id` IS NOT NULL)")); 413 } 414 if (OwnableData.class.isAssignableFrom(c)) 415 { 416 log.info("Adding 'ownedBy' filter to "+c.getName()); 417 classTag.addContent(createFilterElement("ownedBy", ":owner = `owner`")); 418 419 log.info("Adding 'notOwnedBy' filter to "+c.getName()); 420 classTag.addContent(createFilterElement("notOwnedBy", ":owner != `owner`")); 421 422 log.info("Adding 'memberOf' filter to "+c.getName()); 423 classTag.addContent(createFilterElement("memberOf", ":owner != `owner` AND `id` IN (:items)")); 424 425 log.info("Adding 'ownedByOrMemberOf' filter to "+c.getName()); 426 classTag.addContent(createFilterElement("ownedByOrMemberOf", "(:owner = `owner` OR `id` IN (:items))")); 427 } 428 else if (UserData.class.isAssignableFrom(c)) 429 { 430 log.info("Adding 'memberOf' filter to "+c.getName()); 431 classTag.addContent(createFilterElement("memberOf", "(`id` IN (:items) OR `id` = :owner)")); 432 } 433 else 434 { 435 log.info("Adding 'memberOf' filter to "+c.getName()); 436 classTag.addContent(createFilterElement("memberOf", "`id` IN (:items)")); 437 } 438 if (ShareableData.class.isAssignableFrom(c)) 439 { 440 log.info("Adding 'sharedTo' filter to "+c.getName()); 441 classTag.addContent(createFilterElement("sharedTo", ":owner != `owner` AND `itemkey_id` IN (:itemKeys)")); 442 443 log.info("Adding 'inProject' filter to "+c.getName()); 444 classTag.addContent(createFilterElement("inProject", "`projectkey_id` IN (:projectKeys)")); 445 446 log.info("Adding 'ownedByOrSharedTo' filter to "+c.getName()); 447 classTag.addContent(createFilterElement("ownedByOrSharedTo", "(:owner = `owner` OR `itemkey_id` IN (:itemKeys))")); 448 449 log.info("Adding 'ownedByOrInProject' filter to "+c.getName()); 450 classTag.addContent(createFilterElement("ownedByOrInProject", 451 "(:owner = `owner` OR `projectkey_id` IN (:projectKeys))")); 452 453 log.info("Adding 'sharedToOrInProject' filter to "+c.getName()); 454 classTag.addContent(createFilterElement("sharedToOrInProject", 455 "((:owner != `owner` AND `itemkey_id` IN (:itemKeys)) OR `projectkey_id` IN (:projectKeys))")); 456 457 log.info("Adding 'ownedByOrSharedToOrInProject' filter to "+c.getName()); 458 classTag.addContent(createFilterElement("ownedByOrSharedToOrInProject", 459 "(:owner = `owner` OR `itemkey_id` IN (:itemKeys) OR `projectkey_id` IN (:projectKeys))")); 460 } 461 } 462 catch (ClassNotFoundException ex) 463 { 464 throw new RuntimeException(ex); 465 } 466 } 467 468 /** 469 Create a <filter> tag using the given name and condition. 470 @since 2.17 471 */ 472 private static Element createFilterElement(String name, String condition) 473 { 474 Element element = new Element("filter"); 475 element.setAttribute("name", name); 476 element.setAttribute("condition", condition); 477 return element; 478 } 479 301 480 /** 302 481 Generate additional mappings for the raw data types. … … 309 488 final URL templateFile = HibernateUtil.class.getResource("/net/sf/basedb/core/templates/hibernate-properties-RawData.xml"); 310 489 490 DOMOutputter out = new DOMOutputter(); 311 491 for (RawDataType rdt : RawDataTypes.getRawDataTypes() ) 312 492 { … … 315 495 String entityName = rdt.getEntityName(); 316 496 String tableName = rdt.getTableName(); 317 log.info("Adding raw data type " + entityName);497 log.info("Adding raw data type: " + entityName); 318 498 try 319 499 { … … 334 514 for (RawDataProperty property : rdt.getProperties()) 335 515 { 336 log.info("Adding raw data property " + property.getName() + " to class " + entityName); 337 Element el = new Element("property"); 338 el.setAttribute("name", property.getName()); 339 el.setAttribute("column", "`"+property.getColumn()+"`"); 340 el.setAttribute("type", property.getType().getStringValue()); 341 el.setAttribute("length", Integer.toString(property.getLength())); 342 el.setAttribute("not-null", Boolean.toString(!property.isNullable())); 343 el.setAttribute("access", "net.sf.basedb.core.ExtendedPropertyAccessor"); 344 clazz.addContent(el); 516 log.info("Adding raw data property to class '" + entityName + "': " + property.getName()); 517 clazz.addContent(createPropertyElement(property)); 345 518 } 346 cfg.addDocument( new DOMOutputter().output(dom));519 cfg.addDocument(out.output(dom)); 347 520 } 348 521 catch (Exception ex) 349 522 { 350 log.error("Exception while adding raw data type s", ex);523 log.error("Exception while adding raw data type: " + entityName, ex); 351 524 throw new BaseException(ex); 352 525 } … … 355 528 } 356 529 } 357 /** 358 Add an extended property to the specified Hibernate mapping 359 @param t The table the property should be added to 360 @param ep The extended property to add to the mapping 361 @param extendable TRUE if the property belongs to an extendable item, FALSE 362 otherwise (RawData) 363 */ 364 private static Property createExtendedProperty(Mappings mappings, Table t, ExtendedProperty ep, boolean extendable) 365 { 366 org.hibernate.mapping.Column c = new org.hibernate.mapping.Column(); 367 c.setName("`"+ep.getColumn()+"`"); 368 c.setNullable(ep.isNullable()); 369 c.setLength(ep.getLength()); 370 371 t.addColumn(c); 372 373 SimpleValue v = new SimpleValue(mappings, t); 374 v.addColumn(c); 375 v.setTypeName(ep.getType().getTypeWrapper().getHibernateType().getName()); 376 377 Property p = new Property(); 378 p.setValue(v); 379 p.setName(ep.getName()); 380 p.setNodeName(ep.getName()); 381 if (extendable) p.setPropertyAccessorName("net.sf.basedb.core.ExtendedPropertyAccessor"); 382 p.setCascade("none"); 383 p.setUpdateable(ep.isUpdateable()); 384 p.setInsertable(ep.isInsertable()); 385 return p; 386 } 387 388 /** 389 Generate filters for {@link Ownable}, {@link Shareable}, etc. classes 390 that are used by the {@link Query} implementations. We are generating the filters 391 here beacuse we don't want anyone to mess them which could have been the case 392 if the filter had been defined in the xml mapping files. 393 */ 394 private static void addFilterConditions(Configuration cfg) 395 throws BaseException 396 { 397 assert cfg != null : "cfg == null"; 398 Iterator<PersistentClass> iterator = cfg.getClassMappings(); 399 while (iterator.hasNext()) 400 { 401 PersistentClass pc = iterator.next(); 402 Class c = pc.getMappedClass(); 403 log.info("Adding 'denyAll' filter to "+c.getName()); 404 pc.addFilter("denyAll", "`id` != `id`"); 405 if (NewsData.class.isAssignableFrom(c)) 406 { 407 log.info("Adding 'todaysNews' filter to "+c.getName()); 408 pc.addFilter("todaysNews", ":today >= `start_date` AND (:today <= `end_date` OR `end_date` IS NULL)"); 409 } 410 if (MessageData.class.isAssignableFrom(c)) 411 { 412 log.info("Adding 'ownedBy' filter to " + c.getName()); 413 pc.addFilter("ownedBy", ":owner = `to_user_id`"); 414 } 415 if (RemovableData.class.isAssignableFrom(c)) 416 { 417 log.info("Adding 'isRemoved' filter to "+c.getName()); 418 pc.addFilter("isRemoved", ":removed = `removed`"); 419 } 420 if (AnnotatableData.class.isAssignableFrom(c)) 421 { 422 log.info("Adding 'isAnnotatated' filter to "+c.getName()); 423 pc.addFilter("isAnnotated", ":annotated = (`annotationset_id` IS NOT NULL)"); 424 } 425 if (OwnableData.class.isAssignableFrom(c)) 426 { 427 log.info("Adding 'ownedBy' filter to "+c.getName()); 428 pc.addFilter("ownedBy", ":owner = `owner`"); 429 430 log.info("Adding 'notOwnedBy' filter to "+c.getName()); 431 pc.addFilter("notOwnedBy", ":owner != `owner`"); 432 433 log.info("Adding 'memberOf' filter to "+c.getName()); 434 pc.addFilter("memberOf", ":owner != `owner` AND `id` IN (:items)"); 435 436 log.info("Adding 'ownedByOrMemberOf' filter to "+c.getName()); 437 pc.addFilter("ownedByOrMemberOf", "(:owner = `owner` OR `id` IN (:items))"); 438 } 439 else if (UserData.class.isAssignableFrom(c)) 440 { 441 log.info("Adding 'memberOf' filter to "+c.getName()); 442 pc.addFilter("memberOf", "(`id` IN (:items) OR `id` = :owner)"); 443 } 444 else 445 { 446 log.info("Adding 'memberOf' filter to "+c.getName()); 447 pc.addFilter("memberOf", "`id` IN (:items)"); 448 } 449 if (ShareableData.class.isAssignableFrom(c)) 450 { 451 log.info("Adding 'sharedTo' filter to "+c.getName()); 452 pc.addFilter("sharedTo", ":owner != `owner` AND `itemkey_id` IN (:itemKeys)"); 453 454 log.info("Adding 'inProject' filter to "+c.getName()); 455 pc.addFilter("inProject", "`projectkey_id` IN (:projectKeys)"); 456 457 log.info("Adding 'ownedByOrSharedTo' filter to "+c.getName()); 458 pc.addFilter("ownedByOrSharedTo", "(:owner = `owner` OR `itemkey_id` IN (:itemKeys))"); 459 460 log.info("Adding 'ownedByOrInProject' filter to "+c.getName()); 461 pc.addFilter("ownedByOrInProject", "(:owner = `owner` OR `projectkey_id` IN (:projectKeys))"); 462 463 log.info("Adding 'sharedToOrInProject' filter to "+c.getName()); 464 pc.addFilter("sharedToOrInProject", "((:owner != `owner` AND `itemkey_id` IN (:itemKeys)) OR `projectkey_id` IN (:projectKeys))"); 465 466 log.info("Adding 'ownedByOrSharedToOrInProject' filter to "+c.getName()); 467 pc.addFilter("ownedByOrSharedToOrInProject", "(:owner = `owner` OR `itemkey_id` IN (:itemKeys) OR `projectkey_id` IN (:projectKeys))"); 468 } 469 } 470 } 471 530 472 531 /** 473 532 Add more SQL functions to HQL that are not present in a … … 494 553 495 554 /** 496 Last step in setting up the configuration. Use this method to fix 497 things that require special handling. 498 @since 2.9 499 */ 500 private static void fixOtherDbRelatedThings(Configuration cfg) 555 Fixes the definition of the UnitSymbols.symbol column so that 556 it is case-sensitive. We need because some units have both 557 milli (m) and Mega (M) as prefix (eg. mV and MV) 558 @since 2.17 559 */ 560 private static void fixUnitSymbolColumnDefinition(Element classTag) 501 561 { 502 562 // Make the UnitSymbols.symbol column case-sensitive so we can have units 503 563 // with both milli (m) and Mega (M) as prefix. 504 PersistentClass unitSymbols = cfg.getClassMapping("net.sf.basedb.core.data.UnitSymbolData"); 505 Column symbol = (Column)unitSymbols.getProperty("symbol").getColumnIterator().next(); 506 if (dbEngine.caseInsensitiveComparison()) 507 { 508 String sqlType = dbEngine.getCaseSensitiveVarchar(symbol.getLength()); 509 if (sqlType != null) symbol.setSqlType(sqlType); 564 if (!dbEngine.caseInsensitiveComparison()) return; 565 log.debug("Setting UnitSymbols.symbol to a case-sensitive column type"); 566 567 // Find the class/property/column[name=`symbol`] tag 568 Iterator match = classTag.getDescendants( 569 new org.jdom.filter.Filter() 570 { 571 private static final long serialVersionUID = -2876632679035736428L; 572 @Override 573 public boolean matches(Object obj) 574 { 575 if (!(obj instanceof Element)) return false; 576 577 Element e = (Element)obj; 578 return e.getName().equals("column") && e.getAttributeValue("name").equals("`symbol`"); 579 } 580 }); 581 Element col = (Element)match.next(); 582 String sqlType = dbEngine.getCaseSensitiveVarchar(XMLUtil.getIntAttribute(col, "length", 255)); 583 if (sqlType != null) 584 { 585 col.setAttribute("sql-type", sqlType); 510 586 } 511 587 }
Note: See TracChangeset
for help on using the changeset viewer.