Troubleshooting Hibernate SessionFactory

My, that’s a snappy title!

I’m still having loads of problems describing the relationships (@OneToMany, @ManyToOne and @OneToOne) between @Entity objects in my Moodle project.

I did a standard install of Moodle to allow it to create the database. I’m now in the process of annotating/modifying the Groovy classes to represent the database. For those unfamiliar with Moodle, it should be noted that the tables created don’t have foreign key constraints. I can only imagine this is done to make the install process work with databases that don’t support the concept.

When I created the @Entity classes I tried to group them into logical packages. This allows me to introduce them gradually by changing the entity-scan.packages entry in application.yml. At the moment only the config package is enable and it works, though it’s only three tables and they have no relationships. As soon as I include a second package I get the session factory error noted in the previous post.

In trying to understand what was failing I enabled trace logging of the org.hibernate.cfg class:

<logger name="org.hibernate.cfg" level="trace"/>

When only the config package is enabled I see this line, which is missing when other packages are enabled.

org.hibernate.cfg.Settings - SessionFactory name : default

When other packages are enabled I see this line in the log:

Binding column: Ejb3JoinColumn{logicalColumnName='null', referencedColumn='null', mappedBy='null'}

Dumb Error Message

I’ve recently been working on a project (in my own time, open source, but we need it at work) to provide a foundation API for Moodle. (Yes, there is an API already, and I’m told it’s pretty good, but it doesn’t quite cover our needs. Plus it’s written in PHP and I don’t do PHP – scratch your own itch!) I’m coding the system in Micronaut because it rocks.

The big, boring piece of this task is mapping the database tables to @Entity classes. I semi-automated as much of this as I could and now I’m working through adding in the relationships.

Once I’d done a few classes I decided I’d test a simple endpoint, nothing beats seeing code working. Instead I got this error:

Internal Server Error: Failed to inject value for parameter [sessionFactory] of class: io.micronaut.transaction.hibernate5.HibernateTransactionManager

Message: No bean of type [org.hibernate.SessionFactory] exists for the given qualifier: @Named('default'). Make sure the bean is not disabled by bean requirements (enable trace logging for 'io.micronaut.context.condition' to check) and if the bean is enabled then ensure the class is declared a bean and annotation processing is enabled (for Java and Kotlin the 'micronaut-inject-java' dependency should be configured as an annotation processor).
Path Taken: new HibernateTransactionManager([SessionFactory sessionFactory],DataSource dataSource,Interceptor entityInterceptor)

Annoying! I figured I must have configured something incorrectly. I checked other projects. I enabled trace logging as suggested. I left it for the night and came back to it fresh the following day. I even created a very simple Groovy/JPA reference project so I could compare settings.

I’ve put the entity classes into different packages, just to group them by purpose. So the packages bit of the application.yml file is quite long. I thought perhaps I’d configured it incorrectly, so I commented out all but one line. Aha! I was then able to make calls to the endpoint and get data back.

In conclusion then, it seems that if the model in code doesn’t accurately represent the underlying database then this is the error message that one gets back.

Micronaut/GORM Services

After another long evening of trying to get Micronaut to work as expected out of the box I’m beginning to think that the developers really don’t want anyone to use GORM!

Yesterday I was trying to convert one of my Grails apps to Micronaut. Moving the domain classes to entities was easy. Here is my Route entity (with unnecessary elements removed):

@Entity
class Route implements GormEntity<Route> {

    String grade
    Colour colour
    Date firstSeen
    Date removed

    static belongsTo = [line:Line]

}

Next we want a service. The pattern in latest versions of Grails and Micronaut is to define the service as an interface and then, optionally, provide an (abstract) implementation. Perhaps this is an underlying Hibernate pattern, see https://gorm.grails.org/latest/hibernate/manual/index.html#dataServices. A simple service might look something like this:

@Service(Route)
interface RouteService {

    List<Route> list() {}
    
    Route get(Serializable id) {}

}

This seemingly empty file gives hints to GORM to provide two methods. The first returns a list of all Routes, the second the Route with the given ID. In the controller then we can then write:

@Controller("/routes")
class RouteController {

