SpringBoot 整合 Sping Data JPA,附自定义构造查询条件工具类。

SpringBoot 整合 Sping Data JPA,堪称快速搭建项目,快速开发的典范。


JPA: 是Java Persistence API的简称,中文名Java持久层API,是JDK 5.0注解或XML描述对象-关系表的映射关系,并将运行期的实体对象持久化到数据库中。
Hibernate3.2+、TopLink 10.1.3以及OpenJPA都提供了JPA的实现。JPA的总体思想和现有Hibernate、TopLink、JDO等ORM框架大体一致。
JPA是需要Provider来实现其功能的,Hibernate就是JPA Provider中很强的一个,应该说无人能出其右。
开始写SpringBoot整合JPA之前先了解Spring Data JPA,可以看看这两篇博客:
Spring-Data-JPA 学习笔记(一)
Spring-Data-JPA 学习笔记(二)
本篇博客是对上面两篇博客的总结,Demo更加完整适用。这里只列出了关键类代码,有些工具类,查询类代码就不列了,在下面的源码地址中可找到:

项目GitHub地址


pom.xml


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0modelVersion>

    <groupId>com.springbootgroupId>
    <artifactId>jpaartifactId>
    <version>0.0.1-SNAPSHOTversion>
    <packaging>jarpackaging>

    <name>jpaname>
    <description>Demo project for Spring Boot Jpadescription>

    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.0.2.RELEASEversion>
        <relativePath/> 
    parent>

    <properties>
        <project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
        <java.version>1.8java.version>
    properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-data-jpaartifactId>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-webartifactId>
        dependency>

        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <scope>runtimescope>
        dependency>
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <optional>trueoptional>
        dependency>
        
        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>druid-spring-boot-starterartifactId>
            <version>1.1.9version>
        dependency>
        
        <dependency>
            <groupId>io.springfoxgroupId>
            <artifactId>springfox-swagger2artifactId>
            <version>2.8.0version>
        dependency>
        <dependency>
            <groupId>io.springfoxgroupId>
            <artifactId>springfox-swagger-uiartifactId>
            <version>2.8.0version>
        dependency>
        <dependency>
            <groupId>org.apache.commonsgroupId>
            <artifactId>commons-lang3artifactId>
            <version>3.7version>
        dependency>
        
        <dependency>
            <groupId>org.apache.poigroupId>
            <artifactId>poi-ooxmlartifactId>
            <version>3.17version>
        dependency>
        <dependency>
            <groupId>org.springframework.bootgroupId>
            <artifactId>spring-boot-starter-testartifactId>
            <scope>testscope>
        dependency>
    dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-maven-pluginartifactId>
            plugin>
        plugins>
    build>
project>


application.yml

server:
  port: 8080
# 数据库访问配置,主数据源,默认的
spring:
  jackson:
    serialization:
      fail-on-empty-beans: false
  datasource:
    type: com.alibaba.druid.pool.DruidDataSource
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://xxx.xxx.xxx.xxx:3306/springboot?useUnicode=true&;characterEncoding=UTF-8
    username: mistra
    password: 123456
    # 初始化大小,最小,最大
    druid:
      initial-size: 1
      min-idle: 1
      max-active: 20
      # 配置获取连接等待超时的时间
      max-wait: 60000
      # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      time-between-eviction-runs-millis: 60000
      # 配置一个连接在池中最小生存的时间,单位是毫秒
      min-evictable-idle-time-millis: 300000
      validation-query: SELECT 1 FROM DUAL
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      # 打开PSCache,并且指定每个连接上PSCache的大小
      pool-prepared-statements: true
      max-pool-prepared-statement-per-connection-size: 20
      # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
      filters: stat,wall
      # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
      connect-properties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
      # 合并多个DruidDataSource的监控数据
      use-global-data-source-stat: true
  jpa:
    hibernate:
          # 自动创建、更新、验证数据库表结构
          ddl-auto: update  # 第一次简表create  后面用update
    # 显示sql
    show-sql: true

jpa.hibernate.ddl-auto: update 自动维护表结构,当实体类属性改变时,启动项目时会自动更新数据库表结构。


