GENGEN
主页
vuepress
  • GIT命令
  • python+django
  • vue cli搭建项目
  • babel es6转换es5
  • docker aliyun配置
  • npm 配置
  • linux 常用命令
  • Ubuntu 下Linux 命令
  • github
  • gitee
  • csdn
  • 关于我
主页
vuepress
  • GIT命令
  • python+django
  • vue cli搭建项目
  • babel es6转换es5
  • docker aliyun配置
  • npm 配置
  • linux 常用命令
  • Ubuntu 下Linux 命令
  • github
  • gitee
  • csdn
  • 关于我
  • java基础

    • JDK8 函数式编程
    • JDK8 新特性之Date-Time
    • Servlet 源码分析
    • ArrayList 源码
    • LinkedList 源码
    • HashMap 源码
    • String 源码
    • BigDecimal 源码
    • java 类的加载
    • Class 源码
    • Synchronized锁升级
    • 事务的传播机制
    • knowledge
  • JAVA WEB

    • Java Servlet
    • 权限设计
    • logback日志的链路追踪
  • DATABASE

    • MySQL EXPLAIN详解
    • MySQL 索引
    • MySQL 表锁、行锁
    • MySQL ACID与transcation
    • 分布式事务
    • MySQL MVCC机制
    • Mysql 乐观锁与悲观锁
    • 分布式锁1 数据库分布式锁
    • 分布式锁2 Redis分布式锁
    • 分布式锁3 ZK分布式锁
  • SpringCloud

    • SpringCloud服务注册中心之Eureka
    • SpringCloud服务注册中心之Zookeeper
    • SpringCloud服务调用之Ribbon
    • SpringCloud服务调用之OpenFeign
    • SpringCloud服务降级之Hystrix
    • SpringCloud服务网关之Gateway
    • SpringCloud Config分布式配置中心
    • SpringCloud服务总线之Bus
    • SpringCloud消息驱动之Stream
    • SpringCloud链路追踪之Sleuth
    • SpringCloud Alibaba Nacos
    • SpringCloud Alibaba Sentinel
  • Spring

    • SpringBoot
    • Spring-data-jpa入门
    • SpringCloud问题
    • dispatcherServlet 源码分析
    • @SpringBootApplication注解内部实现与原理
    • spring启动初始化初始化
  • 中间件

    • 分布式协调服务器Zookeeper
    • 服务治理Dubbo
    • 分布式配置管理平台Apollo
    • 消息中间件框架Kafka
    • 分布式调度平台ElasticJob
    • 可视化分析工具Kibana
    • ElacticSearch 基础
    • ElacticSearch进阶
    • ElacticSearch集成
  • 环境部署

    • 应用容器引擎Docker
    • DockerCompose服务编排
    • 负载均衡Nginx
    • Nginx的安装配置
    • K8S基础
  • 代码片段

    • listener 监听模式
    • spingboot 整合redis
    • XSS过滤
    • profile的使用
    • ConfigurationProperties注解
  • 设计模式

    • 工厂模式
    • 单例模式
    • 装饰者模式
    • 适配器模式
    • 模板方法模式
    • 观察者模式
  • 读书笔记

    • 《Spring in Action 4》 读书笔记
    • 《高性能mysql》 读书笔记
  • NoSQL

    • Redis基础
    • Redis高级
    • Redis集群
    • Redis应用
  • MQ

    • rabbitMQ基础
    • rabbitMQ高级
    • rabbitMQ集群
  • JVM

    • JVM体系架构概述
    • 堆参数调整
    • GC 分代收集算法
    • JVM 垃圾回收器
    • JVM 相关问题
  • JUC

    • JUC总览
    • volatile关键字
    • CAS
    • ABA问题
    • collections包下线程安全的集合类
    • Lock 锁
    • LockSupport
    • AQS
    • Fork/Join分支框架
    • JUC tools
    • BlockingQueue 阻塞队列
    • Executor 线程池
    • CompletableFuture
    • 死锁以及问题定位分析
  • Shell

    • shell命令
    • shell基础
  • Activiti

    • IDEA下的Activiti HelloWord
    • 流程定义的CRUD
    • 流程实例的执行
    • 流程变量
  • VUE

    • vue基础
    • vue router
    • Vuex
    • Axios 跨域
    • dialog 弹出框使用
    • vue 动态刷新页面
    • vue 封装分页组件
    • vue 动态菜单
    • vue 常用传值
  • Solidity 智能合约

    • Solidity 基础
    • Solidity ERC-20
    • Solidity 101
  • English

    • 时态

