From: Alexey L. <lou...@us...> - 2003-01-31 14:21:45
|
User: loubyansky Date: 03/01/31 06:21:43 Modified: src/main/org/jboss/ejb/plugins/cmp/jdbc/bridge Tag: Branch_3_2 JDBCCMRFieldBridge.java Log: fix for [675932]: added support for partial foreign key fields to primary key columns mapping, i.e. some of the foreign key fields can be mapped to primary key columns and others - to their own. Revision Changes Path No revision No revision 1.43.2.9 +131 -64 jboss/src/main/org/jboss/ejb/plugins/cmp/jdbc/bridge/JDBCCMRFieldBridge.java Index: JDBCCMRFieldBridge.java =================================================================== RCS file: /cvsroot/jboss/jboss/src/main/org/jboss/ejb/plugins/cmp/jdbc/bridge/JDBCCMRFieldBridge.java,v retrieving revision 1.43.2.8 retrieving revision 1.43.2.9 diff -u -r1.43.2.8 -r1.43.2.9 --- JDBCCMRFieldBridge.java 14 Jan 2003 17:01:28 -0000 1.43.2.8 +++ JDBCCMRFieldBridge.java 31 Jan 2003 14:21:41 -0000 1.43.2.9 @@ -67,7 +67,7 @@ * One for each role that entity has. * * @author <a href="mailto:da...@da...">Dain Sundstrom</a> - * @version $Revision: 1.43.2.8 $ + * @version $Revision: 1.43.2.9 $ */ public class JDBCCMRFieldBridge implements JDBCFieldBridge, CMRFieldBridge { /** @@ -100,11 +100,6 @@ */ private boolean hasForeignKey; - /** - * Indicates whether the foreign key is a part of the primary key - */ - private boolean fkPartOfPk; - /** * The key fields that this entity maintains in the relation table. */ @@ -114,6 +109,16 @@ * Foreign key fields of this entity (i.e., related entities pk fields) */ private List foreignKeyFields; + + /** + * Indicates whether all FK fields are mapped to PK fields + */ + private boolean allFkFieldsMappedToPkFields; + + /** + * This map contains related PK fields that are mapped through FK fields to this entity's PK fields + */ + private Map relatedPkFieldsByPkFields = new HashMap(); /** * JDBCType for the foreign key fields. Basically, this is an ordered @@ -268,7 +273,6 @@ // FKs mapped to PKs Map fkFieldsByPkFields = new HashMap(); - for(Iterator i=foreignKeys.iterator(); i.hasNext(); ) { JDBCCMPFieldMetaData fkFieldMetaData = @@ -289,10 +293,6 @@ entity.getMetaData().getCMPFieldByName(pkField.getFieldName()); if(fkColumnName.equals(pkFieldMetaData.getColumnName())) { - - // mark the fk as being a part of the pk - fkPartOfPk = true; - JDBCCMP2xFieldBridge relatedPkField = (JDBCCMP2xFieldBridge)relatedEntity. getFieldByName(fkFieldMetaData.getFieldName()); @@ -312,6 +312,7 @@ ); fkFieldsByPkFields.put(pkField, fkField); + relatedPkFieldsByPkFields.put(pkField, relatedPkField); } } @@ -336,6 +337,10 @@ } } + // are all FK fields mapped to PK fields? + allFkFieldsMappedToPkFields = relatedPkFieldsByPkFields.size() > 0 + && relatedPkFieldsByPkFields.size() == foreignKeyFields.size(); + foreignKeyFields = Collections.unmodifiableList(foreignKeyFields); hasForeignKey = !foreignKeyFields.isEmpty(); if(hasForeignKey) { @@ -416,10 +421,14 @@ } /** - * Returns true if this fk is a part of pk and false otherwise + * Returns true if all FK fields are mapped to PK fields */ - public boolean isFkPartOfPk() { - return fkPartOfPk; + public boolean allFkFieldsMappedToPkFields() { + return allFkFieldsMappedToPkFields; + } + + public boolean hasFkFieldsMappedToPkFields() { + return relatedPkFieldsByPkFields.size() > 0; } /** @@ -556,12 +565,12 @@ throw new IllegalArgumentException("null cannot be assigned to a " + "collection-valued cmr-field [EJB 2.0 Spec. 10.3.8]."); } - if(fkPartOfPk) { + if(allFkFieldsMappedToPkFields) { throw new IllegalStateException( - "CMR field " + this.getFieldName() + - " is mapped to a " + - "foreign key which is a part of a primary key, and primary key " + - "may be set only once in ejbCreate [EJB 2.0 Spec. 10.3.5]."); + "Can't modify relationship: CMR field " + + entity.getEntityName() + "." + getFieldName() + + " has foreign key fields mapped to the primary key columns." + + " Primary key may only be set once in ejbCreate [EJB 2.0 Spec. 10.3.5]."); } setInstanceValue(ctx, value); @@ -599,17 +608,42 @@ * Sets the value of the cmr field for the instance associated with * the context. */ - public void setInstanceValue( - EntityEnterpriseContext myCtx, Object newValue) { - + public void setInstanceValue(EntityEnterpriseContext myCtx, Object newValue) { load(myCtx); - FieldState fieldState = getFieldState(myCtx); // is this just setting our own relation set back if(newValue == fieldState.getRelationSet()) { return; } + + Collection valueCopy; + if(newValue instanceof Collection) { + valueCopy = new ArrayList((Collection) newValue); + } else { + valueCopy = new ArrayList(1); + if(newValue != null) { + valueCopy.add(newValue); + } + } + Iterator newBeans = valueCopy.iterator(); + // list of new pk values. just not to fetch them twice + // if there there are FK fields mapped to PK fields + List newPkValues = null; + + // check whether new value modifies the primary key if there are FK fields + // mapped to PK fields + if(relatedPkFieldsByPkFields.size() > 0) { + newPkValues = new ArrayList(); + while(newBeans.hasNext()) { + EJBLocalObject ejbObject = (EJBLocalObject)newBeans.next(); + if(ejbObject == null) + continue; + Object pkObject = ejbObject.getPrimaryKey(); + checkSetForeignKey(myCtx, pkObject); + newPkValues.add(pkObject); + } + } try{ // Remove old value(s) @@ -620,27 +654,47 @@ } // Add new value(s) - Collection c; - if(newValue instanceof Collection) { - c = (Collection) newValue; - } else { - c = new ArrayList(); - if(newValue != null) { - c.add(newValue); - } - } - - Iterator newBeans = (new ArrayList(c)).iterator(); - while(newBeans.hasNext()) { - EJBLocalObject newBean = (EJBLocalObject)newBeans.next(); - createRelationLinks(myCtx, newBean.getPrimaryKey()); - } + if(newPkValues != null) { + for(Iterator iter = newPkValues.iterator(); iter.hasNext();) { + createRelationLinks(myCtx, iter.next()); + } + } else { + while(newBeans.hasNext()) { + EJBLocalObject newBean = (EJBLocalObject)newBeans.next(); + createRelationLinks(myCtx, newBean.getPrimaryKey()); + } + } } catch(EJBException e) { throw e; } catch(Exception e) { throw new EJBException(e); } } + + /** + * Throws IllegalStateException if new foreign key value will change + * the primary key value, otherwise returns silently. + */ + private void checkSetForeignKey(EntityEnterpriseContext myCtx, Object newValue) + throws IllegalStateException { + for(Iterator pkFields=entity.getPrimaryKeyFields().iterator(); pkFields.hasNext();) { + JDBCCMP2xFieldBridge pkField = (JDBCCMP2xFieldBridge)pkFields.next(); + JDBCCMP2xFieldBridge relatedPkField = (JDBCCMP2xFieldBridge)relatedPkFieldsByPkFields.get(pkField); + if(relatedPkField != null) { + Object comingValue = relatedPkField.getPrimaryKeyValue(newValue); + Object currentValue = pkField.getInstanceValue(myCtx); + + // they shouldn't be null, should they? + if(!comingValue.equals(currentValue)) { + throw new IllegalStateException( + "Can't create relationship: CMR field " + + entity.getEntityName() + "." + getFieldName() + + " has foreign key fields mapped to the primary key columns." + + " Primary key may only be set once in ejbCreate [EJB 2.0 Spec. 10.3.5]."); + } + } + } + } /** * Creates the relation links between the instance associated with the @@ -844,6 +898,8 @@ * any foreign key fields. */ public void addRelation(EntityEnterpriseContext myCtx, Object fk) { + checkSetForeignKey(myCtx, fk); + if(isReadOnly()) { throw new EJBException("Field is read-only: " + getFieldName()); } @@ -860,9 +916,6 @@ // set the foreign key, if we have one. if(hasForeignKey()) { - // if fk is a part of pk then there is no need for UPDATE sql stmt - if(fkPartOfPk) - myState.setClean(); setForeignKey(myCtx, fk); } } @@ -895,9 +948,6 @@ if(updateValueCollection) { FieldState myState = getFieldState(myCtx); myState.removeRelation(fk); - // there is no need to UPDATE fk that is a part of pk - if(fkPartOfPk) - myState.setClean(); } // set the foreign key to null, if we have one. @@ -1079,25 +1129,29 @@ argumentRef); if(field.getPrimaryKeyField() != null) { - // if we are tring to set a null value - // into a null pk, we are already done. - if(argumentRef[0] != null || fkRef[0] != null) { - - // if we don't have a pk object yet create one - if(fkRef[0] == null) { - fkRef[0] = relatedEntity.createPrimaryKeyInstance(); - } + // if there is a null field among FK fields, + // the whole FK field is considered null. + // NOTE: don't throw exception in this case, + // it's ok if FK is partly mapped to a PK + if(argumentRef[0] == null) { + fkRef[0] = null; + break; + } + + // if we don't have a pk object yet create one + if(fkRef[0] == null) { + fkRef[0] = relatedEntity.createPrimaryKeyInstance(); + } - try { - // Set this field's value into the primary key object. - field.getPrimaryKeyField().set( - fkRef[0], - argumentRef[0]); - } catch(Exception e) { - // Non recoverable internal exception - throw new EJBException("Internal error setting foreign-key " + - "field " + getFieldName(), e); - } + try { + // Set this field's value into the primary key object. + field.getPrimaryKeyField().set( + fkRef[0], + argumentRef[0]); + } catch(Exception e) { + // Non recoverable internal exception + throw new EJBException("Internal error setting foreign-key " + + "field " + getFieldName(), e); } } else { // This field is the primary key, so no extraction is necessary. @@ -1111,7 +1165,6 @@ if(!hasForeignKey()) { return false; } - return getFieldState(ctx).isDirty(); } @@ -1119,9 +1172,23 @@ if(!hasForeignKey()) { return; } - getFieldState(ctx).setClean(); } + + /** + * Returns dirty foreign key fields + */ + public List getDirtyForeignKeyFields(EntityEnterpriseContext ctx) { + if(!hasForeignKey) + return Collections.EMPTY_LIST; + List dirtyFields = new ArrayList(); + for(Iterator iter = this.foreignKeyFields.iterator(); iter.hasNext();) { + JDBCCMP2xFieldBridge fkField = (JDBCCMP2xFieldBridge)iter.next(); + if(fkField.isDirty(ctx)) + dirtyFields.add(fkField); + } + return dirtyFields; + } /** * Gets the field state object from the persistence context. |