@OneToMany @MapKeyColumn 关系的可写性映射假定在@Id 字段上

Writability mapping of @OneToMany @MapKeyColumn relationship assumed to be on @Id field

我有一个游戏实体,它通过 Map + @MapKeyColumn 引用两个分数,其中一个实体用于主队,一个用于客队:

我决定使用 Booleanis_home 来解决这个问题。我映射了 @MapKeyColumn 关系 而没有 insertable = false, updatable = false,使关于 is_home 列的注释可写。

由于每列可能只有一个可写 field/annotation,我不得不将 insertable = false, updatable = false 添加到 Score 实体上的 @Id 映射,字段 home,见下文。

游戏:

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

    @Id
    @Column
    private Integer id;

    @Basic(optional = false)
    @Column(name = "scheduled_tipoff")
    private LocalDateTime scheduledTipoff;

    @OneToMany(mappedBy = "game")
    @MapKeyColumn(name = "is_home")                    <- writable
    private Map<Boolean, Score> scores;

    ...
}

得分:

@Entity
@Table(name = "Scores")
@IdClass(ScoreId.class)
public class Score implements Serializable
{
    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "is_home", insertable = false, updatable = false)          <- not writable
    private Boolean home;

    @Basic
    @Column(name = "final_score")
    private Integer finalScore;

    @Id
    @ManyToOne(optional = false, fetch = FetchType.EAGER)
    @JoinColumn(name = "game_id")
    private Game game;

    ...
}

分数:

public class ScoreId implements Serializable
{
    private static final long serialVersionUID = 1L;

    private Integer game;

    private Boolean home;

    ...
}

这导致使用 EclipseLink 时出现异常:

异常

    at java.lang.Thread.run(Thread.java:748)
Caused by: javax.persistence.PersistenceException: Exception [EclipseLink-28013] (Eclipse Persistence Services - 2.7.7.v20200504-69f2c2b80d): org.eclipse.persistence.exceptions.EntityManagerSetupException
Exception Description: Unable to deploy PersistenceUnit [BBStatsPU] in invalid state [DeployFailed].
Internal Exception: javax.persistence.PersistenceException: 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 [Scores.is_home].
Descriptor: RelationalDescriptor(net.bbstats.entity.Score --> [DatabaseTable(Scores)])

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

    at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.deploy(EntityManagerSetupImpl.java:634)
    at org.eclipse.persistence.internal.jpa.EntityManagerFactoryDelegate.getAbstractSession(EntityManagerFactoryDelegate.java:222)
    at org.eclipse.persistence.internal.jpa.EntityManagerFactoryDelegate.createEntityManagerImpl(EntityManagerFactoryDelegate.java:330)
    at org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl.createEntityManagerImpl(EntityManagerFactoryImpl.java:350)
    at org.eclipse.persistence.internal.jpa.EntityManagerFactoryImpl.createEntityManager(EntityManagerFactoryImpl.java:313)
    at org.jboss.as.jpa.container.TransactionScopedEntityManager.createEntityManager(TransactionScopedEntityManager.java:187)
    at org.jboss.as.jpa.container.TransactionScopedEntityManager.getOrCreateTransactionScopedEntityManager(TransactionScopedEntityManager.java:157)
    at org.jboss.as.jpa.container.TransactionScopedEntityManager.getEntityManager(TransactionScopedEntityManager.java:87)
    at org.jboss.as.jpa.container.AbstractEntityManager.createNamedQuery(AbstractEntityManager.java:101)
    at net.bbstats.framework.service.Repository.findByNamedQuery(Repository.java:173)
    at net.bbstats.framework.service.BaseEntityService.findByNamedQuery(BaseEntityService.java:467)
    at net.bbstats.framework.service.BaseEntityService.findByNamedQuery(BaseEntityService.java:453)
    at net.bbstats.framework.service.BaseEntityService.findByNamedQuery(BaseEntityService.java:429)
    at net.bbstats.framework.service.BaseEntityService.findAllWithFetchGraph(BaseEntityService.java:385)
    ... 194 more
Caused by: Exception [EclipseLink-28013] (Eclipse Persistence Services - 2.7.7.v20200504-69f2c2b80d): org.eclipse.persistence.exceptions.EntityManagerSetupException
Exception Description: Unable to deploy PersistenceUnit [BBStatsPU] in invalid state [DeployFailed].
Internal Exception: javax.persistence.PersistenceException: 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 [Scores.is_home].
Descriptor: RelationalDescriptor(net.bbstats.entity.Score --> [DatabaseTable(Scores)])

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

    at org.eclipse.persistence.exceptions.EntityManagerSetupException.cannotDeployWithoutPredeploy(EntityManagerSetupException.java:193)
    ... 208 more
Caused by: javax.persistence.PersistenceException: 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 [Scores.is_home].
Descriptor: RelationalDescriptor(net.bbstats.entity.Score --> [DatabaseTable(Scores)])

问题

我知道 JPA 实现可能假设每个 @Id 字段自动可写,但我想知道:JPA 是否在任何地方指定了这个?

我目前不明白为什么要做出这种可写性假设。

如果没有指定,这是一个错误吗?

在将 cascade = CascadeType.ALL 添加到您的 @OneToMany 映射后,我能够成功地保留具有多个分数的 Game 实体:

@OneToMany(mappedBy = "game", cascade = CascadeType.ALL) // added cascading
@MapKeyColumn(name = "is_home")
private Map<Boolean, Score> scores;

示例:

Game game = new Game();
game.setId(4);
// ...
      
Score score1 = new Score();
score1.setGame(game);
score1.setHome(true);
score1.setFinalScore(25);
      
Score score2 = new Score();
score2.setGame(game);
score2.setHome(false);
score2.setFinalScore(26);
      
game.getScores().put(score1.getHome(), score1);
game.getScores().put(score2.getHome(), score2);
      
entityManager.persist(game);

生成了以下 sql:

23:27:50,396 DEBUG SQL:127 - 
/* insert com.my.entities.Game */
insert into TEST_SCHEMA.TST_GAMES (gm_id) values (?)
23:27:50,408 DEBUG SQL:127 - 
/* insert com.my.entities.Score */
insert into TEST_SCHEMA.TST_SCORES (final_score, is_home, game_id) 
values (?, ?, ?)

23:27:50,415 DEBUG SQL:127 - 
/* insert com.my.entities.Score */
insert into TEST_SCHEMA.TST_SCORES (final_score, is_home, game_id) 
values (?, ?, ?)

23:27:50,440 DEBUG SQL:127 - 
update TEST_SCHEMA.TST_SCORES  
set sc_is_home=? 
where sc_is_home=? and sc_game_id=?

23:27:50,443 DEBUG SQL:127 - 
update TEST_SCHEMA.TST_SCORES  
set sc_is_home=? 
where sc_is_home=? and sc_game_id=?

已使用 Hibernate 5.4 进行测试。10.Final。 (不添加上面提到的级联休眠也会生成异常)

您永远无法更改实体中的 ID 值,因此几乎暗示了 updatable=false;这样做是在创建一个新的身份,需要将其视为一个完全不同的实体。

错误消息是 pre-JPA 的延期。 EclipseLink 在其映射中将可插入与可更新联系在一起,以匹配其已有的 read-only 概念。 EclipseLink(以及 JPA 中实体的定义)要求,如果要插入一个实体,它有一个 ID 值,它可以在实体内控制 - 在这种情况下,您正试图让一些人设置 ID其他不符合实体含义的外部映射('game.scores' 关系)。这使它更像一个嵌入式对象。