User.java

package com.springboot.jpa.entity;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.springboot.jpa.util.hibernate.BaseEntity;
import io.swagger.annotations.ApiParam;
import lombok.Data;

import javax.persistence.Entity;
import javax.persistence.Table;

/**
 * Author: RoronoaZoro丶WangRUi
 * Time: 2018/6/14/014
 * Describe:
 */
@Data
@Entity
@Table(name = "wm_user")
@JsonIgnoreProperties(value = {"hibernateLazyInitializer","handler","fieldHandler"},ignoreUnknown = true)
public class User extends BaseEntity {

    @ApiParam("用户名")
    private String userName;

    @ApiParam("昵称")
    private String nickName;

    @ApiParam("岗位")
    private String position;

    @ApiParam("年龄")
    private Integer age;
}

@Data : Lombok注解
@Entity :指名这是一个实体Bean
@Table(name = “wm_user”) :指定了Entity所要映射带数据库表,其中@Table.name()用来指定映射表的表名。如果缺省@Table注释,系统默认采用类名作为映射表的表名
为什么加@JsonIgnoreProperties注解可以看这里:No serializer found for class org.hibernate.proxy.pojo.javassist.JavassistLazyInitializer,运行项目的时候就是报这个错。

实体类公共父类 BaseEntity.java

package com.springboot.jpa.util.hibernate;

import io.swagger.annotations.ApiParam;
import lombok.Data;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;

/**
 * Author: WangRui
 * Date: 2018/5/20
 * Describe: 实体类基础属性
 */
@Data
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseEntity  implements Serializable  {
    private static final long serialVersionUID = -7674269980281525370L;
    @Id
    @GeneratedValue
    @ApiParam("主键ID")
    protected Long id;

    @CreatedDate
    @ApiParam("创建时间")
    protected Date createTime;

    @LastModifiedDate
    @ApiParam("更新时间")
    protected Date modifyTime;
    
}

@Id :指定主键
@GeneratedValue:指定主键生成规则,默认自增
@MappedSuperclass:加了此注解的类将不是一个完整的实体类,他将不会映射到数据库表,但是他的属性都将映射到其子类的数据库字段中。不能再标注@Entity或@Table注解,也无需实现序列化接口。这样的类还可以直接标注@EntityListeners实体监听器。
@EntityListeners:声明实体监听器,用于实体修改时做处理(切面编程)

@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
public @interface GeneratedValue {
    GenerationType strategy() default GenerationType.AUTO;

    String generator() default "";
}
package javax.persistence;

public enum GenerationType {
    TABLE,
    SEQUENCE,
    IDENTITY,
    AUTO;

    private GenerationType() {
    }
}

详细了解戳这里:
JPA注解之“@GeneratedValue”详解
JPA @Id 和 @GeneratedValue 注解详解


Dao 接口 UserRepository.java

package com.springboot.jpa.repoistory;

import com.springboot.jpa.entity.User;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
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;

/**
 * Author: RoronoaZoro丶WangRUi
 * Time: 2018/6/14/014
 * Describe:
 */
public interface UserRepository extends JpaRepository<User,Long>,JpaSpecificationExecutor<User> {

	//符合JPA命名规则的方法定义
    User findFirstByUserName(String name);

    @Query(value = "from User where position =?1")
    List<User> selectByCustomSqlTest1(String position);

    @Query(value = "select * from wm_user where position = ?1",nativeQuery = true)
    List<User> selectByCustomSqlTest2(String position);

    Page<User> findByNickName(String nickName, Pageable pageable);
}

JpaRepository< User,Long>:User为对应实体类,Long为实体类主键类型。
JpaRepository继承PagingAndSortingRepository,QueryByExampleExecutor,CrudRepository定义了一系列的增删查改,分页,条件查询方法。
findFirstByUserName()符合JPA命名规则的方法,见名知意,根据用户姓名查询第一条数据。这种方法定义时会根据实体类的属性进行提示:
SpringBoot 整合 Sping Data JPA,附自定义构造查询条件工具类。_第1张图片
戳这里了解JPA方法命名规范:Spring Data JPA方法命名规则
selectByCustomSqlTest1和selectByCustomSqlTest2是自定义sql的方法,
注意:

  • selectByCustomSqlTest1的" from User …",表名用的是实体类名。
  • selectByCustomSqlTest2是" from wm_user ",表名用的是数据库表名,并且有配置nativeQuery = true

