/* * To change this template, choose Tools | Templates * and open the template in the editor. */ package com.smilonet.common.dao.jpa; import static org.springframework.data.jpa.repository.query.QueryUtils.toOrders; import java.io.Serializable; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Collections; import java.util.List; import javax.persistence.EntityManager; import javax.persistence.LockModeType; import javax.persistence.PersistenceContext; import javax.persistence.TypedQuery; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageImpl; 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.QueryUtils; import org.springframework.data.jpa.repository.support.LockMetadataProvider; import org.springframework.util.Assert; import com.smilonet.common.entity.IActivable; import com.smilonet.common.entity.IDefaultable; import com.smilonet.common.entity.IHasCode; import com.smilonet.common.entity.IInheritable; import com.smilonet.common.entity.IPersistable; import com.smilonet.common.entity.IValidable; /** * * @author wanglong */ public class BaseDaoImpl<T extends IPersistable<ID>, ID extends Serializable> implements BaseDao<T, ID> { private EntityManager entityManager; private Class<T> clazz; private LockMetadataProvider lockMetadataProvider; private boolean isDefaultable = false; private boolean isActivable = false; private boolean isValidable = false; private boolean isInheritable = false; private boolean isHasCode = false; public BaseDaoImpl() { Type[] types = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments(); if (types[0] instanceof ParameterizedType) { ParameterizedType type = (ParameterizedType) types[0]; clazz = (Class<T>) type.getRawType(); } else { clazz = (Class<T>) types[0]; } checkGenericClass(); } /** * Configures a custom {@link LockMetadataProvider} to be used to detect {@link LockModeType}s to be applied to * queries. * * @param lockMetadataProvider */ public void setLockMetadataProvider(LockMetadataProvider lockMetadataProvider) { this.lockMetadataProvider = lockMetadataProvider; } private void checkGenericClass() { for (@SuppressWarnings("rawtypes") Class i : clazz.getInterfaces()) { if (i == IDefaultable.class) { isDefaultable = true; } else if (i == IActivable.class) { isActivable = true; } else if (i == IValidable.class) { isValidable = true; } else if (i == IInheritable.class) { isInheritable = true; } else if (i == IHasCode.class) { isHasCode = true; } } } /** * Set entity manager. * * @param entityManager entity manager */ @PersistenceContext public void setEntityManager(EntityManager entityManager) { this.entityManager = entityManager; } public T getReference(ID id) { return (T) entityManager.getReference(clazz, id); } public <S extends T> S save(S entity) { if (entity.getId() != null) { preUpdate(entity); return entityManager.merge(entity); } else { entityManager.persist(entity); return entity; } } public void save(final T... entitys) { for (T entity : entitys) { save(entity); } } public void delete(final ID id) throws UnsupportedOperationException { delete(getReference(id)); } public void delete(final ID... ids) throws UnsupportedOperationException { for (ID id : ids) { delete(id); } } public void delete(final T object) throws UnsupportedOperationException { if (isDefaultable) { checkIfDefault(object); } if (isValidable) { ((IValidable) object).setIsValid(false); entityManager.merge(object); } else { entityManager.remove(object); } } public void delete(final T... objects) throws UnsupportedOperationException { // delete(Arrays.asList(objects)); } public void deleteAll() throws UnsupportedOperationException { // deleteAll(getAll()); } private void checkIfDefault(T entity) { if (((IDefaultable) entity).isDefault()) { throw new UnsupportedOperationException("can't delete default entity " + clazz + "#" + entity.getId()); } } public void refresh(final T entity) { entityManager.refresh(entity); } public void flushAndClear() { entityManager.flush(); entityManager.clear(); } /** * Get entity manager. * * @return entity manager */ protected EntityManager getEntityManager() { return entityManager; } protected void preUpdate(final Object entity) { } @Override public T find(ID id) { return entityManager.find(clazz, id); } @Override public List<T> find(Specification<T> spec) { return null; } @Override public T findOne(Specification<T> spec) { return null; } @Override public List<T> find(Iterable<ID> ids) { return null; } @Override public boolean exists(ID id) { return false; } @Override public List<T> findAll() { return null; } @Override public List<T> findAll(Sort sort) { return null; } @Override public List<T> find(Specification<T> spec, Sort sort) { return null; } @Override public Page<T> findAll(Pageable pageable) { return null; } @Override public Page<T> find(Specification<T> spec, Pageable pageable) { return null; } @Override public long countAll() { return 0; } @Override public long count(Specification<T> spec) { return 0; } @Override public void deleteInBatch(Iterable<? extends ID> entityIds) { } @Override public void deleteAllInBatch() { } @Override public void flush() { } @Override public <S extends T> List<S> save(Iterable<S> entities) { return null; } @Override public T saveAndFlush(T entity) { return null; } @Override public <S extends T> List<S> saveAndFlush(Iterable<S> entities) { return null; } /** * Reads the given {@link TypedQuery} into a {@link Page} applying the given {@link Pageable} and * {@link Specification}. * * @param query must not be {@literal null}. * @param spec can be {@literal null}. * @param pageable can be {@literal null}. * @return */ private Page<T> readPage(TypedQuery<T> query, Pageable pageable, Specification<T> spec) { query.setFirstResult(pageable.getOffset()); query.setMaxResults(pageable.getPageSize()); Long total = QueryUtils.executeCountQuery(getCountQuery(spec)); List<T> content = total > pageable.getOffset() ? query.getResultList() : Collections.<T> emptyList(); return new PageImpl<T>(content, pageable, total); } /** * Creates a new {@link TypedQuery} from the given {@link Specification}. * * @param spec can be {@literal null}. * @param pageable can be {@literal null}. * @return */ private TypedQuery<T> getQuery(Specification<T> 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 */ private TypedQuery<T> getQuery(Specification<T> spec, Sort sort) { CriteriaBuilder builder = entityManager.getCriteriaBuilder(); CriteriaQuery<T> query = builder.createQuery(clazz); Root<T> root = applySpecificationToCriteria(spec, query); query.select(root); if (sort != null) { query.orderBy(toOrders(sort, root, builder)); } return applyLockMode(entityManager.createQuery(query)); } /** * Creates a new count query for the given {@link Specification}. * * @param spec can be {@literal null}. * @return */ private TypedQuery<Long> getCountQuery(Specification<T> spec) { CriteriaBuilder builder = entityManager.getCriteriaBuilder(); CriteriaQuery<Long> query = builder.createQuery(Long.class); Root<T> root = applySpecificationToCriteria(spec, query); if (query.isDistinct()) { query.select(builder.countDistinct(root)); } else { query.select(builder.count(root)); } return entityManager.createQuery(query); } /** * Applies the given {@link Specification} to the given {@link CriteriaQuery}. * * @param spec can be {@literal null}. * @param query must not be {@literal null}. * @return */ private <S> Root<T> applySpecificationToCriteria(Specification<T> spec, CriteriaQuery<S> query) { Assert.notNull(query); Root<T> root = query.from(clazz); if (spec == null) { return root; } CriteriaBuilder builder = entityManager.getCriteriaBuilder(); Predicate predicate = spec.toPredicate(root, query, builder); if (predicate != null) { query.where(predicate); } return root; } private TypedQuery<T> applyLockMode(TypedQuery<T> query) { LockModeType type = lockMetadataProvider == null ? null : lockMetadataProvider.getLockModeType(); return type == null ? query : query.setLockMode(type); } }