I have the following simple design:

enter image description here

A player stat is a per-player stat that holds state for the whole game for a player, e.g. the jersey number.

It references a team member, which is identified by a player in a specific roster (basically, a player can be part of multiple rosters, e.g. players playing for multiple teams in a season).

A player stat is tied to the score of a game, either the home or the away score. It's basically a join table with additional properties/columns. You get the idea.

So both tables being referenced by the PlayerStats table have a composite PK. I mapped the PlayerStat entity using derived identifiers, see below.

Score entity + ID class (nothing special):

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

    @Id
    @Column(name = "game_id")
    private Integer gameId;

    @Id
    @Column(name = "is_home")
    private Boolean home;

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

    @OneToMany(mappedBy = "score")
    private List<PlayerStat> playerStats;

    ...
}

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

    private Integer gameId;

    private Boolean home;

    ...
}

Team member entity + ID class (nothing special):

@Entity
@Table(name = "TeamMembers")
@IdClass(TeamMemberId.class)
public class TeamMember implements Serializable
{
    private static final long serialVersionUID = 1L;

    @Id
    @Column(name = "player_id")
    private Integer playerId;

    @Id
    @Column(name = "roster_id")
    private Integer rosterId;

    @OneToMany(mappedBy = "teamMember")
    private List<PlayerStat> playerStats;

    ...    
}

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

    private Integer playerId;

    private Integer rosterId;
    
    ...
}

Player stat entity + ID class (derived identifiers):

@Entity
@Table(name = "PlayerStats")
@IdClass(PlayerStatId.class)
public class PlayerStat implements Serializable
{
    private static final long serialVersionUID = 1L;

    @Basic(optional = false)
    @Column(name = "jersey_nbr")
    private Integer jerseyNbr;

    @Id
    @ManyToOne(optional = false, fetch = FetchType.EAGER)
    @JoinColumn(name = "game_id", referencedColumnName = "game_id")
    @JoinColumn(name = "is_home", referencedColumnName = "is_home")
    private Score score;

    @Id
    @ManyToOne(optional = false, fetch = FetchType.EAGER)
    @JoinColumn(name = "player_id", referencedColumnName = "player_id")
    @JoinColumn(name = "roster_id", referencedColumnName = "roster_id")
    private TeamMember teamMember;
    
    ...
}

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

    private ScoreId score;

    private TeamMemberId teamMember;

    public PlayerStatId()
    {
    }

    public ScoreId getScoreId()
    {
        return score;
    }

    public void setScoreId(ScoreId scoreId)
    {
        this.score = scoreId;
    }

    public TeamMemberId getTeamMemberId()
    {
        return teamMember;
    }

    public void setTeamMemberId(TeamMemberId teamMemberId)
    {
        this.teamMember = teamMemberId;
    }
    
    ...
}

Exception:

11:44:27,315 ERROR [org.jboss.msc.service.fail] (ServerService Thread Pool -- 78) MSC000001: Failed to start service jboss.persistenceunit."bbstatstest-1.0.war#BBStatsPU": org.jboss.msc.service.StartException in service jboss.persistenceunit."bbstatstest-1.0.war#BBStatsPU": javax.persistence.PersistenceException: Exception [EclipseLink-28018] (Eclipse Persistence Services - 2.7.7.v20200504-69f2c2b80d): org.eclipse.persistence.exceptions.EntityManagerSetupException
Exception Description: Predeployment of PersistenceUnit [BBStatsPU] failed.
Internal Exception: Exception [EclipseLink-7150] (Eclipse Persistence Services - 2.7.7.v20200504-69f2c2b80d): org.eclipse.persistence.exceptions.ValidationException
Exception Description: Invalid composite primary key specification. The names of the primary key fields or properties in the primary key class [net.bbstats.entity.PlayerStatId] and those of the entity bean class [class net.bbstats.entity.PlayerStat] must correspond and their types must be the same. Also, ensure that you have specified ID elements for the corresponding attributes in XML and/or an @Id on the corresponding fields or properties of the entity class.
    at org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1$1.run(PersistenceUnitServiceImpl.java:198)
    at org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1$1.run(PersistenceUnitServiceImpl.java:128)
    at org.wildfly.security.manager.WildFlySecurityManager.doChecked(WildFlySecurityManager.java:658)
    at org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1.run(PersistenceUnitServiceImpl.java:212)
    at org.jboss.threads.ContextClassLoaderSavingRunnable.run(ContextClassLoaderSavingRunnable.java:35)
    at org.jboss.threads.EnhancedQueueExecutor.safeRun(EnhancedQueueExecutor.java:1982)
    at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.doRunTask(EnhancedQueueExecutor.java:1486)
    at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1348)
    at java.lang.Thread.run(Thread.java:748)
    at org.jboss.threads.JBossThread.run(JBossThread.java:485)