findByNickName()方法是用来测试JPA自带的分页方法的。


UserService.java

package com.springboot.jpa.service;

import com.springboot.jpa.entity.User;
import com.springboot.jpa.util.hibernate.PageCondition;
import com.springboot.jpa.util.hibernate.Pager;
import com.springboot.jpa.vo.UserQueryVo;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.util.List;


/**
 * Author: RoronoaZoro丶WangRUi
 * Time: 2018/6/14/014
 * Describe:
 */
public interface UserService {

    void save(User user);

    User getOne(Long id);

    void delete(Long id);

    Page<User> getPage(Pageable pageable,UserQueryVo userQueryVo);

    Pager<User> getPager(PageCondition condition, UserQueryVo userQueryVo);

    List<User> findByCustomSqlTest1(UserQueryVo userQueryVo);

    List<User> findByCustomSqlTest2(UserQueryVo userQueryVo);

    User jpaName(UserQueryVo userQueryVo);

    Page<User> jpaPageSelect(PageCondition condition,UserQueryVo userQueryVo);

    Page<User> jpaSpecificationTest1(PageCondition condition,UserQueryVo userQueryVo);

    Page<User> jpaSpecificationTest2(PageCondition condition,UserQueryVo userQueryVo);

}

UserServiceImpl.java

package com.springboot.jpa.service.impl;

import com.springboot.jpa.entity.User;
import com.springboot.jpa.repoistory.UserRepository;
import com.springboot.jpa.service.UserService;
import com.springboot.jpa.util.hibernate.PageCondition;
import com.springboot.jpa.util.hibernate.Pager;
import com.springboot.jpa.util.hibernate.QueryHelper;
import com.springboot.jpa.util.hibernate.dao.CommonDAO;
import com.springboot.jpa.vo.UserQueryVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
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.stereotype.Service;

import javax.persistence.criteria.*;
import java.util.List;

/**
 * Author: RoronoaZoro丶WangRUi
 * Time: 2018/6/14/014
 * Describe:
 */
