diff --git a/.github/workflows/ci-hibernate-dialect.yaml b/.github/workflows/ci-hibernate-dialect.yaml index 6c20a52..620050f 100644 --- a/.github/workflows/ci-hibernate-dialect.yaml +++ b/.github/workflows/ci-hibernate-dialect.yaml @@ -20,7 +20,7 @@ jobs: strategy: matrix: - java: [ '17' ] + java: [ '17', '21' ] steps: - uses: actions/checkout@v4 diff --git a/hibernate-dialect/CHANGELOG.md b/hibernate-dialect/CHANGELOG.md index c0ed08d..420dd90 100644 --- a/hibernate-dialect/CHANGELOG.md +++ b/hibernate-dialect/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.9.5 ## + +- Added query hint for view index for "select * from ... where" queries + ## 0.9.4 ## - Fixed the generated bool value diff --git a/hibernate-dialect/src/main/java/tech/ydb/hibernate/dialect/YdbDialect.java b/hibernate-dialect/src/main/java/tech/ydb/hibernate/dialect/YdbDialect.java index b3f549a..41614b7 100644 --- a/hibernate-dialect/src/main/java/tech/ydb/hibernate/dialect/YdbDialect.java +++ b/hibernate-dialect/src/main/java/tech/ydb/hibernate/dialect/YdbDialect.java @@ -1,6 +1,7 @@ package tech.ydb.hibernate.dialect; import java.time.LocalDateTime; +import java.util.List; import org.hibernate.boot.model.FunctionContributions; import org.hibernate.boot.model.TypeContributions; import org.hibernate.dialect.Dialect; @@ -51,6 +52,7 @@ import org.hibernate.type.StandardBasicTypes; import tech.ydb.hibernate.dialect.exporter.EmptyExporter; import tech.ydb.hibernate.dialect.exporter.YdbIndexExporter; +import tech.ydb.hibernate.dialect.hint.IndexQueryHintHandler; import tech.ydb.hibernate.dialect.translator.YdbSqlAstTranslatorFactory; import tech.ydb.hibernate.dialect.types.LocalDateTimeJavaType; import tech.ydb.hibernate.dialect.types.LocalDateTimeJdbcType; @@ -118,6 +120,11 @@ public void initializeFunctionRegistry(FunctionContributions functionContributio ); } + @Override + public String getQueryHintString(String query, List hintList) { + return IndexQueryHintHandler.INSTANCE.addQueryHints(query, hintList); + } + @Override public LimitHandler getLimitHandler() { return LimitOffsetLimitHandler.INSTANCE; diff --git a/hibernate-dialect/src/main/java/tech/ydb/hibernate/dialect/hint/IndexQueryHintHandler.java b/hibernate-dialect/src/main/java/tech/ydb/hibernate/dialect/hint/IndexQueryHintHandler.java new file mode 100644 index 0000000..f397aab --- /dev/null +++ b/hibernate-dialect/src/main/java/tech/ydb/hibernate/dialect/hint/IndexQueryHintHandler.java @@ -0,0 +1,42 @@ +package tech.ydb.hibernate.dialect.hint; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * @author Kirill Kurdyukov + */ +public class IndexQueryHintHandler { + private static final Pattern SELECT_FROM_WHERE_QUERY_PATTERN = Pattern + .compile("^\\s*(select.+?from\\s+\\w+)(.+where.+)$", Pattern.CASE_INSENSITIVE); + + public static final String HINT_USE_INDEX = "use_index:"; + public static final IndexQueryHintHandler INSTANCE = new IndexQueryHintHandler(); + + public String addQueryHints(String query, List hints) { + if (hints.isEmpty()) { + return query; + } + + var useIndexes = new ArrayList(); + hints.forEach(hint -> { + if (hint.startsWith(HINT_USE_INDEX)) { + useIndexes.add(hint.substring(HINT_USE_INDEX.length())); + } + }); + + if (!useIndexes.isEmpty()) { + Matcher matcher = SELECT_FROM_WHERE_QUERY_PATTERN.matcher(query); + if (matcher.matches() && matcher.groupCount() > 1) { + String startToken = matcher.group(1); + String endToken = matcher.group(2); + + return startToken + " view " + String.join(", ", useIndexes) + endToken; + } + } + + return query; + } +} diff --git a/hibernate-dialect/src/test/java/tech/ydb/hibernate/student/StudentsRepositoryTest.java b/hibernate-dialect/src/test/java/tech/ydb/hibernate/student/StudentsRepositoryTest.java index de17063..1ef2a9e 100644 --- a/hibernate-dialect/src/test/java/tech/ydb/hibernate/student/StudentsRepositoryTest.java +++ b/hibernate-dialect/src/test/java/tech/ydb/hibernate/student/StudentsRepositoryTest.java @@ -143,7 +143,6 @@ void studentsAndCourses_Lazy_ManyToManyTest() { ); } - @Test void studentsByGroupName_Lazy_OneToManyTest() { inTransaction( @@ -160,6 +159,20 @@ void studentsByGroupName_Lazy_OneToManyTest() { ); } + @Test + void groupByGroupName_ViewIndex() { + inTransaction( + session -> { + Group group = session + .createQuery("FROM Group g WHERE g.name = 'M3439'", Group.class) + .addQueryHint("use_index:group_name_index") + .getSingleResult(); + + assertEquals("M3439", group.getName()); + } + ); + } + @Test void studentsByGroupName_Eager_OneToManyTest() { inTransaction( diff --git a/hibernate-dialect/src/test/java/tech/ydb/hibernate/student/entity/Group.java b/hibernate-dialect/src/test/java/tech/ydb/hibernate/student/entity/Group.java index cda1dd8..95b7fce 100644 --- a/hibernate-dialect/src/test/java/tech/ydb/hibernate/student/entity/Group.java +++ b/hibernate-dialect/src/test/java/tech/ydb/hibernate/student/entity/Group.java @@ -7,11 +7,10 @@ import jakarta.persistence.NamedQuery; import jakarta.persistence.OneToMany; import jakarta.persistence.Table; +import java.util.List; import lombok.Getter; import lombok.Setter; -import java.util.List; - /** * @author Kirill Kurdyukov */