Search code examples
hibernategrailsgrails-ormmulti-tenant

Grails Hibernate GORM, Multi-Tenancy with one-to-one relationship


Cloned the code from the tutorial : Database per Tenant Multi-Tenancy

I went into the completed project and began playing around with things.

I understand how the application is resolving tenant ids and connecting to varying datasources without difficulty.

What is leaving me scratching my head is how to create other types of relationships.

The example shown in the tutorial demonstrates a many-to-many relationship. What I'm looking to use is a unidirectional one-to-one relationship.

Naturally, the way I thought to do this was to make a simple change. I changed the Vehicle.groovy file and VehicleService.groovy

Here are my versions of the files:

//Vehicle.groovy
package example

import grails.gorm.MultiTenant

class Vehicle implements MultiTenant<Vehicle> { // <1>
    String model
    Integer year

    Engine engine
    static constraints = {
        model blank:false
        year min:1980
    }
}


//VehicleService.groovy
package example

// tag::class[]
import grails.gorm.multitenancy.CurrentTenant
import grails.gorm.services.Join
import grails.gorm.services.Service
import grails.gorm.transactions.Transactional
import groovy.transform.CompileStatic

@Service(Vehicle) // <1>
@CurrentTenant // <2>
@CompileStatic
abstract class VehicleService {
// end::class[]

    // tag::queries[]
    @Join('engine') // <1>
    abstract List<Vehicle> list(Map args ) // <2>

    abstract Integer count() // <3>

    @Join('engine')
    abstract Vehicle find(Serializable id) // <4>

    // end::queries[]

    // tag::save[]
    abstract Vehicle save(String model,
                            Integer year)
    // end::save[]

    // tag::update[]
    @Transactional
    Vehicle update( Serializable id, // <5>
                    String model,
                    Integer year) {
        Vehicle vehicle = find(id)
        if (vehicle != null) {
            vehicle.model = model
            vehicle.year = year
            vehicle.save(failOnError:true)
        }
        vehicle
    }
    // end::update[]

    // tag::delete[]
    abstract Vehicle delete(Serializable id)
    // end::delete[]
}

This does not give the desired result. It creates the following runtime error: enter image description here

How do I fix this error and establish the correct relationship? What underlying concept am I missing here? I know its SQL under the hood, but why doesn't joining the column work?

Wouldn't the generated query be something like:

SELECT * FROM Vehicles
JOIN Engines
WHERE Vehicles.ID = WHATEVER
ON Vehicles.engine_id= Engines.id

Solution

  • In grails-app/views/vehicle/create.gsp replace line 30:

    <f:all bean="vehicle"/>
    

    with

    ${example.Vehicle.withNewSession { session ->
         f.all([bean: 'vehicle'])
    }}
    

    The error should go away.

    Better solution:

    replace line 30:

    <f:all bean="vehicle"/>
    

    with

    <f:field bean="vehicle" property="model"/>
    <f:field bean="vehicle" property="year"/>
    

    You'll need to either mark engine as nullable and build another interface for it or add it to the form and add some binding code to the save action in VehicleController.