Caused by: javax.persistence.PersistenceException: Exception [EclipseLink-28018] (Eclipse Persistence Services - 2.7.7.v20200504-69f2c2b80d): org.eclipse.persistence.exceptions.EntityManagerSetupException
Exception Description: Predeployment of PersistenceUnit [BBStatsPU] failed.
Internal Exception: Exception [EclipseLink-7150] (Eclipse Persistence Services - 2.7.7.v20200504-69f2c2b80d): org.eclipse.persistence.exceptions.ValidationException
Exception Description: Invalid composite primary key specification. The names of the primary key fields or properties in the primary key class [net.bbstats.entity.PlayerStatId] and those of the entity bean class [class net.bbstats.entity.PlayerStat] must correspond and their types must be the same. Also, ensure that you have specified ID elements for the corresponding attributes in XML and/or an @Id on the corresponding fields or properties of the entity class.
    at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.createPredeployFailedPersistenceException(EntityManagerSetupImpl.java:2109)
    at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.predeploy(EntityManagerSetupImpl.java:2085)
    at org.eclipse.persistence.jpa.PersistenceProvider.createContainerEntityManagerFactoryImpl(PersistenceProvider.java:350)
    at org.eclipse.persistence.jpa.PersistenceProvider.createContainerEntityManagerFactory(PersistenceProvider.java:316)
    at org.jboss.as.jpa.service.PersistenceUnitServiceImpl.createContainerEntityManagerFactory(PersistenceUnitServiceImpl.java:365)
    at org.jboss.as.jpa.service.PersistenceUnitServiceImpl.access$1300(PersistenceUnitServiceImpl.java:71)
    at org.jboss.as.jpa.service.PersistenceUnitServiceImpl$1$1.run(PersistenceUnitServiceImpl.java:190)
    ... 9 more
Caused by: Exception [EclipseLink-28018] (Eclipse Persistence Services - 2.7.7.v20200504-69f2c2b80d): org.eclipse.persistence.exceptions.EntityManagerSetupException
Exception Description: Predeployment of PersistenceUnit [BBStatsPU] failed.
Internal Exception: Exception [EclipseLink-7150] (Eclipse Persistence Services - 2.7.7.v20200504-69f2c2b80d): org.eclipse.persistence.exceptions.ValidationException
Exception Description: Invalid composite primary key specification. The names of the primary key fields or properties in the primary key class [net.bbstats.entity.PlayerStatId] and those of the entity bean class [class net.bbstats.entity.PlayerStat] must correspond and their types must be the same. Also, ensure that you have specified ID elements for the corresponding attributes in XML and/or an @Id on the corresponding fields or properties of the entity class.
    at org.eclipse.persistence.exceptions.EntityManagerSetupException.predeployFailed(EntityManagerSetupException.java:233)
    ... 16 more
Caused by: Exception [EclipseLink-7150] (Eclipse Persistence Services - 2.7.7.v20200504-69f2c2b80d): org.eclipse.persistence.exceptions.ValidationException
Exception Description: Invalid composite primary key specification. The names of the primary key fields or properties in the primary key class [net.bbstats.entity.PlayerStatId] and those of the entity bean class [class net.bbstats.entity.PlayerStat] must correspond and their types must be the same. Also, ensure that you have specified ID elements for the corresponding attributes in XML and/or an @Id on the corresponding fields or properties of the entity class.
    at org.eclipse.persistence.exceptions.ValidationException.invalidCompositePKSpecification(ValidationException.java:1198)
    at org.eclipse.persistence.internal.jpa.metadata.accessors.classes.EntityAccessor.validatePrimaryKey(EntityAccessor.java:1531)
    at org.eclipse.persistence.internal.jpa.metadata.accessors.classes.EntityAccessor.processMappingAccessors(EntityAccessor.java:1251)
    at org.eclipse.persistence.internal.jpa.metadata.accessors.classes.EntityAccessor.process(EntityAccessor.java:701)
    at org.eclipse.persistence.internal.jpa.metadata.MetadataProject.processStage2(MetadataProject.java:1879)
    at org.eclipse.persistence.internal.jpa.metadata.MetadataProcessor.processORMMetadata(MetadataProcessor.java:580)
    at org.eclipse.persistence.internal.jpa.deployment.PersistenceUnitProcessor.processORMetadata(PersistenceUnitProcessor.java:629)
    at org.eclipse.persistence.internal.jpa.EntityManagerSetupImpl.predeploy(EntityManagerSetupImpl.java:2006)
    ... 14 more

QUESTION:

What's wrong with the mappings?

The PlayerStat entity has two relationships score and teamMember and the respective @IdClass called PlayerStatId has two nested composite PK class references called score and teamMember as required and their types are the respective PK classes ScoreId and TeamMemberId.

I'm using the latest EclipseLink 2.7.7 in a WildFly 20 env.

Could this be a bug of some kind?

I looked at several sources describing derived identifiers, but I don't see anything inherently wrong with the mappings.

2

There are 2 best solutions below

0
OrangeDog On

I don't think you can do composite ids when the parts of the composite id are also composite ids. The types of the attributes also need to match: Integer gameId matches, but ScoreId score is not the same as Score score.

You might be able to do it by unnesting the id classes and fiddling about with JoinColumn definitions, or you'll have to bite the bullet and give your entities artificial primary keys.

0
Kawu On

The mappings are absolutely correct and working. I had another test app still deployed on the server with the same package/class names, so the errors came from the other app. Sorry for the confusion. Question could be deleted entirely.