一、前言

  由于项目中的 实体(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)

  这里我们实现第二个构造函数,拿到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;
} @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来代替默认的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> { @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的更多相关文章

  1. 深入探索Spring Data JPA, 从Repository 到 Specifications 和 Querydsl

    数据访问层,所谓的CRUD是后端程序员的必修课程,Spring Data JPA 可以让我们来简化CRUD过程,本文由简入深,从JPA的基本用法,到各种高级用法. Repository Spring ...

  2. spring data jpa 查询自定义字段,转换为自定义实体

    目标:查询数据库中的字段,然后转换成 JSON 格式的数据,返回前台. 环境:idea 2016.3.4, jdk 1.8, mysql 5.6, spring-boot 1.5.2 背景:首先建立 ...

  3. 【spring data jpa】使用repository进行查询,使用userRepository.getOne(id)和userRepository.findById(id)无法从数据库查询到数据

    如题: 使用repository进行查询,使用CrudRepository自带的getOne()方法和findById()方法查询,数据库中有这条数据,但是并不能查到. userRepository. ...

  4. 扩展spring data jpa的repository

    在我们编写代码的过程中,spring data jpa为我们的持久层提供的极大的方便,但有时spring data jpa提供的repository并不能完全满足我们开发的需求,因此就需要进行扩展.s ...

  5. spring data jpa自定义bean字段映射

    当遇到复杂多表查询时,并且同时还需要确保查询性能,此时则需要使用自定义sql查询,然而spring data jpa对于自定义sql则需使用查询需要在对应的bean中做太多的配置映射,我尝试了一下,最 ...

  6. Spring data jpa 复杂动态查询方式总结

    一.Spring data jpa 简介 首先我并不推荐使用jpa作为ORM框架,毕竟对于负责查询的时候还是不太灵活,还是建议使用mybatis,自己写sql比较好.但是如果公司用这个就没办法了,可以 ...

  7. Spring data JPA 理解(默认查询 自定义查询 分页查询)及no session 三种处理方法

    简介:Spring Data JPA 其实就是JDK方式(还有一种cglib的方式需要Class)的动态代理 (需要一个接口 有一大堆接口最上边的是Repository接口来自org.springfr ...

  8. spring data jpa自定义baseRepository

    在一些特殊时候,我们会设计到对Spring Data JPA中的方法进行重新实现,这将会面临一个问题,如果我们新创建一个实现类.如果这个实现类实现了JpaRepository接口,这样我们不得不实现该 ...

  9. 【Spring Data 系列学习】Spring Data JPA 自定义查询,分页,排序,条件查询

    Spring Boot Jpa 默认提供 CURD 的方法等方法,在日常中往往时无法满足我们业务的要求,本章节通过自定义简单查询案例进行讲解. 快速上手 项目中的pom.xml.application ...

随机推荐

  1. Spring Security——核心类简介——获得登录用户的相关信息

    核心类简介 目录 1.1     Authentication 1.2     SecurityContextHolder 1.3     AuthenticationManager和Authenti ...

  2. centos 6.5下安装文件上传下载服务

    centos 6.5下安装文件上传下载服务 由于每次在CentOS中要下载一些配置文件到物理机,和上传一些文件到服务器,导致来回的开启ftp软件有点麻烦,这里我们可以使用文件上传下载服务,来解决上传和 ...

  3. A窗口消失B窗口弹出

    一.设计窗口 1) file---new--Application,新建一个窗体,设置该窗体的属性:Caption==登录窗口 2) procedure TForm1.btn1Click(Sender ...

  4. 编译安装mysql-server5.6.32手记

    起因: CentOS内置源里面默认安装的Mysql-server太老旧,不支持一些新方法的调用,需要重新安装一个新版本 老版本里面有数据,不想卸载,想保留库和软件本身 机器上没有运行Docker,而且 ...

  5. javascript window.confirm确认 取消对话框实现代码小结

    本文章讲述的三种都是基于了javascript confirm提示确认框的做法了,只是在不同的地方写哦,有需要的同学可参考一下   confirm() 方法 confirm() 方法用于显示一个带有指 ...

  6. ASP.NET速度优化

    用过ASP.NET的人都知道吧,页面首次打开很慢,本来网站第一次启动就慢,但别的页面如果没有访问过的第一次访问也会慢. 原因:asp.net程序第一次运行需要验证数字签名,这个验证需要远程连接微软服务 ...

  7. NodeJS的Cluster模块使用

    一.前言大家都知道nodejs是一个单进程单线程的服务器引擎,不管有多么的强大硬件,只能利用到单个CPU进行计算.所以,有人开发了第三方的cluster,让node可以利用多核CPU实现并行. 随着n ...

  8. windows10 subsystem(bash) 如何使用jupter notebook

    按照传统惯例,应该映射端口jupyter notebook --port=15000,然后用浏览器开http://localhost:15000/ 但是当你运行python的时候,居然发现kernel ...

  9. HDU5875

    Function Time Limit: 7000/3500 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)Total ...

  10. Android 隐藏软键盘

    隐藏软键盘 public void hideSoftInputView() { InputMethodManager manager = ((InputMethodManager) this.getS ...