Spring Data JPA 增删改查就是这么简单

在国内,你可能会发现大部分项目用的orm都是Mybatis或者Mybatis-plus,对于个人来讲,我也挺喜欢Mybatis,毕竟它开发起来非常的自由;但是对于简单curd操作jpa效率上还是可以的,所以作为一个合格的Java程序员,熟练使用jpa时非常有必要的。

1.什么是jpa?

jpa是Java EE 5.0 平台标准的 ORM 规范,使得应用程序以统一的方式访问持久层。

在这里插入图片描述

Jpa与Hibernate的关系

  1. JPA 是 Hibernate 的一个抽象(就像JDBC和JDBC驱动的关系);
  2. JPA 是规范:JPA 本质上就是一种 ORM 规范,不是ORM 框架,这是因为 JPA 并未提供 ORM 实现,它只是制订了一些规范,提供了一些编程的 API 接口,但具体实现则由 ORM 厂商提供实现;
  3. Hibernate 是实现:Hibernate 除了作为 ORM 框架之外,它也是一种 JPA 实现
  4. 从功能上来说, JPA 是 Hibernate 功能的一个子集

JPA包含的技术

  1. ORM 映射元数据:JPA 支持 XML 和 JDK 5.0 注解两种元数据的形式,元数据描述对象和表之间的映射关系,框架据此将实体对象持久化到数据库表中。
  2. JPA 的 API:用来操作实体对象,执行CRUD操作,框架在后台完成所有的事情,开发者从繁琐的 JDBC 和 SQL 代码中解脱出来。
  3. 查询语言(JPQL):这是持久化操作中很重要的一个方面,通过面向对象而非面向数据库的查询语言查询数据,避免程序和具体的 SQL 紧密耦合。

2.Spring Data Jpa 环境搭建

引入依赖

        <!-- jpa -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <!--Spring boot Web容器-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
         
         <!-- mysql数据库驱动 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!-- 连接池 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>${druid-spring-boot-starter.version}</version>
        </dependency>
         <!-- fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>    
            

配置文件

server:
  port: 8848
