@Repository
@Transactional(
readOnly = true
)
public class SimpleJpaRepository<T, ID> implements JpaRepositoryImplementation<T, ID> {
public SimpleJpaRepository(JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) {
this.escapeCharacter = EscapeCharacter.DEFAULT;
Assert.notNull(entityInformation, "JpaEntityInformation must not be null");
Assert.notNull(entityManager, "EntityManager must not be null");
this.entityInformation = entityInformation;
this.entityManager = entityManager;
this.provider = PersistenceProvider.fromEntityManager(entityManager);
}
public SimpleJpaRepository(Class<T> domainClass, EntityManager entityManager) {
this(JpaEntityInformationSupport.getEntityInformation(domainClass, entityManager), entityManager);
}
@Transactional
public void deleteById(ID id) {
Assert.notNull(id, "The given id must not be null");
this.findById(id).ifPresent(this::delete);
}
@Transactional
public void delete(T entity) {
Assert.notNull(entity, "Entity must not be null");
if (!this.entityInformation.isNew(entity)) {
if (this.entityManager.contains(entity)) {
this.entityManager.remove(entity);
} else {
Class<?> type = ProxyUtils.getUserClass(entity);
T existing = this.entityManager.find(type, this.entityInformation.getId(entity));
if (existing != null) {
this.entityManager.remove(this.entityManager.merge(entity));
}
}
}
}
@Transactional
public void deleteAllById(Iterable<? extends ID> ids) {
Assert.notNull(ids, "Ids must not be null");
Iterator var3 = ids.iterator();
while(var3.hasNext()) {
ID id = (Object)var3.next();
this.deleteById(id);
}
}
@Transactional
public void deleteAllByIdInBatch(Iterable<ID> ids) {
Assert.notNull(ids, "Ids must not be null");
if (ids.iterator().hasNext()) {
if (this.entityInformation.hasCompositeId()) {
List<T> entities = new ArrayList();
ids.forEach((id) -> {
entities.add(this.getReferenceById(id));
});
this.deleteAllInBatch(entities);
} else {
String queryString = String.format("delete from %s x where %s in :ids", this.entityInformation.getEntityName(), this.entityInformation.getIdAttribute().getName());
Query query = this.entityManager.createQuery(queryString);
Collection<ID> idCollection = toCollection(ids);
query.setParameter("ids", idCollection);
this.applyQueryHints(query);
query.executeUpdate();
}
}
}
@Transactional
public void deleteAll(Iterable<? extends T> entities) {
Assert.notNull(entities, "Entities must not be null");
Iterator var3 = entities.iterator();
while(var3.hasNext()) {
T entity = (Object)var3.next();
this.delete(entity);
}
}
@Transactional
public void deleteAllInBatch(Iterable<T> entities) {
Assert.notNull(entities, "Entities must not be null");
if (entities.iterator().hasNext()) {
QueryUtils.applyAndBind(QueryUtils.getQueryString("delete from %s x", this.entityInformation.getEntityName()), entities, this.entityManager).executeUpdate();
}
}
@Transactional
public void deleteAll() {
Iterator var2 = this.findAll().iterator();
while(var2.hasNext()) {
T element = (Object)var2.next();
this.delete(element);
}
}
@Transactional
public void deleteAllInBatch() {
Query query = this.entityManager.createQuery(this.getDeleteAllQueryString());
this.applyQueryHints(query);
query.executeUpdate();
}
public Optional<T> findById(ID id) {
Assert.notNull(id, "The given id must not be null");
Class<T> domainType = this.getDomainClass();
if (this.metadata == null) {
return Optional.ofNullable(this.entityManager.find(domainType, id));
} else {
LockModeType type = this.metadata.getLockModeType();
Map<String, Object> hints = this.getHints();
return Optional.ofNullable(type == null ? this.entityManager.find(domainType, id, hints) : this.entityManager.find(domainType, id, type, hints));
}
}
/** @deprecated */
@Deprecated
public T getOne(ID id) {
return this.getReferenceById(id);
}
/** @deprecated */
@Deprecated
public T getById(ID id) {
return this.getReferenceById(id);
}
public T getReferenceById(ID id) {
Assert.notNull(id, "The given id must not be null");
return this.entityManager.getReference(this.getDomainClass(), id);
}
public boolean existsById(ID id) {
Assert.notNull(id, "The given id must not be null");
if (this.entityInformation.getIdAttribute() == null) {
return this.findById(id).isPresent();
} else {
String placeholder = this.provider.getCountQueryPlaceholder();
String entityName = this.entityInformation.getEntityName();
Iterable<String> idAttributeNames = this.entityInformation.getIdAttributeNames();
String existsQuery = QueryUtils.getExistsQueryString(entityName, placeholder, idAttributeNames);
TypedQuery<Long> query = this.entityManager.createQuery(existsQuery, Long.class);
this.applyQueryHints(query);
if (!this.entityInformation.hasCompositeId()) {
query.setParameter((String)idAttributeNames.iterator().next(), id);
return (Long)query.getSingleResult() == 1L;
} else {
Iterator var8 = idAttributeNames.iterator();
while(var8.hasNext()) {
String idAttributeName = (String)var8.next();
Object idAttributeValue = this.entityInformation.getCompositeIdAttributeValue(id, idAttributeName);
boolean complexIdParameterValueDiscovered = idAttributeValue != null && !query.getParameter(idAttributeName).getParameterType().isAssignableFrom(idAttributeValue.getClass());
if (complexIdParameterValueDiscovered) {
return this.findById(id).isPresent();
}
query.setParameter(idAttributeName, idAttributeValue);
}
if ((Long)query.getSingleResult() == 1L) {
return true;
} else {
return false;
}
}
}
}
public List<T> findAll() {
return this.getQuery((Specification)null, (Sort)Sort.unsorted()).getResultList();
}
public List<T> findAllById(Iterable<ID> ids) {
Assert.notNull(ids, "Ids must not be null");
if (!ids.iterator().hasNext()) {
return Collections.emptyList();
} else if (!this.entityInformation.hasCompositeId()) {
Collection<ID> idCollection = toCollection(ids);
ByIdsSpecification<T> specification = new ByIdsSpecification(this.entityInformation);
TypedQuery<T> query = this.getQuery(specification, (Sort)Sort.unsorted());
return query.setParameter(specification.parameter, idCollection).getResultList();
} else {
List<T> results = new ArrayList();
Iterator var4 = ids.iterator();
while(var4.hasNext()) {
ID id = (Object)var4.next();
this.findById(id).ifPresent(results::add);
}
return results;
}
}
public List<T> findAll(Sort sort) {
return this.getQuery((Specification)null, (Sort)sort).getResultList();
}
public Page<T> findAll(Pageable pageable) {
return (Page)(pageable.isUnpaged() ? new PageImpl(this.findAll()) : this.findAll((Specification)null, (Pageable)pageable));
}
public Optional<T> findOne(Specification<T> spec) {
try {
return Optional.of(this.getQuery(spec, Sort.unsorted()).setMaxResults(2).getSingleResult());
} catch (NoResultException var2) {
return Optional.empty();
}
}
public List<T> findAll(Specification<T> spec) {
return this.getQuery(spec, Sort.unsorted()).getResultList();
}
public Page<T> findAll(@Nullable Specification<T> spec, Pageable pageable) {
TypedQuery<T> query = this.getQuery(spec, pageable);
return (Page)(pageable.isUnpaged() ? new PageImpl(query.getResultList()) : this.readPage(query, this.getDomainClass(), pageable, spec));
}
public List<T> findAll(@Nullable Specification<T> spec, Sort sort) {
return this.getQuery(spec, sort).getResultList();
}
public boolean exists(Specification<T> spec) {
CriteriaQuery<Integer> cq = this.entityManager.getCriteriaBuilder().createQuery(Integer.class).select(this.entityManager.getCriteriaBuilder().literal(1));
this.applySpecificationToCriteria(spec, this.getDomainClass(), cq);
TypedQuery<Integer> query = this.applyRepositoryMethodMetadata(this.entityManager.createQuery(cq));
return query.setMaxResults(1).getResultList().size() == 1;
}
@Transactional
public long delete(@Nullable Specification<T> spec) {
CriteriaBuilder builder = this.entityManager.getCriteriaBuilder();
CriteriaDelete<T> delete = builder.createCriteriaDelete(this.getDomainClass());
if (spec != null) {
Predicate predicate = spec.toPredicate(delete.from(this.getDomainClass()), builder.createQuery(this.getDomainClass()), builder);
if (predicate != null) {
delete.where(predicate);
}
}
return (long)this.entityManager.createQuery(delete).executeUpdate();
}
public <S extends T, R> R findBy(Specification<T> spec, Function<FluentQuery.FetchableFluentQuery<S>, R> queryFunction) {
Assert.notNull(spec, "Specification must not be null");
Assert.notNull(queryFunction, "Query function must not be null");
return this.doFindBy(spec, this.getDomainClass(), queryFunction);
}
private <S extends T, R> R doFindBy(Specification<T> spec, Class<T> domainClass, Function<FluentQuery.FetchableFluentQuery<S>, R> queryFunction) {
Assert.notNull(spec, "Specification must not be null");
Assert.notNull(queryFunction, "Query function must not be null");
FluentQuerySupport.ScrollQueryFactory scrollFunction = (sort, scrollPosition) -> {
Specification<T> specToUse = spec;
if (scrollPosition instanceof KeysetScrollPosition keyset) {
KeysetScrollSpecification<T> keysetSpec = new KeysetScrollSpecification(keyset, sort, this.entityInformation);
sort = keysetSpec.sort();
specToUse = specToUse.and(keysetSpec);
}
TypedQuery<T> query = this.getQuery(specToUse, domainClass, sort);
if (scrollPosition instanceof OffsetScrollPosition offset) {
if (!offset.isInitial()) {
query.setFirstResult(Math.toIntExact(offset.getOffset()) + 1);
}
}
return query;
};
Function<Sort, TypedQuery<T>> finder = (sort) -> {
return this.getQuery(spec, domainClass, sort);
};
FetchableFluentQueryBySpecification.SpecificationScrollDelegate<T> scrollDelegate = new FetchableFluentQueryBySpecification.SpecificationScrollDelegate(scrollFunction, this.entityInformation);
FetchableFluentQueryBySpecification<?, T> fluentQuery = new FetchableFluentQueryBySpecification(spec, domainClass, finder, scrollDelegate, this::count, this::exists, this.entityManager, this.getProjectionFactory());
return queryFunction.apply(fluentQuery);
}
public <S extends T> Optional<S> findOne(Example<S> example) {
try {
return Optional.of(this.getQuery(new ExampleSpecification(example, this.escapeCharacter), example.getProbeType(), (Sort)Sort.unsorted()).setMaxResults(2).getSingleResult());
} catch (NoResultException var2) {
return Optional.empty();
}
}
public <S extends T> long count(Example<S> example) {
return executeCountQuery(this.getCountQuery(new ExampleSpecification(example, this.escapeCharacter), example.getProbeType()));
}
public <S extends T> boolean exists(Example<S> example) {
Specification<S> spec = new ExampleSpecification(example, this.escapeCharacter);
CriteriaQuery<Integer> cq = this.entityManager.getCriteriaBuilder().createQuery(Integer.class).select(this.entityManager.getCriteriaBuilder().literal(1));
this.applySpecificationToCriteria(spec, example.getProbeType(), cq);
TypedQuery<Integer> query = this.applyRepositoryMethodMetadata(this.entityManager.createQuery(cq));
return query.setMaxResults(1).getResultList().size() == 1;
}
public <S extends T> List<S> findAll(Example<S> example) {
return this.getQuery(new ExampleSpecification(example, this.escapeCharacter), example.getProbeType(), (Sort)Sort.unsorted()).getResultList();
}
public <S extends T> List<S> findAll(Example<S> example, Sort sort) {
return this.getQuery(new ExampleSpecification(example, this.escapeCharacter), example.getProbeType(), (Sort)sort).getResultList();
}
public <S extends T> Page<S> findAll(Example<S> example, Pageable pageable) {
ExampleSpecification<S> spec = new ExampleSpecification(example, this.escapeCharacter);
Class<S> probeType = example.getProbeType();
TypedQuery<S> query = this.getQuery(new ExampleSpecification(example, this.escapeCharacter), probeType, (Pageable)pageable);
return (Page)(pageable.isUnpaged() ? new PageImpl(query.getResultList()) : this.readPage(query, probeType, pageable, spec));
}
public <S extends T, R> R findBy(Example<S> example, Function<FluentQuery.FetchableFluentQuery<S>, R> queryFunction) {
Assert.notNull(example, "Example must not be null");
Assert.notNull(queryFunction, "Query function must not be null");
ExampleSpecification<S> spec = new ExampleSpecification(example, this.escapeCharacter);
Class<S> probeType = example.getProbeType();
return this.doFindBy(spec, probeType, queryFunction);
}
public long count() {
TypedQuery<Long> query = this.entityManager.createQuery(this.getCountQueryString(), Long.class);
this.applyQueryHintsForCount(query);
return (Long)query.getSingleResult();
}
public long count(@Nullable Specification<T> spec) {
return executeCountQuery(this.getCountQuery(spec, this.getDomainClass()));
}
@Transactional
public <S extends T> S save(S entity) {
Assert.notNull(entity, "Entity must not be null");
if (this.entityInformation.isNew(entity)) {
this.entityManager.persist(entity);
return entity;
} else {
return this.entityManager.merge(entity);
}
}
@Transactional
public <S extends T> S saveAndFlush(S entity) {
S result = this.save(entity);
this.flush();
return result;
}
@Transactional
public <S extends T> List<S> saveAll(Iterable<S> entities) {
Assert.notNull(entities, "Entities must not be null");
List<S> result = new ArrayList();
Iterator var4 = entities.iterator();
while(var4.hasNext()) {
S entity = (Object)var4.next();
result.add(this.save(entity));
}
return result;
}
@Transactional
public <S extends T> List<S> saveAllAndFlush(Iterable<S> entities) {
List<S> result = this.saveAll(entities);
this.flush();
return result;
}
@Transactional
public void flush() {
this.entityManager.flush();
}
}