Spring-data-jpa

  • JPA(Java Persistence API)是Sun官方提出的Java持久化规范。它为Java开发人员提供了一种对象/关联映射工具来管理Java应用中的关系数据。

JPA入门

Jpa提供的接口

  • Repository: 最顶层的空接口,目的为了统一所有Repository的类型,且能被组件扫描
  • CrudRepository : Repository子接口,提供CRUD功能
  • PagingAndSortingRepository: 是CrudRepository的子接口,添加分页和排序的功能
  • JpaRepository: 是PagingAndSortingRepository的子接口,增加了一些实用的功能,比如:批量操作等
  • JpaSpecificationExecutor: 用来做负责查询的接口
  • Specification: 是Spring Data JPA提供的一个查询规范,要做复杂的查询,只需围绕这个规范来设置查询条件即可

Jpa查询规则

  • 按照方法名解析规则如下: find+全局修饰+By+实体属性名称+限定词+连接词+(其他实体属性)+OrderBy+排序属性+排序方向
findDistinctByFirstNameIgnoreCaseAndLastNameOrderByAgeDesc(String firstName,String lastName)
  • 全局修饰符:distinct,top,first
  • 关键词: IsNull,IsNotNull,Like,NotLike,Containing,In,NotIn,IgnoreCase,Between,Equals,LessThan,GreaterThan,After,Before
  • 排序方向: Asc,Desc

所有支持的关键词

keyword | sample | JPQL |

  • | :-: | :-: | :-: | -: AND | findByLastnameAndFirstname | ...where x.lastname=?1 and x.firstname=?2 | Or | findByLastnameOrFirstname | ...where x.lastname=?1 or x.firstname=?2 | Between | findByStartDateBetween | ...where x.startDate between ?1 and ?2 | lessThan | findByAgeLessThan | ...where x.age < ?1 | GreaterThan | findByAgeGreaterThan | ...where x.age > ?1 | After | findByStartDateAfter | ...where x.startDate > ?1 | Before | findByStartDateBefore | ...where x.startDate < ?1 | IsNull | findByNameIsNull | ...where x.name is null | IsNotNull、NotNull | findByNameIsNotNull | ...where x.name is not null | Like | findByFirstnameLike | ...where x.firstname like ?1 | NotLike | findByFirstnameNotLike | ...where x.firstname not like ?1 | StartingWith | findByFirstnameStartingWith | ...where x.firstname like ?1(parameter bound with apended %) | EndingWith | findByFirstnameEndingWith | ...where x.firstname like ?1(parameter bound with prepended %) | Containing | findByFirstnameContaining | ...where x.firstname like ?1(parameter bound warpped in %) | OrderBy | findByAgeOrderByLastNameDesc | ...where x.age=?1 order by x.lastname desc | Not | findByNameNot | ...where x.name <> ?1 | In | findByAgeNIn | ...where x.age in ?1 | NotIn | findByAgeNotIn | ...where x.age not in ?1 | True | findByActiveTure | ...where x.active = true | False | findByActiveFalse | ...where x.active = false |

实体解析和关联关系

实体管理器

  • 实体管理器EntityManager是实体与数据库的桥梁(和事务一起发挥作用),相当于Hibenrtae中的session,Mybatis中的sqlSession。
  • 例子
@PersistenceContext
private EntityManager em;  // 注入实体管理器
@SuppressWarnings("unchecked")
public Page<Student> search(User user) {
    String dataSql = "select t from User t where 1 = 1";
    String countSql = "select count(t) from User t where 1 = 1";
    
    if(null != user && !StringUtils.isEmpty(user.getName())) {
        dataSql += " and t.name = ?1";
        countSql += " and t.name = ?1";
    }
    
    Query dataQuery = em.createQuery(dataSql);
    Query countQuery = em.createQuery(countSql);
    
    if(null != user && !StringUtils.isEmpty(user.getName())) {
        dataQuery.setParameter(1, user.getName());
        countQuery.setParameter(1, user.getName());
    }long totalSize = (long) countQuery.getSingleResult();
    Page<User> page = new Page();
    page.setTotalSize(totalSize);
    List<User> data = dataQuery.getResultList();
    page.setData(data);
    return page;
}
  • 实体总共有4个状态:
    • 新建状态(A):新建实体的时候,实体就是属于这个状态,执行persist方法后进入托管状态
    • 托管状态(B):托管状态,实体属于这个状态就说明实体已经被entityManager管理了
    • 删除状态(C):略
    • 游离状态(D):游离状态和A的区别就是D的ID是有值的

