Spring Data JPA: 实现自定义Repository

一、前言

  由于项目中的 实体(entity)默认都是继承一个父类(包含一些公共的属性,比如创建时间,修改时间,是否删除,主键id)。为了实现逻辑删除,一般会自己实现RepositoryFactoryBean 和 Repository。但是由于多个团队开发的结果,表的结构没有同一,也就是会出现有的表没有基础父类对应的字段,这样就会导致自定义的jpa repository操作这些表就会出错。

二、最开始实现

  默认父类

import java.io.Serializable;
import java.sql.Timestamp;import javax.persistence.Column;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.MappedSuperclass;import org.hibernate.annotations.GenericGenerator;@MappedSuperclass
public abstract class AbsIdEntity implements Serializable {private static final long serialVersionUID = 7988377299341530426L;@Id@GenericGenerator(name="uuid", strategy="uuid")@GeneratedValue(generator="uuid")@Column(name="id")protected String id;@Column(name = "creationtime")protected Timestamp creationTimestamp = new Timestamp(System.currentTimeMillis());@Column(name = "lastmodifiedtime")protected Timestamp modificationTimestamp = new Timestamp(System.currentTimeMillis());@Column(name = "dr")protected int dr;// 是否删除。0:未删除;1:已删除/*** 主键,对应id字段*/public String getId() { return id; }public void setId(String id) { this.id = id; }/*** 创建日期,对应ts_insert字段*/public Timestamp getCreationTimestamp() { return creationTimestamp; }public void setCreationTimestamp(Timestamp creationTimestamp) { this.creationTimestamp = creationTimestamp; }/*** 修改日期,对应ts_update字段*/public Timestamp getModificationTimestamp() { return modificationTimestamp; }public void setModificationTimestamp(Timestamp modificationTimestamp) { this.modificationTimestamp = modificationTimestamp; }/*** 是否删除,对应dr字段* @return*/public int getDr() {return dr;}public void setDr(int dr) {this.dr = dr;}}
View Code

  自定义Repository接口

  • 添加BaseDao接口
  • BaseDao继承了JpaSpecificationExecutor、CrudRepository,这样可以保证所有Repository都有基本的增删改查以及分页等方法。
  • 在BaseDao上添加@NoRepositoryBean标注,这样Spring Data Jpa在启动时就不会去实例化BaseDao这个接口
import java.io.Serializable;import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.NoRepositoryBean;/*** Data Access Object基类,已经包含了常用的增删改查操作。<br>* 使用时只需要继承接口,不需要实现类,spring自动通过cglib生成实现类* * @param <T>*            实体类型*/
@NoRepositoryBean
public interface BaseDao<T extends AbsIdEntity> extendsCrudRepository<T, Serializable>/* JpaRepository<T, Serializable> */, JpaSpecificationExecutor<T> {
}
View Code

  然后,使所有Repository接口都继承BaseDao

  实现BaseRepository

  定义好自定义的方法后,我们现在通过一个基本的Repository类来实现该方法:

  首先添加BaseDaoImpl类,继承SimpleJpaRepository类,使其拥有Jpa Repository的基本方法。

  我们发现Repository有两个构造函数:

  • SimpleJpaRepository(JpaEntityInformation entityInformation, EntityManager entityManager)
  • SimpleJpaRepository(Class domainClass, EntityManager em)

  这里我们实现第二个构造函数,拿到domainClassEntityManager两个对象。因为我们要实现的是知道某个Repository是否支持某个领域对象的类型,因此在实现构造函数时我们将domainClass的信息保留下来。

import java.io.Serializable;
import java.sql.Timestamp;import javax.persistence.EntityManager;import org.springframework.data.jpa.repository.support.JpaEntityInformation;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.transaction.annotation.Transactional;import com.yyjz.icop.base.dao.BaseDao;@Transactional
public class BaseDaoImpl<T extends AbsIdEntity> extends SimpleJpaRepository<T, Serializable> implements BaseDao<T> {@SuppressWarnings("unused")private final EntityManager entityManager;public BaseDaoImpl(Class<T> domainClass, EntityManager entityManager) {super(domainClass, entityManager);this.entityManager = entityManager;}public BaseDaoImpl(JpaEntityInformation<T, Serializable> information, EntityManager entityManager) {super(information, entityManager);this.entityManager = entityManager;}@Overridepublic <S extends T> S save(S entity) {entity.setModificationTimestamp(new Timestamp(System.currentTimeMillis()));return super.save(entity);}/*** 只做逻辑删除*/@Overridepublic void delete(T entity) {entity.setDr(1);save(entity);}@Overridepublic void delete(Serializable id) {T entity = findOne(id);entity.setDr(1);this.save(entity);}}
View Code

  RepositoryFactoryBean 实现

