Skip to content

Commit 716e75b

Browse files
Merge pull request #137 from xdev-software/HSQL-support
Hsql support
2 parents f122366 + 47dc3fc commit 716e75b

File tree

23 files changed

+970
-160
lines changed

23 files changed

+970
-160
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 2.2.0
2+
3+
* Implemented ``@Query`` annotation with simple SQL-Selects
4+
15
# 2.1.0
26

37
* Implemented auto-id-generation for UUIDs.

docs/antora.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
name: ROOT
22
title: Spring-Data-Eclipse-Store
33
version: master
4-
display_version: '2.1.0'
4+
display_version: '2.2.0'
55
start_page: index.adoc
66
nav:
77
- modules/ROOT/nav.adoc
88
asciidoc:
99
attributes:
1010
product-name: 'Spring-Data-Eclipse-Store'
11-
display-version: '2.1.0'
12-
maven-version: '2.1.0'
11+
display-version: '2.2.0'
12+
maven-version: '2.2.0'
1313
page-editable: false
1414
page-out-of-support: false

docs/modules/ROOT/nav.adoc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
* xref:configuration.adoc[Configuration]
44
* xref:working-copies.adoc[Working Copies]
55
* xref:features/features.adoc[Features]
6+
** xref:features/ids.adoc[IDs]
67
** xref:features/lazies.adoc[Lazy References]
8+
** xref:features/queries.adoc[Queries]
79
** xref:features/transactions.adoc[Transactions]
810
** xref:features/versions.adoc[Versions]
9-
* xref:migration.adoc[Migration]
11+
* xref:migration.adoc[Migration from JPA]
1012
* xref:known-issues.adoc[Known issues]
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
= Features
22

3+
* xref:features/ids.adoc[IDs]
34
* xref:features/lazies.adoc[Lazy References]
5+
* xref:features/queries.adoc[Queries]
46
* xref:features/transactions.adoc[Transactions]
57
* xref:features/versions.adoc[Versions]
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
= Queries
2+
3+
== Keywords
4+
5+
It is possible to use **most of the standard query keywords** for repositories defined in Spring Data JPA: https://docs.spring.io/spring-data/jpa/reference/repositories/query-keywords-reference.html[Spring Data JPA - Repository query keywords].
6+
7+
Here are a few examples:
8+
9+
[source,java]
10+
----
11+
@Repository
12+
public interface UserRepository extends EclipseStoreRepository<User, Long>
13+
{
14+
List<User> findByFirstName(String firstName, String lastName);
15+
List<User> findByFirstNameAndLastName(String firstName, String lastName);
16+
List<User> findByDateOfBirthBefore(LocalDate date);
17+
List<User> findByAgeIn(List<Integer> ages);
18+
List<User> findByIsActiveFalse();
19+
}
20+
----
21+
22+
More examples are in the https://github.com/xdev-software/spring-data-eclipse-store/blob/develop/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/string/UserRepository.java[test-cases].
23+
24+
== Query by Example
25+
26+
Developers can also use https://docs.spring.io/spring-data/jpa/reference/repositories/query-by-example.html[Query by Example] if preferred.
27+
28+
An example:
29+
30+
[source,java]
31+
----
32+
public List<User> findAllUsersNamedMick()
33+
{
34+
final User probe = new User(1, "Mick", BigDecimal.TEN);
35+
return userRepository.findAll(Example.of(probe));
36+
}
37+
----
38+
39+
More examples are in the https://github.com/xdev-software/spring-data-eclipse-store/blob/develop/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/by/example/QueryByExampleTest.java[test-cases].
40+
41+
== @Query annotation
42+
43+
The support for a ``@Query``-Annotation is currently quite limited, but useful nonetheless.
44+
45+
To keep parse and execute SQL-Queries we use the https://github.com/npgall/cqengine[cqengine] by https://github.com/npgall[Niall Gallagher].
46+
It offers rudimentary support of some SQL-Queries, but not all.
47+
48+
[NOTE]
49+
====
50+
https://github.com/npgall/cqengine[cqengine] parses the SQL String as a SQLite-SQL-String and is therefore different from the https://docs.spring.io/spring-data/jpa/reference/jpa/query-methods.html#jpa.query-methods.at-query[HQL or JPQL] of Spring Data JPA.
51+
====
52+
53+
Here are some working examples:
54+
55+
[source,java]
56+
----
57+
public interface MyEntityRepository extends ListCrudRepository<MyEntity, Long>
58+
{
59+
@Query("SELECT * FROM MyEntity WHERE name = '?1'")
60+
List<MyEntity> findByName(String name);
61+
62+
@Query("SELECT * FROM MyEntity WHERE (name = '?1' AND age > ?2)")
63+
List<MyEntity> findByNameAndAgeGreaterThan(String name, int age);
64+
65+
@Query("SELECT * FROM MyEntity WHERE 'name' LIKE '%?1%'")
66+
List<MyEntity> findByNameContaining(String keyword);
67+
68+
@Query("SELECT * FROM MyEntity WHERE otherEntity IS NOT NULL")
69+
List<MyEntity> findWhereOtherEntityIsNotNull();
70+
}
71+
----
72+
73+
More examples are in the https://github.com/xdev-software/spring-data-eclipse-store/blob/develop/spring-data-eclipse-store/src/test/java/software/xdev/spring/data/eclipse/store/integration/isolated/tests/query/hsql/MyEntityRepository.java[test-cases].

