Skip to content

Commit cb8720a

Browse files
Merge pull request #89 from xdev-software/QueryByExample
Query by example
2 parents 2c38b6a + b4235a7 commit cb8720a

File tree

16 files changed

+1197
-59
lines changed

16 files changed

+1197
-59
lines changed

spring-data-eclipse-store-benchmark/src/main/java/software/xdev/spring/data/eclipse/store/benchmark/benchmarks/with/id/FindByIdCustomerWithAutoIdBenchmark.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import software.xdev.spring.data.eclipse.store.repository.config.EclipseStoreClientConfiguration;
1212

1313

14+
@SuppressWarnings("checkstyle:MagicNumber")
1415
public class FindByIdCustomerWithAutoIdBenchmark
1516
{
1617
public abstract static class ExistingCustomerSpringState extends SpringState
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
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.interfaces;
17+
18+
import org.springframework.data.repository.NoRepositoryBean;
19+
import org.springframework.data.repository.query.QueryByExampleExecutor;
20+
21+
22+
@NoRepositoryBean
23+
public interface EclipseStoreQueryByExampleExecutor<T> extends QueryByExampleExecutor<T>
24+
{
25+
}

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
public interface EclipseStoreRepository<T, ID>
2323
extends
2424
EclipseStoreListCrudRepository<T, ID>,
25-
EclipseStoreListPagingAndSortingRepositoryRepository<T, ID>
25+
EclipseStoreListPagingAndSortingRepositoryRepository<T, ID>,
26+
EclipseStoreQueryByExampleExecutor<T>
2627
{
2728
}

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

Lines changed: 4 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
import java.util.Iterator;
2020
import java.util.Objects;
2121

22+
import jakarta.annotation.Nonnull;
23+
import jakarta.annotation.Nullable;
24+
2225
import org.springframework.data.domain.Sort;
2326
import org.springframework.data.repository.query.ParameterAccessor;
2427
import org.springframework.data.repository.query.parser.AbstractQueryCreator;
@@ -28,10 +31,6 @@
2831
import org.springframework.data.util.TypeInformation;
2932
import org.springframework.util.ObjectUtils;
3033

31-
import jakarta.annotation.Nonnull;
32-
import jakarta.annotation.Nullable;
33-
import software.xdev.spring.data.eclipse.store.exceptions.FieldAccessReflectionException;
34-
import software.xdev.spring.data.eclipse.store.repository.access.AccessHelper;
3534
import software.xdev.spring.data.eclipse.store.repository.query.criteria.AbstractCriteriaNode;
3635
import software.xdev.spring.data.eclipse.store.repository.query.criteria.Criteria;
3736
import software.xdev.spring.data.eclipse.store.repository.query.criteria.CriteriaSingleNode;
@@ -249,16 +248,6 @@ private boolean isSimpleComparisonPossible(final Part part)
249248
private ReflectedField<T, ?> getDeclaredField(final Part part)
250249
{
251250
final String fieldName = part.getProperty().getSegment();
252-
try
253-
{
254-
return new ReflectedField<>(AccessHelper.getInheritedPrivateField(this.domainClass, fieldName));
255-
}
256-
catch(final NoSuchFieldException e)
257-
{
258-
throw new FieldAccessReflectionException(String.format(
259-
"Field %s in class %s was not found!",
260-
fieldName,
261-
this.domainClass.getSimpleName()), e);
262-
}
251+
return ReflectedField.createReflectedField(this.domainClass, fieldName);
263252
}
264253
}

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

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import java.util.Objects;
2020

2121
import jakarta.annotation.Nonnull;
22+
23+
import software.xdev.spring.data.eclipse.store.exceptions.FieldAccessReflectionException;
2224
import software.xdev.spring.data.eclipse.store.repository.access.AccessHelper;
2325

2426

@@ -38,6 +40,21 @@ public ReflectedField(final Field field)
3840
this.field = Objects.requireNonNull(field);
3941
}
4042