  接下来我们来创建一个自定义的BaseDaoFactoryBean来代替默认的RepositoryFactoryBeanRepositoryFactoryBean负责返回一个RepositoryFactory,Spring Data Jpa 将使用RepositoryFactory来创建Repository具体实现,这里我们用BaseDaoImpl代替SimpleJpaRepository作为Repository接口的实现。这样我们就能够达到为所有Repository添加或修改自定义方法的目的。

import xxx.AbsIdEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;import javax.persistence.EntityManager;
import java.io.Serializable;public class BaseDaoFactoryBean<R extends JpaRepository<T, Serializable>, T extends AbsIdEntity> extends JpaRepositoryFactoryBean<R, T, Serializable> {@Overrideprotected RepositoryFactorySupport createRepositoryFactory(final EntityManager entityManager) {return new JpaRepositoryFactory(entityManager) {protected SimpleJpaRepository<T, Serializable> getTargetRepository(RepositoryInformation information,    EntityManager entityManager) {return new BaseDaoImpl((Class<T>) information.getDomainType(), entityManager);}@Overrideprotected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {return BaseDaoImpl.class;}};}
}
View Code

  jpa 配置文件

<!-- Spring Data Jpa配置 -->
<jpa:repositories base-package="com.xxx"transaction-manager-ref="transactionManager"entity-manager-factory-ref="entityManagerFactory"factory-class="xxx.BaseDaoFactoryBean"><!-- 自定义RepositoryFactoryBean -->
</jpa:repositories>

三、改进之后

  由于有的表没有默认父类AbsIdEntity对应的字段,导致生成 Repository 在操作表的时候会报错。需要修改的就是RepositoryFactoryBean的实现逻辑。对于继承了AbsIdEntity的实体类,返回自定义的BaseRepository(也就是BaseDaoImpl),否则就返回SimpleJpaRepository。注意自定义RepositoryFactoryBean的泛型也做了修改。

import xxx.AbsIdEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;import javax.persistence.EntityManager;
import java.io.Serializable;public class BaseDaoFactoryBean<R extends JpaRepository<T, Serializable>, T> extends JpaRepositoryFactoryBean<R, T, Serializable> {@Overrideprotected RepositoryFactorySupport createRepositoryFactory(final EntityManager entityManager) {return new JpaRepositoryFactory(entityManager) {protected SimpleJpaRepository<T, Serializable> getTargetRepository(RepositoryInformation information,    EntityManager entityManager) {Class<T> domainClass = (Class<T>) information.getDomainType();if(AbsIdEntity.class.isAssignableFrom(domainClass)) {return new BaseDaoImpl(domainClass, entityManager);} else { return new SimpleJpaRepository(domainClass, entityManager);}}@Overrideprotected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {return metadata.getDomainType().isAssignableFrom(AbsIdEntity.class) ? BaseDaoImpl.class : SimpleJpaRepository.class;}};}
}
View Code

  至此,完成了适配。

 

