[ https://issues.apache.org/jira/browse/OPENJPA-2929?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=17906039#comment-17906039 ]
Pawel Veselov edited comment on OPENJPA-2929 at 12/16/24 2:08 PM: ------------------------------------------------------------------ We've rewritten this method to be: {code:java} private void setJoinRefColumn( OpenJPAStateManager inverseSm, // foreign object Column[] ownerCols, // columns that can be updated in this Column inverseCol, // foreign column that need to be considered Object val // red herring ) { OpenJPAStateManager ownerSm = getPrimaryKey(); if (ownerSm != null) { ClassMetaData ownerMeta = ownerSm.getMetaData(); FieldMetaData[] ownerFields = ownerMeta.getFields(); // loop through all the fields in the owner entity for (FieldMetaData ownerFM : ownerFields) { // look for any single column in this field references the // same column as the foreign key target column Column[] cols = ((FieldMapping) ownerFM).getColumns(); if (cols.length != 1 // only support attribute of non-compound foreign key || cols == ownerCols // not @Id field // OPENJPA-2929 - this check makes NO sense, it checks if the column in OWNER // is the same that the first column in INVERSE's ID, but if inverse has a compound ID, // it will set all of or none of owner fields // || !cols[0].getIdentifier().equals(ownerCols[0].getIdentifier()) ) { continue; } // we need to answer the question - is cols[0] in ownerCols[0], and does it // actually match the inverse column declared here. I don't necessarily know how to do // this correctly. The column must have been declared as a @JoinColumns on some field, // but I don't know where to find that based on cols[0]. I do see that the column has // an FK constraint, I'm going to base this off of that. boolean found = false; for (Column ownerCol : ownerCols) { if (cols[0].getIdentifier().equals(ownerCol.getIdentifier())) { found = true; break; } } if (!found) { continue; } found = false; for (Constraint c : cols[0].getConstraints()) { if (!(c instanceof ForeignKey)) { continue; } ForeignKey fk = (ForeignKey) c; Column ownerFK = fk.getColumn(inverseCol); if (ownerFK != null && ownerFK.getFullDBIdentifier().equals(cols[0].getFullDBIdentifier())) { found = true; break; } } if (!found) { continue; } FieldMetaData inverseFM = inverseSm.getMetaData().getField( inverseCol.getIdentifier().getName()); if (inverseFM == null) { continue; } int inverseValIndex = inverseFM.getIndex(); Class<?> inverseType = inverseSm.getMetaData().getField(inverseValIndex).getType(); int ownerIndex = ownerFM.getIndex(); Class<?> ownerType = ownerSm.getMetaData().getField(ownerIndex).getType(); if (inverseType == ownerType) { // $TODO: why not just set the incoming val? Object inverseVal = inverseSm.fetch(inverseValIndex); ownerSm.storeField(ownerIndex, inverseVal); } } } } {code} I'm not sure how correct this all is, and generally frown at the amount of for() loops here, but that's the best we can come up with with our limited knowledge of OpenJPA internals. https://github.com/veselov/openjpa/commit/49693a9154cfc996f02272b814e5da362163eb1c was (Author: pveselov): We've rewritten this method to be: {code:java} private void setJoinRefColumn( OpenJPAStateManager inverseSm, // foreign object Column[] ownerCols, // columns that can be updated in this Column inverseCol, // foreign column that need to be considered Object val // red herring ) { OpenJPAStateManager ownerSm = getPrimaryKey(); if (ownerSm != null) { ClassMetaData ownerMeta = ownerSm.getMetaData(); FieldMetaData[] ownerFields = ownerMeta.getFields(); // loop through all the fields in the owner entity for (FieldMetaData ownerFM : ownerFields) { // look for any single column in this field references the // same column as the foreign key target column Column[] cols = ((FieldMapping) ownerFM).getColumns(); if (cols.length != 1 // only support attribute of non-compound foreign key || cols == ownerCols // not @Id field // OPENJPA-2929 - this check makes NO sense, it checks if the column in OWNER // is the same that the first column in INVERSE's ID, but if inverse has a compound ID, // it will set all of or none of owner fields // || !cols[0].getIdentifier().equals(ownerCols[0].getIdentifier()) ) { continue; } // we need to answer the question - is cols[0] in ownerCols[0], and does it // actually match the inverse column declared here. I don't necessarily know how to do // this correctly. The column must have been declared as a @JoinColumns on some field, // but I don't know where to find that based on cols[0]. I do see that the column has // an FK constraint, I'm going to base this off of that. boolean found = false; for (Column ownerCol : ownerCols) { if (cols[0].getIdentifier().equals(ownerCol.getIdentifier())) { found = true; break; } } if (!found) { continue; } found = false; for (Constraint c : cols[0].getConstraints()) { if (!(c instanceof ForeignKey)) { continue; } ForeignKey fk = (ForeignKey) c; Column ownerFK = fk.getColumn(inverseCol); if (ownerFK != null && ownerFK.getFullDBIdentifier().equals(cols[0].getFullDBIdentifier())) { found = true; break; } } if (!found) { continue; } FieldMetaData inverseFM = inverseSm.getMetaData().getField( inverseCol.getIdentifier().getName()); if (inverseFM == null) { continue; } int inverseValIndex = inverseFM.getIndex(); Class<?> inverseType = inverseSm.getMetaData().getField(inverseValIndex).getType(); int ownerIndex = ownerFM.getIndex(); Class<?> ownerType = ownerSm.getMetaData().getField(ownerIndex).getType(); if (inverseType == ownerType) { // $TODO: why not just set the incoming val? Object inverseVal = inverseSm.fetch(inverseValIndex); ownerSm.storeField(ownerIndex, inverseVal); } } } } {code} I'm not sure how correct this all is, and generally frown at the amount of for() loops here, but that's the best we can come up with with our limited knowledge of OpenJPA internals. > Filling inverse FK columns is broken for compound keys > ------------------------------------------------------ > > Key: OPENJPA-2929 > URL: https://issues.apache.org/jira/browse/OPENJPA-2929 > Project: OpenJPA > Issue Type: Bug > Components: jdbc > Affects Versions: 3.1.2, 3.2.2, 4.0.1 > Reporter: Pawel Veselov > Priority: Major > > Say you have entity A that has a reference to entity B. > Say entity B is identified by a compound key > {code:java} > @Entity > public class A { > @Id > private long id; > @JoinColumns({ > @JoinColumn(name = "b_id1", referencedColumnName = "id1"), > @JoinColumn(name = "b_id2", referencedColumnName = "id2") > }) > private B b; > @Column( > name = "b_id1", > insertable = false, > updatable = false > ) > private String bId2; > @Column( > name = "b_id2", > insertable = false, > updatable = false > ) > private String bId2; > } > @Entity @IdClass(ID2.class) > public class B { > @Id @Column("id1") > private String id1; > @Id @Column("id2") > private String id2; > } > public class ID2 { > private String id1; > private String id2; > public ID2() {} > public ID2(String id1, String id2) { > this.id1 = id1; > this.id2 = id2; > } > } > {code} > After calling {{a.setB(b)}}, and calling {{em.persist(a)}}, the values of > {{a.bId1}} and {{a.bId2}} are either not set, or set to the same value, > either from {{b.id1}} or from {{b.id2}}. > This is because {{RowImpl.setJoinRefColumn()}} sets values for all A’s > columns that point to B for every B primary key columns. -- This message was sent by Atlassian Jira (v8.20.10#820010)