spring:
  jackson:
    time-zone: GMT+8

  #配置 Jpa
  jpa:
    properties:
      hibernate:
        dialect: org.hibernate.dialect.MySQL5InnoDBDialect
    open-in-view: true
    # 数据库平台
    database-platform: mysql
    show-sql: true
    database: mysql
    # 每次启动项目时,数据库初始化策略
    hibernate:
      ddl-auto: update
  datasource:
    druid:
      db-type: com.alibaba.druid.pool.DruidDataSource
      driverClassName: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://127.0.0.1:3306/tem-jpa?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false
      username: root
      password: 123456
      # 初始连接数
      initial-size: 5
      # 最小连接数
      min-idle: 15
      # 最大连接数
      max-active: 30
      # 超时时间(以秒数为单位)
      remove-abandoned-timeout: 180
      # 获取连接超时时间
      max-wait: 3000
      # 连接有效性检测时间
      time-between-eviction-runs-millis: 60000
      # 连接在池中最小生存的时间
      min-evictable-idle-time-millis: 300000
      # 连接在池中最大生存的时间
      max-evictable-idle-time-millis: 900000
      # 指明连接是否被空闲连接回收器(如果有)进行检验.如果检测失败,则连接将被从池中去除
      test-while-idle: true
      # 指明是否在从池中取出连接前进行检验,如果检验失败, 则从池中去除连接并尝试取出另一个
      test-on-borrow: true
      # 是否在归还到池中前进行检验
      test-on-return: false
      # 检测连接是否有效
      validation-query: select 1
      # 配置监控统计
      webStatFilter:
        enabled: true
      stat-view-servlet:
        enabled: true
        url-pattern: /druid/*
        reset-enable: false
        login-username: root
        login-password: 123
      filter:
        stat:
          enabled: true
          # 记录慢SQL
          log-slow-sql: true
          slow-sql-millis: 1000
          merge-sql: false
        wall:
          config:
            multi-statement-allow: true
      

每次启动项目时的初始化策略有4种:

  • create:
    每次应用启动的时候会重新根据实体建立表,之前的表和数据都会被删除。
  • create-drop:
    和上面的功能一样,但是多了一样,就是在应用关闭的时候,也就是sessionFactory一关闭,会把表删除。
  • update:
    最常用的,第一次启动根据实体建立表结构,之后启动会根据实体的改变更新表结构,之前的数据都在。
  • validate:
    会验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值,运行程序会校验实体字段与数据库已有的表的字段类型是否相同,不同会报错

3.增删改查一套

准备实体类

@ApiModel(value = "用户表")
@Data
@Table(name = "student")
@Entity
public class Student {

    @ApiModelProperty(value = "自增id")
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ApiModelProperty(value = "用户名")
    @Column(name = "username")
    private String username;

    @ApiModelProperty(value = "密码")
    @Column(name = "password")
    private String password;
}

@ApiModelProperty注解为swagger注解,可以删除

编写持久层

public interface StudentRepository extends JpaRepository<Student, Integer> {
}

增加接口

	@PostMapping
    public Object save(@RequestBody Student user){
        Student save = studentRepository.save(user);
        return save;
    }

删除接口

@DeleteMapping("/{id}")
    public Object delete(@PathVariable Integer id){
        studentRepository.deleteById(id);
        return "delete success";
    }

修改接口

 @PutMapping
    public Object update(@RequestBody Student user){
        Student save = studentRepository.save(user);
        return save;
    }

普通查询接口

 @GetMapping
    public Object getList(){
       List<Student> userList = studentRepository.findAll();
        return userList;
    }

分页查询接口

 @PostMapping(value = "/pageList")
    public Object getListByPage(@RequestBody Student student){

      Pageable  pageable = PageRequest.of(0,10);
      Example<Student> studentExample = Example.of(student, ExampleMatcher.matching().withMatcher("username", ExampleMatcher.GenericPropertyMatcher::contains));
      Page<Student> studentPage = studentRepository.findAll(studentExample, pageable);
      return studentPage;
    }

参考自官网例子,jpa增删改查一套[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HBBGKVDq-1623835238747)(C:\Users\dpf\AppData\Roaming\Typora\typora-user-images\image-20210614200259922.png)]

在持久层,除了使用JPQL已经定义好的还可以通过注解自己写sql,如下代码:

    /**
     * nativeQuery = true 使用原生的sql查询
     * @param username
     * @return
     */
    @Query(value = "select id,username,password from student where username = :username",nativeQuery = true)
    List<Student> getListByName(@Param("username") String username);

    @Query(value = "select id,username,password from student where username = ?1",nativeQuery = true)
    List<Student> getListByName2( String username);

    @Transactional
    @Modifying
    @Query(value = "update student set username=:#{#student.username},password=:#{#student.password} where id=:#{#student.id}",nativeQuery = true)
    int updateStudent(@Param("student") Student student);

如果不使用@Param注解,可以使用?阿拉伯数字进行取值,@Query后面可以直接写sql语句,nativeQuery = true表示使用原生sql进行查询,如果进行修改使用@Modifying注解

4. Repository详细介绍

基础介绍

Mybatis/Mybatis-plus中我们一般将持久层取名为xxxDao,但是在Jpa中我们一般取名为为xxxRepository,这个xxxRepository到底有什么特别的呢?首先我们看看它的继承关系图:

在这里插入图片描述

从图中可以看出实现类不少,我们可以从以下几个进行学习

  1. Repository 接口是 Spring Data 的一个核心接口,它不提供任何方法,开发者需要在自己定义的接口中声明需要的方法 public interface Repository<T, ID extends Serializable> { }
  2. 若我们定义的接口继承了 Repository, 则该接口会被 IOC 容器识别为一个 Repository Bean,进而纳入到 IOC 容器中,进而可以在该接口中定义满足一定规范的方法。
  3. Spring Data可以让我们只定义接口,只要遵循 Spring Data 的规范,就无需写实现类

