Skip to content
This repository was archived by the owner on Aug 1, 2025. It is now read-only.

Commit 8316721

Browse files
committed
Prevent pagination from occurring while join fetching on germs
1 parent ad05e37 commit 8316721

File tree

4 files changed

+128
-72
lines changed

4 files changed

+128
-72
lines changed

src/main/java/org/brapi/test/BrAPITestServer/repository/BrAPIRepository.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import java.io.Serializable;
44
import java.util.List;
55
import java.util.Optional;
6+
import java.util.UUID;
67

78
import org.brapi.test.BrAPITestServer.model.entity.BrAPIPrimaryEntity;
89
import org.brapi.test.BrAPITestServer.service.SearchQueryBuilder;
@@ -16,6 +17,10 @@ public interface BrAPIRepository<T extends BrAPIPrimaryEntity, ID extends Serial
1617

1718
public Page<T> findAllBySearch(SearchQueryBuilder<T> searchQuery, Pageable pageReq);
1819

20+
public Page<UUID> findAllBySearchIdsOnly(SearchQueryBuilder<T> searchQuery, Pageable pageReq);
21+
22+
public Page<T> findAllBySearchNoPage(SearchQueryBuilder<T> searchQuery, Page<UUID> pagedIds, Pageable pageReq);
23+
1924
public Optional<T> findById(ID id);
2025

2126
public <S extends T> S save(S entity);

src/main/java/org/brapi/test/BrAPITestServer/repository/BrAPIRepositoryImpl.java

Lines changed: 56 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@ public BrAPIRepositoryImpl(JpaEntityInformation<T, ID> entityInformation, Entity
3030
this.entityManager = entityManager;
3131
}
3232

