Author: epbernard
Date: 2008-03-04 16:22:12 -0500 (Tue, 04 Mar 2008)
New Revision: 14389
Added:
annotations/trunk/src/java/org/hibernate/cfg/RecoverableException.java
annotations/trunk/src/test/org/hibernate/test/annotations/manytoone/referencedcolumnname/
annotations/trunk/src/test/org/hibernate/test/annotations/manytoone/referencedcolumnname/GenericObject.java
annotations/trunk/src/test/org/hibernate/test/annotations/manytoone/referencedcolumnname/Item.java
annotations/trunk/src/test/org/hibernate/test/annotations/manytoone/referencedcolumnname/ManyToOneReferencedColumnNameTest.java
annotations/trunk/src/test/org/hibernate/test/annotations/manytoone/referencedcolumnname/Vendor.java
annotations/trunk/src/test/org/hibernate/test/annotations/manytoone/referencedcolumnname/WarehouseItem.java
annotations/trunk/src/test/org/hibernate/test/annotations/manytoone/referencedcolumnname/ZItemCost.java
Modified:
annotations/trunk/src/java/org/hibernate/cfg/AnnotationConfiguration.java
annotations/trunk/src/java/org/hibernate/cfg/Ejb3JoinColumn.java
Log:
ANN-509 recover from Foreign Key Mapping exception when it's due to second pass
ordering.
Modified: annotations/trunk/src/java/org/hibernate/cfg/AnnotationConfiguration.java
===================================================================
--- annotations/trunk/src/java/org/hibernate/cfg/AnnotationConfiguration.java 2008-03-04
18:07:28 UTC (rev 14388)
+++ annotations/trunk/src/java/org/hibernate/cfg/AnnotationConfiguration.java 2008-03-04
21:22:12 UTC (rev 14389)
@@ -299,30 +299,35 @@
}
}
caches.clear();
-
- inSecondPass = true;
- processFkSecondPassInOrder();
- Iterator iter = secondPasses.iterator();
- while ( iter.hasNext() ) {
- SecondPass sp = (SecondPass) iter.next();
- //do the second pass of fk before the others and remove them
- if ( sp instanceof CreateKeySecondPass ) {
- sp.doSecondPass( classes );
- iter.remove();
+ try {
+ inSecondPass = true;
+ processFkSecondPassInOrder();
+ Iterator iter = secondPasses.iterator();
+ while ( iter.hasNext() ) {
+ SecondPass sp = (SecondPass) iter.next();
+ //do the second pass of fk before the others and remove them
+ if ( sp instanceof CreateKeySecondPass ) {
+ sp.doSecondPass( classes );
+ iter.remove();
+ }
}
- }
- iter = secondPasses.iterator();
- while ( iter.hasNext() ) {
- SecondPass sp = (SecondPass) iter.next();
- //do the SecondaryTable second pass before any association becasue associations can be
built on joins
- if ( sp instanceof SecondaryTableSecondPass ) {
- sp.doSecondPass( classes );
- iter.remove();
+ iter = secondPasses.iterator();
+ while ( iter.hasNext() ) {
+ SecondPass sp = (SecondPass) iter.next();
+ //do the SecondaryTable second pass before any association becasue associations can
be built on joins
+ if ( sp instanceof SecondaryTableSecondPass ) {
+ sp.doSecondPass( classes );
+ iter.remove();
+ }
}
+ super.secondPassCompile();
+ inSecondPass = false;
}
- super.secondPassCompile();
- inSecondPass = false;
+ catch (RecoverableException e) {
+ //the exception was not recoverable after all
+ throw (RuntimeException) e.getCause();
+ }
Iterator tables = tableUniqueConstraints.entrySet().iterator();
Table table;
Map.Entry entry;
@@ -476,10 +481,34 @@
while ( it.hasNext() ) {
( (SecondPass) it.next() ).doSecondPass( classes );
}
- it = endOfQueueFkSecondPasses.listIterator();
- while ( it.hasNext() ) {
- ( (SecondPass) it.next() ).doSecondPass( classes );
+
+ /*
+ * If a second pass raises a recoverableException, queue it for next round
+ * stop of no pass has to be processed or if the number of pass to processes
+ * does not diminish between two rounds.
+ * If some failing pass remain, raise the original exception
+ */
+ boolean stopProcess = false;
+ RuntimeException originalException = null;
+ while ( ! stopProcess ) {
+ List failingSecondPasses = new ArrayList();
+ it = endOfQueueFkSecondPasses.listIterator();
+ while ( it.hasNext() ) {
+ final SecondPass pass = (SecondPass) it.next();
+ try {
+ pass.doSecondPass( classes );
+ }
+ catch (RecoverableException e) {
+ failingSecondPasses.add( pass );
+ if (originalException == null) originalException = (RuntimeException)
e.getCause();
+ }
+ }
+ stopProcess = failingSecondPasses.size() == 0 || failingSecondPasses.size() ==
endOfQueueFkSecondPasses.size();
+ endOfQueueFkSecondPasses = failingSecondPasses;
}
+ if (endOfQueueFkSecondPasses.size() > 0) {
+ throw originalException;
+ }
}
}
Modified: annotations/trunk/src/java/org/hibernate/cfg/Ejb3JoinColumn.java
===================================================================
--- annotations/trunk/src/java/org/hibernate/cfg/Ejb3JoinColumn.java 2008-03-04 18:07:28
UTC (rev 14388)
+++ annotations/trunk/src/java/org/hibernate/cfg/Ejb3JoinColumn.java 2008-03-04 21:22:12
UTC (rev 14389)
@@ -391,11 +391,16 @@
referencedEntity, columns[0].getReferencedColumn(), mappings
);
if ( columnOwner == null ) {
- throw new MappingException(
- "Unable to find column with logical name: "
- + columns[0].getReferencedColumn() + " in " +
referencedEntity.getTable() + " and its related "
- + "supertables and secondary tables"
- );
+ try {
+ throw new MappingException(
+ "Unable to find column with logical name: "
+ + columns[0].getReferencedColumn() + " in " +
referencedEntity.getTable() + " and its related "
+ + "supertables and secondary tables"
+ );
+ }
+ catch (MappingException e) {
+ throw new RecoverableException(e);
+ }
}
Table matchingTable = columnOwner instanceof PersistentClass ?
( (PersistentClass) columnOwner ).getTable() :
Added: annotations/trunk/src/java/org/hibernate/cfg/RecoverableException.java
===================================================================
--- annotations/trunk/src/java/org/hibernate/cfg/RecoverableException.java
(rev 0)
+++ annotations/trunk/src/java/org/hibernate/cfg/RecoverableException.java 2008-03-04
21:22:12 UTC (rev 14389)
@@ -0,0 +1,25 @@
+//$
+package org.hibernate.cfg;
+
+import org.hibernate.AnnotationException;
+
+/**
+ * Should neven be exposed to the client
+ * An exception that wrap an underlying exception whith the hope
+ * subsequent processing will recover from it.
+ *
+ * @author Emmanuel Bernard
+ */
+public class RecoverableException extends AnnotationException {
+ public RecoverableException(String msg, Throwable root) {
+ super( msg, root );
+ }
+
+ public RecoverableException(Throwable root) {
+ super( root );
+ }
+
+ public RecoverableException(String s) {
+ super( s );
+ }
+}
Added:
annotations/trunk/src/test/org/hibernate/test/annotations/manytoone/referencedcolumnname/GenericObject.java
===================================================================
---
annotations/trunk/src/test/org/hibernate/test/annotations/manytoone/referencedcolumnname/GenericObject.java
(rev 0)
+++
annotations/trunk/src/test/org/hibernate/test/annotations/manytoone/referencedcolumnname/GenericObject.java 2008-03-04
21:22:12 UTC (rev 14389)
@@ -0,0 +1,68 @@
+//$
+package org.hibernate.test.annotations.manytoone.referencedcolumnname;
+
+import java.io.Serializable;
+import java.rmi.server.UID;
+import javax.persistence.GeneratedValue;
+import javax.persistence.GenerationType;
+import javax.persistence.Id;
+import javax.persistence.MappedSuperclass;
+import javax.persistence.Transient;
+import javax.persistence.Version;
+
+@MappedSuperclass
+public class GenericObject implements Serializable {
+ protected int id;
+ protected int version;
+ protected UID uid = new UID();
+
+ @Id
+ @GeneratedValue( strategy = GenerationType.IDENTITY )
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ @Version
+ public int getVersion() {
+ return version;
+ }
+
+ public void setVersion(int version) {
+ this.version = version;
+ }
+
+ public void incrementVersion() {
+ this.version++;
+ }
+
+ public boolean equals(Object other) {
+ if ( this == other )
+ return true;
+ if ( ( other == null ) || !( other.getClass().equals( this.getClass() ) ) )
+ return false;
+ GenericObject anObject = (GenericObject) other;
+ if ( this.id == 0 || anObject.id == 0 )
+ return false;
+
+ return ( this.id == anObject.id );
+ }
+
+ public int hashCode() {
+ if ( this.id == 0 )
+ return super.hashCode();
+ return this.id;
+ }
+
+ @Transient
+ public UID getUid() {
+ return uid;
+ }
+
+ public void setUid(UID uid) {
+ this.uid = uid;
+ }
+}
Added:
annotations/trunk/src/test/org/hibernate/test/annotations/manytoone/referencedcolumnname/Item.java
===================================================================
---
annotations/trunk/src/test/org/hibernate/test/annotations/manytoone/referencedcolumnname/Item.java
(rev 0)
+++
annotations/trunk/src/test/org/hibernate/test/annotations/manytoone/referencedcolumnname/Item.java 2008-03-04
21:22:12 UTC (rev 14389)
@@ -0,0 +1,13 @@
+//$
+package org.hibernate.test.annotations.manytoone.referencedcolumnname;
+
+import java.util.HashSet;
+import java.util.Set;
+import javax.persistence.Entity;
+import javax.persistence.OneToMany;
+import javax.persistence.Transient;
+
+
+@Entity
+public class Item extends GenericObject {
+}
Added:
annotations/trunk/src/test/org/hibernate/test/annotations/manytoone/referencedcolumnname/ManyToOneReferencedColumnNameTest.java
===================================================================
---
annotations/trunk/src/test/org/hibernate/test/annotations/manytoone/referencedcolumnname/ManyToOneReferencedColumnNameTest.java
(rev 0)
+++
annotations/trunk/src/test/org/hibernate/test/annotations/manytoone/referencedcolumnname/ManyToOneReferencedColumnNameTest.java 2008-03-04
21:22:12 UTC (rev 14389)
@@ -0,0 +1,45 @@
+//$
+package org.hibernate.test.annotations.manytoone.referencedcolumnname;
+
+import java.math.BigDecimal;
+
+import org.hibernate.test.annotations.TestCase;
+import org.hibernate.Session;
+
+/**
+ * @author Emmanuel Bernard
+ */
+public class ManyToOneReferencedColumnNameTest extends TestCase {
+ public void testReoverableExceptionInFkOrdering() throws Exception {
+ //SF should not blow up
+ Vendor v = new Vendor();
+ Item i = new Item();
+ ZItemCost ic = new ZItemCost();
+ ic.setCost( new BigDecimal(2) );
+ ic.setItem( i );
+ ic.setVendor( v );
+ WarehouseItem wi = new WarehouseItem();
+ wi.setDefaultCost( ic );
+ wi.setItem( i );
+ wi.setVendor( v );
+ wi.setQtyInStock( new BigDecimal(2) );
+ Session s = openSession( );
+ s.getTransaction().begin();
+ s.save( i );
+ s.save( v );
+ s.save( ic );
+ s.save( wi );
+ s.flush();
+ s.getTransaction().rollback();
+ s.close();
+
+ }
+ protected Class[] getMappings() {
+ return new Class[] {
+ Item.class,
+ Vendor.class,
+ WarehouseItem.class,
+ ZItemCost.class
+ };
+ }
+}
Added:
annotations/trunk/src/test/org/hibernate/test/annotations/manytoone/referencedcolumnname/Vendor.java
===================================================================
---
annotations/trunk/src/test/org/hibernate/test/annotations/manytoone/referencedcolumnname/Vendor.java
(rev 0)
+++
annotations/trunk/src/test/org/hibernate/test/annotations/manytoone/referencedcolumnname/Vendor.java 2008-03-04
21:22:12 UTC (rev 14389)
@@ -0,0 +1,12 @@
+//$
+package org.hibernate.test.annotations.manytoone.referencedcolumnname;
+
+import java.util.HashSet;
+import java.util.Set;
+import javax.persistence.Entity;
+import javax.persistence.OneToMany;
+import javax.persistence.Transient;
+
+@Entity
+public class Vendor extends GenericObject {
+}
\ No newline at end of file
Added:
annotations/trunk/src/test/org/hibernate/test/annotations/manytoone/referencedcolumnname/WarehouseItem.java
===================================================================
---
annotations/trunk/src/test/org/hibernate/test/annotations/manytoone/referencedcolumnname/WarehouseItem.java
(rev 0)
+++
annotations/trunk/src/test/org/hibernate/test/annotations/manytoone/referencedcolumnname/WarehouseItem.java 2008-03-04
21:22:12 UTC (rev 14389)
@@ -0,0 +1,62 @@
+//$
+package org.hibernate.test.annotations.manytoone.referencedcolumnname;
+
+import java.math.BigDecimal;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.JoinColumn;
+import javax.persistence.JoinColumns;
+import javax.persistence.ManyToOne;
+
+@Entity
+public class WarehouseItem extends GenericObject {
+
+
+ Item item;
+ Vendor vendor;
+ ZItemCost defaultCost;
+ BigDecimal qtyInStock;
+
+
+ public BigDecimal getQtyInStock() {
+ return qtyInStock;
+ }
+
+ public void setQtyInStock(BigDecimal qtyInStock) {
+ this.qtyInStock = qtyInStock;
+ }
+
+ @ManyToOne
+//(fetch=FetchType.LAZY)
+ @JoinColumn( name = "ITEM_ID", unique = false, nullable = false, insertable =
true, updatable = true )
+ public Item getItem() {
+ return item;
+ }
+
+ public void setItem(Item item) {
+ this.item = item;
+ }
+
+ @ManyToOne( fetch = FetchType.LAZY )
+ @JoinColumn( name = "VENDOR_ID", unique = false, nullable = false, insertable
= true, updatable = true )
+ public Vendor getVendor() {
+ return vendor;
+ }
+
+ public void setVendor(Vendor vendor) {
+ this.vendor = vendor;
+ }
+
+ @ManyToOne
+ @JoinColumns( {
+ @JoinColumn( name = "vendor_id", referencedColumnName = "vendor_id",
insertable = false, updatable = false ),
+ @JoinColumn( name = "item_id", referencedColumnName = "item_id",
insertable = false, updatable = false )
+ } )
+ public ZItemCost getDefaultCost() {
+ return defaultCost;
+ }
+
+ public void setDefaultCost(ZItemCost defaultCost) {
+ this.defaultCost = defaultCost;
+ }
+}
Added:
annotations/trunk/src/test/org/hibernate/test/annotations/manytoone/referencedcolumnname/ZItemCost.java
===================================================================
---
annotations/trunk/src/test/org/hibernate/test/annotations/manytoone/referencedcolumnname/ZItemCost.java
(rev 0)
+++
annotations/trunk/src/test/org/hibernate/test/annotations/manytoone/referencedcolumnname/ZItemCost.java 2008-03-04
21:22:12 UTC (rev 14389)
@@ -0,0 +1,46 @@
+//$
+package org.hibernate.test.annotations.manytoone.referencedcolumnname;
+
+import java.math.BigDecimal;
+import javax.persistence.Entity;
+import javax.persistence.FetchType;
+import javax.persistence.ManyToOne;
+import javax.persistence.Transient;
+import javax.persistence.JoinColumn;
+
+@Entity
+public class ZItemCost extends GenericObject {
+
+ Item item;
+ Vendor vendor;
+ BigDecimal cost;
+
+ @ManyToOne( fetch = FetchType.LAZY )
+ //@JoinColumn(name="ITEM_ID", unique=false, nullable=false, insertable=true,
updatable=true)
+ public Item getItem() {
+ return item;
+ }
+
+ public void setItem(Item item) {
+ this.item = item;
+ }
+
+ @ManyToOne( fetch = FetchType.LAZY )
+ //@JoinColumn(name="VENDOR_ID", unique=false, nullable=false, insertable=true,
updatable=true)
+ public Vendor getVendor() {
+ return vendor;
+ }
+
+ public void setVendor(Vendor vendor) {
+ this.vendor = vendor;
+ }
+
+ public BigDecimal getCost() {
+ return cost;
+ }
+
+ public void setCost(BigDecimal cost) {
+ this.cost = cost;
+ }
+}
+