001 /* 002 @license.text@ 003 */ 004 package biz.hammurapi.sql; 005 006 import java.io.Serializable; 007 import java.lang.reflect.Constructor; 008 import java.sql.PreparedStatement; 009 import java.sql.SQLException; 010 import java.sql.Types; 011 import java.util.AbstractList; 012 import java.util.ArrayList; 013 import java.util.Collection; 014 import java.util.HashMap; 015 import java.util.HashSet; 016 import java.util.Iterator; 017 import java.util.Map; 018 import java.util.Properties; 019 import java.util.Set; 020 import java.util.Map.Entry; 021 022 import org.w3c.dom.Element; 023 import org.w3c.dom.Node; 024 import org.w3c.dom.NodeList; 025 026 import biz.hammurapi.config.ConfigurationException; 027 import biz.hammurapi.config.Context; 028 import biz.hammurapi.config.ContextConfigurable; 029 import biz.hammurapi.config.DomConfigFactory; 030 import biz.hammurapi.config.DomConfigurable; 031 import biz.hammurapi.convert.CompositeConverter; 032 import biz.hammurapi.sql.columns.Column; 033 import biz.hammurapi.sql.columns.ColumnChangeListener; 034 import biz.hammurapi.util.Attributable; 035 import biz.hammurapi.util.ClassResourceLoader; 036 import biz.hammurapi.util.Observable; 037 import biz.hammurapi.util.Observer; 038 import biz.hammurapi.util.Versioned; 039 import biz.hammurapi.xml.dom.DOMUtils; 040 import biz.hammurapi.xml.dom.AbstractDomObject; 041 import biz.hammurapi.xml.dom.CompositeDomSerializer; 042 import biz.hammurapi.xml.dom.DomSerializable; 043 044 045 /** 046 * SQLC-generated interface implementations implement this method to achieve 047 * differential update functionality - inserting and updating only modified fields. 048 * @author Pavel Vlasov 049 * @version $Revision: 1.11 $ 050 */ 051 public class DatabaseObject 052 implements 053 DomSerializable, 054 ColumnChangeListener, 055 Cloneable, 056 ContextConfigurable, 057 Context, 058 DomConfigurable, 059 Attributable, 060 Versioned, 061 Observable, 062 IDatabaseObject, 063 Serializable { 064 065 protected Collection columns=new ArrayList(); 066 private Map columnMap=new HashMap(); 067 private boolean force; 068 private boolean isDeleted; 069 070 protected Column getColumn(String name) { 071 return (Column) columnMap.get(name); 072 } 073 074 /** 075 * Default constructor 076 */ 077 public DatabaseObject() { 078 // Default constructor 079 } 080 081 /** 082 * 083 * @param force Forces columns to be marked as 084 * modified if setter method is invoked even if value being set equals to existing column 085 * value. Useful during inserts with non-nullable columns which map to primitive 086 * types and as such have default values. 087 */ 088 public DatabaseObject(boolean force) { 089 this.force=force; 090 } 091 092 protected void addColumn(Column column) { 093 column.setForce(force); 094 columns.add(column); 095 columnMap.put(column.getName(), column); 096 column.setListener(this); 097 } 098 099 /* (non-Javadoc) 100 * @see biz.hammurapi.sql.IDatabaseObject#update(biz.hammurapi.sql.SQLProcessor, java.lang.String) 101 */ 102 public int update(SQLProcessor processor, String tableName) throws SQLException { 103 StringBuffer sb=new StringBuffer("UPDATE "); 104 sb.append(tableName); 105 sb.append(" SET "); 106 boolean hasColumns=false; 107 Iterator it=columns.iterator(); 108 while (it.hasNext()) { 109 Column column = (Column) it.next(); 110 String colName=(column).listName(); 111 if (colName!=null) { 112 if (hasColumns) { 113 sb.append(", "); 114 } 115 sb.append(colName); 116 sb.append("=?"); 117 hasColumns=true; 118 119 } 120 } 121 122 if (!hasColumns) { 123 return 0; 124 } 125 126 sb.append(" WHERE "); 127 128 boolean hasPkColumns=false; 129 it=columns.iterator(); 130 while (it.hasNext()) { 131 Column column = (Column) it.next(); 132 if (column.isPrimaryKey()) { 133 String colName=(column).getName(); 134 if (hasPkColumns) { 135 sb.append(" AND "); 136 } 137 sb.append(colName); 138 sb.append("=?"); 139 hasPkColumns=true; 140 } 141 } 142 143 int ret=processor.processUpdate(sb.toString(), new Parameterizer() { 144 public void parameterize(PreparedStatement ps) throws SQLException { 145 int idx=1; 146 Iterator it=columns.iterator(); 147 while (it.hasNext()) { 148 Column column = (Column) it.next(); 149 idx=column.parameterize(ps, idx, false); 150 } 151 152 it=columns.iterator(); 153 while (it.hasNext()) { 154 Column column = (Column) it.next(); 155 if (column.isPrimaryKey()) { 156 column.parameterizeOriginal(ps, idx++); 157 } 158 } 159 } 160 }); 161 162 it=columns.iterator(); 163 while (it.hasNext()) { 164 ((Column) it.next()).clearModified(); 165 } 166 167 originalVersion=objectVersion; 168 isModified=false; 169 170 storeRelationships(processor); 171 172 it=relationships.iterator(); 173 while (it.hasNext()) { 174 RelationshipEntry re=(RelationshipEntry) it.next(); 175 if (re.name!=null) { 176 Iterator iit=re.items.iterator(); 177 while (iit.hasNext()) { 178 IDatabaseObject subItem = (IDatabaseObject) iit.next(); 179 if (subItem.isModified()) { 180 re.items.update(processor, subItem); 181 subItem.update(processor, null); 182 } 183 } 184 } 185 } 186 return ret; 187 } 188 189 /* (non-Javadoc) 190 * @see biz.hammurapi.sql.IDatabaseObject#delete(biz.hammurapi.sql.SQLProcessor, java.lang.String) 191 */ 192 public int delete(SQLProcessor processor, String tableName) throws SQLException { 193 storeRelationships(processor); 194 195 StringBuffer sb=new StringBuffer("DELETE FROM "); 196 sb.append(tableName); 197 sb.append(" WHERE "); 198 199 boolean hasPkColumns=false; 200 Iterator it=columns.iterator(); 201 while (it.hasNext()) { 202 Column column = (Column) it.next(); 203 if (column.isPrimaryKey()) { 204 String colName=(column).getName(); 205 if (hasPkColumns) { 206 sb.append(" AND "); 207 } 208 sb.append(colName); 209 sb.append("=?"); 210 hasPkColumns=true; 211 } 212 } 213 214 int ret=processor.processUpdate(sb.toString(), new Parameterizer() { 215 public void parameterize(PreparedStatement ps) throws SQLException { 216 int idx=1; 217 Iterator it=columns.iterator(); 218 while (it.hasNext()) { 219 Column column = (Column) it.next(); 220 if (!column.isPrimaryKey()) { 221 idx=column.parameterize(ps, idx, false); 222 } 223 } 224 225 it=columns.iterator(); 226 while (it.hasNext()) { 227 Column column = (Column) it.next(); 228 if (column.isPrimaryKey()) { 229 idx=column.parameterize(ps, idx, true); 230 } 231 } 232 } 233 }); 234 235 it=columns.iterator(); 236 while (it.hasNext()) { 237 ((Column) it.next()).clearModified(); 238 } 239 240 originalVersion=objectVersion; 241 isDeleted=true; 242 return ret; 243 } 244 245 /* (non-Javadoc) 246 * @see biz.hammurapi.sql.IDatabaseObject#insert(biz.hammurapi.sql.SQLProcessor, java.lang.String) 247 */ 248 public int insert(SQLProcessor processor, String tableName) throws SQLException { 249 StringBuffer sb=new StringBuffer("INSERT INTO "); 250 sb.append(tableName); 251 sb.append(" ("); 252 int columnsCounter=0; 253 Iterator it=columns.iterator(); 254 while (it.hasNext()) { 255 String colName=((Column) it.next()).listName(); 256 if (colName!=null) { 257 if (columnsCounter>0) { 258 sb.append(", "); 259 } 260 sb.append(colName); 261 columnsCounter++; 262 263 } 264 } 265 266 if (columnsCounter==0) { 267 return 0; 268 } 269 270 sb.append(") VALUES ("); 271 for (int i=0; i<columnsCounter; i++) { 272 if (i>0) { 273 sb.append(", "); 274 } 275 sb.append("?"); 276 } 277 sb.append(")"); 278 279 int ret=processor.processUpdate(sb.toString(), new Parameterizer() { 280 public void parameterize(PreparedStatement ps) throws SQLException { 281 Iterator it=columns.iterator(); 282 for (int i=1; it.hasNext(); i=((Column) it.next()).parameterize(ps, i, false)); 283 } 284 }); 285 286 it=columns.iterator(); 287 while (it.hasNext()) { 288 ((Column) it.next()).clearModified(); 289 } 290 291 originalVersion=objectVersion; 292 isModified=false; 293 294 storeRelationships(processor); 295 return ret; 296 } 297 298 /* (non-Javadoc) 299 * @see biz.hammurapi.sql.IDatabaseObject#fromDom(org.w3c.dom.Element) 300 */ 301 public void fromDom(Element holder) throws ConfigurationException { 302 fromDom(holder, getNameMap(getClass())); 303 } 304 305 private void loadAttributes(Element holder) throws ConfigurationException { 306 DomConfigFactory dcf=new DomConfigFactory(); 307 attributes.clear(); 308 try { 309 Node attributesNode = DOMUtils.selectSingleNode(holder, "object-attributes"); 310 if (attributesNode!=null) { 311 attributes.putAll((Map) dcf.create(attributesNode)); 312 } 313 } catch (Exception e) { 314 throw new ConfigurationException("Cannot load database object attributes: "+e, e); 315 } 316 317 } 318 319 /* (non-Javadoc) 320 * @see biz.hammurapi.sql.IDatabaseObject#fromDom(org.w3c.dom.Element, java.util.Properties) 321 */ 322 public void fromDom(Element holder, Properties nameMap) throws ConfigurationException { 323 Iterator it=columns.iterator(); 324 while (it.hasNext()) { 325 Column column=(Column) it.next(); 326 String nodeName = nameMap.getProperty(column.getName(), column.getName()).trim(); 327 328 if (nodeName.length()==0) { 329 // Zero mapping 330 continue; 331 } else if (nodeName.startsWith("@")) { 332 // Attribute mapping 333 if (holder.hasAttribute(nodeName.substring(1))) { 334 column.load(holder.getAttribute(nodeName.substring(1))); 335 } 336 } else if (nodeName.equals(".")) { 337 // Text content mapping 338 column.load(AbstractDomObject.getElementText(holder)); 339 } else if (nodeName.startsWith("!")) { 340 // Simple node mapping 341 try { 342 Node cNode=DOMUtils.selectSingleNode(holder, nodeName.substring(1)); 343 if (cNode!=null) { 344 column.load(AbstractDomObject.getElementText(cNode)); 345 } 346 } catch (Exception e) { 347 throw new ConfigurationException("Cannot load column "+column.getName()); 348 } 349 } else { 350 // Regular node mapping 351 try { 352 Node cNode=DOMUtils.selectSingleNode(holder, nodeName); 353 if (cNode!=null) { 354 column.configure(cNode, null); 355 } 356 } catch (Exception e) { 357 throw new ConfigurationException("Cannot load column "+column.getName()); 358 } 359 } 360 } 361 362 it=relationships.iterator(); 363 while (it.hasNext()) { 364 RelationshipEntry re=(RelationshipEntry) it.next(); 365 if (re.name!=null) { 366 String domName=re.domName==null ? re.name : re.domName; 367 try { 368 Constructor constructor=re.items.relationship.getItemType().getConstructor(new Class[] {Element.class, boolean.class}); 369 Element rel = ".".equals(domName) ? holder : (Element) DOMUtils.selectSingleNode(holder, domName); 370 String itemName = re.itemName==null ? "element" : re.itemName; 371 NodeList nl=DOMUtils.selectNodeList(rel, itemName); 372 for (int i=0, l=nl.getLength(); i<l; ++i) { 373 Element itemElement = (Element) nl.item(i); 374 re.items.add(constructor.newInstance(new Object[] {itemElement, force ? Boolean.TRUE : Boolean.FALSE})); 375 } 376 } catch (Exception e) { 377 throw new ConfigurationException("Cannot load relationship "+re.name, e); 378 } 379 } 380 } 381 loadAttributes(holder); 382 } 383 384 public void toDom(Element holder) { 385 toDom(holder, getNameMap(getClass()), false); 386 } 387 388 /* (non-Javadoc) 389 * @see biz.hammurapi.sql.IDatabaseObject#toDom(org.w3c.dom.Element, java.util.Properties, boolean) 390 */ 391 public void toDom(Element holder, Properties nameMap, boolean originals) { 392 if (nameMap==null) { 393 nameMap=new Properties(); 394 } 395 396 String cna = nameMap.getProperty("@type", "type").trim(); 397 if (!"".equals(cna)) { 398 holder.setAttribute(cna, getClass().getName()); 399 } 400 401 Iterator it=columns.iterator(); 402 while (it.hasNext()) { 403 Column column = (Column) it.next(); 404 column.toDom(holder, nameMap.getProperty(column.getName(), column.getName()).trim(), originals); 405 } 406 407 it=relationships.iterator(); 408 while (it.hasNext()) { 409 RelationshipEntry re=(RelationshipEntry) it.next(); 410 if (re.name!=null) { 411 String domName=re.domName==null ? re.name : re.domName; 412 Element rel = ".".equals(domName) ? holder : holder.getOwnerDocument().createElement(domName); 413 if (rel!=holder) { 414 holder.appendChild(rel); 415 } 416 417 Iterator iit=re.items.iterator(); 418 while (iit.hasNext()) { 419 String itemName = re.itemName==null ? "element" : re.itemName; 420 Element ie=holder.getOwnerDocument().createElement(itemName); 421 rel.appendChild(ie); 422 ((DatabaseObject) iit.next()).toDom(ie); 423 } 424 } 425 } 426 427 if (!attributes.isEmpty()) { 428 CompositeDomSerializer 429 .getThreadInstance() 430 .toDomSerializable(attributes) 431 .toDom(AbstractDomObject.addElement(holder, "object-attributes")); 432 } 433 } 434 435 public String toString() { 436 StringBuffer ret=new StringBuffer(getClass().getName()); 437 ret.append("["); 438 Iterator it=columns.iterator(); 439 while (it.hasNext()) { 440 ret.append(it.next()); 441 if (it.hasNext()) { 442 ret.append(", "); 443 } 444 } 445 446 ret.append("]"); 447 return ret.toString(); 448 } 449 450 /** 451 * Sets modified flag to true and increments version number. 452 * Also broadcasts the change to observers. Changed column is 453 * passed as second argument of update() method. 454 * Override this method in subclasses to react on 455 * change events, but don't forget to invoke 456 * super.onChange(). 457 * @param column Changed column 458 */ 459 public void onChange(Column column) { 460 isModified=true; 461 462 ++objectVersion; 463 464 if (column.isPrimaryKey()) { 465 isDeleted=false; 466 } 467 468 Iterator it=relationships.iterator(); 469 while (it.hasNext()) { 470 Relationship relationship=((RelationshipEntry) it.next()).items.relationship; 471 if (relationship instanceof ColumnChangeListener) { 472 ((ColumnChangeListener) relationship).onChange(column); 473 } 474 } 475 476 synchronized (observers) { 477 it=observers.iterator(); 478 while (it.hasNext()) { 479 ((Observer) it.next()).update(this, column); 480 } 481 } 482 483 } 484 485 /* (non-Javadoc) 486 * @see biz.hammurapi.sql.IDatabaseObject#setOriginal() 487 */ 488 public void setOriginal() { 489 objectVersion=originalVersion; 490 Iterator it=columns.iterator(); 491 while (it.hasNext()) { 492 ((Column) it.next()).setOriginal(); 493 } 494 } 495 496 private boolean isModified=false; 497 498 /* (non-Javadoc) 499 * @see biz.hammurapi.sql.IDatabaseObject#isModified() 500 */ 501 public boolean isModified() { 502 return isModified; 503 } 504 505 /* (non-Javadoc) 506 * @see biz.hammurapi.sql.IDatabaseObject#isDeleted() 507 */ 508 public boolean isDeleted() { 509 return isDeleted; 510 } 511 512 /** 513 * Two objects are considered equal and all their fields are equal. 514 * @param otherBean Other object 515 * @return true if object classes are equal and all member column values are 516 * equal. 517 */ 518 public boolean equals(Object otherBean) { 519 if (otherBean==null) { 520 return false; 521 } else if (getClass().equals(otherBean.getClass())) { 522 Collection myColumns=new ArrayList(columns); 523 Collection otherColumns=new ArrayList(((DatabaseObject) otherBean).columns); 524 Iterator mcit=myColumns.iterator(); 525 Z: 526 while (mcit.hasNext()) { 527 Column mc=(Column) mcit.next(); 528 Iterator ocit=otherColumns.iterator(); 529 while (ocit.hasNext()) { 530 Column oc=(Column) ocit.next(); 531 if (mc.getName().equals(oc.getName())) { 532 if (mc.equals(oc)) { 533 ocit.remove(); 534 mcit.remove(); 535 continue Z; 536 } 537 538 return false; 539 } 540 } 541 } 542 543 return myColumns.isEmpty() && otherColumns.isEmpty(); 544 } else { 545 return false; 546 } 547 } 548 549 public int hashCode() { 550 int ret=0; 551 Iterator cit=columns.iterator(); 552 while (cit.hasNext()) { 553 ret^=cit.next().hashCode(); 554 } 555 return ret; 556 } 557 558 /** 559 * Clones object, clears columns collection, clears isDeleted and isModified flags. 560 * Subclasses shall add cloned columns. 561 */ 562 public Object clone() throws CloneNotSupportedException { 563 DatabaseObject ret = (DatabaseObject) super.clone(); 564 ret.columns.clear(); 565 ret.isDeleted=false; 566 ret.isModified=false; 567 return ret; 568 } 569 570 /* (non-Javadoc) 571 * @see biz.hammurapi.sql.IDatabaseObject#clear() 572 */ 573 public void clear() { 574 Iterator it = columns.iterator(); 575 while (it.hasNext()) { 576 ((Column) it.next()).clear(); 577 } 578 isModified=false; 579 isDeleted=false; 580 } 581 582 public void configure(Context context, CompositeConverter converter) throws ConfigurationException { 583 Iterator it = columns.iterator(); 584 while (it.hasNext()) { 585 ((Column) it.next()).configure(context, converter); 586 } 587 } 588 589 public Object get(String name) { 590 Column col=(Column) columnMap.get(name); 591 return col==null ? null : col.getObjectValue(false); 592 } 593 594 public void configure(Node configNode, Context context) throws ConfigurationException { 595 fromDom((Element) configNode); 596 } 597 598 private static Map nnMap=new HashMap(); 599 600 private static Properties getNameMap(Class clazz) { 601 synchronized (nnMap) { 602 Properties ret=(Properties) nnMap.get(clazz.getName()); 603 if (ret==null) { 604 ret=new ClassResourceLoader(clazz).getProperties(null, "namemap"); 605 nnMap.put(clazz.getName(), ret); 606 } 607 return ret; 608 } 609 } 610 611 private class RelationshipEntry { 612 String name; 613 String itemName; 614 String domName; 615 RelationshipListImpl items; 616 } 617 618 private class RelationshipListImpl extends AbstractList implements RelationshipList { 619 private ArrayList master=new ArrayList(); 620 private Relationship relationship; 621 private boolean alreadyLoaded; 622 623 private RelationshipListImpl(Relationship relationship) { 624 this.relationship=relationship; 625 relationship.setMaster(master); 626 } 627 628 private void update(SQLProcessor processor, IDatabaseObject subItem) throws SQLException { 629 relationship.update(processor, subItem); 630 } 631 632 private void load(SQLProcessor processor) throws SQLException { 633 if (!alreadyLoaded || relationship.isModified()) { 634 master.clear(); 635 relationship.load(processor, master); 636 alreadyLoaded=true; 637 } 638 } 639 640 private void store(SQLProcessor processor) throws SQLException { 641 relationship.store(processor); 642 } 643 644 public Object get(int index) { 645 return master.get(index); 646 } 647 648 public int size() { 649 return master.size(); 650 } 651 652 public boolean add(Object o) { 653 relationship.add((DatabaseObject) o); 654 return master.add(o); 655 } 656 657 public boolean remove(Object o) { 658 relationship.remove((IDatabaseObject) o); 659 return master.remove(o); 660 } 661 662 public boolean isLazy() { 663 return relationship.isLazy(); 664 } 665 666 public boolean isModified() { 667 return relationship.isModified(); 668 } 669 670 public IDatabaseObject add() { 671 try { 672 IDatabaseObject item = (IDatabaseObject) relationship.getItemType().newInstance(); 673 add(item); 674 return item; 675 } catch (InstantiationException e) { 676 throw new RuntimeException("Cannot instantiate "+relationship.getItemType()); 677 } catch (IllegalAccessException e) { 678 throw new RuntimeException("Cannot instantiate "+relationship.getItemType()); 679 } 680 } 681 } 682 683 private Collection relationships=new ArrayList(); 684 private Map relationshipMap=new HashMap(); 685 686 protected RelationshipList getRelationship(String name) { 687 RelationshipEntry entry=(RelationshipEntry) relationshipMap.get(name); 688 if (entry.items.isLazy() || entry.items.isModified()) { 689 try { 690 entry.items.load(((Lazy) this).getProcessor()); 691 } catch (SQLException e) { 692 throw new SQLRuntimeException(e); 693 } 694 } 695 return entry.items; 696 } 697 698 /** 699 * @param name Name for composite relationships to render in XML, null for shared relationships. 700 * @param itemName name of item element in XML. 701 * @param itemClass item class. This class shall have constructor from Element,boolean in order to load from 702 * XML docs. 703 * @param relationship 704 */ 705 protected RelationshipList addRelationship(String name, String itemName, Relationship relationship) { 706 if (relationship.isLazy() && !(this instanceof Lazy)) { 707 throw new IllegalArgumentException("Cannot add lazy relationship to class which doesn't implement Lazy interface"); 708 } 709 710 if (!DatabaseObject.class.isAssignableFrom(relationship.getItemType())) { 711 throw new IllegalArgumentException("Relationship can be established only between DatabaseObject subclasses"); 712 } 713 RelationshipEntry entry=new RelationshipEntry(); 714 relationships.add(entry); 715 entry.name=name; 716 entry.itemName=itemName; 717 entry.items=new RelationshipListImpl(relationship); 718 Properties nameMap=getNameMap(getClass()); 719 String cName = name==null ? null : nameMap.getProperty(name); 720 entry.domName = cName==null ? null : cName.trim(); 721 relationshipMap.put(name, entry); 722 return entry.items; 723 } 724 725 /** 726 * Use this method to eagerly load relationships in constructors. 727 * @param processor 728 * @throws SQLException 729 */ 730 protected void loadRelationships(SQLProcessor processor) throws SQLException { 731 Iterator it=relationships.iterator(); 732 while (it.hasNext()) { 733 RelationshipEntry relationshipEntry = (RelationshipEntry) it.next(); 734 if (!relationshipEntry.items.isLazy()) { 735 (relationshipEntry).items.load(processor); 736 } 737 } 738 } 739 740 /** 741 * Use this method to eagerly load relationships in constructors. 742 * @param processor 743 * @throws SQLException 744 */ 745 private void storeRelationships(SQLProcessor processor) throws SQLException { 746 Iterator it=relationships.iterator(); 747 while (it.hasNext()) { 748 ((RelationshipEntry) it.next()).items.store(processor); 749 } 750 } 751 752 753 /* (non-Javadoc) 754 * @see biz.hammurapi.sql.IDatabaseObject#copy(biz.hammurapi.sql.DatabaseObject) 755 */ 756 public void copy(DatabaseObject source) { 757 Iterator it=columns.iterator(); 758 while (it.hasNext()) { 759 Column targetColumn=(Column) it.next(); 760 Column sourceColumn=(Column) source.columnMap.get(targetColumn.getName()); 761 if (targetColumn.getClass().isInstance(sourceColumn)) { // Copy values only from compatible columns 762 targetColumn.set(sourceColumn); 763 } 764 } 765 } 766 767 private Map attributes=new HashMap(); 768 769 public void setAttribute(Object key, Object value) { 770 attributes.put(key, value); 771 } 772 773 public Object getAttribute(Object key) { 774 return attributes.get(key); 775 } 776 777 public Object removeAttribute(Object key) { 778 return attributes.remove(key); 779 } 780 781 /* (non-Javadoc) 782 * @see biz.hammurapi.sql.IDatabaseObject#setColumnAttribute(java.lang.String, java.lang.Object, java.lang.Object) 783 */ 784 public void setColumnAttribute(String columnName, Object key, Object value) { 785 Column column=(Column) columnMap.get(columnName); 786 if (column==null) { 787 throw new IllegalArgumentException("Column not found: "+columnName); 788 } 789 column.setAttribute(key, value); 790 } 791 792 /* (non-Javadoc) 793 * @see biz.hammurapi.sql.IDatabaseObject#getColumnAttribute(java.lang.String, java.lang.Object) 794 */ 795 public Object getColumnAttribute(String columnName, Object key) { 796 Column column=(Column) columnMap.get(columnName); 797 if (column==null) { 798 throw new IllegalArgumentException("Column not found: "+columnName); 799 } 800 return column.getAttribute(key); 801 } 802 803 /* (non-Javadoc) 804 * @see biz.hammurapi.sql.IDatabaseObject#removeColumnAttribute(java.lang.String, java.lang.Object) 805 */ 806 public Object removeColumnAttribute(String columnName, Object key) { 807 Column column=(Column) columnMap.get(columnName); 808 if (column==null) { 809 throw new IllegalArgumentException("Column not found: "+columnName); 810 } 811 return column.removeAttribute(key); 812 } 813 814 private static Map sqlTypes=new HashMap(); 815 816 /** 817 * Allows to override generated column types with <class name>.sqltypes resource. 818 * Subclasses shall use this method when dealing with Object columns. 819 * @param columnName 820 * @param generatedType 821 * @return 822 */ 823 protected int getSqlType(String columnName, int generatedType) { 824 Map typeMap; 825 synchronized (sqlTypes) { 826 String className = getClass().getName(); 827 if (sqlTypes.containsKey(className)) { 828 typeMap=(Map) sqlTypes.get(className); 829 } else { 830 Properties literalMap=new ClassResourceLoader(getClass()).getProperties(null, "sqltypes"); 831 typeMap=new HashMap(); 832 sqlTypes.put(className, typeMap); 833 Iterator it=literalMap.entrySet().iterator(); 834 while (it.hasNext()) { 835 Entry entry=(Entry) it.next(); 836 try { 837 Integer type = (Integer) Types.class.getDeclaredField((String) entry.getValue()).get(null) ; 838 typeMap.put(entry.getKey(), type); 839 } catch (IllegalArgumentException e) { 840 System.err.println("Invalid SQL Type "+entry.getValue()+", ignored. Cause: "+e); 841 e.printStackTrace(); 842 } catch (SecurityException e) { 843 System.err.println("Invalid SQL Type "+entry.getValue()+", ignored. Cause: "+e); 844 e.printStackTrace(); 845 } catch (IllegalAccessException e) { 846 System.err.println("Invalid SQL Type "+entry.getValue()+", ignored. Cause: "+e); 847 e.printStackTrace(); 848 } catch (NoSuchFieldException e) { 849 System.err.println("Invalid SQL Type "+entry.getValue()+", ignored. Cause: "+e); 850 e.printStackTrace(); 851 } 852 } 853 } 854 } 855 856 Integer st=(Integer) typeMap.get(columnName); 857 return st==null ? generatedType : st.intValue(); 858 } 859 860 /** 861 * Subclasses can choose to read object version from the database. 862 */ 863 protected int objectVersion; 864 protected int originalVersion; 865 866 public int getObjectVersion() { 867 return objectVersion; 868 } 869 870 private Set observers = new HashSet(); 871 872 public void addObserver(Observer observer) { 873 synchronized (observers) { 874 observers.add(observer); 875 } 876 } 877 878 public void removeObserver(Observer observer) { 879 synchronized (observers) { 880 observers.remove(observer); 881 } 882 } 883 884 }