springboot整合JPA

什么是JPA?
JPA(Java Persistence API),中文名Java持久层API,是Java持久化规范,它为Java开发人员提供了一种对象/关联映射工具来管理Java应用中的关系数据。

JPA的出现主要是为了简化现有的持久化开发工作和整合ORM技术,结束现在Hibernate,TopLink,JDO等ORM框架各自为营的局面。

JPA是在充分吸收了现有Hibernate,TopLink,JDO 等ORM框架的基础上发展而来的,具有易于使用,伸缩性强等优点。总的来说,JPA包括以下3方面的技术:

1、ORM映射元数据

JPA支持XML和JDK5.0注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中;

2、Java持久化API

用来操作实体对象,执行CRUD操作,框架在后台替代我们完成所有的事情,开发者从繁琐的JDBC和SQL代码中解脱出来。

3、查询语言(JPQL)

这是持久化操作中很重要的一个方面,通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合。

那么闲话少说,下面正式开始springboot和jpa的整合
maven依赖

<dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
            <version>2.2.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

application.yml配置文件

spring:
  #数据库配置
  datasource:
    url: jdbc:mysql://localhost:3306/test?useSSL=false&Unicode=true&characterEncoding=UTF-8&serverTimezone=UTC
    username: root
    password: 123456
    driver-class-name: com.mysql.jdbc.Driver
    tomcat:
      max-active: 20
      max-idle: 8
      min-idle: 8
  #JPA(java持久层API)配置
  jpa:
    #指定数据库管理系统
    database: MYSQL
    #显示或者不记录每个sql查询
    show-sql: true
      #create:
      #每次应用启动的时候会重新根据实体建立表,之前的表和数据都会被删除。
      #create-drop:
      #和上面的功能一样,但是多了一样,就是在应用关闭的时候,也就是sessionFactory一关闭,会把表删除。
      #update:
      #最常用的,第一次启动根据实体建立表结构,之后启动会根据实体的改变更新表结构,之前的数据都在。
      #validate:
      #会验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值,运行程序会校验实体字段与数据库已有的表的字段类型是否相同,不同会报错
    hibernate:
      ddl-auto: update
      #命名策略
      naming:
        strategy: org.hibernate.cfg.ImprovedNamingStrategy
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
  #定义数据处理的时间格式,比如在实体类中
  jackson:
    time-zone: UTC
    date-format: yyyy-MM-dd HH:mm:ss
    #指定项目能接收的时间格式
  mvc:
    format:
      date: yyyy-MM-dd HH:mm:ss

创建实体类

import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.experimental.Accessors;

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

@ApiModel("User的实体类")
@Data//创建get,set方法
@Accessors(chain = true)//开启支持链式操作
@Entity
public class User implements Serializable {
    @ApiModelProperty(value = "主键id")
    @Id
    //主键生成策略,自增,IDENTITY表示数据库自动生成
    @GeneratedValue(strategy= GenerationType.IDENTITY)
    private Long id;//Long 对应数据库中的bigint
    @ApiModelProperty("用户名")
    @Column(nullable = false,unique = true,length = 50)//字段非空,唯一,长度50
    private String name;
    @ApiModelProperty("用户地址")
    @Column
    private String addr;
    @ApiModelProperty("用户生日")
    @Column(nullable = false)
//    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date birthDate;//Date 对应数据库的datetime
}

注解说明

  1. @Entity :表明是一个实体类
  2. @Table :对应的数据表名
  3. @Id :主键
  4. @GeneratedValue:主键生成策略
  5. @Column : 映射表对应的字段名
  6. @Basic : 表示该属性是表字段的映射。 如果实体的字段上没有任何注解默认就是@Basic
  7. @Transient : 表示该属性不是表字段的映射
  8. @Lob : 将属性映射成支持的大对象类型 ,如Clob、Blob
  9. @IdClass 联合主键,一般不用也不推荐用
  10. @Temporal : 用来设置Date类型的属性映射到对应精度的字
  11. @Enumerated 直接映射枚举类型的字段

创建dao层

import org.springframework.data.jpa.repository.JpaRepository;
import com.lcj.po.User;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;

