diff --git a/dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java b/dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java index ae131ae300..dda45539f9 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java +++ b/dao/src/main/java/org/thingsboard/server/dao/model/BaseSqlEntity.java @@ -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 implements BaseEntity { @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) diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/IdGenerator.java b/dao/src/main/java/org/thingsboard/server/dao/sql/IdGenerator.java index b0ca2c1530..dc8a0da32f 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/IdGenerator.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/IdGenerator.java @@ -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 getEventTypes() { + return EventTypeSets.INSERT_ONLY; + } + + @Retention(RetentionPolicy.RUNTIME) + @Target(ElementType.FIELD) + @IdGeneratorType(IdGenerator.class) + public @interface GeneratedId { + } + } diff --git a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java index 4a81bc38a9..e0d9f466ac 100644 --- a/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/JpaAbstractDao.java @@ -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, 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, 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, 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) {