嵌入式 ID 映射失败并出现 DescriptorException:应该为主键字段定义一个非只读映射 (EclipseLink)

Embedded ID mapping fails with DescriptorException: There should be one non-read-only mapping defined for the primary key field (EclipseLink)

我有以下非常简单的设计:

一个俱乐部有多个团队,每个团队都有一个团队类型代码(性别 - 年龄组代码)加上一个序号(团队),例如20 岁以上的男性将是“MO20”,序号代表该年龄组的第 1、2 等队。这是Teams的PK。你明白了。

俱乐部映射:

@Entity
@Table(name = "Clubs")
public class Club implements Serializable
{
    private static final long serialVersionUID = 1L;

    @Id
    @Column
    private Integer id;

    @Basic(optional = false)
    @Column
    private String name;

    @Basic(optional = false)
    @Column
    private String code;

    @OneToMany(mappedBy = "club")
    private List<Team> teams;

    ...
}

团队映射:

@Entity
@Table(name = "Teams")
public class Team implements Serializable
{
    private static final long serialVersionUID = 1L;

    @EmbeddedId
    private TeamPk embeddedId;

    @MapsId("clubId")
    @ManyToOne(optional = false, fetch = FetchType.EAGER)
    @JoinColumn(name = "club_id", insertable = false, updatable = false)
    private Club club;

    ...
}

@Embeddable
public class TeamPk implements Serializable
{
    private static final long serialVersionUID = 1L;

    @Column(name = "club_id")
    private Integer clubId;

    @Column(name = "team_type_code")
    private String teamTypeCode;

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

    ...
}

异常:

Caused by: Exception [EclipseLink-28019] (Eclipse Persistence Services - 2.7.7.v20200504-69f2c2b80d): org.eclipse.persistence.exceptions.EntityManagerSetupException
Exception Description: Deployment of PersistenceUnit [BBStatsPU] failed. Close all factories for this PersistenceUnit.
Internal Exception: Exception [EclipseLink-0] (Eclipse Persistence Services - 2.7.7.v20200504-69f2c2b80d): org.eclipse.persistence.exceptions.IntegrityException
Descriptor Exceptions: 
---------------------------------------------------------

Exception [EclipseLink-46] (Eclipse Persistence Services - 2.7.7.v20200504-69f2c2b80d): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: There should be one non-read-only mapping defined for the primary key field [Teams.club_id].
Descriptor: RelationalDescriptor(net.bbstatstest.i265.entity.Team --> [DatabaseTable(Teams)])

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

    at org.eclipse.persistence.exceptions.EntityManagerSetupException.deployFailed(EntityManagerSetupException.java:241)
    ... 182 more
Caused by: Exception [EclipseLink-0] (Eclipse Persistence Services - 2.7.7.v20200504-69f2c2b80d): org.eclipse.persistence.exceptions.IntegrityException
Descriptor Exceptions: 
---------------------------------------------------------

Exception [EclipseLink-46] (Eclipse Persistence Services - 2.7.7.v20200504-69f2c2b80d): org.eclipse.persistence.exceptions.DescriptorException
Exception Description: There should be one non-read-only mapping defined for the primary key field [Teams.club_id].
Descriptor: RelationalDescriptor(net.bbstatstest.i265.entity.Team --> [DatabaseTable(Teams)])

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

    at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.initializeDescriptors(DatabaseSessionImpl.java:762)
    at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.initializeDescriptors(DatabaseSessionImpl.java:698)
    at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.initializeDescriptors(DatabaseSessionImpl.java:629)
    at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.postConnectDatasource(DatabaseSessionImpl.java:868)
    at org.eclipse.persistence.internal.sessions.DatabaseSessionImpl.loginAndDetectDatasource(DatabaseSessionImpl.java:811)
    at org.eclipse.persistence.internal.jpa.EntityManagerFactoryProvider.login(EntityManagerFactoryProvider.java:256)
    at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.deploy(EntityManagerSetupImpl.java:772)
    ... 180 more

EclipseLink 抱怨 Teams.club_id:

的列映射
There should be one non-read-only mapping defined for the primary key field [Teams.club_id].

但是为什么呢?

嵌入式 ID 字段或更确切地说 ID class 在 clubId 字段上有 @Column 而没有 insertable = true, updatable = true,因此它默认为可写,至少这就是文档所说的:https://javaee.github.io/javaee-spec/javadocs/javax/persistence/Column.html#insertable--

因此所有其他 club_id 映射必须是 insertable = true, updatable = true 才能只读,就像 Team.club 关系一样。

问题:

这是怎么回事?

请注意,当在 Team.clubTeamPk.clubId 之间切换 insertable = ..., updatable = ... 时,整个事情都有效。

为什么 EclipseLink 卡在这里?在我看来像是一个错误。

PS:我正在使用 EclipseLink 2.7.7。 (hibernate 标签只是为了吸引更多关注而添加的)

通过将 @MapsId("clubId") 放在关联上,您是在说'clubId 提供自己的映射。相反,这里 属性 上的映射应该用来映射 clubId 属性'.

这意味着当持久化实体时,Eclipselink 将使用 Team.club 的 id 来填充 club_id 连接列,然后,它将使用该连接列的值来填充Java 属性 Team.embeddedId.clubId,只要实体是 fetched/refreshed。 您在 Team.embeddedId.clubId 上放置的任何映射都将被忽略

错误来自于 Eclipselink 没有考虑 club_id 被映射两次。相反,仅考虑 Team.club 上的映射。作为主键 属性 的唯一映射,它可能不是 insertable = false, updatable = false.

解决方法很简单:

  1. 如果您希望Team.club控制club_id列的值,删除insertable = false, updatable = false
  2. 反之,如果你想让Team.embeddedId.clubId控制club_id,那就干脆放弃@MapsId