import java.util.List;

/**
 * 该接口主要提供了多条件查询的支持,并且可以在查询中添加分页与排序。
 * 注意: JpaSpecificationExecutor 是单独存在。完全独立
 * <p>
 * JpaRepository中的泛型1:实体类 泛型2:主键映射类型
 */
@Repository
public interface UserRepository extends JpaSpecificationExecutor<User>, JpaRepository<User, Long> {
    /**
     * nativeQuery :表示 该sql 是否是标准的 sql语句。
     * 默认值为false 。执行的时候会告诉底层这是一个非标准的sql语句,底层会去执行hibernate解析hql语句为标准的sql语句
     * true 标准该sql 为标准的sql语句
     * SELECT  * FROM  t_users where name=?1 and  id=?2
     * 这个 “?1”的1 表示是获取入参的第一个参数  “?2” 的2 表示获取入参的第二个参数
     *
     * @param id
     * @return
     */
    @Query(value = "select name from user where id=?1", nativeQuery = true)
    String queryUserName(Long id);

    @Query(value = "update User set name =?1 where id=?2")
    @Modifying
    //需要执行一个更新操作
    void updateUsersNameById(String name, Integer id);

    @Query(value = "SELECT  * FROM  user where name=?1 ", nativeQuery = true)
    List<User> queryByNameUseSQL(String name);
}

上面的已经可以做简单的查询了,但是我们查询不可能那么简单,多条件查询,where,in,=,is null,order by等条件,但是简单的查询无法满足我们的要求,但是我们UserRepository 继承了JpaSpecificationExecutor类,可以满足我们的要求,简单介绍他的使用

 /**
     * 根据名称查询对应的用户
     *
     * @param name
     */
    public void findUsersByName(String name) {
        Specification<User> specification = new Specification<User>() {

            /**
             * 封装了单个的查询条件
             * @param root 查询对象的属性的封装
             * @param criteriaQuery 封装了需要执行查询中的各个部分的信息: select   from  order  by 等信息
             * @param criteriaBuilder 查询条件的构造器  定义不同的查询条件的
             * @return
             */
            @Override
            public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                //封装条件,相当于where name ='张三'
                Predicate pre = criteriaBuilder.equal(root.get("name"), name);
                return pre;
            }
        };

        List<User> users = userRepository.findAll(specification);

    }

    /**
     * 多条件查询,使用and关联查询条件
     *
     * @param name
     * @param addr
     */
    public void findUsersByConditions(String name, String addr) {
        Specification<User> spec = new Specification<User>() {
            /***
             *  Predicate :封装了单个的查询条件
             * @param root  查询对象的属性的封装
             * @param criteriaQuery 封装了需要执行查询中的各个部分的信息: select   from  order  by 等信息
             * @param criteriaBuilder  CriteriaBuilder:查询条件的构造器  定义不同的查询条件的
             * @return
             */
            @Override
            public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                // where name ='张三' and  addr='上海'
                List<Predicate> list = new ArrayList<>();
                list.add(criteriaBuilder.equal(root.get("name"), name));
                list.add(criteriaBuilder.equal(root.get("addr"), addr));
                Predicate[] arr = new Predicate[list.size()];
                return criteriaBuilder.and(list.toArray(arr));
            }
        };
        List<User> list = userRepository.findAll(spec);
    }


    /**
     * 多个条件查询,使用or关联查询条件
     *
     * @param name
     * @param addr
     * @param id
     */
    public void findUsersByConditions(String name, String addr, Long id) {
        /*
         *  Specification<Users> 封装了一个查询的对象
         */
        Specification<User> spec = new Specification<User>() {
            /***
             *  Predicate :封装了单个的查询条件
             * @param root  查询对象的属性的封装
             * @param query 封装了需要执行查询中的各个部分的信息: select   from  order  by 等信息
             * @param cb  CriteriaBuilder:查询条件的构造器  定义不同的查询条件的
             */
            @Override
            public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                // where (name ='张山' and  addr=上海) or id=2
                return cb.or(cb.and(cb.equal(root.get("name"), name), cb.equal(root.get("addr"), addr)), cb.equal(root.get("id"), id));
            }
        };
        List<User> list = userRepository.findAll(spec);

    }