实体基础映射

  • @Entity 表示这个类是个实体类
  • @Table 指定这个实体对应数据库表名
  • @Column 用于指定持久化属性映射到数据库表的列
    • name (string) 列的名称,默认为属性的名称
    • unique (boolean) 列的值是否是唯一的
    • nullable (boolean) 列的值是否允许为 null。默认为 true
    • insertable (boolean) 列是否包含在 INSERT 语句中,默认为 true。
    • updatable (boolean) 列是否包含在 UPDATE 语句中,默认为 true。
    • table (string) 当前列所属的表的名称。
    • length (int) 列的长度,仅对字符串类型的列生效。默认为255。
    • precision (int) 列的精度,仅对十进制数值有效,表示有效数值的总位数。默认为0。
    • scale (int) 列的精度,仅对十进制数值有效,表示小数位的总位数。默认为0。
  • @Id 主键ID
  • @GenerateValue 主键策略
    • AUTO 和默认的一样
    • IDENTITY 自动增长
    • SEQUENCE 序列,oracle使用

实体高级映射

// TUDO

  • 一对一
  • 一对多
  • 多对多

jpa应用分析

  • 实体管理器高级操作——getReference()
    • 用于查询单记录实体,和find类似 T entity = entityManager.getReference(entityClass, id);
    • 它与find区别是:当根据主键查询记录不存在时,将抛出EntityNotFoundException,我们可以根据异常做一些处理
  • 实体管理器高级操作——提交方式FlushModeType
    • 提交(调用flush)分为两种方式:
      • AUTO:自动提交,实体管理器会在适当的时机同步实际记录到数据库,也是默认的提交方式
      • COMMIT:一旦一个事务完毕了,那么就立刻提交到数据库(忽略事务共享、事务传播)。 很多人建议使用默认的AUTO
  • 大量数据分批提交
    • 有的时候我们需要循环保存数据,当保存大量数据的时候,如果到最后才提交所有数据,那么数据库的负载可能会比较大。我们可以这样做,每30个记录就提交(flush)一次。代码如下:
public void updateBatch(List<Z> list) {
		for (int i = 0; i < list.size(); i++) {
			entityManager.merge(list.get(i)); // 变成托管状态
			if (i % 30 == 0) {
				entityManager.flush(); // 变成持久化状态
				entityManager.clear(); // 变成游离状态
			}
		}
	}
 
	public void saveBatch(List<Z> list) {
		for (int i = 0; i < list.size(); i++) {
			entityManager.persist(list.get(i)); // 变成托管状态
			if (i % 30 == 0) {
				entityManager.flush(); // 变成持久化状态
				entityManager.clear(); // 变成游离状态
			}
		}
	}
  • refresh()
    • 该方法是和flush()相反,是将数据库记录重新读到实体中,这样实体也是出于持久化环境中了,处于托管状态。
  • clear()
    • 该方法是将所有的处于上下文中的实体全部转换成游离状态,此时还没有及时flush到数据库的信息,很遗憾,将不会持久化到数据库中。不是急于释放资源的情况下,请慎用之。

JPQL

  • JPQL类似hibernate中hql,它跟数据库无关,可以自动翻译成对应数据库的sql
  • 要从 Java 代码内发出 JPQL 查询,您需要利用 EntityManager API 和 Query API 的相应方法,执行以下一般步骤:
    • 1.使用注入或通过 EntityManagerFactory 实例获取一个 EntityManager 实例。
    • 2.通过调用相应 EntityManager 的方法(如 createQuery),创建一个 Query 实例。
    • 3.如果有查询参数,使用相应 Query 的 setParameter 方法进行设置。
      1. 如果需要,使用 setMaxResults 和/或 setFirstResult Query 的方法设置要检索的实例的最大数量和/或指定检索的起始实例位置。
    • 5.如果需要,使用 setHint Query 的方法设置供应商特定的提示。
    • 6.如果需要,使用 setFlushMode Query 的方法设置查询执行的刷新模式,覆盖实体管理器的刷新模式。
    • 7.使用相应 Query 的方法 getSingleResult 或 getResultList 执行查询。如果进行更新或删除操作,您必须使用 executeUpdate 方法,它返回已更新或删除的实体实例的数量。
