Fixes for Hibernate 6 support

This commit is contained in:
VIacheslavKlimov 2025-07-23 16:18:52 +03:00
parent 5e566d9d95
commit 24ff462b31
3 changed files with 92 additions and 33 deletions

View File

@ -21,14 +21,13 @@ import jakarta.persistence.Id;
import jakarta.persistence.MappedSuperclass;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import org.hibernate.annotations.UuidGenerator;
import org.thingsboard.common.util.JacksonUtil;
import org.thingsboard.server.common.data.BaseData;
import org.thingsboard.server.common.data.id.EntityId;
import org.thingsboard.server.common.data.id.TenantId;
import org.thingsboard.server.common.data.id.UUIDBased;
import org.thingsboard.server.dao.DaoUtil;
import org.thingsboard.server.dao.sql.IdGenerator;
import org.thingsboard.server.dao.sql.IdGenerator.GeneratedId;
import java.util.Arrays;
import java.util.Collections;
@ -46,7 +45,7 @@ public abstract class BaseSqlEntity<D> implements BaseEntity<D> {
@Id
@Column(name = ModelConstants.ID_PROPERTY, columnDefinition = "uuid")
@UuidGenerator(style = UuidGenerator.Style.AUTO, algorithm = IdGenerator.class)
@GeneratedId
protected UUID id;
@Column(name = ModelConstants.CREATED_TIME_PROPERTY, updatable = false)

View File

@ -17,17 +17,44 @@ package org.thingsboard.server.dao.sql;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.annotations.IdGeneratorType;
import org.hibernate.engine.spi.SharedSessionContractImplementor;
import org.hibernate.id.uuid.UuidValueGenerator;
import org.hibernate.generator.BeforeExecutionGenerator;
import org.hibernate.generator.EventType;
import org.hibernate.generator.EventTypeSets;
import org.thingsboard.server.dao.model.BaseEntity;
import java.util.UUID;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.EnumSet;
@Slf4j
public class IdGenerator implements UuidValueGenerator {
public class IdGenerator implements BeforeExecutionGenerator {
@Override
public UUID generateUuid(SharedSessionContractImplementor session) {
public Object generate(SharedSessionContractImplementor session, Object owner, Object currentValue, EventType eventType) {
if (owner instanceof BaseEntity<?> entity && entity.getUuid() != null) {
return entity.getUuid();
}
return Uuids.timeBased();
}
@Override
public boolean allowAssignedIdentifiers() {
return true;
}
@Override
public EnumSet<EventType> getEventTypes() {
return EventTypeSets.INSERT_ONLY;
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@IdGeneratorType(IdGenerator.class)
public @interface GeneratedId {
}
}

View File

@ -15,12 +15,14 @@
*/
package org.thingsboard.server.dao.sql;
import com.datastax.oss.driver.api.core.uuid.Uuids;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ListenableFuture;
import jakarta.persistence.EntityManager;
import jakarta.persistence.OptimisticLockException;
import jakarta.persistence.PersistenceContext;
import lombok.extern.slf4j.Slf4j;
import org.hibernate.Session;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.jdbc.core.JdbcTemplate;
@ -69,6 +71,14 @@ public abstract class JpaAbstractDao<E extends BaseEntity<D>, D>
boolean isNew = entity.getUuid() == null;
if (isNew) {
entity.setCreatedTime(System.currentTimeMillis());
} else {
if (entity.getCreatedTime() == 0) {
if (entity.getUuid().version() == 1) {
entity.setCreatedTime(Uuids.unixTimestamp(entity.getUuid()));
} else {
entity.setCreatedTime(System.currentTimeMillis());
}
}
}
try {
entity = doSave(entity, isNew, flush);
@ -82,33 +92,9 @@ public abstract class JpaAbstractDao<E extends BaseEntity<D>, D>
boolean flushed = false;
EntityManager entityManager = getEntityManager();
if (isNew) {
entityManager.persist(entity);
if (entity instanceof HasVersion versionedEntity) {
versionedEntity.setVersion(1L);
}
entity = create(entity);
} else {
if (entity instanceof HasVersion versionedEntity) {
if (versionedEntity.getVersion() == null) {
HasVersion existingEntity = entityManager.find(versionedEntity.getClass(), entity.getUuid());
if (existingEntity != null) {
/*
* manually resetting the version to latest to allow force overwrite of the entity
* */
versionedEntity.setVersion(existingEntity.getVersion());
} else {
return doSave(entity, true, flush);
}
}
versionedEntity = entityManager.merge(versionedEntity);
entity = (E) versionedEntity;
/*
* by default, Hibernate doesn't issue an update query and thus version increment
* if the entity was not modified. to bypass this and always increment the version, we do it manually
* */
versionedEntity.setVersion(versionedEntity.getVersion() + 1);
} else {
entity = entityManager.merge(entity);
}
entity = update(entity);
}
if (entity instanceof HasVersion versionedEntity) {
/*
@ -125,6 +111,53 @@ public abstract class JpaAbstractDao<E extends BaseEntity<D>, D>
return entity;
}
private E create(E entity) {
if (entity instanceof HasVersion versionedEntity) {
versionedEntity.setVersion(1L);
}
if (entity.getUuid() == null) {
getEntityManager().persist(entity);
} else {
if (entity instanceof HasVersion) {
/*
* Hibernate 6 does not allow creating versioned entities with preset IDs.
* Bypassing by calling the underlying session directly
* */
Session session = getEntityManager().unwrap(Session.class);
session.save(entity);
} else {
entity = getEntityManager().merge(entity);
}
}
return entity;
}
private E update(E entity) {
if (entity instanceof HasVersion versionedEntity) {
if (versionedEntity.getVersion() == null) {
HasVersion existingEntity = entityManager.find(versionedEntity.getClass(), entity.getUuid());
if (existingEntity != null) {
/*
* manually resetting the version to latest to allow force overwriting of the entity
* */
versionedEntity.setVersion(existingEntity.getVersion());
} else {
return create(entity);
}
}
versionedEntity = entityManager.merge(versionedEntity);
entity = (E) versionedEntity;
/*
* by default, Hibernate doesn't issue an update query and thus version increment
* if the entity was not modified. to bypass this and always increment the version, we do it manually
* */
versionedEntity.setVersion(versionedEntity.getVersion() + 1);
} else {
entity = entityManager.merge(entity);
}
return entity;
}
@Override
@Transactional
public D saveAndFlush(TenantId tenantId, D domain) {