datanucleus-core icon indicating copy to clipboard operation
datanucleus-core copied to clipboard

Failed to load entities regarding objectid-class

Open onacit opened this issue 1 year ago • 4 comments

Environment

Running `/Users/onacit/gitcl/github.com/jinahya/mysql-emploees-persistence/mvnw`...
Apache Maven 3.9.6 (bc0240f3c744dd6b6ec2920b3cd08dcc295161ae)
Maven home: /Users/onacit/.m2/wrapper/dists/apache-maven-3.9.6-bin/3311e1d4/apache-maven-3.9.6
Java version: 21.0.2, vendor: IBM Corporation, runtime: /Library/Java/JavaVirtualMachines/ibm-semeru-open-21.jdk/Contents/Home
Default locale: en_US, platform encoding: UTF-8
OS name: "mac os x", version: "14.3.1", arch: "aarch64", family: "mac"

Dependencies

      <dependency>
        <groupId>org.datanucleus</groupId>
        <artifactId>datanucleus-accessplatform-jakarta-rdbms</artifactId>
        <version>6.0.7</version>
        <type>pom</type>
      </dependency>

Description

With following classs,

@MappedSuperclass
abstract class BaseEntity<ID extends Serializable> implements Serializable {

    @Serial
    private static final long serialVersionUID = -3812806986886347446L;
}


@Setter
@Getter
@ToString(callSuper = true)
@NoArgsConstructor//(access = AccessLevel.PROTECTED)
public class SalaryId implements BaseId {

    @Serial
    private static final long serialVersionUID = -378954798191441067L;

    @Override
    public final boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof SalaryId that)) {
            return false;
        }
        return Objects.equals(empNo, that.empNo) &&
                Objects.equals(fromDate, that.fromDate);
    }

    @Override
    public final int hashCode() {
        return Objects.hash(empNo, fromDate);
    }

    @NotNull
    private Integer empNo;

    @NotNull
    private LocalDate fromDate;
}


@IdClass(SalaryId.class)
@Entity
@Table(name = Salary.TABLE_NAME)
@Setter
@Getter
@ToString(callSuper = true)
@NoArgsConstructor
@SuppressWarnings({
        "java:S1700" // ... salary;
})
public class Salary extends BaseEntity<Integer> {

    @Serial
    private static final long serialVersionUID = 604718367871825963L;

    // -----------------------------------------------------------------------------------------------------------------
    public static final String TABLE_NAME = "salaries";

    // -----------------------------------------------------------------------------------------------------------------
    public static final String COLUMN_NAME_EMP_NO = Employee.COLUMN_NAME_EMP_NO;

    // ---------------------------------------------------------------------------------------------------------- salary
    public static final String COLUMN_NAME_SALARY = "salary";

    // ------------------------------------------------------------------------------------------------------- from_date
    public static final String COLUMN_NAME_FROM_DATE = "from_date";

    // --------------------------------------------------------------------------------------------------------- to_date
    public static final String COLUMN_NAME_TO_DATE = "to_date";

    // ------------------------------------------------------------------------------------------------ java.lang.Object

    @Override
    public boolean equals(final Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof Salary that)) {
            return false;
        }
        return Objects.equals(empNo, that.empNo) &&
                Objects.equals(fromDate, that.fromDate);
    }

    @Override
    public int hashCode() {
        return Objects.hash(empNo, fromDate);
    }

    // ------------------------------------------------------------------------------------------------- Bean-Validation
    //@AssertTrue
    private boolean isFromDateLessThanOrEqualToToDate() {
        if (fromDate == null || toDate == null) {
            return true;
        }
        return !fromDate.isAfter(toDate);
    }

    // -------------------------------------------------------------------------------------------------- empNo/employee
    public Employee getEmployee() {
        return employee;
    }

    public void setEmployee(final Employee employee) {
        this.employee = employee;
        setEmpNo(
                Optional.ofNullable(this.employee)
                        .map(Employee::getEmpNo)
                        .orElse(null)
        );
    }

    // ---------------------------------------------------------------------------------------------------------- salary

    // -------------------------------------------------------------------------------------------------------- fromDate

    // ---------------------------------------------------------------------------------------------------------- toDate

    // -----------------------------------------------------------------------------------------------------------------
    @NotNull
    @Id
    @Column(name = COLUMN_NAME_EMP_NO, nullable = false, insertable = true, updatable = false)
    private Integer empNo;

    @Valid
    @ManyToOne(optional = false, fetch = FetchType.LAZY)
    @JoinColumn(name = COLUMN_NAME_EMP_NO, nullable = false, insertable = false, updatable = false)
    @EqualsAndHashCode.Exclude
    @ToString.Exclude
    private Employee employee;

    // -----------------------------------------------------------------------------------------------------------------
    //@Positive
    @NotNull
    @Basic(optional = false)
    @Column(name = COLUMN_NAME_SALARY, nullable = false, insertable = true, updatable = true)
    private Integer salary;

    // -----------------------------------------------------------------------------------------------------------------
    @NotNull
    @Id
    @Basic(optional = false)
    @Column(name = COLUMN_NAME_FROM_DATE, nullable = false, insertable = true, updatable = true)
    private LocalDate fromDate;

    @NotNull
    @Basic(optional = false)
    @Column(name = COLUMN_NAME_TO_DATE, nullable = false, insertable = true, updatable = true)
    private LocalDate toDate;
}

Failed to load the persistence unit.

