开发者

EclipseLink: multi-column, one-to-one, JPA 2.0 @EmbeddedId using @MapsId fails with read-only on @JoinColumn (derived identities compared)

开发者 https://www.devze.com 2023-03-05 18:55 出处:网络
I have the following design: (source: kawoolutions.com) Simple logic: Foos uses a multi-column PK to PostAddresses using the same columns (same key).

I have the following design:

EclipseLink: multi-column, one-to-one, JPA 2.0 @EmbeddedId using @MapsId fails with read-only on @JoinColumn (derived identities compared)

(source: kawoolutions.com)

Simple logic: Foos uses a multi-column PK to PostAddresses using the same columns (same key).

Here are the JPA 2.0 @EmbeddedId mappings (using a nested ID class PostAddressId in FooId and the @MapsId annotation in Foo to map to it):

@Entity
@Table(name = "PostAddresses")
publi开发者_如何学Pythonc class PostAddress implements Serializable
{
    @EmbeddedId
    private PostAddressId embeddedId;

    ...
}

@Embeddable
public class PostAddressId implements Serializable
{
    @Column(name = "contact_id")
    private Integer contactId;

    @Column(name = "ordinal_nbr")
    private Integer ordinalNbr = 1;

    ...
}

@Entity
@Table(name = "Foos")
public class Foo implements Serializable
{
    @EmbeddedId
    private FooId embeddedId;

    @MapsId(value = "postAddressId")
    @OneToOne
    @JoinColumns(value = {
        @JoinColumn(name = "contact_id", referencedColumnName = "contact_id", insertable = false, updatable = false),
        @JoinColumn(name = "ordinal_nbr", referencedColumnName = "ordinal_nbr", insertable = false, updatable = false)
    })
    private PostAddress postAddress;

    ...
}

@Embeddable
public class FooId implements Serializable
{
    @Embedded
    private PostAddressId postAddressId;

    ...
}

Stack trace:

Exception [EclipseLink-46] (Eclipse Persistence Services - 2.3.0.v20110429-r9282): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: There should be one non-read-only mapping defined for the primary key field [Foos.ordinal_nbr].
Descriptor: RelationalDescriptor(tld.transmuc.model.Foo --> [DatabaseTable(Foos)])

Runtime Exceptions:
---------------------------------------------------------

    at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.initializeDescriptors(DatabaseSessionImpl.java:535)
    at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.initializeDescriptors(DatabaseSessionImpl.java:476)
    at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.initializeDescriptors(DatabaseSessionImpl.java:435)
    at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.postConnectDatasource(DatabaseSessionImpl.java:673)
    at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.loginAndDetectDatasource(DatabaseSessionImpl.java:618)
    at org.eclipse.persistence.internal.jpa.EntityManagerFactoryProvider.login(EntityManagerFactoryProvider.java:206)
    at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.deploy(EntityManagerSetupImpl.java:460)
    ... 4 more

To me the mappings are correct. I checked them several times. Note, using the @Embedded annotation in FooId or not makes no difference in EL.

Please pay attention to Foo.postAddress relationship's @JoinColumn annotations which both were defined with insertable = false, updatable = false to indicate that the embedded ID properties to be the writable ones.

Note, I'm not looking for a solution that works, I can get that simply be deleting the read-only insertable = false, updatable = false stuff. The question however is why the above mappings fail in EclipseLink. Hibernate has no problems with the code. Furthermore the following, functionally equivalent code - which works without problems in EL - using JPA 2.0 @IdClass derived identities looks like:

@Entity
@Table(name = "PostAddresses")
@IdClass(value = PostAddressId.class)
public class PostAddress implements Serializable
{
    @Id
    @Column(name = "contact_id")
    private Integer contactId;

    @Id
    @Column(name = "ordinal_nbr")
    private Integer ordinalNbr = 1;

    ...
}

public class PostAddressId implements Serializable
{
    private Integer contactId;

    private Integer ordinalNbr = 1;

    ...
}

@Entity
@Table(name = "Foos")
@IdClass(value = FooId.class)
public class Foo implements Serializable
{
    @Id
    @OneToOne
    @JoinColumns(value = {
        @JoinColumn(name = "contact_id", referencedColumnName = "contact_id", insertable = false, updatable = false),
        @JoinColumn(name = "ordinal_nbr", referencedColumnName = "ordinal_nbr", insertable = false, updatable = false)
    })
    private PostAddress postAddress;

    ...
}

public class FooId implements Serializable
{
    private PostAddressId postAddress;

    ...
}

As you can see, Foo.postAddress also houses the read-only insertable = false, updatable = false attributes. Foo.postAddress gets mapped to FooId.postAddress by naming them the same. FooId then "points" to the writable PostAddressId class, which is no different from the @EmbeddedId logic above. At least the logic is no different to me. The only difference would be, because I'm using @EmbeddedId, that the writable @Column annotations end up in the @Embeddable class, which is valid. (JPA only allows simple annotations in @Embeddable classes @Embedded, @Basic, @Column, @Temporal, @Enum, and @Binary AFAIK.)

Is there anything I'm missing? What is it the JPA 2.0 spec says here?

Or is this a bug in EclipseLink?

Final note: this question is obviously similar to JPA (Hibernate, EclipseLink) mapping: why doesn't this code work (chain of 2 relationships using JPA 2.0, @EmbeddedId composite PK-FK)? but not the same. The linked question's EL has been fixed in the meantime and I am using that fixed version of EL, so the issues aren't the same. This one is solely about the read-only attributes on @JoinColumn in JPA 2.0 @EmbeddedId using nested ID classes + @MapsId in EclipseLink.


EclipseLink interprets mapsId to mean the relationship controls the contact_id and ordinal_nbr columns defined in your Foos table. Because of this, when you mark the relationship fields as read-only, it means there are no writable mappings for these fields - mapsId tells the provider that the values need to be used from the relationship, yet the relationship is read only.

Is there any reason why you are using mapsId instead of just marking your relationship as the ID and then using a pk class?

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号