上面的代码是通过创建一个Specification对象,在内部类中重写方法,并返回Specification对象,Specification对象就相当于是对查询条件的封装,然后再用JpaRepository进行查询
下面为了方便我们使用,对Specification进行了封装成工厂类


import org.springframework.data.jpa.domain.Specification;

import javax.persistence.criteria.Order;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Date;

public class SpecificationFactory {
    // 时区对象
    private static final ZoneOffset ZONE_OFFSET = ZoneOffset.of("+8");
    // 日期时间格式化对象
    private static final DateTimeFormatter DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");

    /**
     * 模糊匹配头部, like %?1
     *
     * @param fieldName 实体中的字段名称
     * @param value     固定值
     * @return 查询条件的封装对象
     */
    public static Specification likeStart(String fieldName, String value) {
        return (root, query, cb) -> cb.like(root.get(fieldName), "%" + value);
    }

    /**
     * 模糊匹配尾部, like ?1%
     *
     * @param fieldName 实体中的字段名称
     * @param value     固定值
     * @return 查询条件的封装对象
     */
    public static Specification likeEnd(String fieldName, String value) {
        return (root, query, cb) -> cb.like(root.get(fieldName), value + "%");
    }

    /**
     * 完全模糊匹配 , like %?1%
     *
     * @param fieldName 实体中的字段名称
     * @param value     固定值
     * @return 查询条件的封装对象
     */
    private static Specification like(String fieldName, String value) {
        return likeBuild(fieldName, "%" + value + "%");
    }

    private static Specification likeBuild(String fieldName, String value) {
        return (root, query, cb) -> cb.like(root.get(fieldName), "%" + value + "%");
    }

    /**
     * 任意值相等比较
     *
     * @param fieldName 实体中的字段名称
     * @param value     比较值
     * @return 查询条件的封装对象
     */
    public static <T> Specification eq(String fieldName, T value) {
        return (root, query, cb) -> cb.equal(root.get(fieldName), value);
    }

    /**
     * 比较日期区间
     *
     * @param fieldName 实体中的字段名称
     * @param min       最小日期值
     * @param max       最大日期值
     * @return 查询条件的封装对象
     */
    public static Specification betweenDate(String fieldName, Date min, Date max) {
        LocalDateTime lmin = LocalDateTime.ofInstant(min.toInstant(), ZONE_OFFSET);
        LocalDateTime lmax = LocalDateTime.ofInstant(max.toInstant(), ZONE_OFFSET);
        return (root, query, cb) -> cb.between(root.get(fieldName).as(String.class), DATE_TIME_FORMATTER.format(lmin), DATE_TIME_FORMATTER.format(lmax));
    }

    /**
     * 比较任意值的区间
     *
     * @param fieldName 实体中的字段名称
     * @param min       最小值
     * @param max       最大值
     * @param <T>
     * @return 查询条件的封装对象
     */
    public static <T extends Comparable> Specification between(String fieldName, T min, T max) {
        return (root, query, cb) -> cb.between(root.get(fieldName), min, max);
    }


    /**
     * 数值大于比较
     *
     * @param fieldName 实体中的字段名称
     * @param value     比较值
     * @param <T>
     * @return 查询条件的封装对象
     */
    public static <T extends Number> Specification gt(String fieldName, T value) {
        return (root, query, cb) -> cb.gt(root.get(fieldName).as(Number.class), value);
    }


    /**
     * 数值大于等于比较
     *
     * @param fieldName 实体中的字段名称
     * @param value     比较值
     * @param <T>
     * @return 查询条件的封装对象
     */
    public static <T extends Comparable> Specification gte(String fieldName, T value) {
        return (root, query, cb) -> cb.greaterThanOrEqualTo(root.get(fieldName), value);
    }

    /**
     * 数值小于比较
     *
     * @param fieldName 实体中的字段名称
     * @param value     比较值
     * @param <T>
     * @return 查询条件的封装对象
     */
    public static <T extends Number> Specification lt(String fieldName, T value) {
        return (root, query, cb) -> cb.lt(root.get(fieldName).as(Number.class), value);
    }

