Search code examples
sqlhibernateplayframeworkjpql

JPQL and selecting 1:n relationships using Hibernate


I have a an entry MenuItem which has a language. I also have a device which needs all MenuItems which have the language selected by the device. (Then there is also an AccessGroup which determines if a device has access to a MenuItem)

I have a query which works for the AccessGroup selection, but cannot think how to formulate the query to include the language selection.

Simplified:

MenuItem
- id
- published
- Language
- AccessGroup
- App

Device
- id
- List<Languages>
- User

AccessGroup
- id
- universal
- <List>AccessGroupLink

AccessGroupLink
- id
- Device
- User
- AccessGroup
- perpetual
- validTo

How do I do this in JPQL. Currently I have/tried (using the Play! Framework):

return find("SELECT DISTINCT m FROM MenuItem m " +
                    "INNER JOIN m.accessGroups ag " +
                    "LEFT OUTER JOIN ag.accessGroupLinks agl " +
                    "WHERE m.app = ? AND m.language IN ? AND (ag.universal = TRUE OR ((agl.device = ? OR agl.user = ?) AND (agl.validTo >= ? OR agl.perpetual = TRUE))) AND m.published = true", device.app, device.languages, device, device.user, new Date()).fetch();

I added m.language IN device.languages, but that doesn't work. So I guess I have to add a JOIN, but cannot see how for some reason. So any help is really appreciated.

EDIT: The error I get with the query above is:

A play.db.jpa.JPABase$JPAQueryException has been caught, Error while executing query SELECT DISTINCT m FROM MenuItem m INNER JOIN m.accessGroups ag LEFT OUTER JOIN ag.accessGroupLinks agl WHERE m.app = ? AND m.language IN ? AND (ag.universal = TRUE OR ((agl.device = ? OR agl.user = ?) AND (agl.validTo >= ? OR agl.perpetual = TRUE)))AND m.published = true: org.hibernate.PropertyAccessException: could not get a field value by reflection getter of models.Language.id

But I don't understand the error, as model.Language.id is part of the Entity.


Solution

  • You could avoid IN clause by using below query

    return find("SELECT DISTINCT m FROM MenuItem m " +
                        "INNER JOIN m.accessGroups ag " +
                        "LEFT OUTER JOIN ag.accessGroupLinks agl " +
                        "LEFT OUTER JOIN agl.device device " +
                        "LEFT OUTER JOIN device.languages language " +
                        "WHERE m.app = ? AND m.language = language  AND (ag.universal = TRUE OR ((agl.device = ? OR agl.user = ?) AND (agl.validTo >= ? OR agl.perpetual = TRUE))) AND m.published = true", device.app, device, device.user, new Date()).fetch();