quarkus-panachequarkus-reactivequarkus-hibernate-reactive

Cannot persist data with quarkus + hibernate reactive panache entity


I'm trying to create a Hello World application with quarkus. I have many troubles making it work.

Here are my classes :

import java.time.Duration;

import io.quarkus.hibernate.reactive.panache.Panache;
import io.quarkus.hibernate.reactive.panache.PanacheEntityBase;
import io.quarkus.hibernate.reactive.panache.common.WithSession;
import io.quarkus.hibernate.reactive.panache.common.WithSessionOnDemand;
import io.smallrye.mutiny.Uni;
import jakarta.persistence.Cacheable;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;

@Cacheable
@Entity
@Table(name = "category", schema = "administration")
@WithSessionOnDemand
public class Category extends PanacheEntityBase {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    public Long id;
    public String name;

    public static Uni<Category > add(Category cat) {
        return Panache
                .withTransaction(cat::persist)
                .replaceWith(cat)
                .ifNoItem()
                .after(Duration.ofMillis(10000))
                .fail()
                .onFailure()
                .transform(t -> new IllegalStateException(t));
    }

}


   @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Uni<List<Category>> list2() {


        // Create and persist a new category
        Category category = new Category();
        category.name = "test quarkus 3";
        Category.add(category)
                .onItem().transform(id -> id.id)
                .subscribe().with(e -> System.out.println(e));


        // Return the list of categories
        return Category.listAll();
    }

When I hit the GET endpoint, the category is created. But If I hit it 3 times in a row, I get the following error : ERROR [io.qua.mut.run.MutinyInfrastructure] (vert.x-eventloop-thread-1) Mutiny had to drop the following exception: java.lang.IllegalStateException: java.lang.IllegalStateException: No current Mutiny.Session found - no reactive session was found in the context and the context was not marked to open a new session lazily - you might need to annotate the business method with @WithSession at org.test.category.Category.lambda$1(Category.java:39)

So I tried to annotate my Category class with @WithSession and then with @WithSessionOnDemand but nothing worked. What am I doing wrong ?


Solution

  • you need to chain your create and query operations in a stream style thus they can access the session from thin air.

    Entity class:

    import jakarta.persistence.Cacheable;
    import jakarta.persistence.Column;
    import jakarta.persistence.Entity;
    
    import io.quarkus.hibernate.reactive.panache.PanacheEntity;
    
    @Entity
    @Cacheable
    public class Fruit extends PanacheEntity {
    
      @Column(length = 40, unique = false)
      public String name;
    
    }
    

    Service Class:

    import io.quarkus.hibernate.reactive.panache.Panache;
    import io.quarkus.panache.common.Sort;
    import io.smallrye.mutiny.Uni;
    import jakarta.enterprise.context.ApplicationScoped;
    import jakarta.ws.rs.GET;
    import jakarta.ws.rs.POST;
    import jakarta.ws.rs.Path;
    import java.time.Duration;
    import java.util.List;
    
    // first method
    @Path("/fruits")
    @ApplicationScoped
    public class FruitResource {
    
      @GET
      public Uni<List<Fruit>> get() {
        return Fruit.listAll(Sort.by("name"));
      }
    
      @POST
      public Uni<List<Fruit>> create(Fruit fruit) {
    
        Uni<Long> createStage = Panache
            .withTransaction(fruit::persist)
            .replaceWith(fruit)
            .ifNoItem()
            .after(Duration.ofMillis(10000))
            .fail()
            .onFailure()
            .transform(t -> {
              System.out.println("exception happen lalal");
              t.printStackTrace();
              return new IllegalStateException(t);
            })
            .onItem().transform(item -> {
              System.out.println("fruit is created: " + item.id);
              return item.id;
            });
        Uni<List<Fruit>> queryAllFruitsStage = Fruit.listAll();
    
        return createStage.chain(() -> queryAllFruitsStage);
    
      }
    }
    
    // second method
    @Path("/fruits")
    @ApplicationScoped
    public class FruitResource {
    
      @GET
      public Uni<List<Fruit>> get() {
        return Fruit.listAll(Sort.by("name"));
      }
    
      @POST
      public Uni<List<Fruit>> create(Fruit fruit) {
    
        return Panache
            .withTransaction(fruit::persist)
            .replaceWith(fruit)
            .ifNoItem()
            .after(Duration.ofMillis(10000))
            .fail()
            .onFailure()
            .transform(t -> {
              System.out.println("exception happen lalal");
              t.printStackTrace();
              return new IllegalStateException(t);
            })
            .onItem()
            .transformToUni(item -> Fruit.listAll());
      }
    }
    

    The curl test:

    curl --location 'localhost:8080/fruits' \
    --header 'Accept: application/json' \
    --header 'Content-Type: application/json' \
    --data '{
        "name": "fruit1"
    }'
    

    you may have a look at the Mutiny Patterns part at the docs Quarkus - Mutiny Primer and have a better understanding of reactive programming.