    /**
     * 数值小于等于比较
     *
     * @param fieldName 实体中的字段名称
     * @param value     比较值
     * @param <T>
     * @return 查询条件的封装对象
     */
    public static <T extends Comparable> Specification lte(String fieldName, T value) {
        return (root, query, cb) -> cb.lessThanOrEqualTo(root.get(fieldName), value);
    }

    /**
     * 字段为null条件
     * @param fieldName 实体中的字段名称
     * @return 查询条件的封装对象
     */
    public static Specification isNull(String fieldName){
        return (root, query, cb) -> cb.isNull(root.get(fieldName));
    }

    /**
     * 字段不为null条件
     * @param fieldName 实体中的字段名称
     * @return 查询条件的封装对象
     */
    public static Specification isNotNull(String fieldName){
        return (root, query, cb) -> cb.isNotNull(root.get(fieldName));
    }

    /**
     * in 条件
     * @param fieldName
     * @param values
     * @return
     */
    public static Specification in(String fieldName,Object...values){
        return (root, query, cb) -> root.get(fieldName).in(values);
    }

    /**
     * 升序
     * @param fieldName
     * @return
     */
    public static Specification asc(String fieldName) {
        return (root,query,cb)-> query.orderBy(cb.asc(root.get(fieldName))).getRestriction();

    }

    /**
     * 降序
     * @param fieldName
     * @return
     */
    public static Specification desc(String fieldName) {
        return (root,query,cb)-> query.orderBy(cb.desc(root.get(fieldName))).getRestriction();

    }

    /**
     * 排序
     * @param fieldName
     * @return
     */
    public static Specification orderBy(String...fieldName){
        return (root, query, cb) -> {
            Order desc = cb.desc(root.get(fieldName[0]));
            Order asc = cb.asc(root.get(fieldName[1]));
            return query.orderBy(desc, asc).getRestriction();
        };
    }
}

service层

package com.lcj.service.iml;

import com.lcj.dao.UserRepository;
import com.lcj.factory.SpecificationFactory;
import com.lcj.po.User;
import com.lcj.service.UserService;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Optional;

@Service
public class UserServiceImpl implements UserService {

    @Resource
    private UserRepository userRepository;

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

    @Override
    public User queryUserById(Long id) {
        User user = userRepository.findById(id).get();
        return user;
    }

    @Override
    public List<User> queryAllUser() {
        List<User> users = userRepository.findAll();
        return users;
    }

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

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

    @Override
    public String queryUserName(Long id) {
        String userName = userRepository.queryUserName(id);
        return userName;
    }

    /**
     * 根据名称分页查询对应的用户,
     * @param page 当前页码从0开始
     * @param size 当前页的数量
     * @param name 查询条件,name
     * @return
     */
    public Page<User> findUsersByName(Integer page, Integer size,String name) {
   //创建PageRequest的实例,设置分页参数
        PageRequest pageRequest = PageRequest.of(page, size, Sort.Direction.ASC, "id");

        Specification<User> specification = new Specification<User>() {

            /**
             * 封装了单个的查询条件
             * @param root 查询对象的属性的封装
             * @param criteriaQuery 封装了需要执行查询中的各个部分的信息: select   from  order  by 等信息
             * @param criteriaBuilder 查询条件的构造器  定义不同的查询条件的
             * @return
             */
            @Override
            public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                //封装条件,相当于where name ='张三'
                Predicate pre = criteriaBuilder.equal(root.get("name"), name);
                return pre;
            }
        };