@PersistenceContext
private EntityManager em;  // 注入实体管理器
@SuppressWarnings("unchecked")
public Page<Student> search(User user) {
    String dataSql = "select t from User t where 1 = 1";
    String countSql = "select count(t) from User t where 1 = 1";
    
    if(null != user && !StringUtils.isEmpty(user.getName())) {
        dataSql += " and t.name = ?1";
        countSql += " and t.name = ?1";
    }
    
    Query dataQuery = em.createQuery(dataSql);
    Query countQuery = em.createQuery(countSql);
    
    if(null != user && !StringUtils.isEmpty(user.getName())) {
        dataQuery.setParameter(1, user.getName());
        countQuery.setParameter(1, user.getName());
    }
    long totalSize = (long) countQuery.getSingleResult();
    Page<User> page = new Page();
    page.setTotalSize(totalSize);
    List<User> data = dataQuery.getResultList();
    page.setData(data);
    return page;
}
  • JPQL具体写法略

高级查询

  • JPQL使用起来非常方便,但是如果SQL有一个词不小心写错了,只有在程序运行时才能发现错误在哪,这是一个弊端,如果想要在编译期间发现错误该怎么做呢,答案是使用Spring Data Jpa高级查询。
  • Criteria查询
CriteriaBuilder cb = entityManager.getCriteriaBuilder ();  // criteriaQuery工厂
CriteriaQuery cq = cb.createQuery(); //查询语句构造器
Root<User> root = cq.from(User.class);  //获取查询根对象
cq.select(root);   // select u from User u
Predicate pre = cb.greaterThan(root.get("age").as(Integer.class), 20); // age大于20
cq.where(pre); // select u from User u where u.age > 20
// 上面这一串的目的就是为了构造出    select u from User u where u.age > 20  的JPQL语句
Query query = em.createQuery(cq);
List<User> users = query.getResultList();
  • 只查询部分字段:把cq.select(root)改成cq.select(root.get(“字段名”))即可;有多个字段就 cq.multiselect(root.get("age"), root.get("name").......)即可;
  • Fetch查询:在cq.select 前加上:Fetch rootFetch = root.fetch("interests"); // 关联查询出用户的所有兴趣
    • 想要在程序中手动控制fetch,需要先再@ManyToMany等其他注解中将fetch设置成懒加载,然后在程序中控制。
  • 设置JOIN:在cq.select前加:root.join("interests", JoinType.LEFT); // select u from User u left join u.interests where u.age > 20
  • 结果集排序:在cq.orderBy后面: cq.orderBy(cb.desc(root.get("age")), cb.asc(root.get("name")));
  • 多个查询条件:
Predicate pre = cb.greaterThan(root.get("age").as(Integer.class), 20); // age大于20
Predicate pre2 = cb.lessThan(root.get("age").as(Integer.class), 40); // age小于40
cq.where(pre)改成cq.where(pre,pre2);
  • 使用JpaSpecificationExcutor查询
public interface UserRepository extends JpaRepository<User, Integer> , JapSpecificationExecutor<User>{
 
}
  • repository继承了JapSpecificationExecutor之后,在UserServiceImpl中调用userRepository :
public List<User> getUsers(){
    return userRepository.findAll(new Specification<User>(){
        @Override
        public Predicate toPredicate(Root<User>, CriteriaQuery<?> cq, CriteriaBuilder cb){
            List<Predicate> preList = new ArrayList();
            // 跟上面Criteria的条件构造是一样的
            // ..........
            // ..........
            // 条件构造完毕,都在 preList 里面
            return cq.where(preList.toArray(new Predicate[preList.size()])).getRestriction();
        }            
    }) ;       
}
  • @Query和@NamedQuery查询
    • 这个注解允许我们在接口的方法处使用自定义的查询语句(JPQL或者SQL),
    • 如果想使用原生sql的话,@Query注解里有一个属性可以配置:nativeQuery = true
@Query(value="select  u from user u where name like ?1", nativeQuery = true)