基础的 Repository 提供了最基本的数据访问功能,其几个子接口则扩展了一些功能,它的几个常用的实现类如下:

  • CrudRepository: 继承 Repository,实现了一组 CRUD 相关的方法
  • PagingAndSortingRepository: 继承 CrudRepository,实现了一组分页排序相关的方法
  • JpaRepository: 继承 PagingAndSortingRepository,实现一组 JPA 规范相关的方法
  • 自定义的 XxxxRepository 需要继承 JpaRepository,这样的 XxxxRepository 接口就具备了通用的数据访问控制层的能力。
  • JpaSpecificationExecutor: 不属于Repository 体系,实现一组 JPA Criteria 查询相关的方法

方法定义规范

如果你得多了,jpa持久层的方法好像都是遵守某一规范的,例如:查询,一般都是findAaaByBbb,删除一般都是deleteByXxx,只要你按照规范定义方法它就可以帮你实现相关方法。jpa支持以下方法关键字:

在这里插入图片描述

5.多表查询

在关系型数据库中,表与表之间的关系有:一对一、一对多、多对多。这里对1对多和多对多进行演示。

一对多

这里以学生和班级进行举例,一个班级对应多个学生。

班级实体类如下

@Data
@Table(name = "cla")
@Entity
public class Cla implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name")
    private String name;

    @OneToMany(targetEntity = Student.class,mappedBy = "cla")
    private List<Student> studentList;
}

学生实体类

@Data
@Table(name = "student")
@Entity
public class Student implements Serializable {

    @ApiModelProperty(value = "自增id")
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @ApiModelProperty(value = "用户名")
    @Column(name = "username")
    private String username;

    @ApiModelProperty(value = "密码")
    @Column(name = "password")
    private String password;


    @ManyToOne(targetEntity = Cla.class)
    @JsonBackReference// 解决序列化无限递归问题
    private Cla cla;

}

注解说明

@OneToMany:建立一对多的映射关系(主表)

  • targetEntityClass:指定一对多多对应哪个类(常用)
  • mappedBy:指定从表实体类中引用主表对象的名称(上面学生从表班级对象名称cla)。(常用)
  • cascade:指定要使用的级联操作
    • CascadeType.ALL: 进行级联操作,all表示级联所有(insert,delete,update)
    • CascadeType.merge :更新
    • CascadeType.persist:保存
    • CascadeType.remove :删除
  • fetch:指定是否采用延迟加载true/false
  • orphanRemoval:是否使用孤儿删除(非级联)

@ManyToOne:建立多对一的映射关系(从表)

  • targetEntityClass:指定多对一的一是哪个类(常用)
  • cascade:指定要使用的级联操作
  • fetch:指定是否采用延迟加载

@JoinColumn:用于定义主键字段和外键字段的对应关系

  • name:指定外键字段的名称(常用)
  • referencedColumnName:指定引用主表的主键字段名称(常用)
  • unique:是否唯一。默认值不唯一
  • nullable:是否允许为空。默认值允许。
  • insertable:是否允许插入。默认值允许。
  • updatable:是否允许更新。默认值允许。
多对多

这里以商品和订单举例,一个商品可以对应多个订单,一个订单也可以对应多个商品。多对多需要第三个关系表来维系多对多关系。

商品表如下

@Data
@Entity
@Table(name = "t_product")
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name")
    private String name;

    @ManyToMany(mappedBy = "productList")
    @JsonBackReference
    private List<Order> orderList;
}

订单表如下

@Data
@Entity
@Table(name = "t_order")
public class Order {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name")
    private String name;

    @ManyToMany(targetEntity = Product.class)
    @JoinTable(name = "product_order",
            joinColumns = {@JoinColumn(name = "order_id",referencedColumnName = "id")},
            inverseJoinColumns = {@JoinColumn(name = "product_id",referencedColumnName = "id")}
    )
    @JsonBackReference
    private List<Product> productList;
}

注解说明

@ManyToMany:用于映射多对多关系

  • cascade:配置级联操作。
  • fetch:配置是否采用延迟加载。
  • targetEntity:配置对多的时哪个实体类。
  • mappedBy:指定从表实体类中引用主表对象的名称。(常用)

@JoinTable:用来配置中间表的相关属性

  • nam:配置中间表的表名
  • joinColumns:中间表的外键字段关联当前实体类所对应表的主键字段
  • inverseJoinColumn:中间表的外键字段关联对方表的主键字段
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值