Springboot 整合 JPA

2022年8月3日11:14:59

Springboot 整合 JPA

简介

JPAJAVA Persistance API 是一种官方提出的ORM规范。

什么是ORM?

ORMObject-Relational Mapping。他的作用是在关系型数据库和对象之间作一个映射,这样我们就不需要使用具体的sql语句 ,而是像平常一样操作对象即可。例如Hibernate就是很著名的ORM框架

spring-data-jpa便是spring基于Hibernate开发的一个JPA框架。极大的简化了JPA的写法,可以非常简单的实现CRUD和分页操作。

复杂的sql操作也可以使用@Query("SELECT * FROM TABLE WHERE user_name = :name"),不过复杂的sql操作还是建议整合mybatis或者mybatis-plus。

使用示例

程序源码

建表

我们这里首先简单构建customer表,该表有id,name,age,create_time,modify_time属性,其中主键为id,递增。

DROPTABLEIFEXISTS`customer`;CREATETABLE`customer`(`id`BIGINT(20)NOTNULLAUTO_INCREMENTCOMMENT'id',`name`VARCHAR(64)DEFAULTNULLCOMMENT'姓名',`age`SMALLINT(3)DEFAULTNULLCOMMENT'年龄',`create_time`TIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMPCOMMENT'创建时间',`modify_time`TIMESTAMPNOTNULLDEFAULTCURRENT_TIMESTAMPONUPDATECURRENT_TIMESTAMPCOMMENT'更新时间',PRIMARYKEY(`id`))ENGINE=INNODBDEFAULTCHARSET=utf8;

pom.xml

因为要连接数据库,并且使用spring data jpa,所以添加如下依赖

<!-- 数据库连接 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><!--jpa--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency>

application.yml

Springboot参数配置

#端口设置server:port:8090#数据源设置spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driver#设置编码和时区url: jdbc:mysql://localhost:3306/springboot?characterEncoding=utf-8&serverTimezone=GMT%2B8username: rootpassword:1234#jpa设置jpa:#显示sql语句show-sql:true#format sql 语句hibernate:naming:#驼峰匹配physical-strategy: org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy#根据实体类自动更新表结构ddl-auto: update

Spring Data Jpa默认使用驼峰命名法进行匹配

例如实体属性为userName,则匹配数据库表中user_name

若不想使用驼峰匹配可以在属性上设置@Column(name = "user_name")进行匹配。

jpa.hibernate.ddl-auto使用需要注意,该参数设置为每次启动hibernate自动根据实体类对表进行操作。

update:这是最常使用的,用来在每次启动hibernate 根据实体类更新表结构。

create:这就是最为要注意的,因为每次启动hibernate都会删除之前建的表,重新建表。

create-drop:即加载hibernate时创建表,但是关闭之后表自动删除。

Entity

@Data@Entity@Table(name="customer")@EntityListeners(AuditingEntityListener.class)publicclassCustomer{//  主键@Id//  自增//  自增策略由数据库控制@GeneratedValue(strategy= GenerationType.IDENTITY)private Integer id;@Column(name="name")private String name;private Integer age;//  自动修改创建更新时间@CreatedDateprivate Date createTime;@LastModifiedDateprivate Date modifiedTime;}

实体类有五个属性id,name,age,createTime,modifyTime与表中id,name,age,create_time,modify_time一一对应。

@Id标明这是主键

@GeneratedValue(strategy = GenerationType.IDENTITY)标明主键是自增的,strategy为自增策略。

strategy有五种策略

TABLE:spring-data-jpa 会自动建立一个hibernate_sequence表存放自增值

SEQUENCE:根据数据库的序列生成主键(主要用于Oracle等不支持主键自增长的数据库)

IDENTITY:使用数据库主键自增策略

AUTO 程序自动选择一种策略

@Column(name = "user_name")标明该属性对应表中的user_name列,可以不加,如果开启且满足驼峰匹配)。

@CreatedDate@LastModifiedDatespring data jpa提供的非常方便的自动修改数据库中表信息创建和修改时间。但是该实体类需要加上@EntityListeners(AuditingEntityListener.class),且启动类需要加上@EnableJpaAuditing,以开启修改权限。

@Entity标明这是一个实体类,只有添加了这个注释,spring data jpa才会去将它与数据库中的表进行映射。

@Table 标明该实体类对应数据库中的表名称,与@Entity一样,也是可加可不加。

@Data是插件lombok中的一个注释,他包含了常用的get(),set()和无参全参构造器等等,帮我们省略了很多代码。

数据库访问对象(DAO)

publicinterfaceCustomerRepositoryextendsJpaRepository<Customer,Integer>{

    LinkedList<Customer>findByName(String name);}

我们可以看到spring data jpa中建立数据库访问对象的方法是继承JpaRepository<,>,其中泛型括号中第一个参数为实体类,第二个对象为主键类型