QueryDSL

  • 使用JPA对单表操作很灵活,复杂一点的SQL就处理不好,这时候用QueryDSL 来实现复杂SQL
  • QueryDSL仅仅是一个通用的查询框架,专注于通过 JavaAPI 构建类型安全的 Sql 查询,也可以说 QueryDSL 是基于各种 ORM 框架以及 Sql 之上的一个通用的查询框架,QueryDSL 的查询,类似于 SQL 查询

引入QueryDSL相关

  • 引入依赖
<!--query dsl-->
<dependency>
    <groupId>com.querydsl</groupId>
    <artifactId>querydsl-jpa</artifactId>
    <version>4.2.2</version>
</dependency>
<dependency>
    <groupId>com.querydsl</groupId>
    <artifactId>querydsl-apt</artifactId>
    <version>4.2.2</version>
    <scope>provided</scope>
</dependency>
<!--query dsl end-->
  • 引入插件,用于生成查询实例
<!--该插件可以生成querysdl需要的查询对象,执行mvn compile即可-->
<plugin>
  <groupId>com.mysema.maven</groupId>
  <artifactId>apt-maven-plugin</artifactId>
  <version>1.1.3</version>
  <executions>
    <execution>
      <goals>
        <goal>process</goal>
      </goals>
      <configuration>
        <outputDirectory>target/generated-sources/java</outputDirectory>
        <processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
      </configuration>
    </execution>
  </executions>
</plugin>

单表演示

@RunWith(SpringRunner.class)
@SpringBootTest
class JpaProjectApplicationTests {
    @Autowired
    private EntityManager entityManager;
    //查询工厂实体
    private JPAQueryFactory queryFactory;

    @PostConstruct
    public void initFactory() {
        queryFactory = new JPAQueryFactory(entityManager);
    }
    @Test
    void contextLoads() {
        User user = new User("ws","123");
        QUser qUser = QUser.user;
        // 定于获取条件
        BooleanBuilder booleanBuilder = new BooleanBuilder();
        // 要查询的条件
        if(!StringUtils.isEmpty(user.getUsername())){
            // 放入要查询的条件信息
            booleanBuilder.and(qUser.username.contains(user.getUsername()));
        }
        // 要查询的条件
        if(!StringUtils.isEmpty(user.getPassword())){
            booleanBuilder.and(qUser.password.contains(user.getPassword()));
        }
        List<User> fetch = queryFactory.select(qUser).from(qUser).where(booleanBuilder).orderBy(qUser.creatTime.desc()).fetch();
        for (User fetch1 : fetch) {
            System.out.println(fetch1.getId()+"+++++++++++++++++++++");
        }
    }
}
  • 单表只为了演示基本使用,单表查询JPA就可以解决,QueryDSL主要解决多表联查问题

多表关联

  • 三张表 user role user_role ,user_role是中间表
@RunWith(SpringRunner.class)
@SpringBootTest
class JpaProjectApplicationTests {
    @Autowired
    private EntityManager entityManager;
    //查询工厂实体
    private JPAQueryFactory queryFactory;

    private int roleId = 1;
    private int userId = 1;

    @PostConstruct
    public void initFactory() {
        queryFactory = new JPAQueryFactory(entityManager);
    }

    @Test
    void contextLoads() {
        QUser qUser = QUser.user;
        QRole qRole = QRole.role;
        QUserRole qUserRole = QUserRole.userRole;

        // 定于获取条件
        BooleanBuilder booleanBuilder = new BooleanBuilder();
        // 要查询的条件
        if (!StringUtils.isEmpty(roleId)) {
            // 放入要查询的条件信息
            booleanBuilder.and(qUserRole.roleId.eq(roleId));
        }
        // 要查询的条件
        if (!StringUtils.isEmpty(userId)) {
            // 放入要查询的条件信息
            booleanBuilder.and(qUserRole.userId.eq(userId));
        }
        
        QueryResults<UserRoleVo> queryResults = queryFactory.select(Projections.bean(UserRoleVo.class, qUser.username, qRole.roleName)).from(qUser, qUserRole, qRole).where(booleanBuilder).fetchResults();
        queryResults.getResults().stream().forEach(r ->{
            System.out.println(r.getUsername() + "++++++++++++"+r.getRoleName());
        });

    }
}
Last Updated:
Contributors: song1.wang@symbio.com
Prev
SpringBoot
Next
SpringCloud问题