        return userRepository.findAll(specification, pageRequest);
    }

    /**
     * 多条件查询,使用and关联查询条件
     *
     * @param name
     * @param addr
     */
    public void findUsersByConditions(String name, String addr) {
        Specification<User> spec = new Specification<User>() {
            /***
             *  Predicate :封装了单个的查询条件
             * @param root  查询对象的属性的封装
             * @param criteriaQuery 封装了需要执行查询中的各个部分的信息: select   from  order  by 等信息
             * @param criteriaBuilder  CriteriaBuilder:查询条件的构造器  定义不同的查询条件的
             * @return
             */
            @Override
            public Predicate toPredicate(Root<User> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder criteriaBuilder) {
                // where name ='张三' and  addr='上海'
                List<Predicate> list = new ArrayList<>();
                list.add(criteriaBuilder.equal(root.get("name"), name));
                list.add(criteriaBuilder.equal(root.get("addr"), addr));
                Predicate[] arr = new Predicate[list.size()];
                return criteriaBuilder.and(list.toArray(arr));
            }
        };
        List<User> list = userRepository.findAll(spec);
    }


    /**
     * 多个条件查询,使用or关联查询条件
     *
     * @param name
     * @param addr
     * @param id
     */
    public void findUsersByConditions(String name, String addr, Long id) {
        /*
         *  Specification<Users> 封装了一个查询的对象
         */
        Specification<User> spec = new Specification<User>() {
            /***
             *  Predicate :封装了单个的查询条件
             * @param root  查询对象的属性的封装
             * @param query 封装了需要执行查询中的各个部分的信息: select   from  order  by 等信息
             * @param cb  CriteriaBuilder:查询条件的构造器  定义不同的查询条件的
             */
            @Override
            public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) {
                // where (name ='张山' and  addr=上海) or id=2
                return cb.or(cb.and(cb.equal(root.get("name"), name), cb.equal(root.get("addr"), addr)), cb.equal(root.get("id"), id));
            }
        };
        List<User> list = userRepository.findAll(spec);

    }

    public List<User> testJspSpecification(String name) {
        //where name='张三'
        Specification spec = SpecificationFactory.eq("name", name);
        List<User> list = userRepository.findAll(spec);
        return list;
    }


    public void testJspSpecification(User user) {
        // 构造条件
        Specification specification = SpecificationFactory.lt("birthDate", new Date().getTime());
        specification = specification.and(SpecificationFactory.likeEnd("name", user.getName()));
        specification = specification.and(SpecificationFactory.isNull("addr"));
        // 打印结果
        List<User> systemUserList = this.userRepository.findAll(specification);
        for (User user1 : systemUserList) {
            System.out.println(user1);
        }
    }

    /**
     * 根据地址查询所有人,然后按id降序排列
     * @param addr
     * @return
     */
    public List<User> findAllByAddr(String addr, String order){
        Specification specification = SpecificationFactory.eq("addr", addr);
        specification=specification.and(SpecificationFactory.desc(order));
        List<User> users = userRepository.findAll(specification);
        return users;
    }

}

测试类

package com.lcj;

import com.lcj.dao.UserRepository;
import com.lcj.factory.SpecificationFactory;
import com.lcj.po.User;
import com.lcj.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.jpa.domain.Specification;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;

@SpringBootTest(classes = SpringbootJpaApplication.class)
public class SpringbootJpaApplicationTests {

    @Resource
    private UserService userService;
    @Resource
    private UserRepository userRepository;

    @Test
    public void test() {
        User user = new User()
                .setName("张三")
                .setAddr("湖南")
                .setBirthDate(new Date());
        userService.addUser(user);
    }

    @Test
    public void test1() {
        User user = userService.queryUserById(1L);
        System.out.println(user.toString());
    }

    @Test
    public void test2() {
        List<User> users = userService.queryAllUser();
        System.out.println(users);
    }

    @Test
    public void test3() {
        User user = userService.queryUserById(1L);
        user.setAddr("海南");
        userService.updateUserById(user);
        System.out.println(user.toString());
    }

    @Test
    public void test4() {
        userService.deleteUserById(1L);
    }

    @Test
    public void test5() {
        String name = userService.queryUserName(1L);
        System.out.println(name);
    }

    @Test
    public void test6() {
        List<User> users = userService.findAllByAddr("上海", "id");
        System.out.println(users);
    }
    @Test
    public void test7() {
        //查询name不为null或者addr是上海
        Specification spe = SpecificationFactory.isNotNull("name");
        spe.or(SpecificationFactory.eq("addr", "上海"));
        List<User> users = userRepository.findAll(spe);
        System.out.println(users);
    }

}

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值