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 ...
随机推荐
- ubuntu系统内核替换
此处将内核由高版本替换成低版本.替换前的系统为ubuntu 12.04 kernel 3.8.0. 替换后的内核版本为2.6.35. 首先下载需要替换的内核文件,下载链接:https://www.ke ...
- PHP之Mysql常用SQL语句示例的深入分析
1.插入数据insert into表名(列名1,列名2,列名..) values(值1,值2,值...); insert into product(name, price, pic_path) val ...
- 破解&屏蔽防止嵌入框架代码 top.location != self.location
<script type="text/javascript"> if (top.location != self.location) top.location = se ...
- js原生:封装document.getElementByClassName()函数
//接口封装:封装document.getElementByClassName()函数function getElementsByClassName (cName,domTag,root) {//该函 ...
- Intent的属性及Intent-filter配置——Component属性
Intent的Component属性需要接受一个ComponentName对象,ComponentName对象包含如下几个构造器. ComponentName(String pkg,String cl ...
- ZeroMQ 的模式
在需要并行化处理数据的时候,采用消息队列通讯的方式来协作,比采用共享状态的方式要好的多.Erlang ,Go 都使用这一手段来让并行任务之间协同工作. 最近读完了 ZeroMQ 的 Guide.写的很 ...
- 《RDLC部署》RDLC部署到IIS缺少DLL程序集
1.错误:从vs生成网站部署到服务器后打开RDLC报表却提示缺少DLL程序集. 一般是缺少如下文件 1. Microsoft.ReportViewer.Common.dll 2. Microsof ...
- CodeForces 333A
Secrets Time Limit:1000MS Memory Limit:262144KB 64bit IO Format:%I64d & %I64u Submit Sta ...
- tbl.js div实现的表格控件,完全免费,no jquery
html上现在有比较好用的表格控件是datatable,但是编辑.按钮等部分是收费的,只有基础功能免费.而且尺寸发生变化时需要手工刷新等繁琐操作较多.所以我开发一个免费的供大家使用. 本项目已用于“虚 ...
- IOS获取经度纬度
仔细研究了一下SDK文档,再结合网上的方法,写了这一个简单的获取经纬度的方法,大家看看就好. 首先要导入CoreLocation.Frame 包 .h 文件 1 2 3 4 5 6 7 8 9 #im ...