Skip to content

Instantly share code, notes, and snippets.

@FrancescoJo
Last active November 16, 2023 02:35
Show Gist options
  • Save FrancescoJo/ca72628ee7be42b6f49d2f6b90dbfa26 to your computer and use it in GitHub Desktop.
Save FrancescoJo/ca72628ee7be42b6f49d2f6b90dbfa26 to your computer and use it in GitHub Desktop.
Hibernate error while upgrading Spring boot 2.7 to 3.0(Hibernate 5.x to 6.1)
// UserEntity.kt
@Entity
@Table(name = "users")
class UserEntity(
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
val id: Long = 0L,
@Column(name = "isDeleted")
var isDeleted: Boolean = false,
@Version
@Column(name = "version")
override var version: Long = Versioned.DEFAULT_LONG_INT
)
// BuyerEntity.kt
// Since Hibernate 6 we have to make BuyerEntity extend UserEntity to find businessInformation properly.
@Entity
@Table(name = "users_buyers")
class BuyerEntity(
@Id
@Column(name = "users_id")
val id: Long = 0L,
user: UserEntity,
/*
* This configuration was fine in Hibernate 5, but in version 6 it causes application startup failure by raising a Runtime exception as follows:
*
* org.hibernate.AnnotationException: Association 'BuyerEntity.businessInformation' is 'mappedBy' a property named 'ownerUser' which references the wrong entity type 'UserEntity', expected 'BuyerEntity'
*/
@OneToMany(
fetch = FetchType.LAZY,
mappedBy = "ownerUser",
cascade = [CascadeType.ALL],
orphanRemoval = true
)
var businessInformation: MutableSet<BuyerBusinessInfoEntity> = mutableSetOf(),
)
// AbstractBusinessInfoEntityTemplate.kt
@Inheritance // Caution: We can't code this as Kotlin sealed class since Hibernate could not makes proxy object properly.
@Entity
@Table(name = "business_info")
@DiscriminatorColumn(name = "business_type_code")
abstract class AbstractBusinessInfoEntityTemplate {
@get:jakarta.persistence.Transient
abstract val type: AbstractBusinessInfoEntityTemplate.Type
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
val id: Long = 0L
// To refer this field in BuyerEntity the type of this field should be BuyerEntity but it is impossible since both types are incompatible at the moment
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "users_id")
var ownerUser: UserEntity? = null
@Column(name = "licence_number")
var licenceNumber: String = ""
@Column(name = "name")
var name: String = ""
enum class Type {
BUYER,
USER
}
}
// BuyerBusinessInfoEntity.kt
@Entity
@DiscriminatorValue(DISCRIMINATOR_VALUE)
class BuyerBusinessInfoEntity : AbstractBusinessInfoEntityTemplate() {
@Transient
override val type: AbstractBusinessInfoEntityTemplate.Type = AbstractBusinessInfoEntityTemplate.Type.BUYER
companion object {
const val DISCRIMINATOR_VALUE = "b"
}
}
/*
Full exception stack trace:
Caused by: org.hibernate.AnnotationException: Association 'BuyerEntity.businessInformation' is 'mappedBy' a property named 'ownerUser' which references the wrong entity type 'UserEntity', expected 'BuyerEntity'
at app//org.hibernate.boot.model.internal.BinderHelper.checkMappedByType(BinderHelper.java:1104)
at app//org.hibernate.boot.model.internal.CollectionBinder.isReversePropertyInJoin(CollectionBinder.java:1572)
at app//org.hibernate.boot.model.internal.CollectionBinder.noAssociationTable(CollectionBinder.java:1583)
at app//org.hibernate.boot.model.internal.CollectionBinder.bindStarToManySecondPass(CollectionBinder.java:1544)
at app//org.hibernate.boot.model.internal.CollectionBinder$1.secondPass(CollectionBinder.java:1535)
at app//org.hibernate.boot.model.internal.CollectionSecondPass.doSecondPass(CollectionSecondPass.java:45)
at app//org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1870)
at app//org.hibernate.boot.internal.InFlightMetadataCollectorImpl.processSecondPasses(InFlightMetadataCollectorImpl.java:1826)
at app//org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:331)
at app//org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:1380)
at app//org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:1451)
at app//org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:75)
at app//org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:376)
at app//org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:409)
at app//org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:396)
at app//org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:352)
at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1817)
at app//org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1766)
*/
// Table structure
CREATE TABLE IF NOT EXISTS `users`
(
`id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`isDeleted` BOOLEAN DEFAULT FALSE,
`version` BIGINT NOT NULL
);
CREATE TABLE IF NOT EXISTS `buyers`
(
`users_id` BIGINT PRIMARY KEY REFERENCES `users` (`id`)
);
CREATE TABLE IF NOT EXISTS `business_info`
(
`id` BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
`users_id` BIGINT NULL,
`licence_number` VARCHAR(24) DEFAULT NULL,
`name` VARCHAR(64) DEFAULT NULL
`business_type_code` VARCHAR(4) NOT NULL,
INDEX `idx_business_info_identity` (`licence_number`, `name`, `business_type_code`),
CONSTRAINT `fk_business_info_user_id` FOREIGN KEY (`users_id`) REFERENCES `users` (`id`)
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment