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;
} }
自定义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> extends
CrudRepository<T, Serializable>/* JpaRepository<T, Serializable> */, JpaSpecificationExecutor<T> {
}
然后,使所有Repository接口都继承BaseDao
实现BaseRepository
定义好自定义的方法后,我们现在通过一个基本的Repository类来实现该方法:
首先添加BaseDaoImpl类,继承SimpleJpaRepository类,使其拥有Jpa Repository的基本方法。
我们发现Repository有两个构造函数:
- SimpleJpaRepository(JpaEntityInformation entityInformation, EntityManager entityManager)
- SimpleJpaRepository(Class domainClass, EntityManager em)
这里我们实现第二个构造函数,拿到domainClass和EntityManager两个对象。因为我们要实现的是知道某个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;
} @Override
public <S extends T> S save(S entity) {
entity.setModificationTimestamp(new Timestamp(System.currentTimeMillis()));
return super.save(entity);
} /**
* 只做逻辑删除
*/
@Override
public void delete(T entity) {
entity.setDr(1);
save(entity);
} @Override
public void delete(Serializable id) {
T entity = findOne(id);
entity.setDr(1);
this.save(entity);
} }
RepositoryFactoryBean 实现
接下来我们来创建一个自定义的BaseDaoFactoryBean来代替默认的RepositoryFactoryBean。RepositoryFactoryBean负责返回一个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> { @Override
protected 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);
} @Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return BaseDaoImpl.class;
}
};
}
}
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> { @Override
protected 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);
}
} @Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
return metadata.getDomainType().isAssignableFrom(AbsIdEntity.class) ? BaseDaoImpl.class : SimpleJpaRepository.class;
}
};
}
}
至此,完成了适配。
生活不止眼前的bug,还有诗和远方。。。
Spring Data JPA: 实现自定义Repository的更多相关文章
- 深入探索Spring Data JPA, 从Repository 到 Specifications 和 Querydsl
数据访问层,所谓的CRUD是后端程序员的必修课程,Spring Data JPA 可以让我们来简化CRUD过程,本文由简入深,从JPA的基本用法,到各种高级用法. Repository Spring ...
- spring data jpa 查询自定义字段,转换为自定义实体
目标:查询数据库中的字段,然后转换成 JSON 格式的数据,返回前台. 环境:idea 2016.3.4, jdk 1.8, mysql 5.6, spring-boot 1.5.2 背景:首先建立 ...
- 【spring data jpa】使用repository进行查询,使用userRepository.getOne(id)和userRepository.findById(id)无法从数据库查询到数据
如题: 使用repository进行查询,使用CrudRepository自带的getOne()方法和findById()方法查询,数据库中有这条数据,但是并不能查到. userRepository. ...
- 扩展spring data jpa的repository
在我们编写代码的过程中,spring data jpa为我们的持久层提供的极大的方便,但有时spring data jpa提供的repository并不能完全满足我们开发的需求,因此就需要进行扩展.s ...
- spring data jpa自定义bean字段映射
当遇到复杂多表查询时,并且同时还需要确保查询性能,此时则需要使用自定义sql查询,然而spring data jpa对于自定义sql则需使用查询需要在对应的bean中做太多的配置映射,我尝试了一下,最 ...
- Spring data jpa 复杂动态查询方式总结
一.Spring data jpa 简介 首先我并不推荐使用jpa作为ORM框架,毕竟对于负责查询的时候还是不太灵活,还是建议使用mybatis,自己写sql比较好.但是如果公司用这个就没办法了,可以 ...
- Spring data JPA 理解(默认查询 自定义查询 分页查询)及no session 三种处理方法
简介:Spring Data JPA 其实就是JDK方式(还有一种cglib的方式需要Class)的动态代理 (需要一个接口 有一大堆接口最上边的是Repository接口来自org.springfr ...
- spring data jpa自定义baseRepository
在一些特殊时候,我们会设计到对Spring Data JPA中的方法进行重新实现,这将会面临一个问题,如果我们新创建一个实现类.如果这个实现类实现了JpaRepository接口,这样我们不得不实现该 ...
- 【Spring Data 系列学习】Spring Data JPA 自定义查询,分页,排序,条件查询
Spring Boot Jpa 默认提供 CURD 的方法等方法,在日常中往往时无法满足我们业务的要求,本章节通过自定义简单查询案例进行讲解. 快速上手 项目中的pom.xml.application ...
随机推荐
- Python计算一个项目中含有的代码行数
最近想要知道以前做过的project有多少行代码,因为文件太多,直接手工数效率太低,于是编写一个python程序用来计算一个project有多少代码行. 首先,在一个项目中,有很多子文件夹,子文件夹中 ...
- JS的组成部分、引入页面的方法以及命名规范
JS的组成部分.引入页面的方法以及命名规范 一.页面是由三部分组成 1)html标签(超文本标记语言) 2)css样式(层叠样式表) 3)javascript脚本编程语言(动态脚本语言) 二.将c ...
- 多因子降维法(MDR,multifactor dimensionality reduction)
多因子降维法(MDR,Multifactor Dimensionality Reduction ) MDR是近年统计学中发展起来的一种新的分析方法.其中,“因子” 即交互作用研究中的变量,“维” 是指 ...
- Selenium2(java)框架设计 九
设计框架原则: 数据分离,业务层和逻辑层不要混杂在一起. 设计图: 框架结构初始化: com.wymall.test:这是存放这个框架源代码的根目录 base:里面有个基类(BaseParpaare. ...
- iOS 之 const
const int a与 int const a一样. const int *a ;//指针可以修改,指向常整形的指针 int* const a;// 常指针, int* 作为一个整体被限制, 所以指 ...
- Pomelo的Protobuf
pomelo的protobuf实现,借助了javascript的动态性,使得应用程序可以在运行时解析proto文件,不需要进行proto文件的编译.pomelo的实现中,为了更方便地解析proto文件 ...
- Cocos2d-x 多分辨率适配完全解析
从Cocos2d-x 2.0.4开始,Cocos2d-x提出了自己的多分辨率支持方案,废弃了之前的retina相关设置接口,提出了design resolution概念. 有以下相关接口: CCEGL ...
- localStorage的黑科技-js和css缓存机制
一.发现黑科技的起因 今天在微信公众号看到一篇技术博文,想用印象笔记收藏,所以发送了文章链接到pc上.然后习惯性地打开控制台,看看源码,想了解下最近微信用了什么新技术. 呵呵,以下勾起了我侦探的欲 ...
- Android之com.nostra13.universalimageloader加载图片抛出OutOfMemroyError错误的多种解决办法
com.nostra13.universalimageloader是用来加载图片非常好的框架,但是也有问题,一旦图片过多的话,很容易就会提示OutOfMemroyError错误,也就是内存溢出的问题, ...
- HTML5常用标签分类
1.行级元素标签:a.span.sup.sub.em.b.big.i.strong 2.块元素标签:div.p.h1~h6.ul.ol.li.table.form.article.footer.hea ...