docs/modules/ROOT/pages/known-issues.adoc

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,5 @@
11
= Known issues
22

3-
== Query annotations
4-
5-
In Spring-Data-JPA you can write a Query over methods of repositories like this:
6-
7-
[source,java,title="From https://github.com/spring-projects/spring-petclinic/blob/main/src/main/java/org/springframework/samples/petclinic/owner/OwnerRepository.java[spring-petclinic]"]
8-
----
9-
@Query("SELECT ptype FROM PetType ptype ORDER BY ptype.name")
10-
List<PetType> findPetTypes();
11-
----
12-
13-
We created https://github.com/xdev-software/spring-data-eclipse-store/issues/32[an issue] for that but right now we *do not support Query annotations*.
14-
153
== Data changes
164

175
There are two basic ways to keep your data up to date.

docs/modules/ROOT/pages/migration.adoc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
= Migration
1+
= Migration from JPA
22

33
Migrating from Spring Data JPA is very easy.
44
We implemented a https://github.com/xdev-software/spring-data-eclipse-store-migration[OpenRewrite recipe] for that.

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
<groupId>software.xdev</groupId>
88
<artifactId>spring-data-eclipse-store-root</artifactId>
9-
<version>2.1.0-SNAPSHOT</version>
9+
<version>2.2.0-SNAPSHOT</version>
1010
<packaging>pom</packaging>
1111

1212
<organization>

spring-data-eclipse-store-benchmark/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<parent>
66
<groupId>software.xdev</groupId>
77
<artifactId>spring-data-eclipse-store-root</artifactId>
8-
<version>2.1.0-SNAPSHOT</version>
8+
<version>2.2.0-SNAPSHOT</version>
99
</parent>
1010

1111
<artifactId>spring-data-eclipse-store-benchmark</artifactId>

spring-data-eclipse-store-demo/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<parent>
88
<groupId>software.xdev</groupId>
99
<artifactId>spring-data-eclipse-store-root</artifactId>
10-
<version>2.1.0-SNAPSHOT</version>
10+
<version>2.2.0-SNAPSHOT</version>
1111
</parent>
1212

1313
<artifactId>spring-data-eclipse-store-demo</artifactId>

spring-data-eclipse-store-jpa/pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<parent>
88
<groupId>software.xdev</groupId>
99
<artifactId>spring-data-eclipse-store-root</artifactId>
10-
<version>2.1.0-SNAPSHOT</version>
10+
<version>2.2.0-SNAPSHOT</version>
1111
</parent>
1212

1313
<artifactId>spring-data-eclipse-store-jpa</artifactId>

spring-data-eclipse-store/pom.xml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@
4747
<maven.compiler.release>${javaVersion}</maven.compiler.release>
4848

4949
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
50+
5051
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
5152

5253
<!-- Should be in sync with org.eclipse.store:integrations-spring-boot3 -->
@@ -163,6 +164,26 @@
163164
</exclusion>
164165
</exclusions>
165166
</dependency>
167+
168+
<dependency>
169+
<groupId>com.googlecode.cqengine</groupId>
170+
<artifactId>cqengine</artifactId>
171+
<version>3.6.0</version>
172+
<exclusions>
173+
<exclusion>
174+
<artifactId>kryo</artifactId>
175+
<groupId>com.esotericsoftware</groupId>
176+
</exclusion>
177+
<exclusion>
178+
<artifactId>kryo-serializers</artifactId>
179+
<groupId>de.javakaffee</groupId>
180+
</exclusion>
181+
<exclusion>
182+
<artifactId>sqlite-jdbc</artifactId>
183+
<groupId>org.xerial</groupId>
184+
</exclusion>
185+
</exclusions>
186+
</dependency>
166187

167188
<dependency>
168189
<groupId>org.junit.jupiter</groupId>

