From 45a57230c1bad7f5d67e1148d604273b575fb805 Mon Sep 17 00:00:00 2001 From: VIacheslavKlimov Date: Tue, 22 Jul 2025 12:40:57 +0300 Subject: [PATCH 1/6] Upgrade versions of most libraries; refactor dependency management --- application/pom.xml | 4 +- .../ThingsboardSecurityConfiguration.java | 20 +- .../SslCredentialsWebServerCustomizer.java | 38 +- pom.xml | 611 +----------------- 4 files changed, 60 insertions(+), 613 deletions(-) diff --git a/application/pom.xml b/application/pom.xml index e2faa21929..f4d1235a64 100644 --- a/application/pom.xml +++ b/application/pom.xml @@ -186,8 +186,8 @@ jjwt - org.freemarker - freemarker + org.springframework.boot + spring-boot-starter-freemarker commons-io diff --git a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java index 93cf9da8e0..2fbc89a84d 100644 --- a/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java +++ b/application/src/main/java/org/thingsboard/server/config/ThingsboardSecurityConfiguration.java @@ -26,9 +26,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; import org.springframework.http.HttpHeaders; import org.springframework.security.authentication.AuthenticationManager; -import org.springframework.security.authentication.DefaultAuthenticationEventPublisher; -import org.springframework.security.config.annotation.ObjectPostProcessor; -import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; +import org.springframework.security.authentication.ProviderManager; import org.springframework.security.config.annotation.method.configuration.EnableMethodSecurity; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; @@ -183,15 +181,12 @@ public class ThingsboardSecurityConfiguration { } @Bean - public AuthenticationManager authenticationManager(ObjectPostProcessor objectPostProcessor) throws Exception { - DefaultAuthenticationEventPublisher eventPublisher = objectPostProcessor - .postProcess(new DefaultAuthenticationEventPublisher()); - var auth = new AuthenticationManagerBuilder(objectPostProcessor); - auth.authenticationEventPublisher(eventPublisher); - auth.authenticationProvider(restAuthenticationProvider); - auth.authenticationProvider(jwtAuthenticationProvider); - auth.authenticationProvider(refreshTokenAuthenticationProvider); - return auth.build(); + public AuthenticationManager authenticationManager() { + return new ProviderManager(List.of( + restAuthenticationProvider, + jwtAuthenticationProvider, + refreshTokenAuthenticationProvider + )); } @Autowired @@ -265,4 +260,5 @@ public class ThingsboardSecurityConfiguration { return new CorsFilter(source); } } + } diff --git a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/SslCredentialsWebServerCustomizer.java b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/SslCredentialsWebServerCustomizer.java index 0884208aee..6c73bb03de 100644 --- a/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/SslCredentialsWebServerCustomizer.java +++ b/common/transport/transport-api/src/main/java/org/thingsboard/server/common/transport/config/ssl/SslCredentialsWebServerCustomizer.java @@ -20,14 +20,17 @@ import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.autoconfigure.condition.ConditionalOnExpression; import org.springframework.boot.autoconfigure.web.ServerProperties; import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.boot.ssl.SslBundle; +import org.springframework.boot.ssl.SslBundles; +import org.springframework.boot.ssl.SslStoreBundle; import org.springframework.boot.web.server.Ssl; -import org.springframework.boot.web.server.SslStoreProvider; import org.springframework.boot.web.server.WebServerFactoryCustomizer; import org.springframework.boot.web.servlet.server.ConfigurableServletWebServerFactory; import org.springframework.context.annotation.Bean; import org.springframework.stereotype.Component; -import java.security.KeyStore; +import java.util.List; +import java.util.function.Consumer; @Component @ConditionalOnExpression("'${spring.main.web-environment:true}'=='true' && '${server.ssl.enabled:false}'=='true'") @@ -43,6 +46,9 @@ public class SslCredentialsWebServerCustomizer implements WebServerFactoryCustom @Qualifier("httpServerSslCredentials") private SslCredentialsConfig httpServerSslCredentialsConfig; + @Autowired + SslBundles sslBundles; + private final ServerProperties serverProperties; public SslCredentialsWebServerCustomizer(ServerProperties serverProperties) { @@ -56,16 +62,32 @@ public class SslCredentialsWebServerCustomizer implements WebServerFactoryCustom ssl.setKeyAlias(sslCredentials.getKeyAlias()); ssl.setKeyPassword(sslCredentials.getKeyPassword()); factory.setSsl(ssl); - factory.setSslStoreProvider(new SslStoreProvider() { + factory.setSslBundles(sslBundles); + } + + @Bean + public SslBundles sslBundles() { + SslStoreBundle storeBundle = SslStoreBundle.of( + httpServerSslCredentialsConfig.getCredentials().getKeyStore(), + httpServerSslCredentialsConfig.getCredentials().getKeyPassword(), + null + ); + return new SslBundles() { @Override - public KeyStore getKeyStore() { - return sslCredentials.getKeyStore(); + public SslBundle getBundle(String name) { + return SslBundle.of(storeBundle); } @Override - public KeyStore getTrustStore() { - return null; + public List getBundleNames() { + return List.of("default"); } - }); + + @Override + public void addBundleUpdateHandler(String name, Consumer handler) { + // no-op + } + }; } + } diff --git a/pom.xml b/pom.xml index 27de67d218..552250421c 100755 --- a/pom.xml +++ b/pom.xml @@ -38,65 +38,35 @@ ${project.name} /var/log/${pkg.name} /usr/share/${pkg.name} - 3.0.0 - 4.0.2 + 3.4.7 2.4.0-b180830.0359 - 4.0.5 - 10.1.42 - 2.5.2 - 3.2.12 - 3.2.12 - 3.2.12 - 6.1.21 - 6.2.11 - 6.3.9 5.1.5 0.12.5 - 2.0.17 - 2.24.3 - 1.5.6 0.10 4.17.0 4.2.25 5.0.4 33.1.0-jre - 3.1.8 - 3.14.0 - 1.16.1 2.16.1 1.3.1 1.10.0 - 5.3.1 - 5.2.4 4.5.14 - 4.4.16 2.12.7 - 2.17.2 - 2.17.2 - 1.7.0 4.4.0 1.5.6 0.6.12 3.12.1 2.0.0-M15 - 2.10.1 - 2.3.32 2.0.1 5.6.0 3.9.3 3.25.5 - 1.63.0 + 1.68.1 1.2.8 - 1.18.32 + 1.18.38 1.2.5 1.2.5 - 4.1.119.Final - 2.0.65.Final - 1.1.18 - 1.0.4 - 3.6.12 1.7.1 - 5.21.0 3.2.5 3.4.0 2.4.0TB @@ -105,11 +75,9 @@ 1.19.0 1.78.1 2.0.1 - 42.7.7 org/thingsboard/server/gen/**/*, org/thingsboard/server/extensions/core/plugin/telemetry/gen/**/* - 8.13.2 0.4.5 15.4 4.0.2 - 3.0.2 1.7.5 3.8.0 - 2.9.0 1.1.0 2.38.0 1.24 @@ -152,23 +116,17 @@ 0.27.0 1.7.0 - 4.2.1 2.7.3 1.5.6 - 5.10.5 5.15.0 1.3.0 1.2.7 5.0.0 7.10.1 - 3.25.3 - 5.4.0 - 2.2 - 1.20.4 - 1.0.1 + 1.20.6 + 1.0.2 1.12 - 4.19.1 5.8.0 2.27.0 2.12.0 @@ -935,6 +893,21 @@ + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + dev.langchain4j + langchain4j-bom + ${langchain4j.version} + pom + import + + org.thingsboard netty-mqtt @@ -1150,100 +1123,11 @@ test-jar test - - jakarta.annotation - jakarta.annotation-api - ${jakarta-annotation.version} - - - jakarta.xml.bind - jakarta.xml.bind-api - ${jakarta.xml.bind-api.version} - javax.xml.bind jaxb-api ${javax.xml.bind-api.version} - - org.glassfish.jaxb - jaxb-runtime - ${jaxb-runtime.version} - - - org.apache.tomcat.embed - tomcat-embed-core - ${tomcat.version} - - - org.apache.tomcat.embed - tomcat-embed-el - ${tomcat.version} - - - org.apache.tomcat.embed - tomcat-embed-websocket - ${tomcat.version} - - - - net.minidev - json-smart - ${net.minidev.json-smart} - - - - org.springframework.boot - spring-boot-starter - ${spring-boot.version} - - - org.springframework.boot - spring-boot-starter-security - ${spring-boot.version} - - - org.springframework.security - spring-security-oauth2-client - ${spring-security.version} - - - org.springframework.security - spring-security-oauth2-jose - ${spring-security.version} - - - - org.springframework.security - spring-security-config - ${spring-security.version} - - - org.springframework.security - spring-security-web - ${spring-security.version} - - - - org.springframework - spring-core - ${spring.version} - - - org.springframework.boot - spring-boot-starter-web - ${spring-boot.version} - - - org.springframework.boot - spring-boot-starter-websocket - ${spring-boot.version} - - - org.springframework.boot - spring-boot-autoconfigure - ${spring-boot.version} - org.springframework.boot spring-boot-starter-test @@ -1256,72 +1140,11 @@ - - org.springframework.boot - spring-boot-starter-data-jpa - ${spring-boot.version} - - - org.springframework.data - spring-data-commons - ${spring-data.version} - - - org.springframework.boot - spring-boot-starter-webflux - ${spring-boot.version} - - - io.projectreactor.netty - reactor-netty-http - ${reactor-netty.version} - - - org.reactivestreams - reactive-streams - ${reactive-streams.version} - - - io.projectreactor - reactor-core - ${reactor-core.version} - org.apache.kafka kafka-clients ${kafka.version} - - org.postgresql - postgresql - ${postgresql.driver.version} - - - org.springframework - spring-context - ${spring.version} - - - org.springframework - spring-context-support - ${spring.version} - - - org.springframework - spring-tx - ${spring.version} - - - org.springframework - spring-web - ${spring.version} - - - org.springframework.security - spring-security-test - ${spring-security.version} - test - com.github.springtestdbunit spring-test-dbunit @@ -1333,11 +1156,6 @@ jjwt ${jjwt.version} - - org.freemarker - freemarker - ${freemarker.version} - org.yaml snakeyaml @@ -1348,11 +1166,6 @@ antlr ${antlr.version} - - com.rabbitmq - amqp-client - ${rabbitmq.version} - com.sun.mail jakarta.mail @@ -1380,150 +1193,16 @@ - - com.jayway.jsonpath - json-path - ${json-path.version} - - - com.jayway.jsonpath - json-path-assert - ${json-path.version} - test - - - io.netty - netty-all - ${netty.version} - - - io.netty - netty-tcnative-boringssl-static - ${netty-tcnative.version} - - - io.netty - netty-tcnative-classes - ${netty-tcnative.version} - - - io.netty - netty-buffer - ${netty.version} - - - io.netty - netty-codec - ${netty.version} - - - io.netty - netty-codec-dns - ${netty.version} - - - io.netty - netty-codec-http - ${netty.version} - - - io.netty - netty-codec-http2 - ${netty.version} - - - io.netty - netty-codec-mqtt - ${netty.version} - - - io.netty - netty-codec-socks - ${netty.version} - - - io.netty - netty-common - ${netty.version} - - - io.netty - netty-handler - ${netty.version} - - - io.netty - netty-handler-proxy - ${netty.version} - - - io.netty - netty-resolver - ${netty.version} - - - io.netty - netty-resolver-dns - ${netty.version} - - - io.netty - netty-resolver-dns-classes-macos - ${netty.version} - io.netty netty-resolver-dns-native-macos - ${netty.version} - - - io.netty - netty-resolver-dns-native-macos - ${netty.version} osx-x86_64 - - io.netty - netty-transport - ${netty.version} - - - io.netty - netty-transport-classes-epoll - ${netty.version} - - - io.netty - netty-transport-classes-kqueue - ${netty.version} - - - io.netty - netty-transport-native-epoll - ${netty.version} - - - io.netty - netty-transport-native-epoll - ${netty.version} - linux-x86_64 - - - io.netty - netty-transport-native-kqueue - ${netty.version} - io.netty netty-transport-native-kqueue - ${netty.version} osx-x86_64 - - io.netty - netty-transport-native-unix-common - ${netty.version} - com.datastax.oss java-driver-core @@ -1539,21 +1218,11 @@ metrics-jmx ${metrics.version} - - org.apache.commons - commons-lang3 - ${commons-lang3.version} - commons-io commons-io ${commons-io.version} - - commons-codec - commons-codec - ${commons-codec.version} - commons-logging commons-logging @@ -1564,16 +1233,6 @@ commons-csv ${commons-csv.version} - - org.apache.httpcomponents.client5 - httpclient5 - ${apache-httpclient5.version} - - - org.apache.httpcomponents.core5 - httpcore5 - ${apache-httpcore5.version} - org.apache.httpcomponents httpclient @@ -1585,66 +1244,11 @@ - - org.apache.httpcomponents - httpcore - ${apache-httpcore.version} - joda-time joda-time ${joda-time.version} - - com.fasterxml.jackson.core - jackson-databind - ${jackson-databind.version} - - - com.fasterxml.jackson.core - jackson-core - ${jackson.version} - - - com.fasterxml.jackson.core - jackson-annotations - ${jackson.version} - - - com.fasterxml.jackson.dataformat - jackson-dataformat-cbor - ${jackson.version} - - - com.fasterxml.jackson.dataformat - jackson-dataformat-yaml - ${jackson.version} - - - com.fasterxml.jackson.datatype - jackson-datatype-jdk8 - ${jackson.version} - - - com.fasterxml.jackson.datatype - jackson-datatype-joda - ${jackson.version} - - - com.fasterxml.jackson.datatype - jackson-datatype-jsr310 - ${jackson.version} - - - com.fasterxml.jackson.module - jackson-module-parameter-names - ${jackson.version} - - - com.fasterxml - classmate - ${fasterxml-classmate.version} - com.auth0 java-jwt @@ -1699,61 +1303,11 @@ scandium ${californium.version} - - com.google.code.gson - gson - ${gson.version} - - - org.slf4j - slf4j-api - ${slf4j.version} - - - org.slf4j - log4j-over-slf4j - ${slf4j.version} - - - org.slf4j - jul-to-slf4j - ${slf4j.version} - - - org.apache.logging.log4j - log4j-api - ${log4j.version} - - - org.apache.logging.log4j - log4j-core - ${log4j.version} - - - org.apache.logging.log4j - log4j-to-slf4j - ${log4j.version} - - - ch.qos.logback - logback-core - ${logback.version} - - - ch.qos.logback - logback-classic - ${logback.version} - com.google.guava guava ${guava.version} - - com.github.ben-manes.caffeine - caffeine - ${caffeine.version} - com.google.protobuf protobuf-java @@ -1849,12 +1403,6 @@ tbel ${tbel.version} - - org.springframework - spring-test - ${spring.version} - test - io.takari.junit takari-cpsuite @@ -1872,42 +1420,12 @@ cassandra-all ${cassandra-all.version} - - org.junit.vintage - junit-vintage-engine - ${jupiter.version} - test - - - org.hamcrest - hamcrest-core - - - org.testng testng ${testng.version} test - - org.assertj - assertj-core - ${assertj.version} - test - - - io.rest-assured - rest-assured - ${rest-assured.version} - test - - - org.seleniumhq.selenium - selenium-java - ${selenium.version} - test - io.github.bonigarcia webdrivermanager @@ -1926,18 +1444,6 @@ ${allure-maven.version} test - - org.hamcrest - hamcrest - ${hamcrest.version} - test - - - org.awaitility - awaitility - ${awaitility.version} - test - org.dbunit dbunit @@ -1995,40 +1501,6 @@ bcprov-ext-jdk18on ${bouncycastle.version} - - org.testcontainers - cassandra - ${testcontainers.version} - test - - - org.testcontainers - postgresql - ${testcontainers.version} - test - - - org.testcontainers - jdbc - ${testcontainers.version} - test - - - org.testcontainers - hivemq - ${testcontainers.version} - test - - - org.springframework.data - spring-data-redis - ${spring-data-redis.version} - - - org.springframework.integration - spring-integration-redis - ${spring-redis.version} - redis.clients jedis @@ -2042,17 +1514,6 @@ exe provided - - org.elasticsearch.client - elasticsearch-rest-client - ${elasticsearch.version} - - - commons-logging - commons-logging - - - org.javadelight delight-nashorn-sandbox @@ -2181,21 +1642,6 @@ ${java-websocket.version} test - - org.springframework.boot - spring-boot-starter-actuator - ${spring-boot.version} - - - io.micrometer - micrometer-core - ${micrometer.version} - - - io.micrometer - micrometer-registry-prometheus - ${micrometer.version} - org.thingsboard protobuf-dynamic @@ -2225,11 +1671,6 @@ - - org.hibernate.validator - hibernate-validator - ${hibernate-validator.version} - io.hypersistence hypersistence-utils-hibernate-63 @@ -2240,11 +1681,6 @@ jakarta.el ${jakarta.el.version} - - jakarta.validation - jakarta.validation-api - ${jakarta.validation-api.version} - org.owasp.antisamy antisamy @@ -2437,13 +1873,6 @@ threetenbp ${threetenbp.version} - - dev.langchain4j - langchain4j-bom - ${langchain4j.version} - pom - import - From 560189dab934c1ba86ee6dacf756e589a4eb10fb Mon Sep 17 00:00:00 2001 From: VIacheslavKlimov Date: Tue, 22 Jul 2025 13:36:59 +0300 Subject: [PATCH 2/6] Fix CVE-2025-52520, CVE-2025-53506, CVE-2025-48924 --- pom.xml | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/pom.xml b/pom.xml index 552250421c..3e0f225606 100755 --- a/pom.xml +++ b/pom.xml @@ -39,6 +39,7 @@ /var/log/${pkg.name} /usr/share/${pkg.name} 3.4.7 + 10.1.43 2.4.0-b180830.0359 5.1.5 0.12.5 @@ -47,6 +48,7 @@ 4.2.25 5.0.4 33.1.0-jre + 3.18.0 2.16.1 1.3.1 1.10.0 @@ -1128,6 +1130,21 @@ jaxb-api ${javax.xml.bind-api.version} + + org.apache.tomcat.embed + tomcat-embed-core + ${tomcat.version} + + + org.apache.tomcat.embed + tomcat-embed-el + ${tomcat.version} + + + org.apache.tomcat.embed + tomcat-embed-websocket + ${tomcat.version} + org.springframework.boot spring-boot-starter-test @@ -1218,6 +1235,11 @@ metrics-jmx ${metrics.version} + + org.apache.commons + commons-lang3 + ${commons-lang3.version} + commons-io commons-io From 43425961d65d98e209cc7c85faa630cdd8d01bbe Mon Sep 17 00:00:00 2001 From: VIacheslavKlimov Date: Tue, 22 Jul 2025 15:04:04 +0300 Subject: [PATCH 3/6] Fix CVE-2023-52428 --- pom.xml | 7 +++++++ rule-engine/rule-engine-components/pom.xml | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/pom.xml b/pom.xml index 3e0f225606..8dfa6ef9ec 100755 --- a/pom.xml +++ b/pom.xml @@ -121,6 +121,7 @@ 2.7.3 1.5.6 5.15.0 + 9.37.2 1.3.0 1.2.7 5.0.0 @@ -1777,6 +1778,12 @@ + + com.nimbusds + nimbus-jose-jwt + ${nimbus-jose-jwt.version} + test + org.mock-server mockserver-client-java diff --git a/rule-engine/rule-engine-components/pom.xml b/rule-engine/rule-engine-components/pom.xml index 8fb92b80fd..a914b2c8f0 100644 --- a/rule-engine/rule-engine-components/pom.xml +++ b/rule-engine/rule-engine-components/pom.xml @@ -144,6 +144,11 @@ mockserver-netty test + + com.nimbusds + nimbus-jose-jwt + test + org.mock-server mockserver-client-java From 5e566d9d95d7433638b8de63a52c875e22fd2678 Mon Sep 17 00:00:00 2001 From: VIacheslavKlimov Date: Tue, 22 Jul 2025 17:14:58 +0300 Subject: [PATCH 4/6] Fix id generation issues after updating to Hibernate 6 --- .../server/dao/model/BaseSqlEntity.java | 3 ++ .../server/dao/sql/IdGenerator.java | 33 +++++++++++++++++++ .../server/dao/sql/JpaAbstractDao.java | 5 +-- 3 files changed, 37 insertions(+), 4 deletions(-) create mode 100644 dao/src/main/java/org/thingsboard/server/dao/sql/IdGenerator.java 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 084df4455c..ae131ae300 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,12 +21,14 @@ 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 java.util.Arrays; import java.util.Collections; @@ -44,6 +46,7 @@ public abstract class BaseSqlEntity implements BaseEntity { @Id @Column(name = ModelConstants.ID_PROPERTY, columnDefinition = "uuid") + @UuidGenerator(style = UuidGenerator.Style.AUTO, algorithm = IdGenerator.class) 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 new file mode 100644 index 0000000000..b0ca2c1530 --- /dev/null +++ b/dao/src/main/java/org/thingsboard/server/dao/sql/IdGenerator.java @@ -0,0 +1,33 @@ +/** + * Copyright © 2016-2025 The Thingsboard Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.thingsboard.server.dao.sql; + +import com.datastax.oss.driver.api.core.uuid.Uuids; +import lombok.extern.slf4j.Slf4j; +import org.hibernate.engine.spi.SharedSessionContractImplementor; +import org.hibernate.id.uuid.UuidValueGenerator; + +import java.util.UUID; + +@Slf4j +public class IdGenerator implements UuidValueGenerator { + + @Override + public UUID generateUuid(SharedSessionContractImplementor session) { + return Uuids.timeBased(); + } + +} 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 6f0380752f..4a81bc38a9 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,7 +15,6 @@ */ 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; @@ -69,9 +68,7 @@ public abstract class JpaAbstractDao, D> log.debug("Saving entity {}", entity); boolean isNew = entity.getUuid() == null; if (isNew) { - UUID uuid = Uuids.timeBased(); - entity.setUuid(uuid); - entity.setCreatedTime(Uuids.unixTimestamp(uuid)); + entity.setCreatedTime(System.currentTimeMillis()); } try { entity = doSave(entity, isNew, flush); From 24ff462b311eacad7f213a04641392fdb2dc3af5 Mon Sep 17 00:00:00 2001 From: VIacheslavKlimov Date: Wed, 23 Jul 2025 16:18:52 +0300 Subject: [PATCH 5/6] Fixes for Hibernate 6 support --- .../server/dao/model/BaseSqlEntity.java | 5 +- .../server/dao/sql/IdGenerator.java | 35 +++++++- .../server/dao/sql/JpaAbstractDao.java | 85 +++++++++++++------ 3 files changed, 92 insertions(+), 33 deletions(-) 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) { From 223aae58eabee0aa6c3a5b22891c05879ae51d5e Mon Sep 17 00:00:00 2001 From: VIacheslavKlimov Date: Wed, 23 Jul 2025 19:03:09 +0300 Subject: [PATCH 6/6] Fix double version increment when creating device profiles --- .../thingsboard/server/dao/sql/JpaAbstractDao.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 e0d9f466ac..fd37b3c9dd 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 @@ -97,6 +97,11 @@ public abstract class JpaAbstractDao, D> entity = update(entity); } if (entity instanceof HasVersion 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); /* * flushing and then removing the entity from the persistence context so that it is not affected * by next flushes (e.g. when a transaction is committed) to avoid double version increment @@ -113,7 +118,7 @@ public abstract class JpaAbstractDao, D> private E create(E entity) { if (entity instanceof HasVersion versionedEntity) { - versionedEntity.setVersion(1L); + versionedEntity.setVersion(0L); } if (entity.getUuid() == null) { getEntityManager().persist(entity); @@ -147,11 +152,6 @@ public abstract class JpaAbstractDao, D> } 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); }