@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserRepository userRepository;

    @Autowired
    private CommonDAO commonDAO;

    @Override
    public void save(User user) {
        userRepository.save(user);
    }

    @Override
    public User getOne(Long id) {
        return userRepository.getOne(id);
    }

    @Override
    public void delete(Long id) {
        userRepository.deleteById(id);
    }

    @Override
    public Page<User> getPage(Pageable pageable, UserQueryVo userQueryVo) {
        return userRepository.findAll(pageable);
    }

    @Override
    public Pager<User> getPager(PageCondition condition, UserQueryVo userQueryVo) {
        /*QueryHelper helper = new QueryHelper(User.class)
                .addCondition(StringUtils.isNoneBlank(userQueryVo.getUserName()), "userName like ", "%" + userQueryVo.getUserName() + "%")
                .addCondition(StringUtils.isNoneBlank(userQueryVo.getNickName()), "nickName like ", "%" + userQueryVo.getNickName() + "%")
                .addCondition(StringUtils.isNoneBlank(userQueryVo.getPosition()), "position = ?", userQueryVo.getPosition())
                .setPageCondition(condition)
                .useNativeSql(false);*/
        QueryHelper helper = new QueryHelper("id,user_name as userName,nick_name as nickName,position,age"," wm_user")
                .setPageCondition(condition)
                .useNativeSql(true);
        Pager<User> pager = commonDAO.findPager(helper,User.class);
        return pager;
    }

    @Override
    public List<User> findByCustomSqlTest1(UserQueryVo userQueryVo) {
        return userRepository.selectByCustomSqlTest1(userQueryVo.getPosition());
    }

    @Override
    public List<User> findByCustomSqlTest2(UserQueryVo userQueryVo) {
        return userRepository.selectByCustomSqlTest2(userQueryVo.getPosition());
    }

    @Override
    public User jpaName(UserQueryVo userQueryVo) {
        return userRepository.findFirstByUserName(userQueryVo.getUserName());
    }

    /**
     * Jpa自带的分页,排序和条件查询测试
     *
     * @param condition
     * @param userQueryVo
     * @return
     */
    @Override
    public Page<User> jpaPageSelect(PageCondition condition, UserQueryVo userQueryVo) {
        PageRequest pageRequest = PageRequest.of(condition.getPageNum(), condition.getPageSize(), Sort.Direction.ASC, condition.getOrder());
        Page<User> users = userRepository.findByNickName(userQueryVo.getNickName(), pageRequest);
        return users;
    }

    /**
     * JpaSpecificationExecutor条件查询接口测试1
     *
     * @param condition
     * @param userQueryVo
     * @return
     */
    @Override
    public Page<User> jpaSpecificationTest1(PageCondition condition, UserQueryVo userQueryVo) {
        PageRequest pageRequest = PageRequest.of(condition.getPageNum(), condition.getPageSize(), Sort.Direction.DESC, condition.getOrder());
        Page<User> page = userRepository.findAll(new Specification<User>() {
            @Override
            public javax.persistence.criteria.Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                Path<String> userName = root.get("userName");
                Path<String> position = root.get("position");
                //这里可以设置任意条查询条件
                query.where(cb.like(userName, "%" + userQueryVo.getUserName() + "%"), cb.like(position, "%" + userQueryVo.getPosition() + "%"));
                //这种方式使用JPA的API设置了查询条件,所以不需要再返回查询条件Predicate给Spring Data Jpa,故最后return null;即可。
                return null;
            }
        }, pageRequest);
        return page;
    }

    /**
     * JpaSpecificationExecutor条件查询接口测试2
     *
     * @param condition
     * @param userQueryVo
     * @return
     */
    @Override
    public Page<User> jpaSpecificationTest2(PageCondition condition, UserQueryVo userQueryVo) {
        PageRequest pageRequest = PageRequest.of(condition.getPageNum(), condition.getPageSize(), Sort.Direction.DESC, condition.getOrder());
        Page<User> page = userRepository.findAll(new Specification<User>() {
            @Override
            public javax.persistence.criteria.Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                Predicate predicate1 = cb.like(root.get("userName"),"%" + userQueryVo.getUserName() + "%");
                Predicate predicate2 = cb.like(root.get("position"),"%" + userQueryVo.getPosition() + "%");
                query.where(cb.or(predicate1,predicate2));
                //等于:query.where(cb.or(cb.like(root.get("userName"),"%" + userQueryVo.getUserName() + "%"),cb.like(root.get("position"),"%" + userQueryVo.getPosition() + "%")));
                return null;
            }
        }, pageRequest);
        return page;
    }
}

CommonDAO 、QueryHelper 、Pager、PageCondition都是自定义的查询辅助类,可查询GitHub源码了解。
主要介绍下JPA自带的分页,条件查询方法。
Jpa自带的分页,排序和条件查询测试:jpaPageSelect();

PageRequest pageRequest = PageRequest.of(condition.getPageNum(), condition.getPageSize(), Sort.Direction.ASC, condition.getOrder());
        Page<User> users = userRepository.findByNickName(userQueryVo.getNickName(), pageRequest);

findByNickName()就是userRepository中定义那个符合JPA命名规范的条件查询、分页方法。
JPA的条件构造查询方法,感觉有点难用,没有上面自定义的那个查询辅助类好用,介绍一下,要使用JPA自带的条件构造查询,UserRepository必须要继承JpaSpecificationExecutor。
方法:jpaSpecificationTest1();

PageRequest pageRequest = PageRequest.of(condition.getPageNum(), condition.getPageSize(), Sort.Direction.DESC, condition.getOrder());
        Page<User> page = userRepository.findAll(new Specification<User>() {
            @Override
            public javax.persistence.criteria.Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                Path<String> userName = root.get("userName");
                Path<String> position = root.get("position");
                //这里可以设置任意条查询条件
                query.where(cb.like(userName, "%" + userQueryVo.getUserName() + "%"), cb.like(position, "%" + userQueryVo.getPosition() + "%"));
                //这种方式使用JPA的API设置了查询条件,所以不需要再返回查询条件Predicate给Spring Data Jpa,故最后return null;即可。
                return null;
            }
        }, pageRequest);
        return page;