spring-data-eclipse-store/src/main/java/software/xdev/spring/data/eclipse/store/repository/Query.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,5 @@
4040
@QueryAnnotation
4141
public @interface Query
4242
{
43-
@SuppressWarnings("unused") String value() default "";
43+
String value() default "";
4444
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/*
2+
* Copyright © 2024 XDEV Software (https://xdev.software)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package software.xdev.spring.data.eclipse.store.repository.query;
17+
18+
import java.util.Objects;
19+
20+
import org.springframework.data.repository.query.QueryMethod;
21+
import org.springframework.data.repository.query.RepositoryQuery;
22+
23+
import software.xdev.spring.data.eclipse.store.core.EntityListProvider;
24+
import software.xdev.spring.data.eclipse.store.repository.query.antlr.HSqlQueryExecutor;
25+
import software.xdev.spring.data.eclipse.store.repository.support.copier.working.WorkingCopier;
26+
27+
28+
public class HSqlQueryProvider<T> implements RepositoryQuery
29+
{
30+
private final HSqlQueryExecutor<T> executor;
31+
private final String sqlValue;
32+
private final QueryMethod queryMethod;
33+
34+
public HSqlQueryProvider(
35+
final String sqlValue,
36+
final QueryMethod queryMethod,
37+
final Class<T> domainClass,
38+
final EntityListProvider entityListProvider,
39+
final WorkingCopier<T> copier
40+
)
41+
{
42+
this.queryMethod = queryMethod;
43+
this.executor = new HSqlQueryExecutor<>(
44+
Objects.requireNonNull(domainClass),
45+
Objects.requireNonNull(entityListProvider),
46+
copier
47+
);
48+
this.sqlValue = sqlValue;
49+
}
50+
51+
@Override
52+
public Object execute(final Object[] parameters)
53+
{
54+
return this.executor.execute(this.sqlValue, parameters);
55+
}
56+
57+
@Override
58+
public QueryMethod getQueryMethod()
59+
{
60+
return this.queryMethod;
61+
}
62+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
/*
2+
* Copyright © 2024 XDEV Software (https://xdev.software)
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package software.xdev.spring.data.eclipse.store.repository.query.antlr;
17+
18+
import java.time.LocalDate;
19+
import java.time.format.DateTimeFormatter;
20+
import java.util.Collection;
21+
import java.util.List;
22+
import java.util.Map;
23+
import java.util.TreeMap;
24+
import java.util.stream.Collectors;
25+
26+
import com.googlecode.cqengine.ConcurrentIndexedCollection;
27+
import com.googlecode.cqengine.IndexedCollection;
28+
import com.googlecode.cqengine.attribute.Attribute;
29+
import com.googlecode.cqengine.attribute.ReflectiveAttribute;
30+
import com.googlecode.cqengine.query.parser.sql.SQLParser;
31+
import com.googlecode.cqengine.resultset.ResultSet;
32+
33+
import software.xdev.spring.data.eclipse.store.core.EntityListProvider;
34+
import software.xdev.spring.data.eclipse.store.repository.access.AccessHelper;
35+
import software.xdev.spring.data.eclipse.store.repository.support.copier.working.WorkingCopier;
36+
37+
38+
public class HSqlQueryExecutor<T>
39+
{
40+
private final SQLParser<T> parser;
41+
private final EntityListProvider entityListProvider;
42+
private final Class<T> domainClass;
43+
private final WorkingCopier<T> copier;
44+
45+
public HSqlQueryExecutor(
46+
final Class<T> domainClass,
47+
final EntityListProvider entityListProvider,
48+
final WorkingCopier<T> copier)
49+
{
50+
this.domainClass = domainClass;
51+
this.parser = SQLParser.forPojoWithAttributes(domainClass, this.createAttributes(domainClass));
52+
this.entityListProvider = entityListProvider;
53+
this.copier = copier;
54+
}
55+
56+
public List<T> execute(final String sqlValue, final Object[] parameters)
57+
{
58+
final IndexedCollection<T> entities = new ConcurrentIndexedCollection<>();
59+
entities.addAll(this.entityListProvider.getEntityProvider(this.domainClass).toCollection());
60+
final String sqlStringWithReplacedValues = this.replacePlaceholders(sqlValue, parameters);
61+
final ResultSet<T> retrieve = this.parser.retrieve(entities, sqlStringWithReplacedValues);
62+
final List<T> results = retrieve.stream().toList();
63+
return this.copier.copy(results);
64+
}
65+
66+
private String replacePlaceholders(final String sqlValue, final Object[] parameters)
67+
{
68+
String stringWithReplacedValues = sqlValue;
69+
// Replace positional placeholders with actual parameter values
70+
for(int i = 0; i < parameters.length; i++)
71+
{
72+
final String placeholder = "\\?" + (i + 1);
73+
String value = parameters[i].toString();
74+
if(parameters[i] instanceof final Collection<?> collection)
75+
{
76+
value =
77+
collection.stream()
78+
.map(o -> "'" + o.toString() + "'")
79+
.collect(Collectors.joining(", ", "(", ")"));
80+
}
81+
if(parameters[i] instanceof final LocalDate localDate)
82+
{
83+
value = localDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
84+
}
85+
stringWithReplacedValues = stringWithReplacedValues.replaceAll(placeholder, value);
86+
}
87+
return stringWithReplacedValues;
88+
}
89+
90+
private <O> Map<String, ? extends Attribute<O, ?>> createAttributes(final Class<O> domainClass)
91+
{
92+
final Map<String, Attribute<O, ?>> attributes = new TreeMap<>();
93+
AccessHelper.getInheritedPrivateFieldsByName(domainClass).forEach(
94+
(fieldName, field) -> attributes.put(
95+
fieldName,
96+
new ReflectiveAttribute<>(
97+
domainClass,
98+
field.getType(),
99+
fieldName
100+
)
101+
)
102+
);
103+
return attributes;
104+
}
105+
}

0 commit comments

Comments
 (0)