- 浏览: 66575 次
- 性别:
- 来自: 上海
最新评论
-
springdata-jpa:
springdata jpa相关demo源代码下载,地址:ht ...
在 Spring Data Jpa 中使用逻辑删除需做的工作 -
danny.chiu:
谢谢分享,不知道现在有没有更好的办法,透明系统也自带一个,an ...
构造个全透明的Activity -
gozap_lanbo:
最近我也在做这个事情,我想问下如何让自己定义的JpaQuery ...
在 Spring Data Jpa 中使用逻辑删除需做的工作 -
tag13346:
好丑的API
在 Spring Data Jpa 中使用逻辑删除需做的工作 -
kjj:
本身就是变态的做法
在 Spring Data Jpa 中使用逻辑删除需做的工作
Spring Data 是个好东西,极大简化了后端dao的操作,只需要在 dao 接口写个 findByXXX 的方法就能自动实现按条件查询这个简直太爽了。
不过问题也出现了,我的应用对于数据的操作没有物理删除,全是逻辑删除,也就是每个表都有个字段 deleted,1表示此记录已删除,默认值为 0 。这就与 spring data 提供的模式有冲突了,那剩下的就是:改之。
CRUD 操作
对于基础的CRUD 操作搞起来比较简单,按照其官方文档重新实现个 factory-class 就ok了,具体的 repository 类可以继承 org.springframework.data.jpa.repository.support.SimpleJpaRepository 进行修改,不过我为了省事,直接把这个类复制过来然后下手:
/* * $Id$ */ package com.someok.common.base.spring.data; import static org.springframework.data.jpa.repository.query.QueryUtils.DELETE_ALL_QUERY_STRING; import static org.springframework.data.jpa.repository.query.QueryUtils.applyAndBind; import static org.springframework.data.jpa.repository.query.QueryUtils.getQueryString; 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.List; 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.springframework.dao.EmptyResultDataAccessException; 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.support.JpaEntityInformation; import org.springframework.data.jpa.repository.support.JpaEntityInformationSupport; import org.springframework.data.jpa.repository.support.LockMetadataProvider; import org.springframework.data.jpa.repository.support.SimpleJpaRepository; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; import com.someok.common.base.mvc.BaseDao; import com.someok.common.base.mvc.BaseDefaultModel; import com.someok.common.utils.StringUtil; /** * 修改自 * {@link org.springframework.data.jpa.repository.support.SimpleJpaRepository}, * 提供逻辑删除功能(logicDelete),为适应这个要求,相关的查询也都做了修改。 * * 需要注意的是那些 delete 方法仍然是物理删除,而新增的那些 logic 开头的方法才是逻辑删除, 用时候需要注意这点. * * @author wangjxe * */ @org.springframework.stereotype.Repository @Transactional(readOnly = true) public class CustomSimpleJpaRepository<T, ID extends Serializable> implements BaseDao<T, ID> { /** * 逻辑删除字段名. */ public final static String DELETEED_FIELD = "deleted"; public static final String COUNT_QUERY_STRING = "select count(%s) from %s x where x.deleted = false"; public static final String EXISTS_QUERY_STRING = "select count(%s) from %s x where x.%s = :id and x.deleted = false"; private final JpaEntityInformation<T, ?> entityInformation; private final EntityManager em; // private final PersistenceProvider provider; private LockMetadataProvider lockMetadataProvider; /** * Creates a new {@link SimpleJpaRepository} to manage objects of the given * {@link JpaEntityInformation}. * * @param entityInformation * must not be {@literal null}. * @param entityManager * must not be {@literal null}. */ public CustomSimpleJpaRepository( JpaEntityInformation<T, ?> entityInformation, EntityManager entityManager) { Assert.notNull(entityInformation); Assert.notNull(entityManager); this.entityInformation = entityInformation; this.em = entityManager; // this.provider = PersistenceProvider.fromEntityManager(entityManager); } /** * Creates a new {@link SimpleJpaRepository} to manage objects of the given * domain type. * * @param domainClass * must not be {@literal null}. * @param em * must not be {@literal null}. */ public CustomSimpleJpaRepository(Class<T> domainClass, EntityManager em) { this(JpaEntityInformationSupport.getMetadata(domainClass, em), em); } /** * 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 Class<T> getDomainClass() { return entityInformation.getJavaType(); } private String getDeleteAllQueryString() { return getQueryString(DELETE_ALL_QUERY_STRING, entityInformation.getEntityName()); } private String getCountQueryString() { String countQuery = String.format(COUNT_QUERY_STRING, getCountQueryPlaceholder(), "%s"); return getQueryString(countQuery, entityInformation.getEntityName()); } /* * (non-Javadoc) * * @see org.springframework.data.repository.CrudRepository#delete(java.io. * Serializable) */ @Transactional public void delete(ID id) { Assert.notNull(id, "The given id must not be null!"); if (!exists(id)) { throw new EmptyResultDataAccessException(String.format( "No %s entity with id %s exists!", entityInformation.getJavaType(), id), 1); } delete(findOne(id)); } /* * (non-Javadoc) * * @see * org.springframework.data.repository.CrudRepository#delete(java.lang.Object * ) */ @Transactional public void delete(T entity) { Assert.notNull(entity, "The entity must not be null!"); em.remove(em.contains(entity) ? entity : em.merge(entity)); } /* * (non-Javadoc) * * @see * org.springframework.data.repository.CrudRepository#delete(java.lang.Iterable * ) */ @Transactional public void delete(Iterable<? extends T> entities) { Assert.notNull(entities, "The given Iterable of entities not be null!"); for (T entity : entities) { delete(entity); } } /* * (non-Javadoc) * * @see * org.springframework.data.jpa.repository.JpaRepository#deleteInBatch(java * .lang.Iterable) */ @Transactional public void deleteInBatch(Iterable<T> entities) { Assert.notNull(entities, "The given Iterable of entities not be null!"); if (!entities.iterator().hasNext()) { return; } applyAndBind( getQueryString(DELETE_ALL_QUERY_STRING, entityInformation.getEntityName()), entities, em) .executeUpdate(); } /* * (non-Javadoc) * * @see org.springframework.data.repository.Repository#deleteAll() */ @Transactional public void deleteAll() { for (T element : findAll()) { delete(element); } } /* * (non-Javadoc) * * @see * org.springframework.data.jpa.repository.JpaRepository#deleteAllInBatch() */ @Transactional public void deleteAllInBatch() { em.createQuery(getDeleteAllQueryString()).executeUpdate(); } /* * (non-Javadoc) * * @see * org.springframework.data.repository.Repository#readById(java.io.Serializable * ) */ public T findOne(ID id) { Assert.notNull(id, "The given id must not be null!"); return em.find(getDomainClass(), id); } /* * (non-Javadoc) * * @see org.springframework.data.repository.CrudRepository#exists(java.io. * Serializable) */ public boolean exists(ID id) { Assert.notNull(id, "The given id must not be null!"); if (entityInformation.getIdAttribute() != null) { String placeholder = getCountQueryPlaceholder(); String entityName = entityInformation.getEntityName(); String idAttributeName = entityInformation.getIdAttribute() .getName(); String existsQuery = String.format(EXISTS_QUERY_STRING, placeholder, entityName, idAttributeName); TypedQuery<Long> query = em.createQuery(existsQuery, Long.class); query.setParameter("id", id); return query.getSingleResult() == 1; } else { return findOne(id) != null; } } /* * (non-Javadoc) * * @see org.springframework.data.jpa.repository.JpaRepository#findAll() */ public List<T> findAll() { return getQuery(null, (Sort) null).getResultList(); } /* * (non-Javadoc) * * @see org.springframework.data.repository.CrudRepository#findAll(ID[]) */ public List<T> findAll(Iterable<ID> ids) { return getQuery(new Specification<T>() { public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder cb) { Path<?> path = root.get(entityInformation.getIdAttribute()); return path.in(cb.parameter(List.class, "ids")); } }, (Sort) null).setParameter("ids", ids).getResultList(); } /* * (non-Javadoc) * * @see org.springframework.data.jpa.repository.JpaRepository#findAll(org. * springframework.data.domain.Sort) */ public List<T> findAll(Sort sort) { return getQuery(null, sort).getResultList(); } /* * (non-Javadoc) * * @see * org.springframework.data.repository.PagingAndSortingRepository#findAll * (org.springframework.data.domain.Pageable) */ public Page<T> findAll(Pageable pageable) { if (null == pageable) { return new PageImpl<T>(findAll()); } return findAll(null, pageable); } /* * (non-Javadoc) * * @see * org.springframework.data.jpa.repository.JpaSpecificationExecutor#findOne * (org.springframework.data.jpa.domain.Specification) */ public T findOne(Specification<T> spec) { try { return getQuery(spec, (Sort) null).getSingleResult(); } catch (NoResultException e) { return null; } } /* * (non-Javadoc) * * @see * org.springframework.data.jpa.repository.JpaSpecificationExecutor#findAll * (org.springframework.data.jpa.domain.Specification) */ public List<T> findAll(Specification<T> 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<T> findAll(Specification<T> spec, Pageable pageable) { TypedQuery<T> query = getQuery(spec, pageable); return pageable == null ? new PageImpl<T>(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<T> findAll(Specification<T> spec, Sort sort) { return getQuery(spec, sort).getResultList(); } /* * (non-Javadoc) * * @see org.springframework.data.repository.CrudRepository#count() */ public long count() { return em.createQuery(getCountQueryString(), Long.class) .getSingleResult(); } /* * (non-Javadoc) * * @see * org.springframework.data.jpa.repository.JpaSpecificationExecutor#count * (org.springframework.data.jpa.domain.Specification) */ public long count(Specification<T> spec) { return getCountQuery(spec).getSingleResult(); } /* * (non-Javadoc) * * @see * org.springframework.data.repository.CrudRepository#save(java.lang.Object) */ @Transactional public <S extends T> S save(S entity) { if (entityInformation.isNew(entity)) { em.persist(entity); return entity; } else { return em.merge(entity); } } /* * (non-Javadoc) * * @see * org.springframework.data.jpa.repository.JpaRepository#saveAndFlush(java * .lang.Object) */ @Transactional public T saveAndFlush(T entity) { T result = save(entity); flush(); return result; } /* * (non-Javadoc) * * @see * org.springframework.data.jpa.repository.JpaRepository#save(java.lang. * Iterable) */ @Transactional public <S extends T> List<S> save(Iterable<S> entities) { List<S> result = new ArrayList<S>(); if (entities == null) { return result; } for (S entity : entities) { result.add(save(entity)); } return result; } /* * (non-Javadoc) * * @see org.springframework.data.jpa.repository.JpaRepository#flush() */ @Transactional public void flush() { em.flush(); } /** * 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 = getCountQuery(spec).getSingleResult(); 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 = em.getCriteriaBuilder(); CriteriaQuery<T> query = builder.createQuery(getDomainClass()); Root<T> root = applySpecificationToCriteria(spec, query); query.select(root); if (sort != null) { query.orderBy(toOrders(sort, root, builder)); } return applyLockMode(em.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 = em.getCriteriaBuilder(); CriteriaQuery<Long> query = builder.createQuery(Long.class); Root<T> root = applySpecificationToCriteria(spec, query); query.select(builder.count(root)); return em.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(getDomainClass()); CriteriaBuilder builder = em.getCriteriaBuilder(); // 增加了删除条件判断,从而将被逻辑删除的数据过滤掉 Predicate deletedPredicate = null; if (BaseDefaultModel.class.isAssignableFrom(getDomainClass())) { Path<Boolean> deletedPath = root.<Boolean> get(DELETEED_FIELD); deletedPredicate = builder.isFalse(deletedPath); } if (spec == null) { // 没有其它条件的时候只判断deleted字段 query.where(deletedPredicate); return root; } Predicate predicate = spec.toPredicate(root, query, builder); if (predicate != null) { // 存在其它条件的时候还需要组合一下 deleted 条件 if (null != deletedPredicate) { predicate = builder.and(predicate, deletedPredicate); } 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); } /* * (non-Javadoc) * * @see com.someok.common.base.mvc.BaseDao#logicDelete(java.io.Serializable) */ @Override public void logicDelete(ID id) { T entity = findOne(id); if (null == entity || !(entity instanceof BaseDefaultModel)) { return; } BaseDefaultModel model = (BaseDefaultModel) entity; model.setDeleted(true); this.em.merge(model); } /* * (non-Javadoc) * * @see com.someok.common.base.mvc.BaseDao#logicDelete(java.lang.Object) */ @Override public void logicDelete(T entity) { if (null == entity || !(entity instanceof BaseDefaultModel)) { return; } BaseDefaultModel model = (BaseDefaultModel) entity; model.setDeleted(true); if (StringUtil.isBlank(model.getId())) { em.persist(model); } else { em.merge(model); } } /* * (non-Javadoc) * * @see com.someok.common.base.mvc.BaseDao#logicDelete(java.lang.Iterable) */ @Override public void logicDelete(Iterable<? extends T> entities) { if (null == entities) { return; } for (T entity : entities) { logicDelete(entity); } } protected String getCountQueryPlaceholder() { return "x"; } }
主要的改动是 applySpecificationToCriteria 方法,与 SimpleJpaRepository 比对下就知道改了啥了。
findByXXX 操作
CURD 的修改还是比较简单的,不过那些根据接口方法自动实现查询修改起来就比较麻烦了。当然,不做任何修改也可以用,只需要在dao接口的方法上面加个 @Query 就行了,但是这样就需要写大量的jpql了,与采用 spring data的原意不符,咱用这玩意目的不就是为了个简单嘛。
本来的想法是继承某些类来对需要调整的方法重新实现就ok了,可惜spring data 这块的实现有点太封闭了,多个类没有 public,只能包内可见,更多的需要的方法只提供了 private 属性。没办法,只好把 org.springframework.data.jpa.repository.query 包内的代码都拷贝过来,其实真正需要改动的地方只有一处:
com.someok.common.base.spring.data.query.JpaQueryCreator.complete(Predicate, Sort, CriteriaQuery<Object>, CriteriaBuilder, Root<?>)
具体修改方法如下:
protected CriteriaQuery<Object> complete(Predicate predicate, Sort sort, CriteriaQuery<Object> query, CriteriaBuilder builder, Root<?> root) { // 增加了删除条件判断,从而将被逻辑删除的数据过滤掉 Predicate deletedPredicate = null; if (BaseDefaultModel.class.isAssignableFrom(this.domainClass)) { Path<Boolean> deletedPath = root.<Boolean> get(CustomSimpleJpaRepository.DELETEED_FIELD); deletedPredicate = builder.isFalse(deletedPath); } // 在原有条件基础上组合 deleted 条件 if (null != deletedPredicate) { predicate = builder.and(predicate, deletedPredicate); } return this.query.select(root).where(predicate) .orderBy(QueryUtils.toOrders(sort, root, builder)); }
相关推荐
我们都知道Spring是一个非常优秀的JavaEE整合框架,它尽可能的减少我们开发的工作量和难度。... 而现在,Spring Data JPA帮助我们自动完成了持久层的业务逻辑处理,我们要做的,仅仅是声明一个持久层接口。
我们不用理会EntityManager的创建,事务处理等等.Spring又进步了,只需要声明一下方法接口,Spring Data JPA可以帮你完成数据访问层的实现代码,开发者把更多的心思放在业务逻辑和需求上吧!详细参考...
Spring Boot + Spring Data JPA + Hazelcast缓存 + JSONRpc + Swagger2 + Quartz + MySql + TYK + HTTPS + Junit等实现的 毕业设计是高等教育阶段学生在完成学业前所进行的一项重要学术任务,旨在检验学生通过学习...
对数据库的操作无非就“增删改查”。就最为普遍的单表操作而言,除了表和字段不同外,语句都是类似的,开发人员需要写...Spring-data-jpa的出现正可以让这样一个已经很“薄”的数据访问层变成只是一层接口的编写方式。
springMvc+spring+spring-data-jpa maven整合例子,附带简单逻辑实现代码。
4、删除微博 时间线模块 1、微博首页 2、微博广场 3、用户微博首页 4、我的主页 分层结构如下 • 实体层,ORM映射 • User类、 Status类、Comment类 • 存储层,数据库CRUD • UserRepository类、...
#Spring + SpringMVC + Spring数据JPA构建快速开发环境### Spring数据JPA压缩数据JPA提供了一个完整的数据访问层(DAO)的解决方案,致力于减少数据访问层的开发量,并支持云服务的开源框架。 Spring Data作为...
Spring Data JPA 简介在执行exercise2时使用包exercise1和练习 2。 解决方案可以在相应的解决方案包中找到。 例如,可以在包solution1找到exercise1的solution1 。练习 1 在本练习中,您将在 Spring Data JPA 的帮助...
Spring Data JPA 做的便是规范方法的名字,根据符合规范的名字来确定方法需要实现什么样的逻辑。 --------------------- 版权声明:本文为CSDN博主「殇莫忆」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文...
该库将RSQL转换为Spring Data JPA规范 RSQL支持的运算符: 逻辑运算符 描述 例子 != 不等于 ?filter = name!= Yonit == 等于 ?filter = name == Yonit?filter = name == nit?filter = name == Y > = = ge...
使用 Spring 可插入的 MVC 架构,从而在使用Spring进行WEB开发时,可以选择使用Spring的Spring MVC框架或集成其他MVC开发框架 JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-...
它利用Spring Web MVC,带有Hibernate实现的Spring Data JPA和Spring Security。 实时网站: : 范围这注定是一个入门者。 它配置有用于实现最佳实践身份验证方案的用户和角色数据模型。 该应用程序设置了两个预先...
系统通过Spring Data JPA与MySQL数据库集成,确保数据的安全性和持久性。 模块的关键功能包括: 用户管理:用户可以注册新账户,登录系统,并更新自己的个人资料信息。系统还提供了忘记密码和重置密码的功能。 ...
6.8.1. 在Spring中使用AspectJ进行domain object的依赖注入 6.8.2. Spring中其他的AspectJ切面 6.8.3. 使用Spring IoC来配置AspectJ的切面 6.8.4. 在Spring应用中使用AspectJ加载时织入(LTW) 6.9. 更多资源 7...
2. 创建项目:使用Spring Initializr创建一个新的Spring Boot项目,选择需要的依赖,如Spring Web、Spring Data JPA、Spring Security等。 3. 配置数据库连接:在项目的配置文件中配置数据库连接信息,包括数据库...
Spring-Data-Jpa 核 Jpa存储库 JpaRepository注册原则 DDD 骨料 DomainEvents 学习 对象关系映射(ORM) 如何映射对象和关系数据库 解决paramime的不一致 继承RDBMS没有定义类似于继承的东西,继承是面向对象编程...
首先,我们需要创建一个Spring Boot项目,并在pom.xml文件中添加必要的依赖,如Spring Web、Spring Data JPA和MySQL Connector Java等。然后,我们需要定义一些实体类(如User、Course、Homework和Exam等),并使用...
Spring框架还提供了丰富的功能组件,如Spring MVC用于构建Web应用,Spring Data JPA用于简化数据访问操作,Spring Security用于实现安全控制等。这些组件不仅功能强大,而且易于集成和使用,使得开发者能够快速地...
在项目中,我们可以使用 Spring MVC 框架来处理 HTTP 请求并返回响应,使用 Spring Data JPA 来访问数据库,使用 Thymeleaf 来渲染 HTML 模板页面。 接下来,我们可以创建一些实体类来表示商城中的商品、订单等对象...
通过示例查询(QBE)的Java持久性API(JPA) 基于Spring Data JPA 版本 0.0.1 下一个版本 独特的支持 数学支持 特征 支持和/或逻辑运算 支持为(空/布尔/空) 支持等于/不等于/输入/不输入/赞/不赞 支持gt / ge / ...