43+
public static <T, E> ReflectedField<T, E> createReflectedField(final Class<T> domainClass, final String fieldName)
44+
{
45+
try
46+
{
47+
return new ReflectedField<>(AccessHelper.getInheritedPrivateField(domainClass, fieldName));
48+
}
49+
catch(final NoSuchFieldException e)
50+
{
51+
throw new FieldAccessReflectionException(String.format(
52+
"Field %s in class %s was not found!",
53+
fieldName,
54+
domainClass.getSimpleName()), e);
55+
}
56+
}
57+
4158
/**
4259
* Reads the field of the given object. If the fields is not accessible, it is made accessible with the
4360
* {@link AccessHelper#readFieldVariable(Field, Object)}.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
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.by.example;
17+
18+
import java.util.Collection;
19+
import java.util.List;
20+
import java.util.stream.Stream;
21+
22+
import org.springframework.data.domain.Example;
23+
import org.springframework.data.domain.Page;
24+
import org.springframework.data.domain.Pageable;
25+
import org.springframework.data.domain.Sort;
26+
import org.springframework.data.repository.query.FluentQuery;
27+
28+
import software.xdev.spring.data.eclipse.store.repository.EclipseStoreStorage;
29+
import software.xdev.spring.data.eclipse.store.repository.query.criteria.CriteriaByExample;
30+
import software.xdev.spring.data.eclipse.store.repository.query.executors.CountQueryExecutor;
31+
import software.xdev.spring.data.eclipse.store.repository.query.executors.ExistsQueryExecutor;
32+
import software.xdev.spring.data.eclipse.store.repository.query.executors.ListQueryExecutor;
33+
import software.xdev.spring.data.eclipse.store.repository.query.executors.PageableQueryExecutor;
34+
import software.xdev.spring.data.eclipse.store.repository.query.executors.SingleQueryExecutor;
35+
import software.xdev.spring.data.eclipse.store.repository.support.copier.working.WorkingCopier;
36+
37+
38+
/**
39+
* Needed to support {@link QueryByExampleExecutor}.
40+
*/
41+
public class EclipseStoreFetchableFluentQuery<T, S extends T> implements FluentQuery.FetchableFluentQuery<S>
42+
{
43+
private final WorkingCopier<T> copier;
44+
private final Example<S> example;
45+
private final Class<T> domainClass;
46+
private final EclipseStoreStorage storage;
47+
private final Sort sort;
48+
49+
public EclipseStoreFetchableFluentQuery(
50+
final WorkingCopier<T> copier,
51+
final Example<S> example,
52+
final Class<T> domainClass,
53+
final EclipseStoreStorage storage,
54+
final Sort sort
55+
)
56+
{
57+
this.copier = copier;
58+
this.example = example;
59+
this.domainClass = domainClass;
60+
this.storage = storage;
61+
this.sort = sort;
62+
}
63+
64+
@Override
65+
public FetchableFluentQuery<S> sortBy(final Sort sort)
66+
{
67+
return new EclipseStoreFetchableFluentQuery(
68+
this.copier,
69+
this.example,
70+
this.domainClass,
71+
this.storage,
72+
sort
73+
);
74+
}
75+
76+
@Override
77+
public <R> FetchableFluentQuery<R> as(final Class<R> resultType)
78+
{
79+
throw new UnsupportedOperationException("The method as() is not yet supported");
80+
}
81+
82+
@Override
83+
public FetchableFluentQuery<S> project(final Collection<String> properties)
84+
{
85+
throw new UnsupportedOperationException("The method project() is not yet supported");
86+
}
87+
88+
@Override
89+
public S oneValue()
90+
{
91+
return this.firstValue();
92+
}
93+
94+
@Override
95+
public S firstValue()
96+
{
97+
final SingleQueryExecutor<T> query =
98+
new SingleQueryExecutor<>(this.copier, new CriteriaByExample<>((Example<T>)this.example), this.sort);
99+
return this.storage.getReadWriteLock().read(
100+
() ->
101+
(S)query.execute(
102+
this.domainClass,
103+
this.storage.getEntityList(this.domainClass),
104+
new Object[]{this.sort})
105+
);
106+
}
107+
108+
@Override
109+
public List<S> all()
110+
{
111+
final ListQueryExecutor<T> query =
112+
new ListQueryExecutor<>(this.copier, new CriteriaByExample<>(this.example));
113+
return this.storage.getReadWriteLock().read(
114+
() -> (List<S>)query.execute(this.domainClass, this.storage.getEntityList(this.domainClass), new Object[]{
115+
this.sort})
116+
);
117+
}
118+
119+
@Override
120+
public Page<S> page(final Pageable pageable)
121+
{
122+
final PageableQueryExecutor<T> pageableQuery =
123+
new PageableQueryExecutor<>(this.copier, new CriteriaByExample<>(this.example), this.sort);
124+
return this.storage.getReadWriteLock().read(
125+
() ->
126+
(Page<S>)pageableQuery.execute(
127+
this.domainClass,
128+
this.storage.getEntityList(this.domainClass),
129+
new Object[]{pageable, this.sort})
130+
);
131+
}
132+
133+
@Override
134+
public Stream<S> stream()
135+
{
136+
return this.all().stream();
137+
}
138+
139+
@Override
140+
public long count()
141+
{
142+
final CountQueryExecutor<T> query = new CountQueryExecutor<>(new CriteriaByExample<>(this.example));
143+
return this.storage.getReadWriteLock().read(
144+
() -> query.execute(this.domainClass, this.storage.getEntityList(this.domainClass), null)
145+
);
146+
}
147+
148+
@Override
149+
public boolean exists()
150+
{
151+
final ExistsQueryExecutor<T> query = new ExistsQueryExecutor<>(new CriteriaByExample<>(this.example));
152+
return this.storage.getReadWriteLock().read(
153+
() -> query.execute(this.domainClass, this.storage.getEntityList(this.domainClass), null)
154+
);
155+
}
156+
}

0 commit comments

Comments
 (0)