这相当于是建立了一个实体类仓库,仓库中对应这数据库中的该实体类映射的所有数据信息。

其实spring data jpa提供了不只这一种接口。

接口 说明
JpaRepository PagingAndSortingRepository的子接口,增加了一些功能,比如findAll()
PagingAndSortingRepository CrudRepository的子接口,添加分页和排序的功能
CrudRepository Repository的子接口,提供CRUD的功能
Repository 最顶层的接口,是一個空的接口,目的是为了统一所有Repository的类型,且能让组件扫描的时候自动识别

所以我们进行数据库简单操作的时候不需要自己编写具体代码,只需要继承相关接口,直接调用其中某些方法就行了。

我们之所以不用写这些接口的实现类,是因为Spring Data JPA 最终将每个这样的bean最终映射到了一个统一的实现类SimpleJpaRepository的代理对象,而这个代理对象能支持所有每个自定义的JpaRepository接口定义的功能和SimpleJpaRepository类中定义的所有功能。

查询方法

spring data jpa提供两种查询方法

  1. 根据方法命名规范查询

    例如findByName(String name)findAll()

    关键字 方法命名 对应 sql 语句
    And findByNameAndPwd where name= ? and pwd =?
    Or findByNameOrSex where name= ? or sex=?
    Is,Equals findById,findByIdEquals where id= ?
    Between findByIdBetween where id between ? and ?
    LessThan findByIdLessThan where id < ?
    LessThanEquals findByIdLessThanEquals where id <= ?
    GreaterThan findByIdGreaterThan where id > ?
    GreaterThanEquals findByIdGreaterThanEquals where id > = ?
    After findByIdAfter where id > ?
    Before findByIdBefore where id < ?
    IsNull findByNameIsNull where name is null
    isNotNull,NotNull findByNameNotNull where name is not null
    Like findByNameLike where name like ?
    NotLike findByNameNotLike where name not like ?
    StartingWith findByNameStartingWith where name like ‘?%’
    EndingWith findByNameEndingWith where name like ‘%?’
    Containing findByNameContaining where name like ‘%?%’
    OrderBy findByIdOrderByXDesc where id=? order by x desc
    Not findByNameNot where name <> ?
    In findByIdIn(Collection<?> c) where id in (?)
    NotIn findByIdNotIn(Collection<?> c) where id not in (?)
    True findByAaaTue where aaa = true
    False findByAaaFalse where aaa = false
    IgnoreCase findByNameIgnoreCase where UPPER(name)=UPPER(?)
  2. 使用@Query()查询

    @Query(value="select * from customer c where c.name=?1")
    List<Customer>finByName(String name)

    个人觉得如果需要使用复杂sql语句,还是建议使用mybatis/mybatis-plus

Service

publicinterfaceCustomerService{public Page<Customer>getAll(Integer pageNum, Integer pageSize);public Customersave(Customer customer);public LinkedList<Customer>searchByName(String name);}

ServiceImpl

@ServicepublicclassCustomerServiceImplimplementsCustomerService{//    注入数据仓库@Autowiredprivate CustomerRepository customerRepository;//    分页操作@Overridepublic Page<Customer>getAll(Integer pageNum, Integer pageSize){
        Pageable pageable= PageRequest.of(pageNum,pageSize);
        Page<Customer> page= customerRepository.findAll(pageable);return page;}//    对数据库修改之后的保存操作@Overridepublic Customersave(Customer customer){return customerRepository.save(customer);}//    查询操作@Overridepublic LinkedList<Customer>searchByName(String name){return customerRepository.findByName(name);}}

服务层是我们对功能逻辑实现的模块。

所以我们这边提供一些接口,以供控制层传入参数使用。

对于接口的实现类,我们首先注入我们自己定义的数据仓库,之后在具体方法内调用对数据仓库的具体操作。

demo中定义了三个基本功能

分页

由于我们的数据仓库继承了PagingAndSortingRepository接口,所以我们只需传入一个Pageable对象即可。

pageablespring data里面的一个接口,可以通过PageRequest.of(,)传入页码分页大小排序方式 进行构建,也可以只传页码分页大小排序默认不排序。

//PageRequest的构造方式publicstatic PageRequestof(int page,int size){returnof(page, size, Sort.unsorted());}publicstatic PageRequestof(int page,int size, Sort sort){returnnewPageRequest(page, size, sort);}//继承了AbstractPageRequest//super(page,size)其实只是设定了page和sizeprotectedPageRequest(int page,int size, Sort sort){super(page, size);
        Assert.notNull(sort,"Sort must not be null!");this.sort= sort;}

所以我们这里首先传入pageNumpageSize构建一个pageable对象

然后调用数据仓库的findAll(pageable)方法,即可返回一个page对象

