SpringBoot快速集成JPA

2022-07-06 12:38:38

前言:一说JavaWeb,很多小伙伴都知道SSH,这个H代表的就是Hibernate框架,可是什么又是JPA呢?相信许多刚入门的小伙伴听说过但不是特别清楚,首先JPA的全称叫做Java Persistence API,JPA是一个基于O/R映射的标准规范,在这个规范中,JPA只定义标准规则,不提供实现,使用者则需要按照规范中定义的方式来使用。目前JPA的主要实现有Hibernate、EclipseLink、OpenJPA等,事实上,由于Hibernate在数据访问解决技术领域的霸主地位,JPA的标准基本是由Hibernate来主导的。另外,Spring框架为我们提供了Spring Data JPA,可以减少我们使用JPA时的代码量。

一、JPA简介

1.1、JPA

JPA的全称叫做Java Persistence API意即Java持久化API,是Sun官方在JDK5.0后提出的Java持久化规范(JSR 338,这些接口所在包为javax.persistence)。JPA是一个基于O/R映射的标准规范,在这个规范中,JPA只定义标准规则,不提供实现,使用者则需要按照规范中定义的方式来使用。

目前JPA的主要实现有Hibernate、EclipseLink、OpenJPA等,事实上,由于Hibernate在数据访问解决技术领域的霸主地位,JPA的标准基本是由Hibernate来主导的。SpringBoot中的Spring Data JPA 实现默认就是Hibernate。

JPA的出现主要是为了简化持久层开发以及整合ORM技术,结束Hibernate、TopLink、JDO等ORM框架各自为营的局面。JPA是在吸收现有ORM框架的基础上发展而来,易于使用,伸缩性强。

(1) 总的来说,JPA包括以下3方面的技术:

ORM映射元数据:支持XML和注解两种元数据的形式,元数据描述对象和表之间的映射关系

API: 操作实体对象来执行CRUD操作

查询语言: 通过面向对象而非面向数据库的查询语言(JPQL)查询数据,避免程序的SQL语句紧密耦合

(2)ORM框架

ORM全英文名为Object-Relational Mapping:对象关系映射,简单来说为了不用JDBC那一套原始方法来操作数据库,ORM框架横空出世(mybatis、hibernate等等)。然而ORM框架出的太多了,百花齐放,琳琅满目,你一套标准我一套标准,要是想换一套框架实现项目,可能要从头再写。

Sun引入新的JPA ORM规范出于两个原因:

其一,简化现有Java EE和Java SE应用开发工作;

其二,Sun希望整合ORM技术,实现天下归一。

1.2、Spring Data JPA

Spirng Data JPA 是Spring提供的一套简化JPA开发的框架,按照约定好的【方法命名规则】写dao层接口,就可以在不写接口实现的情况下,实现对数据库的访问和操作。同时提供了很多除了CRUD之外的功能,如分页、排序、复杂查询等等。Spring Data JPA 可以理解为 JPA 规范的再次封装抽象,底层还是使用了 Hibernate 的 JPA 技术实现。

Spring Data JPA是Spring Data家族的一部分,可以轻松实现基于JPA的存储库。 此模块处理对基于JPA的数据访问层的增强支持,它使构建使用数据访问技术的Spring驱动应用程序变得更加容易。

总的来说JPA是ORM规范,Hibernate、TopLink等是JPA规范的具体实现,这样的好处是开发者可以面向JPA规范进行持久层的开发,而底层的实现则是可以切换的。Spring Data Jpa则是在JPA之上添加另一层抽象(Repository层的实现),极大地简化持久层开发及ORM框架切换的成本。

1.3、JPA默认的接口方法

 使用 Spring Data JPA 访问数据,只需要数据访问层接口继承了JpaRepository接口(SpringDataJPA提供的简单数据操作接口)和JpaSpecificationExecutor(SpringDataJPA提供的复杂查询接口),SpringDataJPA就会为我们搞定单表的基本CRUD操作。


二、SpringBoot集成Spring Data JPA

2.1、准备MySQL数据,新建表t_user