33+
// This is the method that should be used for simple entities with few collections and eager loading requirements.
3334
public Page<T> findAllBySearch(SearchQueryBuilder<T> searchQuery, Pageable pageReq) {
34-
searchQuery = applyUserId(searchQuery);
35+
applyUserId(searchQuery);
3536
List<T> content = getPagedContent(searchQuery, pageReq);
3637
Long totalCount = getTotalCount(searchQuery);
3738

@@ -40,6 +41,28 @@ public Page<T> findAllBySearch(SearchQueryBuilder<T> searchQuery, Pageable pageR
4041
return page;
4142
}
4243

44+
public Page<T> findAllBySearchNoPage(SearchQueryBuilder<T> searchQuery, Page<UUID> pagedIds, Pageable pageReq) {
45+
applyUserId(searchQuery);
46+
47+
var entities = searchEntitiesWithIds(searchQuery, pagedIds);
48+
49+
return new PageImpl<>(entities, pageReq, pagedIds.getTotalElements());
50+
}
51+
52+
53+
54+
// For entities that are complex and have lots of Collections and lazy loaded entities, it is more efficient to grab the IDs of the on Page,
55+
// and afterward fetch these collections using the Ids, this time without paging.
56+
public Page<UUID> findAllBySearchIdsOnly(SearchQueryBuilder<T> searchQuery, Pageable pageReq) {
57+
applyUserId(searchQuery);
58+
List<UUID> content = getPagedContentIdsOnly(searchQuery, pageReq);
59+
Long totalCount = getTotalCount(searchQuery);
60+
61+
Page<UUID> page = new PageImpl<>(content, pageReq, totalCount);
62+
63+
return page;
64+
}
65+
4366
public Optional<T> findById(ID id) {
4467
Optional<T> response = super.findById(id);
4568
if (response.isPresent()) {
@@ -82,16 +105,7 @@ public void fetchXrefs(Page<T> page, Class<T> searchClass) {
82105
page.forEach(entity -> entity.setExternalReferences(xrefByEntity.get(entity.getId())));
83106
}
84107

85-
private String getCurrentUserId() {
86-
SecurityContext context = SecurityContextHolder.getContext();
87-
String userId = "";
88-
if (context.getAuthentication().getPrincipal() != null) {
89-
userId = context.getAuthentication().getPrincipal().toString();
90-
}
91-
return userId;
92-
}
93-
94-
private SearchQueryBuilder<T> applyUserId(SearchQueryBuilder<T> searchQuery) {
108+
private void applyUserId(SearchQueryBuilder<T> searchQuery) {
95109

96110
SecurityContext context = SecurityContextHolder.getContext();
97111
Set<String> userRolesSet = context.getAuthentication().getAuthorities().stream()
@@ -100,14 +114,12 @@ private SearchQueryBuilder<T> applyUserId(SearchQueryBuilder<T> searchQuery) {
100114
List<String> userIds = new ArrayList<>();
101115
userIds.add(getCurrentUserId());
102116
if (userRolesSet.contains("ROLE_ADMIN")) {
103-
return searchQuery;
117+
return;
104118
} else if (userRolesSet.contains("ROLE_USER")) {
105119
userIds.add("anonymousUser");
106120
}
107121

108122
searchQuery.appendList(userIds, "authUserId");
109-
110-
return searchQuery;
111123
}
112124

113125
private List<T> getPagedContent(SearchQueryBuilder<T> searchQuery, Pageable pageReq) {
@@ -125,6 +137,36 @@ private List<T> getPagedContent(SearchQueryBuilder<T> searchQuery, Pageable page
125137
return content;
126138
}
127139

140+
private List<T> searchEntitiesWithIds(SearchQueryBuilder<T> searchQuery, Page<UUID> ids) {
141+
searchQuery.appendList(ids.stream().map(UUID::toString).toList(), "id");
142+
143+
TypedQuery<T> query = entityManager.createQuery(searchQuery.getQuery(), searchQuery.getClazz());
144+
145+
for (Entry<String, Object> entry : searchQuery.getParams().entrySet()) {
146+
query.setParameter(entry.getKey(), entry.getValue());
147+
}
148+
149+
List<T> content = query.getResultList();
150+
return content; }
151+
152+
private List<UUID> getPagedContentIdsOnly(SearchQueryBuilder<T> searchQuery, Pageable pageReq) {
153+
154+
TypedQuery<UUID> query = entityManager.createQuery(searchQuery.getIdQuery(), UUID.class);
155+
156+
for (Entry<String, Object> entry : searchQuery.getParams().entrySet()) {
157+
query.setParameter(entry.getKey(), entry.getValue());
158+
}
159+
160+
query.setFirstResult((int) pageReq.getOffset());
161+
query.setMaxResults(pageReq.getPageSize());
162+
163+
List<UUID> content = query.getResultList();
164+
return content;
165+
}
166+
167+
168+
169+
128170
private Long getTotalCount(SearchQueryBuilder<T> searchQuery) {
129171
String countQueryStr = searchQuery.getQuery()
130172
.replaceFirst("(select|Select|SELECT)( distinct)? ([^\\s]*) ", "select count($2 $3) ")

src/main/java/org/brapi/test/BrAPITestServer/service/SearchQueryBuilder.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,15 @@
1616
public class SearchQueryBuilder<T> {
1717

1818
private String selectClause;
19+
private String selectOnlyIds;
1920
private String whereClause;
2021
private String sortClause;
2122
private Map<String, Object> params;
2223
private Class<T> clazz;
2324

2425
public SearchQueryBuilder(Class<T> clazz) {
2526
this.selectClause = "SELECT distinct entity FROM " + clazz.getSimpleName() + " entity ";
27+
this.selectOnlyIds = "SELECT entity.id FROM " + clazz.getSimpleName() + " entity ";
2628
this.whereClause = "WHERE 1=1 ";
2729
this.sortClause = "";
2830
this.params = new HashMap<>();
@@ -33,6 +35,8 @@ public String getQuery() {
3335
return selectClause + whereClause + sortClause;
3436
}
3537

38+
public String getIdQuery() { return selectOnlyIds + whereClause + sortClause;}
39+
3640
public Map<String, Object> getParams() {
3741
return params;
3842
}
@@ -221,6 +225,7 @@ public SearchQueryBuilder<T> withExRefs(List<String> exRefIds, List<String> exRe
221225

222226
public SearchQueryBuilder<T> join(String join, String name) {
223227
this.selectClause += "JOIN " + entityPrefix(join) + " " + paramFilter(name) + " ";
228+
this.selectOnlyIds += "JOIN " + entityPrefix(join) + " " + paramFilter(name) + " ";
224229
return this;
225230
}
226231

src/main/java/org/brapi/test/BrAPITestServer/service/germ/GermplasmService.java

Lines changed: 62 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ public Page<GermplasmEntity> findGermplasmEntities(@Valid GermplasmSearchRequest
137137
"*institute.instituteCode");
138138
}
139139

140-
searchQuery.withExRefs(request.getExternalReferenceIDs(), request.getExternalReferenceSources())
140+
searchQuery.withExRefs(request.getExternalReferenceIDs(), request.getExternalReferenceSources())
141141
.appendList(request.getAccessionNumbers(), "accessionNumber")
142142
.appendList(request.getCollections(), "collection")
143143
.appendList(request.getCommonCropNames(), "crop.cropName").appendList(request.getGermplasmDbIds(), "id")
@@ -149,146 +149,150 @@ public Page<GermplasmEntity> findGermplasmEntities(@Valid GermplasmSearchRequest
149149
.appendNamesList(request.getBinomialNames(), "genus", "genus", "species")
150150
.appendList(request.getFamilyCodes(), "familyCode");
151151

152-
Page<GermplasmEntity> page = germplasmRepository.findAllBySearch(searchQuery, pageReq);
152+
Page<UUID> page = germplasmRepository.findAllBySearchIdsOnly(searchQuery, pageReq);
153153

154-
if(!page.isEmpty()) {
155-
log.debug("fetching xrefs");
156-
fetchXrefs(page);
154+
Page<GermplasmEntity> germs = germplasmRepository.findAllBySearchNoPage(searchQuery, page, pageReq);
155+
156+
if (!page.isEmpty()) {
157+
log.debug("Fetching xrefs");
158+
fetchXrefs(page, germs);
157159
log.debug("fetching attributes");
158-
fetchAttributes(page);
160+
fetchAttributes(page, germs);
159161
log.debug("fetching donors");
160-
fetchDonors(page);
162+
fetchDonors(page, germs);
161163
log.debug("fetching origins");
162-
fetchOrigin(page);
164+
fetchOrigin(page, germs);
163165
log.debug("fetching institutes");
164-
fetchInstitutes(page);
166+
fetchInstitutes(page, germs);
165167
log.debug("fetching taxons");
166-
fetchTaxons(page);
168+
fetchTaxons(page, germs);
167169
log.debug("fetching storage codes");
168-
fetchStorageCodes(page);
170+
fetchStorageCodes(page, germs);
169171
log.debug("fetching pedigree edges");
170-
fetchPedigreeEdges(page);
172+
fetchPedigreeEdges(page, germs);
171173
}
172-
173-
return page;
174+
return germs;
174175
}
175176

176-
private void fetchXrefs(Page<GermplasmEntity> page) {
177+
private void fetchXrefs(Page<UUID> page, Page<GermplasmEntity> germEntities) {
177178
SearchQueryBuilder<GermplasmEntity> searchQuery = new SearchQueryBuilder<GermplasmEntity>(GermplasmEntity.class);
178-
searchQuery.leftJoinFetch("externalReferences", "externalReferences")
179-
.leftJoinFetch("pedigree", "pedigree")
180-
.appendList(page.stream().map(BrAPIBaseEntity::getId).collect(Collectors.toList()), "id");
179+
searchQuery.leftJoinFetch("externalReferences", "externalReferences");
181180

182-
Page<GermplasmEntity> xrefs = germplasmRepository.findAllBySearch(searchQuery, PageRequest.of(0, page.getSize()));
181+
Page<GermplasmEntity> xrefs = germplasmRepository.findAllBySearchNoPage(searchQuery, page, PageRequest.of(0, page.getSize()));
183182

184183
Map<String, List<ExternalReferenceEntity>> xrefByEntity = new HashMap<>();
185184
xrefs.forEach(entity -> xrefByEntity.put(entity.getId(), entity.getExternalReferences()));
186185

187-
page.forEach(entity -> entity.setExternalReferences(xrefByEntity.get(entity.getId())));
186+
germEntities.forEach(entity -> entity.setExternalReferences(xrefByEntity.get(entity.getId().toString())));
188187
}
189188

190-
private void fetchAttributes(Page<GermplasmEntity> page) {
189+
private void fetchAttributes(Page<UUID> page, Page<GermplasmEntity> germEntities) {
191190
SearchQueryBuilder<GermplasmEntity> searchQuery = new SearchQueryBuilder<GermplasmEntity>(
192191
GermplasmEntity.class);
193-
searchQuery.leftJoinFetch("attributes", "attributes")
194-
.appendList(page.stream().map(BrAPIBaseEntity::getId).collect(Collectors.toList()), "id");
192+
searchQuery.leftJoinFetch("attributes", "attributes");
195193

196-
Page<GermplasmEntity> attributes = germplasmRepository.findAllBySearch(searchQuery, PageRequest.of(0, page.getSize()));
194+
Page<GermplasmEntity> attributes = germplasmRepository.findAllBySearchNoPage(searchQuery,
195+
page,
196+
PageRequest.of(0, page.getSize()));
197197

198198
Map<String, List<GermplasmAttributeValueEntity>> attributesByGerm = new HashMap<>();
199199
attributes.forEach(germ -> attributesByGerm.put(germ.getId(), germ.getAttributes()));
200200

201-
page.forEach(germ -> germ.setAttributes(attributesByGerm.get(germ.getId())));
201+
germEntities.forEach(germ -> germ.setAttributes(attributesByGerm.get(germ.getId().toString())));
202202
}
203203

204-
private void fetchDonors(Page<GermplasmEntity> page) {
204+
private void fetchDonors(Page<UUID> page, Page<GermplasmEntity> germEntities) {
205205
SearchQueryBuilder<GermplasmEntity> searchQuery = new SearchQueryBuilder<GermplasmEntity>(
206206
GermplasmEntity.class);
207-
searchQuery.leftJoinFetch("donors", "donors")
208-
.appendList(page.stream().map(BrAPIBaseEntity::getId).collect(Collectors.toList()), "id");
207+
searchQuery.leftJoinFetch("donors", "donors");
209208

210-
Page<GermplasmEntity> donors = germplasmRepository.findAllBySearch(searchQuery, PageRequest.of(0, page.getSize()));
209+
Page<GermplasmEntity> donors = germplasmRepository.findAllBySearchNoPage(searchQuery,
210+
page,
211+
PageRequest.of(0, page.getSize()));
211212

212213
Map<String, List<DonorEntity>> donorsByGerm = new HashMap<>();
213214
donors.forEach(germ -> donorsByGerm.put(germ.getId(), germ.getDonors()));
214215

215-
page.forEach(germ -> germ.setDonors(donorsByGerm.get(germ.getId())));
216+
germEntities.forEach(germ -> germ.setDonors(donorsByGerm.get(germ.getId().toString())));
216217
}
217218

218-
private void fetchOrigin(Page<GermplasmEntity> page) {
219+
private void fetchOrigin(Page<UUID> page, Page<GermplasmEntity> germEntities) {
219220
SearchQueryBuilder<GermplasmEntity> searchQuery = new SearchQueryBuilder<GermplasmEntity>(
220221
GermplasmEntity.class);
221-
searchQuery.leftJoinFetch("germplasmOrigin", "germplasmOrigin")
222-
.appendList(page.stream().map(BrAPIBaseEntity::getId).collect(Collectors.toList()), "id");
222+
searchQuery.leftJoinFetch("germplasmOrigin", "germplasmOrigin");
223223

224-
Page<GermplasmEntity> origins = germplasmRepository.findAllBySearch(searchQuery, PageRequest.of(0, page.getSize()));
224+
Page<GermplasmEntity> origins = germplasmRepository.findAllBySearchNoPage(searchQuery,
225+
page,
226+
PageRequest.of(0, page.getSize()));
225227

226228
Map<String, List<GermplasmOriginEntity>> originsByGerm = new HashMap<>();
227229
origins.forEach(germ -> originsByGerm.put(germ.getId(), germ.getGermplasmOrigin()));
228230

229-
page.forEach(germ -> germ.setGermplasmOrigin(originsByGerm.get(germ.getId())));
231+
germEntities.forEach(germ -> germ.setGermplasmOrigin(originsByGerm.get(germ.getId().toString())));
230232
}
231233

232-
private void fetchInstitutes(Page<GermplasmEntity> page) {
234+
private void fetchInstitutes(Page<UUID> page, Page<GermplasmEntity> germEntities) {
233235
SearchQueryBuilder<GermplasmEntity> searchQuery = new SearchQueryBuilder<GermplasmEntity>(
234236
GermplasmEntity.class);
235-
searchQuery.leftJoinFetch("institutes", "institutes")
236-
.appendList(page.stream().map(BrAPIBaseEntity::getId).collect(Collectors.toList()), "id");
237+
searchQuery.leftJoinFetch("institutes", "institutes");
237238

238-
Page<GermplasmEntity> institutes = germplasmRepository.findAllBySearch(searchQuery, PageRequest.of(0, page.getSize()));
239+
Page<GermplasmEntity> institutes = germplasmRepository.findAllBySearchNoPage(searchQuery,
240+
page,
241+
PageRequest.of(0, page.getSize()));
239242

240243
Map<String, List<GermplasmInstituteEntity>> institutesByGerm = new HashMap<>();
241244
institutes.forEach(germ -> institutesByGerm.put(germ.getId(), germ.getInstitutes()));
242245

243-
page.forEach(germ -> germ.setInstitutes(institutesByGerm.get(germ.getId())));
246+
germEntities.forEach(germ -> germ.setInstitutes(institutesByGerm.get(germ.getId().toString())));
244247
}
245248

246-
private void fetchTaxons(Page<GermplasmEntity> page) {
249+
private void fetchTaxons(Page<UUID> page, Page<GermplasmEntity> germEntities) {
247250
SearchQueryBuilder<GermplasmEntity> searchQuery = new SearchQueryBuilder<GermplasmEntity>(
248251
GermplasmEntity.class);
249-
searchQuery.leftJoinFetch("taxonIds", "taxonIds")
250-
.appendList(page.stream().map(BrAPIBaseEntity::getId).collect(Collectors.toList()), "id");
252+
searchQuery.leftJoinFetch("taxonIds", "taxonIds");
251253

252-
Page<GermplasmEntity> taxonIds = germplasmRepository.findAllBySearch(searchQuery, PageRequest.of(0, page.getSize()));
254+
Page<GermplasmEntity> taxonIds = germplasmRepository.findAllBySearchNoPage(searchQuery,
255+
page,
256+
PageRequest.of(0, page.getSize()));
253257

254258
Map<String, List<TaxonEntity>> taxonIdsByGerm = new HashMap<>();
255259
taxonIds.forEach(germ -> taxonIdsByGerm.put(germ.getId(), germ.getTaxonIds()));
256260

257-
page.forEach(germ -> germ.setTaxonIds(taxonIdsByGerm.get(germ.getId())));
261+
germEntities.forEach(germ -> germ.setTaxonIds(taxonIdsByGerm.get(germ.getId().toString())));
258262
}
259263

260-
private void fetchStorageCodes(Page<GermplasmEntity> page) {
264+
private void fetchStorageCodes(Page<UUID> page, Page<GermplasmEntity> germEntities) {
261265
SearchQueryBuilder<GermplasmEntity> searchQuery = new SearchQueryBuilder<GermplasmEntity>(
262266
GermplasmEntity.class);
263-
searchQuery.leftJoinFetch("typeOfGermplasmStorageCode", "typeOfGermplasmStorageCode")
264-
.appendList(page.stream().map(BrAPIBaseEntity::getId).collect(Collectors.toList()), "id");
267+
searchQuery.leftJoinFetch("typeOfGermplasmStorageCode", "typeOfGermplasmStorageCode");
265268

266-
Page<GermplasmEntity> storageCodes = germplasmRepository.findAllBySearch(searchQuery, PageRequest.of(0, page.getSize()));
269+
Page<GermplasmEntity> storageCodes = germplasmRepository.findAllBySearchNoPage(searchQuery,
270+
page,
271+
PageRequest.of(0, page.getSize()));
267272

268273
Map<String, List<GermplasmStorageTypesEnum>> storageCodesByGerm = new HashMap<>();
269274
storageCodes.forEach(germ -> storageCodesByGerm.put(germ.getId(), germ.getTypeOfGermplasmStorageCode()));
270275

271-
page.forEach(germ -> germ.setTypeOfGermplasmStorageCode(storageCodesByGerm.get(germ.getId())));
276+
germEntities.forEach(germ -> germ.setTypeOfGermplasmStorageCode(storageCodesByGerm.get(germ.getId().toString())));
272277
}
273278

274-
private void fetchPedigreeEdges(Page<GermplasmEntity> page) {
279+
private void fetchPedigreeEdges(Page<UUID> page, Page<GermplasmEntity> germEntities) {
275280
SearchQueryBuilder<GermplasmEntity> searchQuery = new SearchQueryBuilder<GermplasmEntity>(
276281
GermplasmEntity.class);
277282
searchQuery.leftJoinFetch("pedigree", "pedigree")
278283
.leftJoinFetch("*pedigree.crossingProject", "crossingProject")
279284
.leftJoinFetch("*pedigree.edges", "pedigreeEdges")
280-
.leftJoinFetch("*pedigreeEdges.connectedNode", "connectedNode")
281-
.appendList(page.stream()
282-
.map(BrAPIBaseEntity::getId)
283-
.collect(Collectors.toList()), "id");
285+
.leftJoinFetch("*pedigreeEdges.conncetedNode", "connectedNode");
284286

285-
Page<GermplasmEntity> pedigree = germplasmRepository.findAllBySearch(searchQuery, PageRequest.of(0, page.getSize()));
287+
Page<GermplasmEntity> pedigree = germplasmRepository.findAllBySearchNoPage(searchQuery,
288+
page,
289+
PageRequest.of(0, page.getSize()));
286290

287291
Map<String, PedigreeNodeEntity> pedigreeByGerm = new HashMap<>();
288292
pedigree.forEach(germ -> pedigreeByGerm.put(germ.getId(), germ.getPedigree()));
289293

290-
page.forEach(germ -> {
291-
germ.setPedigree(pedigreeByGerm.get(germ.getId()));
294+
germEntities.forEach(germ -> {
295+
germ.setPedigree(pedigreeByGerm.get(germ.getId().toString()));
292296
});
293297
}
294298

0 commit comments

Comments
 (0)