ebean icon indicating copy to clipboard operation
ebean copied to clipboard

NullPointerException thrown when updating an entity with multiple primary keys

Open lakxtxue opened this issue 2 years ago • 3 comments

This exception occurs in both 12.16.1 and the latest 13.25.1 versions, I have not tried earlier versions.

Expected behavior

Expect the value of the col column to be updated to "success"

Actual behavior

java.lang.NullPointerException: Cannot invoke "io.ebean.bean.EntityBean._ebean_getField(int)" because "bean" is null

Steps to reproduce

Define the table under postgresql and add test records (the same problem occurs when testing under mysql)


DROP TABLE IF EXISTS "public"."test_multi_pk_update";
CREATE TABLE "public"."test_multi_pk_update" (
  "p1" varchar(32) COLLATE "pg_catalog"."default" NOT NULL,
  "p2" varchar(32) COLLATE "pg_catalog"."default" NOT NULL,
  "col" varchar(64) COLLATE "pg_catalog"."default"
);

INSERT INTO "public"."test_multi_pk_update" VALUES ('p1', 'p2', 'column_content');


ALTER TABLE "public"."test_multi_pk_update" ADD CONSTRAINT "test_id_class_pkey" PRIMARY KEY ("p1", "p2");

Define java beans

@Embeddable
public class TestMultiPkUpdateId {
    @AttributeOverride(name = "p1", column = @Column(name = "p1"))
    @Column(name = "p1")
    private String p1;

    @AttributeOverride(name = "p2", column = @Column(name = "p2"))
    @Column(name = "p2")
    private String p2;

    public String getP1() {
        return this.p1;
    }

    public void setP1(String p1) {
        this.p1 = p1;
    }

    public String getP2() {
        return this.p2;
    }

    public void setP2(String p2) {
        this.p2 = p2;
    }

    @Override
    public int hashCode() {
        int result = 17 * 31;
        result = 31 * result + Objects.hashCode(p1);
        result = 31 * result + Objects.hashCode(p2);
        return result;
    }

    @Override
    public boolean equals(Object that) {
        if (this == that) {
            return true;
        }
        if (!(that instanceof TestMultiPkUpdateId other)) {
            return false;
        }
        if (!Objects.equals(p1, other.p1)) return false;
        return Objects.equals(p2, other.p2);
    }
}
@Entity(name = "TestMultiPkUpdate")
@Table(name = "test_multi_pk_update")
@IdClass(TestMultiPkUpdateId.class)
public class TestMultiPkUpdate {
    @Id
    @Column(name = "p1")
    private String p1;

    @Id
    @Column(name = "p2")
    private String p2;

    @Column(name = "col", length = 64, nullable = true)
    private String col;

    public String getP1() {
        return this.p1;
    }

    public void setP1(String p1) {
        this.p1 = p1;
    }

    public String getP2() {
        return this.p2;
    }

    public void setP2(String p2) {
        this.p2 = p2;
    }

    public String getCol() {
        return this.col;
    }

    public void setCol(String col) {
        this.col = col;
    }
}

Execute the following code to reproduce the exception:

TestMultiPkUpdate testMultiPk = new TestMultiPkUpdate();
        testMultiPk.setP1("p1");
        testMultiPk.setP2("p2");
        testMultiPk.setCol("success");
        // Expect the value of the col column to be updated to "success". And the actual exception is thrown:
        // java.lang.NullPointerException: Cannot invoke "io.ebean.bean.EntityBean._ebean_getField(int)" because "bean" is null
        DB.update(testMultiPk);

  Caused by: java.lang.NullPointerException: Cannot invoke "io.ebean.bean.EntityBean._ebean_getField(int)" because "bean" is null
	at io.ebeaninternal.server.properties.EnhanceBeanPropertyAccess$Getter.get(EnhanceBeanPropertyAccess.java:57)
	at io.ebeaninternal.server.deploy.BeanProperty.getValue(BeanProperty.java:768)

lakxtxue avatar Nov 23 '23 03:11 lakxtxue

Hi. I just want to say that this is an easily reproducible exception, and it is very common to use multiple columns as primary keys, I look forward to this exception being resolved. Or am I using it incorrectly? Please guide me past this blocker, thank you very much

lakxtxue avatar Jan 16 '24 15:01 lakxtxue

Can you change your code to use embeddedId as composite PK? See https://github.com/ebean-orm/ebean/blob/master/ebean-test/src/test/java/org/tests/model/composite/ROrder.java for an example

rPraml avatar Jan 16 '24 20:01 rPraml

Can you change your code to use embeddedId as composite PK? See https://github.com/ebean-orm/ebean/blob/master/ebean-test/src/test/java/org/tests/model/composite/ROrder.java for an example

Thanks for the guidance, I did successfully get the record updated using @EmbeddedId. I want to confirm that using @IdClass is not supported? Or is this a problem? Because I do tend to use @IdClass to map concatenated primary keys.

I noticed that ebean docs mentioned using @IdClass to map concatenated primary keys: https://ebean.io/docs/interesting/embedded-id

Well I saw this location where there are unit tests using @IdClass: https://github.com/ebean-orm/ebean/blob/master/tests/test-java16/src/test/java/org/example/records/RecordIdClassTest.java

lakxtxue avatar Jan 17 '24 02:01 lakxtxue