[ 
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)

Reply via email to