page对象中包含了该页的所有数据以及一些相关信息,我们可以通过调用page.getContent()获取内容List

保存

我们所有的对数据库的增,改都需要调用数据仓库的save()方法

save() 参数传入实体类对象,数据仓库实现对数据库中该实体类对应信息的修改操作

查询

这里查询便是使用的上述查询的第一种方式根据方法命名规范查询

customerRepository.findByName(name) 数据仓库识别方法名,即通过Name属性到数据库中进行查询。

查询方法返回可以是一个实体类对象,也可以是一个实体类对象列表

VO

返回视图类主要包括信息码,返回信息,和具体数据。

我这里简单就只有一个CustomerVO,即实体类对象的一些信息。

ResultVO
@Data@JsonInclude(JsonInclude.Include.NON_NULL)publicclassResultVO<T>{private Integer code;private String msg;private T data;}
CustomerVO
@DatapublicclassCustomerVO{@JsonProperty(value="id")private Integer id;@JsonProperty(value="name")private String name;@JsonProperty(value="age")private Integer age;@JsonProperty(value="createTime")private String createTime;@JsonProperty(value="modifiedTime")private String modifiedTime;public CustomerVOsetByCustomer(Customer customer){
        CustomerVO customerVO=newCustomerVO();
        customerVO.setId(customer.getId());
        customerVO.setName(customer.getName());
        customerVO.setAge(customer.getAge());
        customerVO.setCreateTime(customer.getCreateTime().toString());
        customerVO.setModifiedTime(customer.getModifiedTime().toString());return customerVO;}}

Controller

比较简单的controller,就不多赘述,相关信息见注释

@RestController@RequestMapping("/customer")publicclassCustomerController{//  注入service接口@Autowiredprivate CustomerService customerService;/**
     * 分页展示
     * @param pagenum 页码(第一页为0)
     * @param pagesize 分页大小
     * @return
     */@GetMapping("/getall")public ResultVO<LinkedList<CustomerVO>>getAll(@RequestParam("pagenum")Integer pagenum,@RequestParam("pagesize")Integer pagesize){

        Page<Customer> page= customerService.getAll(pagenum,pagesize);//        获取页内数据内容
        List<Customer> customerList= page.getContent();//        设置VO
        ResultVO<LinkedList<CustomerVO>> resultVO=newResultVO();
        resultVO.setCode(1);
        resultVO.setMsg("succuss");
        LinkedList<CustomerVO> customerVOLinkedList=newLinkedList<CustomerVO>();for(Customer customer:customerList){
            CustomerVO customerVO=newCustomerVO().setByCustomer(customer);}

        resultVO.setData(customerVOLinkedList);return resultVO;}/**
     * 添加用户
     * @param name
     * @param age
     * @return
     */@PostMapping("/add")public ResultVO<CustomerVO>addCustomer(@RequestParam(value="name",required=true)String name,@RequestParam(value="age",required=true)Integer age){

        Customer customer=newCustomer();
        customer.setAge(age);
        customer.setName(name);//        保存修改
        customerService.save(customer);//        设置VO
        ResultVO<CustomerVO> resultVO=newResultVO();
        resultVO.setCode(1);
        resultVO.setMsg("add success");

        CustomerVO customerVO=newCustomerVO().setByCustomer(customer);

        resultVO.setData(customerVO);return resultVO;}/**
     * 通过名字查询
     * 可以有同名存在,所以返回List
     * @param name
     * @return
     */@GetMapping("/search")public ResultVO<LinkedList<CustomerVO>>searchByName(@RequestParam(value="name",required=true)String name){

        ResultVO<LinkedList<CustomerVO>> resultVO=newResultVO();
        resultVO.setCode(1);
        resultVO.setMsg("search success");//        获取查询结果
        LinkedList<Customer> customerList= customerService.searchByName(name);//        设置VO
        LinkedList<CustomerVO> customerVOLinkedList=newLinkedList();for(Customer customer:customerList){
            CustomerVO customerVO=newCustomerVO().setByCustomer(customer);
            customerVOLinkedList.add(customerVO);}
        resultVO.setData(customerVOLinkedList);return  resultVO;}}

运行结果

GET/getAll

Springboot 整合 JPA

POST/add

Springboot 整合 JPA

GET/search

Springboot 整合 JPA

总结

怎么说呢,Spring Data Jpa给我们提供了非常简单的CRUD分页排序操作,但是呢,对复杂的查询就没什么优势了。

所以个人建议,只有简单CRUD操作的可以用Spring Data Jpa,复杂的还是推荐Mybatis/Mybatis-plus

  • 作者:Cwh_971111
  • 原文链接:https://blog.csdn.net/Cwh_971111/article/details/118208650
    更新时间:2022年8月3日11:14:59 ,共 9482 字。