7

I'm doing some tests with Quarkus and PanacheRepository and I'm getting trouble in update an entity data. The update doesn't work, the field values are not updated.

In short: I create an entity and persist the data, after that in another request I get the entity from database using repository.findById(id);, change some field value, but the new value is not persisted in the database. I tried call repository.persist(person); after but the behavior is the same, the data is not updated.

I tried this with Quarkus version 1.9.0.Final, 1.9.0.CR1, 1.8.3.Final

I'm using postgreSQL 12. I also tried with mysql 5.7.26

I use Eclipse 2020-06 (4.16.0) only to write code and I run the application in the command line, with: ./mvnw compile quarkus:dev

I've created a brand new simple application and the behavior is the same. Here is the main configurations and some code snippets:

pom.xml

    <properties>
        <compiler-plugin.version>3.8.1</compiler-plugin.version>
        <maven.compiler.parameters>true</maven.compiler.parameters>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <quarkus-plugin.version>1.9.0.Final</quarkus-plugin.version>
        <quarkus.platform.artifact-id>quarkus-universe-bom</quarkus.platform.artifact-id>
        <quarkus.platform.group-id>io.quarkus</quarkus.platform.group-id>
        <quarkus.platform.version>1.9.0.Final</quarkus.platform.version>
        <surefire-plugin.version>3.0.0-M5</surefire-plugin.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-resteasy-jsonb</artifactId>
        </dependency>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-hibernate-orm-panache</artifactId>
        </dependency>
        <dependency>
            <groupId>io.quarkus</groupId>
            <artifactId>quarkus-jdbc-postgresql</artifactId>
        </dependency>
    </dependencies>

application.properties:

quarkus.datasource.db-kind = postgresql
quarkus.datasource.username = theusername
quarkus.datasource.password = thepassword
quarkus.datasource.jdbc.url = jdbc:postgresql://localhost:5432/testpanache

# drop and create the database at startup (use `update` to only update the schema)
quarkus.hibernate-orm.database.generation = drop-and-create

Entity:

@Entity
public class Person {

    @Id @GeneratedValue public Long id;
    
    public String name;

    @Override
    public String toString() {
        return "Person [id=" + id + ", name= '" + name + "']";
    }   
}

REST Resource:

@Path("/people")
@Produces(MediaType.APPLICATION_JSON)
public class PersonResource {

    @Inject
    PersonRepository repository;
    
    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public List<Person> hello() {
        return repository.listAll();
    }
    
    @POST
    @Transactional
    public void create() {
        Person person = new Person();
        person.name = "some name";
        repository.persist(person);
    }

    @PUT
    @Path("{id}")
    @Transactional
    public Person update(@PathParam("id") Long id) {
        Person person = repository.findById(id);
        person.name = "updated updated updated"; // does not work
//      repository.persist(person); // does not work
//      repository.persistAndFlush(person); // does not work
        repository.getEntityManager().merge(person); // does not work
        return person;
    }
}

Repository:

@ApplicationScoped
public class PersonRepository implements PanacheRepository<Person> {
}

I made some requests using curl to demonstrate the behavior:

$ curl -w "\n" http://localhost:8080/people
[]

$ curl -X POST http://localhost:8080/people

$ curl -w "\n" http://localhost:8080/people
[{"id":1,"name":"some name"}]

$ curl -X PUT http://localhost:8080/people/1
{"id":1,"name":"updated updated updated"}

$ curl -w "\n" http://localhost:8080/people
[{"id":1,"name":"some name"}]

So, the list starts empty, the second POST request creates a Person with "some name", as shown by the third request; the fourth request does a PUT that is intended to change the name to "updated updated updated", but the fifth request shows the name was not updated.

Although it's not needed, I tried repository.persist(person);, repository.persistAndFlush(person);, and even repository.getEntityManager().merge(person); (one each time) as show in the PersonResource snippet above. But none of them made effect.

What I am missing?

PS.: I tried to change my entity to extends PanacheEntity and used Person.findById(id); to find the entity, this way the subsequent updates did make effect. But it's not my point, I wanna use PanacheRepository and want to understand what I'm missing with this.

Isabel Tiburski
  • 113
  • 1
  • 1
  • 7

1 Answers1

7

Please consider accessing your entity using getter/setter, so Hibernate Proxies will work properly.

@Entity
public class Person {

    @Id @GeneratedValue public Long id;
    
    private String name;  // <--- private field

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

    @Override
    public String toString() {
        return "Person [id=" + id + ", name= '" + name + "']";
    }   
}

and in your resource:


    @PUT
    @Path("{id}")
    @Transactional
    public Person update(@PathParam("id") Long id) {
        Person person = repository.findById(id);
        person.setName("updated updated updated");  // <--- this way works
        repository.persist(person);
        return person;
    }
  • 9
    I get my mistake. I was trying to use public fields along with PanacheRepository, but the setter's absent does not enable the hibernate proxy that tracks fields' changes, so the update doesn't work. With Panache is possible to use public fields, but it works only when the entity extends PanacheEntity/PanacheEntityBase (behind the scenes, Panache will provide the getters/setters so the hibernate proxies work properly). Otherwise (even when using PanacheRepository), the entity must be defined in the "hibernate/JPA way", that is, with getters/setters, in order to proxies work properly. – Isabel Tiburski Oct 31 '20 at 21:44
  • @IsabelTiburski Your comment should be an answer as this is correct. If it were an answer, people could upvote it. – Heiko Rupp Jun 24 '21 at 07:18
  • Hibernate can work perfectly fine without Setters. We only use Getters and domain-methods to update state, HIbernate will access the private fields directly. Setters are the bane of the Java ecosystem and will push you to an Anemic Domain Model. Of course if you you explicitly want CRUD than just go for PanacheEntity (though I have yet to see a pure CRUD application) – lostiniceland Dec 16 '22 at 16:15