Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Quarkus Hibernate: Entity not updated when changed value provided via method

I use Quarkus + Hibernate to sync data to the DB and I've noticed during testing that sometimes my entity isn't updated. I've created a minimal example adjusting the original example https://github.com/quarkusio/quarkus-quickstarts/tree/main/hibernate-orm-quickstart

Here are my adjustments:

import.sql

DROP TABLE IF EXISTS fruit CASCADE;
CREATE TABLE fruit (
    fruitsSequence INT PRIMARY KEY,
    name TEXT NOT NULL,
    test INT
);

Fruit.java

package org.acme.hibernate.orm;

import javax.persistence.Access;
import javax.persistence.AccessType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import javax.persistence.Transient;

import com.google.common.base.Objects;

@Entity
@Table(name = "known_fruits")
public class Fruit {

    @Id
    @SequenceGenerator(name = "fruitsSequence", sequenceName = "known_fruits_id_seq", allocationSize = 1, initialValue = 10)
    @GeneratedValue(generator = "fruitsSequence")
    private Integer id;

    @Transient
    private String name = "";

    @Column(name = "test")
    private Integer test = -1;

    public Fruit() {
    }

    public Fruit(String name) {
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Column(name = "name")
    @Access(AccessType.PROPERTY)
    public String getChangedName() {
        return "a" + name;
    }

    public String getName() {
        return name;
    }

    public void setTest(Integer test) {
        this.test = test;
    }

    public Integer getTest() {
        return test;
    }

    @Column(name = "name")
    @Access(AccessType.PROPERTY)
    protected void setChangedName(String name) {
        this.name = name.substring(1);
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(name, test);
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof Fruit) {
            Fruit other = (Fruit) o;
            return name.equals(other.name) && test.equals(other.test);
        }

        return false;
    }
}

Replaced the test withDBTest.java

package org.acme.hibernate.orm;

import static io.restassured.RestAssured.given;
import static org.junit.jupiter.api.Assertions.assertEquals;

import javax.inject.Inject;
import javax.persistence.EntityManager;
import javax.transaction.UserTransaction;

import org.junit.jupiter.api.Test;

import io.quarkus.test.junit.QuarkusTest;

@QuarkusTest
public class DBTest {

    @Inject
    EntityManager m_em;

    @Inject
    UserTransaction m_transaction;

    @Test
    void testUpdate() throws Exception {
        Fruit fruit = given().when().body("{\"name\" : \"Pear\"}").contentType("application/json").post("/fruits")
                .then().statusCode(201).extract().as(Fruit.class);

        m_transaction.begin();
        Fruit db = m_em.find(Fruit.class, fruit.getId());
        db.setName("Apple");
        db.setTest(13);
        m_transaction.commit();

        db = m_em.find(Fruit.class, fruit.getId());

        assertEquals(13, db.getTest(), "Unexpected test");
        assertEquals("Apple", db.getName(), "Unexpected name");
    }

    @Test
    void testUpdateLongName() throws Exception {
        Fruit fruit = given().when().body(
                "{\"name\" : \"PeeeeeeeeeeeeeeeeeeeeeeeaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaDBaaaaaaaaaaaaaar\"}")
                .contentType("application/json").post("/fruits").then().statusCode(201).extract().as(Fruit.class);

        m_transaction.begin();
        Fruit db = m_em.find(Fruit.class, fruit.getId());
        db.setName(fruit.getName() + "Apple");
        db.setTest(13);
        m_transaction.commit();

        db = m_em.find(Fruit.class, fruit.getId());

        assertEquals(13, db.getTest(), "Unexpected test");
        assertEquals(fruit.getName() + "Apple", db.getName(), "Unexpected name");
    }

    @Test
    void testUpdateNameOnly() throws Exception {
        Fruit fruit = given().when().body("{\"name\" : \"Pear\"}").contentType("application/json").post("/fruits")
                .then().statusCode(201).extract().as(Fruit.class);

        m_transaction.begin();
        Fruit db = m_em.find(Fruit.class, fruit.getId());
        db.setName("Apple");
        m_transaction.commit();

        db = m_em.find(Fruit.class, fruit.getId());

        assertEquals(-1, db.getTest(), "Unexpected test");
        assertEquals("Apple", db.getName(), "Unexpected name");
    }

    @Test
    void testUpdateNameOnlyREST() throws Exception {
        Fruit fruit = given().when().body("{\"name\" : \"Pear\"}").contentType("application/json").post("/fruits")
                .then().statusCode(201).extract().as(Fruit.class);

        given().when().body("{\"name\" : \"Apple\"}").contentType("application/json").put("/fruits/" + fruit.getId())
                .then().statusCode(200).extract().as(Fruit.class);

        Fruit db = m_em.find(Fruit.class, fruit.getId());

        assertEquals(-1, db.getTest(), "Unexpected test");
        assertEquals("Apple", db.getName(), "Unexpected name");
    }
}

What I see is that testUpdateNameOnly and testUpdateNameOnlyREST fail whereas the other tests run as expected. In my original testcase, even changing another field didn't update the TEXT field hence the test with the long name. The reason why the name is altered when writing it to the DB is to encrypt it (the outcome is a BASE64 string). I am not sure if this is a configuration issue or an actual bug.

Thanks for helps in advance!

like image 255
MoH Avatar asked Jan 01 '26 11:01

MoH


1 Answers

As suggested by the comments this seems to be a bug (https://github.com/quarkusio/quarkus/issues/16619). It works with the AttributeConver:

Fruit.java:

package org.acme.hibernate.orm;

import javax.persistence.Column;
import javax.persistence.Convert;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;

import com.google.common.base.Objects;

@Entity
@Table(name = "known_fruits")
public class Fruit {

    @Id
    @SequenceGenerator(name = "fruitsSequence", sequenceName = "known_fruits_id_seq", allocationSize = 1, initialValue = 10)
    @GeneratedValue(generator = "fruitsSequence")
    private Integer id;

    @Column(name = "name")
    @Convert(converter = NameConverter.class)
    private String name = "";

    @Column(name = "test")
    private Integer test = -1;

    public Fruit() {
    }

    public Fruit(String name) {
        this.name = name;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setTest(Integer test) {
        this.test = test;
    }

    public Integer getTest() {
        return test;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public int hashCode() {
        return Objects.hashCode(name, test);
    }

    @Override
    public boolean equals(Object o) {
        if (o instanceof Fruit) {
            Fruit other = (Fruit) o;
            return name.equals(other.name) && test.equals(other.test);
        }

        return false;
    }
}

NameConverter.java:

package org.acme.hibernate.orm;

import javax.persistence.AttributeConverter;
import javax.persistence.Converter;

@Converter
public class NameConverter implements AttributeConverter<String, String> {

    @Override
    public String convertToDatabaseColumn(String name) {
        return "a" + name;
    }

    @Override
    public String convertToEntityAttribute(String name) {
        return name.substring(1);
    }

}
like image 104
MoH Avatar answered Jan 04 '26 05:01

MoH



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!