 生活不止眼前的bug,还有诗和远方。。。

转载于:https://www.cnblogs.com/hujunzheng/p/6494671.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/531271.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

js冲刺一下

js中__proto__和prototype的区别和关系 1.对象有属性__proto__,指向该对象的构造函数的原型对象。  2.方法除了有属性__proto__,还有属性prototype&#xff0c;prototype指向该方法的原型对象。 深入浅出妙用 Javascript 中 apply、call、bind ***两道面试题*** 关于js中伪数…

Jackson序列化实例

参考文章 Jackson使用ContextualSerializer在序列化时获取字段注解的属性 使用BeanSerializerModifier定制jackson的自定义序列化(null值的处理) 关于使用ContextualSerializer的补充 BeanSerializerFactory中有如下代码&#xff0c; 关于设置SerializerModifier&#xff0c;如…

cas4.2.7实现单点登录

准备前参考&#xff1a;  cas server下载地址 cas client 下载地址 安全cookie setSecure详解 Spring通过构造方法注入的四种方式 cas 学习博文 自定义登录页和登录认证 cas server端的login-webflow详细流程 CAS服务端自定义数据库认证用户 准备工作 1. cas server下载之后解…

swagger restful api form映射实体对象和body映射实体对象配置

实体Model ModelAttribute一个具有如下三个作用&#xff1a; ①绑定请求参数到命令对象&#xff1a;放在功能处理方法的入参上时&#xff0c;用于将多个请求参数绑定到一个命令对象&#xff0c;从而简化绑定流程&#xff0c;而且自动暴露为模型数据用于视图页面展示时使用&…

ssh端口转发(之kettle ssh方式连接数据库)

ssh参数解释 格式  ssh [user]host [command] 选项&#xff1a; -1&#xff1a;强制使用ssh协议版本1&#xff1b; -2&#xff1a;强制使用ssh协议版本2&#xff1b; -4&#xff1a;强制使用IPv4地址&#xff1b; -6&#xff1a;强制使用IPv6地址&#xff1b; -A&#xff1a…

ThreadLocal和InheritableThreadLocal使用

InheritableThreadLocal代码 public class InheritableThreadLocal<T> extends ThreadLocal<T> {protected T childValue(T parentValue) {return parentValue;}ThreadLocalMap getMap(Thread t) {return t.inheritableThreadLocals;}void createMap(Thread t, T f…

mybatis generator修改默认生成的sql模板

相关连接&#xff1a; mybatis-generator扩展教程系列 -- 自定义sql xml文件 git项目地址 转载于:https://www.cnblogs.com/hujunzheng/p/7110510.html

oauth简单使用

一、oauth原理参考 理解OAuth 2.0 二、本例中采用授权码模式 大致流程 &#xff08;A&#xff09;用户访问客户端&#xff0c;后者将前者导向认证服务器。  &#xff08;B&#xff09;用户选择是否给予客户端授权。  &#xff08;C&#xff09;假设用户给予授权&#xff0c…

我眼中的服务提供和服务消费

服务提供和消费脑图 服务提供和消费脑图 参见: 服务提供者, 服务消费者, 服务注册中心 服务提供者 1.服务提供者启动&#xff0c;解析xml文件中配置的服务&#xff0c;这里使用Dom4j解析。 2.将服务的一些相关信息注册到 服务注册中心。 注&#xff1a;服务相关信息&#xff1a…

shiro整合oauth

前言 如果oauth原理还不清楚的地方&#xff0c;其参考这里。 一、基本思路脑图 二、客户端shiro配置 shiro配置文件 <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans"xmlns:util&q…

springmvc+swagger2

一、swagger2依赖 <!--swagger--> <dependency><groupId>io.springfox</groupId><artifactId>springfox-swagger2</artifactId><exclusions><exclusion><artifactId>spring-aop</artifactId><groupId>org.s…

获取资源文件工具类

如果没有依赖spring&#xff0c;可以将分割线下的方法去掉 import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframe…

无状态shiro认证组件(禁用默认session)

准备内容 简单的shiro无状态认证 无状态认证拦截器 import com.hjzgg.stateless.shiroSimpleWeb.Constants; import com.hjzgg.stateless.shiroSimpleWeb.realm.StatelessToken; import org.apache.shiro.web.filter.AccessControlFilter;import javax.servlet.ServletRequest;…

Spring根据包名获取包路径下的所有类

参考mybatis MapperScannerConfigurer.java 最终找到 Spring的一个类 ClassPathBeanDefinitionScanner.java 参考ClassPathBeanDefinitionScanner 和它的父类 ClassPathScanningCandidateComponentProvider&#xff0c;将一些代码进行抽取&#xff0c;得到如下工具类。 import…

idea模板注释

类文件头部的注释 #if (${PACKAGE_NAME} && ${PACKAGE_NAME} ! "")package ${PACKAGE_NAME};#end #parse("File Header.java") /** * ${DESCRIPTION} * author ${USER} hujunzheng * create ${YEAR}-${MONTH}-${DAY} ${TIME} **/ public class ${N…

redis分布式锁小试

一、场景 项目A监听mq中的其他项目的部署消息&#xff08;包括push_seq, status, environment&#xff0c;timestamp等&#xff09;&#xff0c;然后将部署消息同步到数据库中&#xff08;项目X在对应环境[environment]上部署的push_seq[项目X的版本]&#xff09;。那么问题来了…

Jackson ObjectMapper readValue过程

1.整体调用栈 2.看一下调用栈的两个方法 resolve 方法中通过 Iterator i$ this._beanProperties.iterator() 遍历属性的所有子属性&#xff0c;缓存对应的 deserializer。观察调用栈的方法&#xff0c;可以发现是循环调用的。 3.比如寻找自定义的 LocalDateTime类的序列化实现…

java如何寻找main函数对应的类

参考springboot Class<?> deduceMainApplicationClass() {try {StackTraceElement[] stackTrace new RuntimeException().getStackTrace();for (StackTraceElement stackTraceElement : stackTrace) {if ("main".equals(stackTraceElement.getMethodName())…

jooq实践

用法 sql语句 SELECT AUTHOR.FIRST_NAME, AUTHOR.LAST_NAME, COUNT(*)FROM AUTHORJOIN BOOK ON AUTHOR.ID BOOK.AUTHOR_IDWHERE BOOK.LANGUAGE DEAND BOOK.PUBLISHED > DATE 2008-01-01 GROUP BY AUTHOR.FIRST_NAME, AUTHOR.LAST_NAMEHAVING COUNT(*) > 5 ORDER BY AUT…

git根据用户过滤提交记录

使用SourceTree 使用gitk 转载于:https://www.cnblogs.com/hujunzheng/p/8398203.html