JpaRepository如何实现增删改查并进行单元测试

目录
  • JpaRepository增删改查进行单元测试
    • 项目结构
    • 单元测试
  • SpringDataJPA的Repository理解
    • repository抽取扩展理解
    • 接下来贴一贴代码

JpaRepository增删改查进行单元测试

项目结构

创建UserInfoDaoI.java文件,继承JpaRepository。(不需要实现类)

根据条件查询/删除

更新

参考此文章进行开发

单元测试

SpringDataJPA的Repository理解

repository抽取扩展理解

在SpringDataJPA中使用repository来进行数据层操作(作用相当于dao层),直接使用repository对象调用已经实现好的数据层操作方法进行CRUD、分页、排序等操作,还可以在自定义repository接口中依据一定规则扩展功能。在一个项目中,我们平常需要为每一个实体类都创建一个repository接口,我们自定义的repository接口都需要去继承JpaRepository接口,以具有所有的数据层操作功能,但也仅仅限于简单查询等操作。通常我们为了能实现复杂查询操作,会继续继承JpaSpecificationExecutor接口,简单说就是建立一个规则来进行查询。

这样虽然在很大程度上简化了开发代码难度,但在为每一实体类创建对应repository接口时,发现存在大量的重复代码,这时很容易的我们会想到抽取父类来简化一些公共代码并顺带可以对子类进行一些规范。虽然SpringDataJPA提供的功能已经足够强大了,但是依然还是不能满足实际开发中的所有需求,所以我们想在抽取父类的同时实现对其功能的扩展。在进行这个操作之前我们需要了解一下整个repository的结构。

应该免不了有疑问,我们仅仅是定义了一个接口去实现了JpaRepository接口,怎么就能创建对象了,而且还能给我们实现一系列的数据层操作?这就要从为什么自定义repository接口要去继承JpaRepository接口说起了:

通过它的结构图可以看出,JpaRepository的父类分别是PagingAndSortingRepository、CrudRepository和Repository,前两个接口中前者定义了分页和排序功能,后者看名字就可以知道里面全是CRUD操作,值得注意的是,它这保存和修改方法都是save。Repository接口呢没有定义任何功能,它的作用就是标识。这么说吧我们自定义的接口继承JpaRepository接口就相当于继承了Repository接口,那么SpringDataJPA在扫描时只要扫描到我们某个接口实现了Repository接口,就为自动的为其创建代理实现子类(通过AOP实现)。但是为什么就只为我们自定义的repository接口创建了实现子类而没有为PagingAndSortingRepository、CrudRepository、JpaRepository创建呢?这个同样能从结构图中看出答案:它们都是打上了NoRepositoryBean注解的,凡是打上了这个注解的接口SPringDataJPA都不会为其创建实现子类。

这样一来,我们自定义的接口继承了以上三个接口后就相当于“集百家之长”了,拥有了它们所有功能,但是问题又来了,它们再牛终归还是接口啊,又不能创建对象,是怎么操作的呢?

原来,SpringDataJPA自动的为自动创建的repository实现子类继承了SimpleJpaRepository类,而SimpleJpaRepository又是SpringDataJPA的Repository默认实现两大方式之一,自然而然的自动创建的repository代理子类也就具有了所有的功能。至此,SpringDataJPA中repository实现数据层操作的原理也大概讲清楚了,接下来就说说扩展了:虽然SPringDataJPA提供的功能已经足够强大了,但是依然还是不能满足实际开发中的所有需求。所以说了这么久终于要说到重点了:抽取父类、扩展功能。

