Java tutorial
/** * Copyright (c) 2005-2012 https://github.com/zhangkaitao * * Licensed under the Apache License, Version 2.0 (the "License"); */ package cn.guoyukun.spring.jpa.repository.support; import static org.springframework.data.jpa.repository.query.QueryUtils.toOrders; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import java.util.Set; import javax.persistence.EntityManager; import javax.persistence.LockModeType; import javax.persistence.NoResultException; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Path; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import org.apache.commons.beanutils.BeanUtils; import org.apache.commons.lang3.ArrayUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; import org.springframework.data.jpa.repository.query.Jpa21Utils; import org.springframework.data.jpa.repository.query.QueryUtils; import org.springframework.data.jpa.repository.support.JpaEntityInformation; import org.springframework.data.jpa.repository.support.SimpleJpaRepository; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; import cn.guoyukun.spring.jpa.PageImpl4jqgrid; import cn.guoyukun.spring.jpa.entity.search.Searchable; import cn.guoyukun.spring.jpa.plugin.entity.LogicDeleteable; import cn.guoyukun.spring.jpa.repository.BaseRepository; import cn.guoyukun.spring.jpa.repository.RepositoryHelper; import cn.guoyukun.spring.jpa.repository.callback.SearchCallback; import cn.guoyukun.spring.jpa.repository.support.annotation.QueryJoin; import com.google.common.collect.Sets; /** * <p>Custom Repository </p> * <p>User: * <p>Date: 13-1-15 ?7:33 * <p>Version: 1.0 */ public class SimpleBaseRepository<M, ID extends Serializable> extends SimpleJpaRepository<M, ID> implements BaseRepository<M, ID> { // private static final Logger LOG = LoggerFactory.getLogger(SimpleBaseRepository.class); public static final String LOGIC_DELETE_ALL_QUERY_STRING = "update %s x set x.deleted=true where x in (?1)"; public static final String DELETE_ALL_QUERY_STRING = "delete from %s x where x in (?1)"; public static final String FIND_QUERY_STRING = "from %s x where 1=1 "; public static final String COUNT_QUERY_STRING = "select count(x) from %s x where 1=1 "; private final EntityManager em; private final JpaEntityInformation<M, ID> entityInformation; private final RepositoryHelper repositoryHelper; private Class<M> entityClass; private String entityName; private String idName; /** * QL */ private String findAllQL; /** * QL */ private String countAllQL; private QueryJoin[] joins; private SearchCallback searchCallback = SearchCallback.DEFAULT; public SimpleBaseRepository(JpaEntityInformation<M, ID> entityInformation, EntityManager entityManager) { super(entityInformation, entityManager); this.entityInformation = entityInformation; this.entityClass = this.entityInformation.getJavaType(); this.entityName = this.entityInformation.getEntityName(); this.idName = this.entityInformation.getIdAttributeNames().iterator().next(); this.em = entityManager; repositoryHelper = new RepositoryHelper(entityClass); findAllQL = String.format(FIND_QUERY_STRING, entityName); countAllQL = String.format(COUNT_QUERY_STRING, entityName); } /** * searchCallback * * @param searchCallback */ public void setSearchCallback(SearchCallback searchCallback) { this.searchCallback = searchCallback; } /** * ql * * @param findAllQL */ public void setFindAllQL(String findAllQL) { this.findAllQL = findAllQL; } /** * ql * * @param countAllQL */ public void setCountAllQL(String countAllQL) { this.countAllQL = countAllQL; } public void setJoins(QueryJoin[] joins) { this.joins = joins; } ///////////////////////////////////////////////// ////////spring data jpa//////////// ///////////////////////////////////////////////// /** * ? * * @param id */ @Transactional @Override public void delete(final ID id) { M m = findOne(id); delete(m); } /** * * * @param m */ @Transactional @Override public void delete(final M m) { if (m == null) { return; } if (m instanceof LogicDeleteable) { ((LogicDeleteable) m).markDeleted(); save(m); } else { super.delete(m); } } /** * ? * * @param ids */ @Transactional @Override public void delete(final ID[] ids) { if (ArrayUtils.isEmpty(ids)) { return; } List<M> models = new ArrayList<M>(); for (ID id : ids) { M model = null; try { model = entityClass.newInstance(); } catch (Exception e) { throw new RuntimeException("batch delete " + entityClass + " error", e); } try { BeanUtils.setProperty(model, idName, id); } catch (Exception e) { throw new RuntimeException("batch delete " + entityClass + " error, can not set id", e); } models.add(model); } deleteInBatch(models); } @Transactional @Override public void deleteInBatch(final Iterable<M> entities) { Iterator<M> iter = entities.iterator(); if (entities == null || !iter.hasNext()) { return; } Set<M> models = Sets.newHashSet(iter); boolean logicDeleteableEntity = LogicDeleteable.class.isAssignableFrom(this.entityClass); if (logicDeleteableEntity) { String ql = String.format(LOGIC_DELETE_ALL_QUERY_STRING, entityName); repositoryHelper.batchUpdate(ql, models); } else { String ql = String.format(DELETE_ALL_QUERY_STRING, entityName); repositoryHelper.batchUpdate(ql, models); } } /** * * * @param id * @return id */ @Transactional @Override public M findOne(ID id) { if (id == null) { return null; } if (id instanceof Integer && ((Integer) id).intValue() == 0) { return null; } if (id instanceof Long && ((Long) id).longValue() == 0L) { return null; } return super.findOne(id); } ////////?Specification SimpleJpaRepository??/////////////////////////////////// @Override public M findOne(Specification<M> spec) { try { return getQuery(spec, (Sort) null).getSingleResult(); } catch (NoResultException e) { return null; } } /* * (non-Javadoc) * @see org.springframework.data.repository.CrudRepository#findAll(ID[]) */ public List<M> findAll(Iterable<ID> ids) { return getQuery(new Specification<M>() { public Predicate toPredicate(Root<M> root, CriteriaQuery<?> query, CriteriaBuilder cb) { Path<?> path = root.get(entityInformation.getIdAttribute()); return path.in(cb.parameter(Iterable.class, "ids")); } }, (Sort) null).setParameter("ids", ids).getResultList(); } /* * (non-Javadoc) * @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#findAll(org.springframework.data.jpa.domain.Specification) */ public List<M> findAll(Specification<M> spec) { return getQuery(spec, (Sort) null).getResultList(); } /* * (non-Javadoc) * @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#findAll(org.springframework.data.jpa.domain.Specification, org.springframework.data.domain.Pageable) */ public Page<M> findAll(Specification<M> spec, Pageable pageable) { TypedQuery<M> query = getQuery(spec, pageable); return pageable == null ? new PageImpl4jqgrid<M>(query.getResultList()) : readPage(query, pageable, spec); } /* * (non-Javadoc) * @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#findAll(org.springframework.data.jpa.domain.Specification, org.springframework.data.domain.Sort) */ public List<M> findAll(Specification<M> spec, Sort sort) { return getQuery(spec, sort).getResultList(); } /* * (non-Javadoc) * @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#count(org.springframework.data.jpa.domain.Specification) */ public long count(Specification<M> spec) { return getCountQuery(spec).getSingleResult(); } ////////?Specification SimpleJpaRepository??/////////////////////////////////// ///////SimpleJpaRepository??/////////////////////////////// /** * Reads the given {@link javax.persistence.TypedQuery} into a {@link org.springframework.data.domain.Page} applying the given {@link org.springframework.data.domain.Pageable} and * {@link org.springframework.data.jpa.domain.Specification}. * * @param query must not be {@literal null}. * @param spec can be {@literal null}. * @param pageable can be {@literal null}. * @return */ @Override protected Page<M> readPage(TypedQuery<M> query, Pageable pageable, Specification<M> spec) { query.setFirstResult(pageable.getOffset()); query.setMaxResults(pageable.getPageSize()); Long total = QueryUtils.executeCountQuery(getCountQuery(spec)); List<M> content = total > pageable.getOffset() ? query.getResultList() : Collections.<M>emptyList(); return new PageImpl4jqgrid<M>(content, pageable, total); } /** * Creates a new count query for the given {@link org.springframework.data.jpa.domain.Specification}. * * @param spec can be {@literal null}. * @return */ protected TypedQuery<Long> getCountQuery(Specification<M> spec) { CriteriaBuilder builder = em.getCriteriaBuilder(); CriteriaQuery<Long> query = builder.createQuery(Long.class); Root<M> root = applySpecificationToCriteria(spec, query); if (query.isDistinct()) { query.select(builder.countDistinct(root)); } else { query.select(builder.count(root)); } TypedQuery<Long> q = em.createQuery(query); repositoryHelper.applyEnableQueryCache(q); return q; } /** * Creates a new {@link TypedQuery} from the given {@link Specification}. * * @param spec can be {@literal null}. * @param pageable can be {@literal null}. * @return */ protected TypedQuery<M> getQuery(Specification<M> spec, Pageable pageable) { Sort sort = pageable == null ? null : pageable.getSort(); return getQuery(spec, sort); } /** * Creates a {@link TypedQuery} for the given {@link Specification} and {@link Sort}. * * @param spec can be {@literal null}. * @param sort can be {@literal null}. * @return */ protected TypedQuery<M> getQuery(Specification<M> spec, Sort sort) { CriteriaBuilder builder = em.getCriteriaBuilder(); CriteriaQuery<M> query = builder.createQuery(getDomainClass()); Root<M> root = applySpecificationToCriteria(spec, query); query.select(root); if (sort != null) { query.orderBy(toOrders(sort, root, builder)); } return applyRepositoryMethodMetadata(em.createQuery(query)); } private TypedQuery<M> applyRepositoryMethodMetadata(TypedQuery<M> query) { if (getRepositoryMethodMetadata() == null) { return query; } LockModeType type = getRepositoryMethodMetadata().getLockModeType(); TypedQuery<M> toReturn = type == null ? query : query.setLockMode(type); for (Entry<String, Object> hint : getRepositoryMethodMetadata().getQueryHints().entrySet()) { query.setHint(hint.getKey(), hint.getValue()); } return Jpa21Utils.tryConfigureFetchGraph(em, toReturn, getRepositoryMethodMetadata().getEntityGraph()); } private void applyJoins(Root<M> root) { if (joins == null) { return; } for (QueryJoin join : joins) { root.join(join.property(), join.joinType()); } } /** * Applies the given {@link org.springframework.data.jpa.domain.Specification} to the given {@link javax.persistence.criteria.CriteriaQuery}. * * @param spec can be {@literal null}. * @param query must not be {@literal null}. * @return */ private <S> Root<M> applySpecificationToCriteria(Specification<M> spec, CriteriaQuery<S> query) { Assert.notNull(query); Root<M> root = query.from(entityClass); if (spec == null) { return root; } CriteriaBuilder builder = em.getCriteriaBuilder(); Predicate predicate = spec.toPredicate(root, query, builder); if (predicate != null) { query.where(predicate); } return root; } ///////SimpleJpaRepository??/////////////////////////////// @Override public List<M> findAll() { return repositoryHelper.findAll(findAllQL); } @Override public List<M> findAll(final Sort sort) { return repositoryHelper.findAll(findAllQL, sort); } @Override public Page<M> findAll(final Pageable pageable) { return new PageImpl4jqgrid<M>(repositoryHelper.<M>findAll(findAllQL, pageable), pageable, repositoryHelper.count(countAllQL)); } @Override public long count() { return repositoryHelper.count(countAllQL); } ///////////////////////////////////////////////// /////////////////////////////////////// ///////////////////////////////////////////////// @Override public Page<M> findAll(final Searchable searchable) { LOG.trace("findAllQL:{} || searchable{}", findAllQL, searchable); List<M> list = repositoryHelper.findAll(findAllQL, searchable, searchCallback); long total = searchable.hasPageable() ? count(searchable) : list.size(); return new PageImpl4jqgrid<M>(list, searchable.getPage(), total); } @Override public long count(final Searchable searchable) { LOG.trace("countAllQL:{} || searchable{}", countAllQL, searchable); return repositoryHelper.count(countAllQL, searchable, searchCallback); } /** * ? ?/ * * @param id * @return */ @Override public boolean exists(ID id) { return findOne(id) != null; } }