Specification:封装 JPA Criteria 查询条件。通常使用匿名内部类的方式来创建该接口的对象。使用Specification的要点就是CriteriaBuilder,通过这个对象来创建条件,之后返回一个Predicate对象。想必这个JPA自带的构造条件查询,我更喜欢用那个自定义的QueryHelper。


UserController.java

package com.springboot.jpa.controller;

import com.springboot.jpa.entity.User;
import com.springboot.jpa.service.UserService;
import com.springboot.jpa.util.hibernate.PageCondition;
import com.springboot.jpa.util.hibernate.Pager;
import com.springboot.jpa.util.web.annotation.*;
import com.springboot.jpa.vo.UserQueryVo;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

/**
 * Author: RoronoaZoro丶WangRUi
 * Time: 2018/6/14/014
 * Describe:
 */
@RestController
@RequestMapping("/user")
@Api("UserController")
public class UserController {

    @Autowired
    private UserService userService;

    @ApiOperation("新增")
    @AddUrl
    public void save(User user){
        userService.save(user);
    }

    @ApiOperation("查询单条")
    @GetOneUrl
    public User getOne(Long id){
        return userService.getOne(id);
    }

    @ApiOperation("删除")
    @DeleteUrl
    public void delete(Long id){
        userService.delete(id);
    }

    @ApiOperation("分页查询1-自定义的查询辅助类")
    @SelectPageUrl
    public Pager<User> getPager(PageCondition condition, UserQueryVo userQueryVo){
        return userService.getPager(condition,userQueryVo);
    }

    @ApiOperation("分页查询2-Jpa自带的查询辅助类")
    @PageUrl
    public Page<User> getPage(Pageable pageable,UserQueryVo userQueryVo){
        return userService.getPage(pageable,userQueryVo);
    }

    @ApiOperation("自定义sql测试1")
    @GetMapping("/customSql1")
    public List<User> customSql1(UserQueryVo userQueryVo){
        return userService.findByCustomSqlTest1(userQueryVo);
    }

    @ApiOperation("自定义sql测试2")
    @GetMapping("/customSql2")
    public List<User> customSql2(UserQueryVo userQueryVo){
        return userService.findByCustomSqlTest2(userQueryVo);
    }

    @ApiOperation("Jpa命名规范接口测试")
    @GetMapping("/jpaName")
    public User jpaName(UserQueryVo userQueryVo){
        return userService.jpaName(userQueryVo);
    }

    @ApiOperation("Jpa自带的分页,排序和条件查询测试")
    @GetMapping("/jpaPageSelect")
    public Page<User> jpaPageSelect(PageCondition condition,UserQueryVo userQueryVo){
        return userService.jpaPageSelect(condition,userQueryVo);
    }

    @ApiOperation("JpaSpecificationExecutor条件查询接口测试1")
    @GetMapping("/jpaSpecification1")
    public Page<User> jpaSpecificationTest1(PageCondition condition,UserQueryVo userQueryVo){
        return userService.jpaSpecificationTest1(condition,userQueryVo);
    }

    @ApiOperation("JpaSpecificationExecutor条件查询接口测试2")
    @GetMapping("/jpaSpecification2")
    public Page<User> jpaSpecificationTest2(PageCondition condition,UserQueryVo userQueryVo){
        return userService.jpaSpecificationTest2(condition,userQueryVo);
    }
}



swagger接口文档:
SpringBoot 整合 Sping Data JPA,附自定义构造查询条件工具类。_第2张图片


我的:

  • >>>>>>>CSDN<<<<<<<
  • >>>>>>>GitHub<<<<<<<
  • >>>>>>>个人博客<<<<<<<

SpringBoot 整合 Sping Data JPA,附自定义构造查询条件工具类。_第3张图片

你可能感兴趣的:(SpringBoot,SpringDataJPA)