4

I am facing a problem about how to manage mapping for a specific model.

This is a multitenant application, and we have made the choice of including the "tenant_id" in every entity, so we don't have to make a joint everytime we need to get an entity (in fact, this is the root of my problem...).

The model is as follow :

+--------------------+   +---------------+
|        Book        |   |    Author     |
+--------------------+   +---------------+
| id (pk)            |   | id (pk)       |
| tenant_id (pk)(fk) |   | tenant_id (pk |
| author_id (fk)     |   | name          |
| title              |   +---------------+
+--------------------+

As you can see, the tenant-id is in each entity, and part of the primary key. We use @IdClass to manage the composite key. Here is the code :


    @Data
    public class TenantAwareKey implements Serializable {

        private UUID id;
        private Integer tenantId;
    }


    @IdClass(TenantAwareKey.class)
    @Entity
    @Table(name = "BOOK")
    @Data
    public class Book {

        @Id
        @GeneratedValue
        @Column(name = "ID")
        private UUID id;
        @Id
        @Column(name = "TENANT_ID")
        private Integer tenantId;

        private String title;

        @ManyToOne
        @JoinColumns(
            value = {
                @JoinColumn(referencedColumnName = "id", name = "author_id"),
                @JoinColumn(referencedColumnName = "tenant_id", name = "tenant_id", insertable = false, updatable = false)
            })
        private Author author;   
    }


    @IdClass(TenantAwareKey.class)
    @Entity
    @Data
    public class Author {
        @Id
        @GeneratedValue
        @Column(name = TenantAwareConstant.ENTITY_ID_COLUMN_NAME)
        private UUID id;
        @Id
        @Column(name = TenantAwareConstant.TENANT_ID_COLUMN_NAME)
        private Integer tenantId;

        private String name;
    }

And then, when running my application I ended up with :


    Caused by: org.hibernate.AnnotationException: Mixing insertable and non insertable columns in a property is not allowed: 
    com.pharmagest.durnal.tenant.entity.BookNoDuplicateColumn.author
        at org.hibernate.cfg.Ejb3Column.checkPropertyConsistency(Ejb3Column.java:725)
        at org.hibernate.cfg.AnnotationBinder.bindManyToOne(AnnotationBinder.java:3084)
    (...)

I manage to make it work if I don't try to "mutualize" the tenant_id column, it can be acceptable when I have only one foreign key with this tenant_id, but less and less as the number of foreign key increase, resulting in adding a tenant_id column each time, duplicating information et spoiling memory...

After digging a bit, I found an open issue in Hibernate : https://hibernate.atlassian.net/browse/HHH-6221

It has not been fixed for years... So, my question is : Have you faced a mapping like this, and is there a solution to avoid duplicated columen when I have a foreign-key that share a field with the primary key?

guilbill
  • 91
  • 6
  • Search how to use @EmbeddedId annotation, You need to create separate Entity class for Id – Dhanraj Feb 20 '19 at 10:47
  • At first I was thinking about using `@EmbeddedId`, but there is a limitation with it as part of my primary key is a `@GeneratedValue` and it is not possible as you can see in this issue : https://hibernate.atlassian.net/browse/HHH-6044 – guilbill Feb 20 '19 at 14:54

1 Answers1

6

As described here, you can bypass the validation by using @JoinColumnOrFormula for the column id_tenant.

You should map the author's association like this:

    @JoinColumnsOrFormulas(
        value = {
                @JoinColumnOrFormula(column = @JoinColumn(referencedColumnName = "id", name = "author_id")),
                @JoinColumnOrFormula(formula = @JoinFormula(referencedColumnName = "tenant_id", value = "tenant_id"))
        })