通过上图,大概也能看出结构了,大概思想:在原本应该继承JpaRepository的实体类repository接口与JpaRepository之间增加了一层而已,让BaseRepository继承JpaSpecificationExecutor、JpaRepository,实体类repository接口继承BaseRepository接口;这样便能实现在拥有原有SpringDataJPA的Repository功能的情况下在BaseRepository扩展其他功能,但在这需要注意:在SpringDataJPA中默认会使自动创建的repository代理实现子类(例:图中的EmployeeRepositoryImpl)去继承SimpleJpaRepository,很明显我们并不能使用默认的,因为这样会使得我们的BaseRepository接口定义的一无是处,所以我们需要再创建一个自定义的BaseRepository实现BaseRepositoryImpl继承SimpleJpaRepository,再修改配置使得SpringDataJPA自动创建出的所有实体类repository的实现代理子类都去继承我们的BaseRepositoryImpl,这样我们便能彻底实现扩展了。

前面说过SpringDataJPA会默认的将自动创建出的实现代理子类继承SimpleJpaRepository类,所以我们需要修改SimpleJpaRepository为我们自定义的BaseRepositoryImpl类,SpringDataJPA是通过JpaRepositoryFactoryBean来实现创建并继承过程的。我们只用自定义一个BaseRepositoryFactoryBean来继承JpaRepositoryFactoryBean,接下来只用修改最终返回功能对象,和确定功能对象的类型即可。最后记得要在spring的配置文件中在SpringDataJPA的配置中添加修改创建对象的factoryBean为我们自定义的BaseRepositoryFactoryBean。

最后,记得修改各实体repository接口继承我们定义的BaseRepository接口

接下来贴一贴代码

接下来贴一贴代码

BaseRepository

import com.xer.aisell.query.BaseQuery;
import org.springframework.data.domain.Page;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.NoRepositoryBean;
import java.io.Serializable;
import java.util.List;
/**
 * 虽然jpadata提供的功能已经足够强大了,但是依然还是不能满足实际开发中的所有需求
 * 所以希望在具有它功能的前提下再拓展一些功能
 * 之前是通过每个实体类的repository接口来直接继承JpaRepository接口,现在我们可以在中间添加一个父类接口BaseRepository
 * BaseRepository继承JpaRepository,再由实体类repository来继承BaseRepository
 * 这样就能够实现既实现了功能,又能随时扩展功能
 * @param <T>
 * @param <ID>
 */
@NoRepositoryBean
public interface BaseRepository<T,ID extends Serializable> extends JpaRepository<T,ID>,JpaSpecificationExecutor<T>{
    //根据Query拿到分页对象(分页)
    Page findPageByQuery(BaseQuery baseQuery);
    //根据Query拿到对应的所有数据(不分页)
    List<T> findByQuery(BaseQuery baseQuery);
    //根据jpql与对应的参数拿到数据
    List findByJpql(String jpql,Object... values);
}

BaseRepositoryImpl

import com.xer.aisell.query.BaseQuery;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
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.SimpleJpaRepository;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.io.Serializable;
import java.util.List;
public class BaseRepositoryImpl<T,ID extends Serializable> extends SimpleJpaRepository<T,ID> implements BaseRepository<T,ID> {
    private final EntityManager entityManager;
    public BaseRepositoryImpl(Class<T> domainClass, EntityManager em) {
        super(domainClass, em);
        this.entityManager = em;
    }
    @Override
    /**
     * 分页
     */
    public Page findPageByQuery(BaseQuery baseQuery) {
        //拿到条件
        Specification spec = baseQuery.createSpec();
        //创建排序
        Sort sort = baseQuery.getSort();
        //分页
        Pageable page = new PageRequest(baseQuery.getJPACurrentPage(), baseQuery.getPageSize(), sort);
        return super.findAll(spec,page);
    }
    /**
     * 不分页查询
     * @param baseQuery
     * @return
     */
    @Override
    public List findByQuery(BaseQuery baseQuery) {
        //获取到条件
        Specification spec = baseQuery.createSpec();
        //排序
        Sort sort = baseQuery.getSort();
        return super.findAll(spec,sort);
    }
    /**
     *
     * 根据传入JPQL查询
     * @param jpql
     * @param values
     * @return
     */
    @Override
    public List findByJpql(String jpql, Object... values) {
        Query query = entityManager.createQuery(jpql);
        //为传入JPQL填充条件值
        if (values != null) {
            for (int i = 0;i < values.length;i++) {
                query.setParameter(i+1,values[i]);
            }
        }
        return query.getResultList();
    }
}