    @Inject RouteService service

    @Get("/")
    def index() {
        return service.list()
    }

    @Get("/{id}")
    def get(Serializable id) {
         return service.get(id)
    }

}

At this point I expected to be able to run the app and be able to get a list of routes, or a single route, in JSON format. Instead, I got this exception:

Caused by: com.fasterxml.jackson.databind.JsonMappingException: 
could not initialize proxy [uk.co.channele.uclimb.domain.Colour#65] - no Session 
(through reference chain: java.util.ArrayList[0]->
uk.co.channele.uclimb.domain.Route["colour"]->
uk.co.channele.uclimb.domain.Colour$HibernateProxy$y3jqGOWU["name"])

So my evening of searching the interwebs began. Coming from a Grails background I tried creating a service with actual methods, but this didn’t work. (Also, I found that replacing @Inject with @Autowired didn’t work.)

I found various Jackson annotations that allowed me to exclude the colour field but excluding line wasn’t obvious. I found some stuff about making fetches “eager” and about marking items as transient, but none of these helped.

Finally I did find a solution and that was to add in a dependency so that Jackson with work with Hibernate:

implementation 'com.fasterxml.jackson.datatype:jackson-datatype-hibernate5:2.10.2'

Why was this so difficult to find? Did I do something wrong when creating the application, there are lots of features available? I’d used the command:

mn create-app --features=hibernate-gorm,jdbc-hikari --lang=groovy uk.co.channele.uclimb

So I think it’s reasonable that I expected the application to render entities to JSON. Who knows?? As always, I hope that others might find this post useful.

Micronaut/GORM Unit Tests

I’ve recently revisited Micronaut as I need to re-write some old systems at work. Both of the systems are written in Grails, one provides an API, the other a very lightweight website. So Micronaut seems like the idea candidate. As a Grails developer I want to continue to use Groovy and GORM and both of these are supported.

I made a start on a few basic domain (Entity) classes with a few simple constraints. At this point I wanted to add some tests – yep, test driven development! However I ran in to problems getting the tests to run.

I created a test file following the same pattern as I would do in my Grails projects, but with what felt like common sense adaptations for Hibernate.

package uk.co.channele.mngorm

import io.micronaut.test.annotation.MicronautTest
import spock.lang.Specification

@MicronautTest
class AreaSpec extends Specification {

    void "test constraints"() {
        when: "an empty Area"
        def area = new Area()

        then: "not valid"
        !area.validate()

        when: "set the name"
        area.name = "name"

        then: "valid"
        area.validate()
    }
}

But when I tried to run the test with ./gradlew test I just got an exception:

org.springframework.dao.DataAccessResourceFailureException: Could not obtain current Hibernate Session; 
nested exception is org.hibernate.HibernateException: No Session found for current thread

This didn’t make sense, unit tests in Grails don’t expect the system to be running, mock objects are used instead. After a few late nights of searching/reading/cursing and trying many different variations I found this website https://guides.grails.org/grails-gorm-data-services/guide/index.html (notice no mention of Micronaut!) which gave me this solution:

package uk.co.channele.mngorm

import grails.gorm.transactions.Transactional
import io.micronaut.test.annotation.MicronautTest
import org.grails.orm.hibernate.HibernateDatastore
import org.springframework.transaction.PlatformTransactionManager
import spock.lang.AutoCleanup
import spock.lang.Shared
import spock.lang.Specification

@MicronautTest
@Transactional
class AreaSpec extends Specification {

    @Shared
    @AutoCleanup
    HibernateDatastore hibernateDatastore

    @Shared
    PlatformTransactionManager transactionManager

    void setupSpec() {
        hibernateDatastore = new HibernateDatastore(Area)
        transactionManager = hibernateDatastore.getTransactionManager()
    }

    void "test constraints"() {
        when: "an empty Area"
        def area = new Area()

        then: "not valid"
        !area.validate()

        when: "set the name"
        area.name = "name"

        then: "valid"
        area.validate()
    }
}

Which runs nicely and passes. Now I need to find a way to improve the Micronaut documentation so that potential users don’t suffer the same frustration as me!