Caused by: org.datanucleus.metadata.InvalidClassMetaDataException: Class "....Salary" has the persistence-capable-superclass ....BaseEntity, but defines a different objectid-class than its superclass. No objectid-class should be specified because the superclasses id is always used.
	at org.datanucleus.metadata.AbstractClassMetaData.inheritIdentity(AbstractClassMetaData.java:782)
	at org.datanucleus.metadata.ClassMetaData.populate(ClassMetaData.java:180)
	at org.datanucleus.metadata.MetaDataManagerImpl$1.run(MetaDataManagerImpl.java:2822)
	at java.base/java.security.AccessController.doPrivileged(AccessController.java:319)
	at org.datanucleus.metadata.MetaDataManagerImpl.populateAbstractClassMetaData(MetaDataManagerImpl.java:2816)
	at org.datanucleus.metadata.MetaDataManagerImpl.populateFileMetaData(MetaDataManagerImpl.java:2617)
	at org.datanucleus.metadata.MetaDataManagerImpl.initialiseFileMetaDataForUse(MetaDataManagerImpl.java:1345)
	at org.datanucleus.metadata.MetaDataManagerImpl.loadPersistenceUnit(MetaDataManagerImpl.java:1124)
	at org.datanucleus.api.jakarta.JakartaEntityManagerFactory.initialiseNucleusContext(JakartaEntityManagerFactory.java:919)
	at org.datanucleus.api.jakarta.JakartaEntityManagerFactory.initialise(JakartaEntityManagerFactory.java:513)
	at org.datanucleus.api.jakarta.JakartaEntityManagerFactory.<init>(JakartaEntityManagerFactory.java:417)
	at org.datanucleus.api.jakarta.PersistenceProviderImpl.createEntityManagerFactory(PersistenceProviderImpl.java:103)
	at jakarta.persistence.Persistence.createEntityManagerFactory(Persistence.java:90)
	at jakarta.persistence.Persistence.createEntityManagerFactory(Persistence.java:66)

onacit avatar Mar 01 '24 05:03 onacit

As a guess, by not specifying an IdClass for the base class it probably defaults to using a simple 'long' for the OID and it expects that to be used for all subclasses but in your subclass Salary you have specified:

@IdClass(SalaryId.class)

which clashes with the default set for the base class - just a theory ;)

I've always used a separate meta data file (.jdo) to avoid spraying persistence annotations throughout the model classes' source code so I'm not familiar with some of those annotation but just guessing from their names.

chrisco484 avatar Mar 01 '24 07:03 chrisco484

@chrisco484 Thank you for you comment, sir. Yes the fault is mine. I should've specify the right id type.

class Salary extends BaseEntity<SalaryId>

onacit avatar Mar 01 '24 08:03 onacit

I still seeing

Class "....Salary" has the persistence-capable-superclass ...._BaseEntity, 
but defines a different objectid-class than its superclass. 
No objectid-class should be specified because the superclasses id is always used.
@MappedSuperclass
public abstract class _BaseEntity<ID extends Serializable> implements Serializable {
}

interface _BaseId extends Serializable {
}

@Setter
@Getter
@ToString(callSuper = true)
@NoArgsConstructor//(access = AccessLevel.PROTECTED)
public class SalaryId implements _BaseId {

    @NotNull
    private Integer empNo;

    @NotNull
    private LocalDate fromDate;
}

@IdClass(SalaryId.class)
@Entity
@Table(name = Salary.TABLE_NAME)
@Setter
@Getter
@ToString(callSuper = true)
@NoArgsConstructor
@SuppressWarnings({
        "java:S1700" // ... salary;
})
public class Salary extends _BaseEntity<SalaryId> {

    @NotNull
    @Id
    @Column(name = COLUMN_NAME_EMP_NO, nullable = false, insertable = true, updatable = false)
    private Integer empNo;

    @NotNull
    @Id
    @Basic(optional = false)
    @Column(name = COLUMN_NAME_FROM_DATE, nullable = false, insertable = true, updatable = true)
    private LocalDate fromDate;
}

onacit avatar Mar 04 '24 02:03 onacit

I may be not sure what I'm talking about... but,

    // AbstractClassMetaData.java
    ...
                if (superObjectIdClass == null || !objectidClass.equals(superObjectIdClass))
                {
                    throw new InvalidClassMetaDataException("044085", fullName, persistableSuperclass);
                }
    ...

Shouldn't it be...

                if (superObjectIdClass != null && !objectidClass.equals(superObjectIdClass))
                {
                    throw new InvalidClassMetaDataException("044085", fullName, persistableSuperclass);
                }

onacit avatar Mar 08 '24 13:03 onacit

Impossible to comment on something that can't be easily seen. Kindly follow https://www.datanucleus.org/documentation/problem_reporting.html#jpa

andyjefferson avatar Apr 01 '24 08:04 andyjefferson

I may be not sure what I'm talking about... but,

    // AbstractClassMetaData.java
    ...
                if (superObjectIdClass == null || !objectidClass.equals(superObjectIdClass))
                {
                    throw new InvalidClassMetaDataException("044085", fullName, persistableSuperclass);
                }
    ...

Shouldn't it be...

                if (superObjectIdClass != null && !objectidClass.equals(superObjectIdClass))
                {
                    throw new InvalidClassMetaDataException("044085", fullName, persistableSuperclass);
                }

That looks ok, the intent is to throw the exception if superObjectIdClass is not equal to objectidClass which it won't be if null (hence the first == null is correct).

chrisco484 avatar Apr 01 '24 17:04 chrisco484