2

I'm using Spring boot v3.0.1 and hibernate v6.1.6.Final. I'm trying the @TenantId annotation in my entity class. This annotation works fine if I declare the annotation in the entity class directly. But if I create a @Embeddable class and then declare the @TenantId annotation inside it, then the application doesn't start.

Below is the sample code:

BaseEntity.Java

import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import jakarta.persistence.MappedSuperclass;
import org.hibernate.annotations.TenantId;

@MappedSuperclass
@Embeddable
public class BaseEntity {

  @TenantId
  @Column(name = "tenant_id", nullable = false)
  private String tenantId;

  public BaseEntity() {
  }

  public BaseEntity(String tenantId) {
    this.tenantId = tenantId;
  }

  public String getTenantId() {
    return tenantId;
  }

  public void setTenantId(String tenantId) {
    this.tenantId = tenantId;
  }
}

Person.java

import jakarta.persistence.*;


import java.util.Objects;

@Entity
public class Person {

    @Id @GeneratedValue private Long id;

    @Embedded
    private BaseEntity baseEntity;

    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public BaseEntity getBaseEntity() {
        return baseEntity;
    }

    public void setBaseEntity(BaseEntity baseEntity) {
        this.baseEntity = baseEntity;
    }

    @Override
    public String toString() {
        return "Person{" + "id=" + id + ", name='" + name + '\'' + '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Person person = (Person) o;
        return Objects.equals(id, person.id) && Objects.equals(baseEntity, person.baseEntity) && Objects.equals(name, person.name);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id, baseEntity, name);
    }
}

Error message:

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.cfg.NotYetImplementedException: Still need to wire in composite in-memory value generation at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1751) ~[spring-beans-6.0.3.jar:6.0.3] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:599) ~[spring-beans-6.0.3.jar:6.0.3] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:521) ~[spring-beans-6.0.3.jar:6.0.3] at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:326) ~[spring-beans-6.0.3.jar:6.0.3] at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:234) ~[spring-beans-6.0.3.jar:6.0.3] at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:324) ~[spring-beans-6.0.3.jar:6.0.3] at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:200) ~[spring-beans-6.0.3.jar:6.0.3] at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1130) ~[spring-context-6.0.3.jar:6.0.3] at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:905) ~[spring-context-6.0.3.jar:6.0.3] at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:584) ~[spring-context-6.0.3.jar:6.0.3] at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:730) ~[spring-boot-3.0.1.jar:3.0.1] at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:432) ~[spring-boot-3.0.1.jar:3.0.1] at org.springframework.boot.SpringApplication.run(SpringApplication.java:308) ~[spring-boot-3.0.1.jar:3.0.1] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1302) ~[spring-boot-3.0.1.jar:3.0.1] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1291) ~[spring-boot-3.0.1.jar:3.0.1] at example.springdata.jpa.hibernatemultitenant.partition.Application.main(Application.java:9) ~[classes/:na] Caused by: jakarta.persistence.PersistenceException: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested exception is org.hibernate.cfg.NotYetImplementedException: Still need to wire in composite in-memory value generation at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:421) ~[spring-orm-6.0.3.jar:6.0.3] at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396) ~[spring-orm-6.0.3.jar:6.0.3] at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:352) ~[spring-orm-6.0.3.jar:6.0.3] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1797) ~[spring-beans-6.0.3.jar:6.0.3] at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1747) ~[spring-beans-6.0.3.jar:6.0.3] ... 15 common frames omitted Caused by: org.hibernate.cfg.NotYetImplementedException: Still need to wire in composite in-memory value generation at org.hibernate.tuple.entity.EntityMetamodel$CompositeGenerationStrategyPairBuilder.buildPair(EntityMetamodel.java:601) ~[hibernate-core-6.1.6.Final.jar:6.1.6.Final] at org.hibernate.tuple.entity.EntityMetamodel.buildGenerationStrategyPair(EntityMetamodel.java:460) ~[hibernate-core-6.1.6.Final.jar:6.1.6.Final] at org.hibernate.tuple.entity.EntityMetamodel.(EntityMetamodel.java:296) ~[hibernate-core-6.1.6.Final.jar:6.1.6.Final] at org.hibernate.persister.entity.AbstractEntityPersister.(AbstractEntityPersister.java:728) ~[hibernate-core-6.1.6.Final.jar:6.1.6.Final] at org.hibernate.persister.entity.SingleTableEntityPersister.(SingleTableEntityPersister.java:152) ~[hibernate-core-6.1.6.Final.jar:6.1.6.Final] at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:77) ~[na:na] at java.base/jdk.internal.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45) ~[na:na] at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java:499) ~[na:na] at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java:480) ~[na:na] at org.hibernate.persister.internal.PersisterFactoryImpl.createEntityPersister(PersisterFactoryImpl.java:92) ~[hibernate-core-6.1.6.Final.jar:6.1.6.Final] at org.hibernate.persister.internal.PersisterFactoryImpl.createEntityPersister(PersisterFactoryImpl.java:75) ~[hibernate-core-6.1.6.Final.jar:6.1.6.Final] at org.hibernate.metamodel.model.domain.internal.MappingMetamodelImpl.processBootEntities(MappingMetamodelImpl.java:278) ~[hibernate-core-6.1.6.Final.jar:6.1.6.Final] at org.hibernate.metamodel.model.domain.internal.MappingMetamodelImpl.finishInitialization(MappingMetamodelImpl.java:211) ~[hibernate-core-6.1.6.Final.jar:6.1.6.Final] at org.hibernate.metamodel.internal.RuntimeMetamodelsImpl.finishInitialization(RuntimeMetamodelsImpl.java:60) ~[hibernate-core-6.1.6.Final.jar:6.1.6.Final] at org.hibernate.internal.SessionFactoryImpl.(SessionFactoryImpl.java:311) ~[hibernate-core-6.1.6.Final.jar:6.1.6.Final] at org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:415) ~[hibernate-core-6.1.6.Final.jar:6.1.6.Final] at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1425) ~[hibernate-core-6.1.6.Final.jar:6.1.6.Final] at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:66) ~[spring-orm-6.0.3.jar:6.0.3] at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:376) ~[spring-orm-6.0.3.jar:6.0.3] at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409) ~[spring-orm-6.0.3.jar:6.0.3] ... 19 common frames omitted