EmployeeRepository接口

import com.xer.aisell.domain.Employee;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
/***
 *用于数据库操作
 */
public interface EmployeeRepository extends BaseRepository<Employee,Long> {
    /**
     * 可以自己扩展
     */
    List<Employee> findByUsernameLike(String username);
    /**
     * 使用@Query
     *  实现自己写JPQL查询
     */
    @Query("select o from Employee o where username like ?1")
    List<Employee> findByUsername(String username);
    /**
     * 当然也可以实现自己写SQL
     */
    @Query(nativeQuery = true,value = "SELECT COUNT(*) FROM employee")
    Long getCount();
}

BaseRepositoryFactoryBean类(难)

import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
import org.springframework.data.repository.Repository;
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 BaseRepositoryFactoryBean<T extends Repository<S, ID>, S, ID extends Serializable> extends JpaRepositoryFactoryBean<T,S,ID> {
    @Override
    protected RepositoryFactorySupport createRepositoryFactory(EntityManager entityManager) {
        return new MyRepositoryFactory<T,ID>(entityManager); //注:这里创建是我们的自定义类
    }
    //继承JpaRepositoryFactory后,把返回的对象修改成我们自己的实现
    private static  class MyRepositoryFactory<T,ID extends Serializable>   extends JpaRepositoryFactory {
        private final EntityManager entityManager;
        /**
         * Creates a new {@link JpaRepositoryFactory}.
         *
         * @param entityManager must not be {@literal null}
         */
        public MyRepositoryFactory(EntityManager entityManager) {
            super(entityManager);
            this.entityManager = entityManager;
        }
        //这里返回最后的功能对象
        @Override
        protected Object getTargetRepository(RepositoryInformation information) {
            return new BaseRepositoryImpl<T,ID>((Class<T>)information.getDomainType(),entityManager);
        }
        //确定功能对象的类型
        @Override
        protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {
            return BaseRepositoryImpl.class;
        }
    }
}

spring配置文件中关于SpringDataJPA的配置

 <jpa:repositories base-package="com.xer.aisell.dao" entity-manager-factory-ref="entityManagerFactory"
                      transaction-manager-ref="transactionManager"
                        factory-class="com.xer.aisell.dao.BaseRepositoryFactoryBean"/>

以上为个人经验,希望能给大家一个参考,也希望大家多多支持我们。

时间: 2021-11-22

spring-data-jpa实现增删改查以及分页操作方法

有几个坑一定要注意: 实现删除操作的时候一定要在各层类中 增加 @Transactional 注释,否则会一直报错 在自己使用@Query定义操作时,会碰到编译器报错,这个时候只需要禁用QL的语法检查即可 以下是部分代码: //Repository package com.example.myproject.dao; import com.example.myproject.domain.User; import org.springframework.data.domain.Page; imp

Spring boot2+jpa+thymeleaf实现增删改查

一.pom.xml引入相关模块web.jpa.thymeleaf.oracle: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot

Spring Boot中使用Spring-data-jpa实现数据库增删查改

在实际开发过程中,对数据库的操作无非就"增删改查".就最为普遍的单表操作而言,除了表和字段不同外,语句都是类似的,开发人员需要写大量类似而枯燥的语句来完成业务逻辑. 为了解决这些大量枯燥的数据操作语句,我们第一个想到的是使用ORM框架,比如:Hibernate.通过整合Hibernate之后,我们以操作Java实体的方式最终将数据改变映射到数据库表中. 为了解决抽象各个Java实体基本的"增删改查"操作,我们通常会以泛型的方式封装一个模板Dao来进行抽象简化,但是这

JPA之使用JPQL语句进行增删改查

