1
16 package org.springframework.data.jpa.repository.support;
17
18 import static org.springframework.data.jpa.repository.query.QueryUtils.*;
19
20 import java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.Collections;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Map.Entry;
26 import java.util.Optional;
27
28 import javax.persistence.EntityManager;
29 import javax.persistence.LockModeType;
30 import javax.persistence.NoResultException;
31 import javax.persistence.Parameter;
32 import javax.persistence.Query;
33 import javax.persistence.TypedQuery;
34 import javax.persistence.criteria.CriteriaBuilder;
35 import javax.persistence.criteria.CriteriaQuery;
36 import javax.persistence.criteria.Order;
37 import javax.persistence.criteria.ParameterExpression;
38 import javax.persistence.criteria.Path;
39 import javax.persistence.criteria.Predicate;
40 import javax.persistence.criteria.Root;
41
42 import org.springframework.dao.EmptyResultDataAccessException;
43 import org.springframework.data.domain.Example;
44 import org.springframework.data.domain.Page;
45 import org.springframework.data.domain.PageImpl;
46 import org.springframework.data.domain.Pageable;
47 import org.springframework.data.domain.Sort;
48 import org.springframework.data.jpa.convert.QueryByExamplePredicateBuilder;
49 import org.springframework.data.jpa.domain.Specification;
50 import org.springframework.data.jpa.provider.PersistenceProvider;
51 import org.springframework.data.jpa.repository.EntityGraph;
52 import org.springframework.data.jpa.repository.query.EscapeCharacter;
53 import org.springframework.data.jpa.repository.query.QueryUtils;
54 import org.springframework.data.jpa.repository.support.QueryHints.NoHints;
55 import org.springframework.data.repository.support.PageableExecutionUtils;
56 import org.springframework.data.util.ProxyUtils;
57 import org.springframework.lang.Nullable;
58 import org.springframework.stereotype.Repository;
59 import org.springframework.transaction.annotation.Transactional;
60 import org.springframework.util.Assert;
61
62
78 @Repository
79 @Transactional(readOnly = true)
80 public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
81
82 private static final String ID_MUST_NOT_BE_NULL = "The given id must not be null!";
83
84 private final JpaEntityInformation<T, ?> entityInformation;
85 private final EntityManager em;
86 private final PersistenceProvider provider;
87
88 private @Nullable CrudMethodMetadata metadata;
89 private EscapeCharacter escapeCharacter = EscapeCharacter.DEFAULT;
90
91 private static <T> Collection<T> toCollection(Iterable<T> ts) {
92
93 if (ts instanceof Collection) {
94 return (Collection<T>) ts;
95 }
96
97 List<T> tCollection = new ArrayList<T>();
98 for (T t : ts) {
99 tCollection.add(t);
100 }
101 return tCollection;
102 }
103
104
110 public SimpleJpaRepository(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
111
112 Assert.notNull(entityInformation, "JpaEntityInformation must not be null!");
113 Assert.notNull(entityManager, "EntityManager must not be null!");
114
115 this.entityInformation = entityInformation;
116 this.em = entityManager;
117 this.provider = PersistenceProvider.fromEntityManager(entityManager);
118 }
119
120
126 public SimpleJpaRepository(Class<T> domainClass, EntityManager em) {
127 this(JpaEntityInformationSupport.getEntityInformation(domainClass, em), em);
128 }
129
130
136 @Override
137 public void setRepositoryMethodMetadata(CrudMethodMetadata crudMethodMetadata) {
138 this.metadata = crudMethodMetadata;
139 }
140
141 @Override
142 public void setEscapeCharacter(EscapeCharacter escapeCharacter) {
143 this.escapeCharacter = escapeCharacter;
144 }
145
146 @Nullable
147 protected CrudMethodMetadata getRepositoryMethodMetadata() {
148 return metadata;
149 }
150
151 protected Class<T> getDomainClass() {
152 return entityInformation.getJavaType();
153 }
154
155 private String getDeleteAllQueryString() {
156 return getQueryString(DELETE_ALL_QUERY_STRING, entityInformation.getEntityName());
157 }
158
159 private String getCountQueryString() {
160
161 String countQuery = String.format(COUNT_QUERY_STRING, provider.getCountQueryPlaceholder(), "%s");
162 return getQueryString(countQuery, entityInformation.getEntityName());
163 }
164
165
169 @Transactional
170 @Override
171 public void deleteById(ID id) {
172
173 Assert.notNull(id, ID_MUST_NOT_BE_NULL);
174
175 delete(findById(id).orElseThrow(() -> new EmptyResultDataAccessException(
176 String.format("No %s entity with id %s exists!", entityInformation.getJavaType(), id), 1)));
177 }
178
179
183 @Override
184 @Transactional
185 @SuppressWarnings("unchecked")
186 public void delete(T entity) {
187
188 Assert.notNull(entity, "Entity must not be null!");
189
190 if (entityInformation.isNew(entity)) {
191 return;
192 }
193
194 Class<?> type = ProxyUtils.getUserClass(entity);
195
196 T existing = (T) em.find(type, entityInformation.getId(entity));
197
198
199 if (existing == null) {
200 return;
201 }
202
203 em.remove(em.contains(entity) ? entity : em.merge(entity));
204 }
205
206
210 @Transactional
211 @Override
212 public void deleteAll(Iterable<? extends T> entities) {
213
214 Assert.notNull(entities, "Entities must not be null!");
215
216 for (T entity : entities) {
217 delete(entity);
218 }
219 }
220
221
225 @Transactional
226 @Override
227 public void deleteInBatch(Iterable<T> entities) {
228
229 Assert.notNull(entities, "Entities must not be null!");
230
231 if (!entities.iterator().hasNext()) {
232 return;
233 }
234
235 applyAndBind(getQueryString(DELETE_ALL_QUERY_STRING, entityInformation.getEntityName()), entities, em)
236 .executeUpdate();
237 }
238
239
243 @Transactional
244 @Override
245 public void deleteAll() {
246
247 for (T element : findAll()) {
248 delete(element);
249 }
250 }
251
252
256 @Transactional
257 @Override
258 public void deleteAllInBatch() {
259 em.createQuery(getDeleteAllQueryString()).executeUpdate();
260 }
261
262
266 @Override
267 public Optional<T> findById(ID id) {
268
269 Assert.notNull(id, ID_MUST_NOT_BE_NULL);
270
271 Class<T> domainType = getDomainClass();
272
273 if (metadata == null) {
274 return Optional.ofNullable(em.find(domainType, id));
275 }
276
277 LockModeType type = metadata.getLockModeType();
278
279 Map<String, Object> hints = getQueryHints().withFetchGraphs(em).asMap();
280
281 return Optional.ofNullable(type == null ? em.find(domainType, id, hints) : em.find(domainType, id, type, hints));
282 }
283
284
290 protected QueryHints getQueryHints() {
291 return metadata == null ? NoHints.INSTANCE : DefaultQueryHints.of(entityInformation, metadata);
292 }
293
294
298 @Override
299 public T getOne(ID id) {
300
301 Assert.notNull(id, ID_MUST_NOT_BE_NULL);
302 return em.getReference(getDomainClass(), id);
303 }
304
305
309 @Override
310 public boolean existsById(ID id) {
311
312 Assert.notNull(id, ID_MUST_NOT_BE_NULL);
313
314 if (entityInformation.getIdAttribute() == null) {
315 return findById(id).isPresent();
316 }
317
318 String placeholder = provider.getCountQueryPlaceholder();
319 String entityName = entityInformation.getEntityName();
320 Iterable<String> idAttributeNames = entityInformation.getIdAttributeNames();
321 String existsQuery = QueryUtils.getExistsQueryString(entityName, placeholder, idAttributeNames);
322
323 TypedQuery<Long> query = em.createQuery(existsQuery, Long.class);
324
325 if (!entityInformation.hasCompositeId()) {
326 query.setParameter(idAttributeNames.iterator().next(), id);
327 return query.getSingleResult() == 1L;
328 }
329
330 for (String idAttributeName : idAttributeNames) {
331
332 Object idAttributeValue = entityInformation.getCompositeIdAttributeValue(id, idAttributeName);
333
334 boolean complexIdParameterValueDiscovered = idAttributeValue != null
335 && !query.getParameter(idAttributeName).getParameterType().isAssignableFrom(idAttributeValue.getClass());
336
337 if (complexIdParameterValueDiscovered) {
338
339
340 return findById(id).isPresent();
341 }
342
343 query.setParameter(idAttributeName, idAttributeValue);
344 }
345
346 return query.getSingleResult() == 1L;
347 }
348
349
353 @Override
354 public List<T> findAll() {
355 return getQuery(null, Sort.unsorted()).getResultList();
356 }
357
358
362 @Override
363 public List<T> findAllById(Iterable<ID> ids) {
364
365 Assert.notNull(ids, "Ids must not be null!");
366
367 if (!ids.iterator().hasNext()) {
368 return Collections.emptyList();
369 }
370
371 if (entityInformation.hasCompositeId()) {
372
373 List<T> results = new ArrayList<T>();
374
375 for (ID id : ids) {
376 findById(id).ifPresent(results::add);
377 }
378
379 return results;
380 }
381
382 Collection<ID> idCollection = toCollection(ids);
383
384 ByIdsSpecification<T> specification = new ByIdsSpecification<T>(entityInformation);
385 TypedQuery<T> query = getQuery(specification, Sort.unsorted());
386
387 return query.setParameter(specification.parameter, idCollection).getResultList();
388 }
389
390
394 @Override
395 public List<T> findAll(Sort sort) {
396 return getQuery(null, sort).getResultList();
397 }
398
399
403 @Override
404 public Page<T> findAll(Pageable pageable) {
405
406 if (isUnpaged(pageable)) {
407 return new PageImpl<T>(findAll());
408 }
409
410 return findAll((Specification<T>) null, pageable);
411 }
412
413
417 @Override
418 public Optional<T> findOne(@Nullable Specification<T> spec) {
419
420 try {
421 return Optional.of(getQuery(spec, Sort.unsorted()).getSingleResult());
422 } catch (NoResultException e) {
423 return Optional.empty();
424 }
425 }
426
427
431 @Override
432 public List<T> findAll(@Nullable Specification<T> spec) {
433 return getQuery(spec, Sort.unsorted()).getResultList();
434 }
435
436
440 @Override
441 public Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable) {
442
443 TypedQuery<T> query = getQuery(spec, pageable);
444 return isUnpaged(pageable) ? new PageImpl<T>(query.getResultList())
445 : readPage(query, getDomainClass(), pageable, spec);
446 }
447
448
452 @Override
453 public List<T> findAll(@Nullable Specification<T> spec, Sort sort) {
454 return getQuery(spec, sort).getResultList();
455 }
456
457
461 @Override
462 public <S extends T> Optional<S> findOne(Example<S> example) {
463
464 try {
465 return Optional
466 .of(getQuery(new ExampleSpecification<S>(example, escapeCharacter), example.getProbeType(), Sort.unsorted())
467 .getSingleResult());
468 } catch (NoResultException e) {
469 return Optional.empty();
470 }
471 }
472
473
477 @Override
478 public <S extends T> long count(Example<S> example) {
479 return executeCountQuery(
480 getCountQuery(new ExampleSpecification<S>(example, escapeCharacter), example.getProbeType()));
481 }
482
483
487 @Override
488 public <S extends T> boolean exists(Example<S> example) {
489 return !getQuery(new ExampleSpecification<S>(example, escapeCharacter), example.getProbeType(), Sort.unsorted())
490 .getResultList().isEmpty();
491 }
492
493
497 @Override
498 public <S extends T> List<S> findAll(Example<S> example) {
499 return getQuery(new ExampleSpecification<S>(example, escapeCharacter), example.getProbeType(), Sort.unsorted())
500 .getResultList();
501 }
502
503
507 @Override
508 public <S extends T> List<S> findAll(Example<S> example, Sort sort) {
509 return getQuery(new ExampleSpecification<S>(example, escapeCharacter), example.getProbeType(), sort)
510 .getResultList();
511 }
512
513
517 @Override
518 public <S extends T> Page<S> findAll(Example<S> example, Pageable pageable) {
519
520 ExampleSpecification<S> spec = new ExampleSpecification<>(example, escapeCharacter);
521 Class<S> probeType = example.getProbeType();
522 TypedQuery<S> query = getQuery(new ExampleSpecification<>(example, escapeCharacter), probeType, pageable);
523
524 return isUnpaged(pageable) ? new PageImpl<>(query.getResultList()) : readPage(query, probeType, pageable, spec);
525 }
526
527
531 @Override
532 public long count() {
533 return em.createQuery(getCountQueryString(), Long.class).getSingleResult();
534 }
535
536
540 @Override
541 public long count(@Nullable Specification<T> spec) {
542 return executeCountQuery(getCountQuery(spec, getDomainClass()));
543 }
544
545
549 @Transactional
550 @Override
551 public <S extends T> S save(S entity) {
552
553 if (entityInformation.isNew(entity)) {
554 em.persist(entity);
555 return entity;
556 } else {
557 return em.merge(entity);
558 }
559 }
560
561
565 @Transactional
566 @Override
567 public <S extends T> S saveAndFlush(S entity) {
568
569 S result = save(entity);
570 flush();
571
572 return result;
573 }
574
575
579 @Transactional
580 @Override
581 public <S extends T> List<S> saveAll(Iterable<S> entities) {
582
583 Assert.notNull(entities, "Entities must not be null!");
584
585 List<S> result = new ArrayList<S>();
586
587 for (S entity : entities) {
588 result.add(save(entity));
589 }
590
591 return result;
592 }
593
594
598 @Transactional
599 @Override
600 public void flush() {
601 em.flush();
602 }
603
604
614 @Deprecated
615 protected Page<T> readPage(TypedQuery<T> query, Pageable pageable, @Nullable Specification<T> spec) {
616 return readPage(query, getDomainClass(), pageable, spec);
617 }
618
619
629 protected <S extends T> Page<S> readPage(TypedQuery<S> query, final Class<S> domainClass, Pageable pageable,
630 @Nullable Specification<S> spec) {
631
632 if (pageable.isPaged()) {
633 query.setFirstResult((int) pageable.getOffset());
634 query.setMaxResults(pageable.getPageSize());
635 }
636
637 return PageableExecutionUtils.getPage(query.getResultList(), pageable,
638 () -> executeCountQuery(getCountQuery(spec, domainClass)));
639 }
640
641
648 protected TypedQuery<T> getQuery(@Nullable Specification<T> spec, Pageable pageable) {
649
650 Sort sort = pageable.isPaged() ? pageable.getSort() : Sort.unsorted();
651 return getQuery(spec, getDomainClass(), sort);
652 }
653
654
662 protected <S extends T> TypedQuery<S> getQuery(@Nullable Specification<S> spec, Class<S> domainClass,
663 Pageable pageable) {
664
665 Sort sort = pageable.isPaged() ? pageable.getSort() : Sort.unsorted();
666 return getQuery(spec, domainClass, sort);
667 }
668
669
676 protected TypedQuery<T> getQuery(@Nullable Specification<T> spec, Sort sort) {
677 return getQuery(spec, getDomainClass(), sort);
678 }
679
680
688 protected <S extends T> TypedQuery<S> getQuery(@Nullable Specification<S> spec, Class<S> domainClass, Sort sort) {
689
690 CriteriaBuilder builder = em.getCriteriaBuilder();
691 CriteriaQuery<S> query = builder.createQuery(domainClass);
692
693 Root<S> root = applySpecificationToCriteria(spec, domainClass, query);
694 query.select(root);
695
696 if (sort.isSorted()) {
697 query.orderBy(toOrders(sort, root, builder));
698 }
699
700 return applyRepositoryMethodMetadata(em.createQuery(query));
701 }
702
703
710 @Deprecated
711 protected TypedQuery<Long> getCountQuery(@Nullable Specification<T> spec) {
712 return getCountQuery(spec, getDomainClass());
713 }
714
715
722 protected <S extends T> TypedQuery<Long> getCountQuery(@Nullable Specification<S> spec, Class<S> domainClass) {
723
724 CriteriaBuilder builder = em.getCriteriaBuilder();
725 CriteriaQuery<Long> query = builder.createQuery(Long.class);
726
727 Root<S> root = applySpecificationToCriteria(spec, domainClass, query);
728
729 if (query.isDistinct()) {
730 query.select(builder.countDistinct(root));
731 } else {
732 query.select(builder.count(root));
733 }
734
735
736 query.orderBy(Collections.<Order> emptyList());
737
738 return em.createQuery(query);
739 }
740
741
749 private <S, U extends T> Root<U> applySpecificationToCriteria(@Nullable Specification<U> spec, Class<U> domainClass,
750 CriteriaQuery<S> query) {
751
752 Assert.notNull(domainClass, "Domain class must not be null!");
753 Assert.notNull(query, "CriteriaQuery must not be null!");
754
755 Root<U> root = query.from(domainClass);
756
757 if (spec == null) {
758 return root;
759 }
760
761 CriteriaBuilder builder = em.getCriteriaBuilder();
762 Predicate predicate = spec.toPredicate(root, query, builder);
763
764 if (predicate != null) {
765 query.where(predicate);
766 }
767
768 return root;
769 }
770
771 private <S> TypedQuery<S> applyRepositoryMethodMetadata(TypedQuery<S> query) {
772
773 if (metadata == null) {
774 return query;
775 }
776
777 LockModeType type = metadata.getLockModeType();
778 TypedQuery<S> toReturn = type == null ? query : query.setLockMode(type);
779
780 applyQueryHints(toReturn);
781
782 return toReturn;
783 }
784
785 private void applyQueryHints(Query query) {
786
787 for (Entry<String, Object> hint : getQueryHints().withFetchGraphs(em)) {
788 query.setHint(hint.getKey(), hint.getValue());
789 }
790 }
791
792
798 private static long executeCountQuery(TypedQuery<Long> query) {
799
800 Assert.notNull(query, "TypedQuery must not be null!");
801
802 List<Long> totals = query.getResultList();
803 long total = 0L;
804
805 for (Long element : totals) {
806 total += element == null ? 0 : element;
807 }
808
809 return total;
810 }
811
812 private static boolean isUnpaged(Pageable pageable) {
813 return pageable.isUnpaged();
814 }
815
816
824 @SuppressWarnings("rawtypes")
825 private static final class ByIdsSpecification<T> implements Specification<T> {
826
827 private static final long serialVersionUID = 1L;
828
829 private final JpaEntityInformation<T, ?> entityInformation;
830
831 @Nullable ParameterExpression<Collection<?>> parameter;
832
833 ByIdsSpecification(JpaEntityInformation<T, ?> entityInformation) {
834 this.entityInformation = entityInformation;
835 }
836
837
841 @Override
842 public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
843
844 Path<?> path = root.get(entityInformation.getIdAttribute());
845 parameter = (ParameterExpression<Collection<?>>) (ParameterExpression) cb.parameter(Collection.class);
846 return path.in(parameter);
847 }
848 }
849
850
858 private static class ExampleSpecification<T> implements Specification<T> {
859
860 private static final long serialVersionUID = 1L;
861
862 private final Example<T> example;
863 private final EscapeCharacter escapeCharacter;
864
865
871 ExampleSpecification(Example<T> example, EscapeCharacter escapeCharacter) {
872
873 Assert.notNull(example, "Example must not be null!");
874 Assert.notNull(escapeCharacter, "EscapeCharacter must not be null!");
875
876 this.example = example;
877 this.escapeCharacter = escapeCharacter;
878 }
879
880
884 @Override
885 public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
886 return QueryByExamplePredicateBuilder.getPredicate(root, cb, example, escapeCharacter);
887 }
888 }
889 }
890