Any help is appreciated. Thanks in advance,

2 Answers 2

2

You can use inheritance to achieve the desired result (moving the tenant_id column out of the entity), rather than the @Embedded annotation. Here's how:

For the BaseEntity class (please forgive my use of Lombok annotations)

import jakarta.persistence.Column;
import jakarta.persistence.MappedSuperclass;
import org.hibernate.annotations.TenantId;

@MappedSuperclass
@Getter
@Setter
@NoArgsConstructor
public abstract class BaseEntity {

  @TenantId
  @Column(name = "tenant_id", nullable = false)
  private String tenantId;
}

Then in the Person class, simply extend BaseEntity

import jakarta.persistence.*;


import java.util.Objects;

@Entity
public class Person extends BaseEntity {

    @Id 
    @GeneratedValue private Long id;

    private String name;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person{" + "id=" + id + ", name='" + name + '\'' + '}';
    }

    @Override
    public boolean equals(Object o) {
        // TODO implement a suitable equals method
    }

    @Override
    public int hashCode() {
        return 0; // TODO implement a suitable hashcode method
    }
}
Sign up to request clarification or add additional context in comments.

Comments

0

I ran into this problem when creating a value object for the tenantId:

My Stack:

  • Java 21
  • Spring boot 3.4.0-M3
import jakarta.persistence.Embeddable;
import org.jmolecules.ddd.annotation.ValueObject;

import java.io.Serializable;

@ValueObject
@Embeddable
public record TenantId(Long id) implements Serializable {

    public TenantId {
        if (id == null || id <= 0) {
            throw new IllegalArgumentException("Invalid tenant ID");
        }
    }

    public static TenantId of(Long id) {
        return new TenantId(id);
    }

    @Override
    public String toString() {
        return id.toString();
    }
}

and using in my AggregateRoot:

@AggregateRoot
@Entity
@Table(name = "product")
public class Product {

    @EmbeddedId
    @Identity
    private ProductLineId id;

    @Embbeded
    @org.hibernate.annotations.TenantId
    private TenantId tenantId;

    @Embedded
    private Name name;
}

To solve the problem I had to remove the value object TenantId and use the primitive data type, in my case, Long.

Solution: remove the

I ran into this problem when creating a value object for the tenantId:

import jakarta.persistence.Embeddable;
import org.jmolecules.ddd.annotation.ValueObject;

import java.io.Serializable;

@ValueObject
@Embeddable
public record TenantId(Long id) implements Serializable {

    public TenantId {
        if (id == null || id <= 0) {
            throw new IllegalArgumentException("Invalid tenant ID");
        }
    }

    public static TenantId of(Long id) {
        return new TenantId(id);
    }

    @Override
    public String toString() {
        return id.toString();
    }
}

and using in my AggregateRoot:

@AggregateRoot
@Entity
@Table(name = "product")
public class Product {

    @EmbeddedId
    @Identity
    private ProductLineId id;

    @Embbeded
    @org.hibernate.annotations.TenantId
    private TenantId tenantId;

    @Embedded
    private Name name;
}

To solve the problem I had to remove the value object TenantId and use the primitive data type, in my case, Long.

Solution: remove the @Embedded annotation

@AggregateRoot
@Entity
@Table(name = "product")
public class Product {

    @EmbeddedId
    @Identity
    private ProductLineId id;

    @org.hibernate.annotations.TenantId
    private Long tenantId; // < -- Change here to be a primitive and remove the @Embedded annotation

    @Embedded
    private Name name;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.