DROP TABLE IF EXISTS `t_user`;
CREATE TABLE `t_user` (
  `t_id` int(11) NOT NULL,
  `t_name` varchar(255) DEFAULT NULL,
  `t_age` int(11) DEFAULT NULL,
  `t_address` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`t_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
————————————————

2.2、引入JPA和MySQL驱动依赖

      <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

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

        <!--数据库驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

 2.3、application.yml配置数据源以及JPA

今后我们修改application.properties文件配置为application.yml配置。yml配置文件比properties配置要更清晰更有层次感,可以很明了的看懂配置信息。

server:
  port: 8088

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/shankeng?serverTimezone=UTC
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: update
      naming:
        physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy
    show-sql: true

2.4、UserEntity实体类

根据数据库中的字段对应建立一个实体类UserEntity

package com.hs.demo.entity;

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

@Entity
@Table(name="t_user")
public class UserEntity implements Serializable {
    @Id
    @GeneratedValue
    @Column(name="t_id")
    private Long id;
    @Column(name="t_name")
    private String name;
    @Column(name="t_age")
    private int age;
    @Column(name="t_address")
    private String address;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public Long getId() {

        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }
}

2.5、UserJPA创建

既然实体类我们也已经创建完成了,那么接下来我们需要使用SpringDataJPA来完成数据库操作,我们新建UserJPA接口并且继承SpringDataJPA内的接口作为父类:

package com.hs.demo.jpa;

import com.hs.demo.entity.UserEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import javax.transaction.Transactional;
import java.io.Serializable;
import java.util.List;

/**
 * JPA 提供了许多接口不用自己再去写底层封装了
 *
 * 按照命名规则来定义方法,即可以操作数据库了,如查询:findBy + 实体属性名, get + 实体类名 + By + 实体属性名,...
 * 还可以使用@Query注解,直接语句查询...
 *
 */
public interface UserJPA extends JpaRepository<UserEntity, Long>, JpaSpecificationExecutor<UserEntity>, Serializable {


    //按名称查找所有用户
    List<UserEntity> findByName(String userName);

    //按姓名查找单个用户
    UserEntity getUserEntityByName(String userName);

    //按姓名查找单个用户 使用Query注解方式,默认是jpql语句对应实体类操作,如有需要加上nativeQuery=true来声明这是一个本地查询(sql查询)
    @Query("select u from UserEntity u where u.name = ?1")
    UserEntity findUserEntityByName(String userName);

//    //按姓名查找单个用户 使用原生SQL语句查询方式
//    @Query(value = "select * from t_user where t_name = ?1", nativeQuery = true)
//    UserEntity findUserEntityByName(String userName);

    //根据名称删除用户
    @Transactional
    void deleteByName(String userName);

}

我们UserJPA继承了JpaRepository接口(SpringDataJPA提供的简单数据操作接口)、JpaSpecificationExecutor(SpringDataJPA提供的复杂查询接口)、Serializable(序列化接口)。我们并不需要做其他的任何操作了,因为SpringDataJPA会为我们搞定单表的基本CRUD操作。SpringDataJPA内部使用了类代理的方式让继承了它接口的子接口都以Spring管理的Bean的形式存在,也就是说我们可以直接使用@Autowired注解在spring管理bean使用。

注意事项

1、异步查询的时候,使用@ResponseBody注解将返回值写入到Http response body中,不然前端获取不到返回值。

2、删除的时候,删除方法要使用@Transactional返将该方法注入到事务中。

2.6、新建UserController类,编写CRUD方法

package com.hs.demo.controller;

import com.hs.demo.entity.UserEntity;
import com.hs.demo.jpa.UserJPA;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserJPA userJPA;

    /**
     * 查询所有数据
     *
     * @return
     */
    @RequestMapping("/list")
    public List<UserEntity> list() {
        return  userJPA.findAll();
    }

    @RequestMapping("/save")
    public UserEntity save(UserEntity entity) {
        return  userJPA.save(entity);
    }

    @RequestMapping(value = "/delete", method = RequestMethod.GET)
    public List<UserEntity> delete(Long id) {
        userJPA.deleteById(id);
        return userJPA.findAll();
    }

   @RequestMapping("/find")
    //接收前端请求参数,普通方式-请求参数名和Controller方法的参数一致
    public UserEntity find(String name) {
        return userJPA.getUserEntityByName(name);
    }

}

2.7、启动SpringBoot项目开始测试

启动项目后,由于数据库中并无数据,直接访问127.0.0.1:8088/user/list显示为空,首先向数据库中插入数据,在浏览器中输入127.0.0.1:8088/user/save?name=admin&age=16&address=shanghai

效果如下图:

重复插入后,再次访问127.0.0.1:8088/user/list,效果如下图所示:

 验证JPA的@Query方式,访问:http://127.0.0.1:8088/user/find?name=xmh

 2.8、JPA总结

本篇博客介绍了如何使用Spring Boot JPA与MySQL进行数据交互,可以看到使用JPA可以大量减少Dao代码,大大提升开发效率。Spring Data JPA实现单表查询非常方便,还提供了方法命名规则定义方法的名称和@Query注解查询;当然这里只是简单的单表查询,接下来我会介绍JPA如何多表联合查询、动态条件查询和分页等等。


三、自定义方法命名规则查询

方法命名规则查询顾名思义就是根据方法的名字,就能创建查询。只需要按照Spring Data JPA提供的方法命名规则定义方法的名称,就可以完成查询工作。Spring Data JPA在程序执行的时候会根据方法名称进行解析,并自动生成SQL查询语句进行查询。

按照Spring Data JPA 定义的规则为:findBy + 实体属性名, get + 实体类名 + By + 实体属性名,...条件属性首字母需大写

例如:

List<User>   findByUserName(String  username);

User  findByUserNameAndPassword(String  username,String password);

Long countByUserName(String username);

void deleteById(Long id);

按方法名解析的查询方法通常只适用于单表查询,且建议where条件参数不多于三条的情况下,返回值通常对应表的实体Bean或者实体类List类型。


参考链接:

Spring Data Jpa的四种查询方式详解

Spring Data JPA提供的方法命名规则定义方法的名称

Spring Data JPA 简单查询--方法定义规则

@Query注解的用法(Spring Data JPA)

JPA的@Query用法

JPA中实体类常用注解、默认继承方法和关键字抽象方法

【Spring Boot | Jpa】Spring Boot 2.0 JPA 实现分页(简单查询分页、复杂查询分页)

SpringData Jpa实现分页功能

JPA查询getOne()与findOne()的差异以及一些小问题

  • 作者:Java后端何哥
  • 原文链接:https://blog.csdn.net/CSDN2497242041/article/details/120421971
    更新时间:2022-07-06 12:38:38