Search code examples
javahibernatekotlinjpadropwizard

Invoking a Hibernate REST @POST method from Dropwizard application run() does not work, but method works when invoked normally from frontend


I am writing my first web-application as practice learning different frameworks. Please forgive me if I make any very obvious mistakes as I am new to this.

I am using Dropwizard and Kotlin to write the backend of my application. I am running 2 docker containers: one with the application.jar file and another with a PostgreSQL database. As I am learning the process and individual frameworks step-by-step, I am writing certain code snippets to help me work out what is actually happening.

I am using Hibernate as my ORM to map my Kotlin objects to the database. I tried creating a random object using a @POST method in my resources class, as shown below.

@POST
@UnitOfWork
fun createNote(@Valid noteRequest : Note){
    println("adding new note")
    val note = createNoteToSave(noteRequest)
    notesDAO.save(note)
}

private fun createNoteToSave(noteRequest: Note) : Note{
    val note = Note()
    note.data = noteRequest.data
    note.type = noteRequest.type
    return note
}

I don't have my frontend created yet so I wasn't sure the best way to access this method's URL to test whether the creation was working (but I believe I can curl from the cmd line now). In order to test the creation, I simply invoked the method notesResource.createNote(note) from the .run() method of my Dropwizard main application class and I got the following error:

org.hibernate.HibernateException: No session currently bound to execution context

at org.hibernate.context.internal.ManagedSessionContext.currentSession(ManagedSessionContext.java:58)

at org.hibernate.internal.SessionFactoryImpl.getCurrentSession(SessionFactoryImpl.java:508)

at io.dropwizard.hibernate.AbstractDAO.currentSession(AbstractDAO.java:44)

at com.ac.notetaker.dao.HibernateDAO.save(HibernateDAO.kt:34)

at com.ac.notetaker.resources.NotesResource.createNote(NotesResource.kt:34)

at com.ac.notetaker.NotetakerApplication.run(NotetakerApplication.kt:95)

at com.ac.notetaker.NotetakerApplication.run(NotetakerApplication.kt:22)

It says the Hibernate session has not started, but shouldn't the @UnitOfWork annotation signify that whenever the method is invoked, a session should begin?

Instead, as I don't have a frontend, I created a @GET method to then in turn invoke the @POST method, as below. When I visit this URL, the @POST method is successively invoked and an object is created and added to the database.

@GET
@Path("add")
@UnitOfWork
fun addRandomNote(){
    this.createNote(Note(
        UUID.randomUUID(),
        "{\"question\":\"answer\"}",
        Note.Type.QuestionAndAnswer.toString(),
    ))
}

Why, when I access the method with the @GET url, is the method successfully creating the Hibernate session and creating the object, but when I invoke the method directly from my backend code, it isn't? I appreciate any insight you guys can provide here, I am just trying to learn :)


Solution

  • This makes sense - if you invoke your method directly then you are just executing your method's code - the annotation doesn't cause any extra behaviour.

    The way frameworks like Spring and JAX-RS work is that on startup, the code that actually handles http requests is given a reference not to your class but to a dynamically generated subclass (this is what cglib is for) - if you hit a breakpoint in your method you will see that the object is not an instance of your class but something with a generated name.

    The subclass overrides your method to decorate it with additional code - in this case to create the Session. This wrapper code is commonly defined in aspects with join points based on the annotation, and cglib pulls that aspect code into the compiled subclass. (if the annotated method is on an interface then a Java dynamic proxy is used rather than cglib and it just implements the interface rather than having to extend a class).

    You can indeed call your post from curl - try something like curl -X POST localhost:8080/the_path -H 'Content-type:application/json' -d '{"question":"answer"}'