JPA支持两种表达查询的方法来检索实体和来自数据库的其他持久化数据:查询语句(Java Persistence Query Language,JPQL)和条件API(criteria API).JPQL是独立于数据库的查询语句,其用于操作逻辑上的实体模型而非物理的数据模型.条件API是根据实体模型构建查询条件 1.Java持久化查询语句入门 复制代码 代码如下: List<Person> persons= entityManager.createQuery("select p fro

SpringBoot+MySQL+Jpa实现对数据库的增删改查和分页详解

一. 使用Springboot+Jpa实现对mysql数据库的增删改查和分页功能 JPA是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中. 使用Springboot和jpa对数据库进行操作时,能够大大减少我们的工作量,在jpa中,已经在底层封装好了增删查的功能和sql语句,可以使我们进行快速开发 二.项目过程和配置文件 1.applaction.properties文件配置

基于sqlalchemy对mysql实现增删改查操作

需求场景: 老大让我利用爬虫爬取的数据写到或更新到mysql数据库中,百度了两种方法 1 是使用pymysql连接mysql,通过操作原生的sql语句进行增删改查数据: 2 是使用sqlalchemy连接mysql,通过ORM模型建表并操作数据库,不需要写原生的sql语句,相对简单些: 以下就是本次使用sqlalchemy的经验之谈. 实现流程:连接数据库>通过模型类创建表>建立会话>执行创建表语句>通过会话进行增删改查 from sqlalchemy import exists,

SpringBoot JPA实现增删改查、分页、排序、事务操作等功能示例

今天给大家介绍一下SpringBoot中JPA的一些常用操作,例如:增删改查.分页.排序.事务操作等功能. 下面先来介绍一下JPA中一些常用的查询操作: //And --- 等价于 SQL 中的 and 关键字,比如 findByHeightAndSex(int height,char sex): public List<User> findByHeightAndSex(int height,char sex); // Or --- 等价于 SQL 中的 or 关键字,比如 findByHei

利用python模拟sql语句对员工表格进行增删改查

本文主要给大家介绍了关于python模拟sql语句对员工表格进行增删改查的相关内容,分享出来供大家参考学习,下面来一起看看详细的介绍: 具体需求: 员工信息表程序,实现增删改查操作: 可进行模糊查询,语法支持下面3种: select name,age from staff_data where age > 22                  多个查询参数name,age 用','分割 select * from staff_data where dept = 人事 select * from

mysql增删改查基础语句

语法 这里是INSERT INTO命令将数据插入到MySQL表的通用SQL语法: INSERT INTO table_name ( field1, field2,...fieldN ) VALUES ( value1, value2,...valueN ); 要插入字符串类型数据,则需要双或单引号保留到所有的值,例如: "value". 1.从命令提示符插入数据 这将使用SQL 的INSERT INTO命令将数据插入到MySQL表:tutorials_tbl 示例 root@host#

Mysql的增删改查语句简单实现

Mysql的增删改查语句简单实现 增加记录: insert into tablename(...) values(...) //如果增加的记录包括所有的列,则不需要写数据列表 insert into tablename values(...) 删除记录: delete from tablename where condition ; 修改记录: update tablename set xx=xx , xx=xx... where condition ; alter table tablenam

MySQL 详细单表增删改查crud语句

MySQL 增删改查语句 1.创建练习表 这里练习表没有满足三范式 第一范式(又称 1NF):保证每列的原子性 数据表中的每一列(字段),必须是不可拆分的最小单元,也就是确保每一列的原子性.满足第一范式是关系模式规范化的最低要求,否则,将有很多基本操作在这样的关系模式中实现不了. 第二范式(又称 2NF):保证一张表只描述一件事情 满足1NF后要求表中的所有列,每一行的数据只能与其中一列相关,即一行数据只做一件事.只要数据列中出现数据重复,就要把表拆分开来. 第三范式(又称 3NF):保证每列都

Springboot+hibernate实现简单的增删改查示例

1.创建好项目之后在配置端口号(也可以不用配置,默认端口8080) #server server.port=8080 server.tomcat.uri-encoding=utf-8 2.配置mysql #MySQL spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.datasource.url=jdbc:mysql://localhost:3306/test?characterEncoding=utf8 sprin