SpringBoot集成JPA及基本使用

参考地址: https://blog.csdn.net/JingAi_jia917/article/details/138159418

前言
在讲解SpringBoot集成JPA之前,先简单了解一下几个概念,JDBC、ORM、JPA以及Spring Data JPA。

1.1 JDBC

JDBC(Java DataBase Connectivity),是java连接数据库操作的原生接口API,为开发者访问数据库提供标准的接口。各数据库厂商依照JDBC规范,实现规范中的接口,实现数据库的连接。Java开发者使用同样的访问代码,配置不同的Driver、url以及账号,即可实现不同数据库厂家的数据库连接。

当数据库连接之后,通过拼接SQL语句,发送到数据库,达到对数据库中数据的操作。

缺点:

1)业务代码耦合SQL字符串拼接语句,维护比较麻烦;

2)不符合Java面向对象的编程思想;

 

1.2 ORM

对象-关系映射(Object-Relational Mapping,简称ORM),是一种描述对象与关系数据库之间映射的规范,采用面向对象编程的思想,操作数据库。

在Java中,ORM就是将Java类与DB中的Table表进行映射,代码中对相关Java类的操作,关联到数据库后,即体现为DB中关联的Table表的操作。

1.3 JPA

JPA是Java Persistence API的简称,中文名Java持久层API,是JDK5.x版本引入的。JPA的宗旨是为POJO提供持久化标准规范。

JPA采用ORM对象关系映射,以Java面向对象的编程思想,在javax.persistence包下提供对实体对象的CRUD操作,将开发者从繁琐的JDBC和SQL代码中解脱出来。

1.4 Spring Data JPA


Spring Data JPA是Spring提供的一套简化JPA开发的框架,按照约定好的方法名命规则写DAO层接口,可以在不写接口实现的情况下,实现对数据库中Table的操作,同时提供了除CRUD操作之外的许多功能,如分页、复杂查询等。

 

SpringBoot集成Spring Data JPA

2.1 引入依赖

在SpringBoot项目的pom.xml中引入相关依赖。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- 参数校验 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-validation</artifactId>
        </dependency>
        <!-- JPA是针对数据库的操作,需要引入对应的数据库 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
            <version>2.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.28</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
            <scope>compile</scope>
        </dependency>

2.2 参数配置

在application.yml中配置数据库连接信息。

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false&allowMultiQueries=true
    username: root
    password: 123456
    druid:
      stat-view-servlet:
        login-username: druid
        login-password: 123456
        url-pattern: /druid/*
        enabled: true
      filters: stat,wall
      web-stat-filter:
        url-pattern: /
        enabled: true
        exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'
      filter:
        wall:
          config: #支持单个事物多条sql语句执行
            multi-statement-allow: true
            none-base-statement-allow: true
          enabled: true
  jpa:
    hibernate:
      naming:
        #Java代码实体字段命名与数据库表结构字段之间的名称映射策略
        #当没有使用@Table和@Column注解时,implicit-strategy配置项才会被使用,当对象模型中已经指定时,
        #implicit-strategy并不会起作用。
        #implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
        #physical-strategy一定会被应用,与对象模型中是否显式地指定列名或者已经被隐式决定无关,
        #SpringPhysicalNamingStrategy:表名,字段为小写,当有大写字母的时候会添加下划线分隔符号,默认值。
        #PhysicalNamingStrategyStandardImpl:直接映射,不会做过多的处理,会禁止将驼峰转为下划线
        #physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
    # 用于指定 Session 是否在视图渲染完成后自动关闭,默认为false,意味着在视图渲染完成后,session会自动关闭
    open-in-view: false
    # 控制是否打印运行时的SQL语句与参数信息
    show-sql: true

说明:

spring.jpa.open-in-view通常设置为false,即当视图渲染完成后,Session自动关闭。Spring使用AOP(面向切面编程思想)管理事务,在方法调用前和调用后插入事务处理逻辑。如果open-in-view设置为true时,由于Session保持打开状态,可能导致事务的隔离性问题。另外,在多线程环境中,如果多个线程共享同一个Session,并且该Session的open-in-view设置为true,也可能导致事务的隔离性问题。

2.3 添加数据库表实体类Entity

package com.jingai.jpa.dao.entity;
 
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
 
import javax.persistence.*;
import java.util.Date;
 
@AllArgsConstructor
@NoArgsConstructor
@Data
@Entity
@JsonIgnoreProperties(value = { "hibernateLazyInitializer"})
@Table(name = "tb_product")
public class ProductEntity {
 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long pid;
 
    private String name;
 
    private String deliveryNo;
 
    private String customer;
 
    private String securityCode;
 
    private Date createTime;
 
    private Date validateTime;
 
    private int validateNum;
 
}

说明:如果数据库访问时报com.fasterxml.jackson.databind.exc.InvalidDefinitionException异常,是因为在转化成json的时候,fasterxml.jackson将对象转换为json报错,发现有字段值为null。底层的hibernate会给被管理的Entity加入一个hibernateLazyInitializer属性,jsonplugin会把hibernateLazyInitializer也拿出来操作,并读取里面一个不能被反射操作的属性就产生了这个异常。

@GeneratedValue注解用于提供主键的生成策略规范。GenerationType有四种标准用法,分别为为:

1)TABLE:使用一个特定的数据库表格来保存主键。该策略通常和@TableGenerator注解一起使用,@TableGenerator注解指定生成主键的表(可以在实体类上指定,也可以在主键字段或属性上指定),JPA将会根据主键内容自动生成一张表作为系列表(或使用现有的系列表)。

2)SEQUENCE:使用底层数据库的序列来生成主键,前提是数据库支持序列。MySql数据库不支持序列。该策略通常与@SequenceGenerator主键一起使用,@SequenceGenerator注解指定了生成主键的系列。

3)IDENTITY:主键由数据库自动生成(主要是自动增长型)。比如MySql可以在创建表时声明auto_increment来指定主键自增长,在实体类中使用该策略。

4)AUTO:主键由程序控制,把主键生成策略较给持久化引擎,持久化引起会根据数据库在以上三种主键生成策略中选择其中一种。

 

2.4 添加Repository

继承JpaRepository接口,自动提供了基本的CRUD、分页、批量保存接口。

package com.jingai.jpa.dao;
 
import com.jingai.jpa.dao.entity.ProductEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
 
import java.util.List;
 
public interface ProductRepository extends JpaRepository<ProductEntity, Long> {
 
    List<ProductEntity> findByPidBetween(long startPid, long endPid);
 
    @Query("from ProductEntity where name like ?1")
    List<ProductEntity> searchByName(String name);
 
}

 

在Repository接口中,除了JpaRepository自动提供的接口以外,可以自定义接口。

1)通过Spring Data JPA的命名规范,直接定义接口,无需写Sql语句;

2)使用自定义的SQL语句;

2.5 添加Service

package com.jingai.jpa.service;
 
import com.jingai.jpa.dao.entity.ProductEntity;
 
import java.util.List;
 
public interface ProductService {
 
    ProductEntity save(ProductEntity entity);
 
    ProductEntity getById(long id);
 
    List<ProductEntity> findByPidBetween(long start, long end);
 
    List<ProductEntity> searchByName(String name);
 
    int batchSave(List<ProductEntity> list)
 
}

2.6 添加Service实现类

在Service实现类中,引入Repository对象,对数据库表进行操作。在此处,不仅可以使用ProductRepository中定义的searchByName和findByIdBetween(),而且还可以访问save、getById以及saveAll,这些是在JpaRespository中提供的实现。

package com.jingai.jpa.service.impl;
 
import com.jingai.jpa.dao.ProductRepository;
import com.jingai.jpa.dao.entity.ProductEntity;
import com.jingai.jpa.service.ProductService;
import org.apache.logging.log4j.util.Strings;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
 
import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.util.List;
 
@Service
public class ProductServiceImpl implements ProductService {
 
    @Resource
    private ProductRepository productRepository;
 
    @Override
    public ProductEntity save(ProductEntity entity) {
        return productRepository.save(entity);
    }
 
    @Override
    public ProductEntity getById(long id) {
        return productRepository.getById(id);
    }
 
    @Override
    public List<ProductEntity> findByPidBetween(long startPid, long endPid) {
        return productRepository.findByPidBetween(startPid, endPid);
    }
 
    @Override
    public List<ProductEntity> searchByName(String name) {
        return productRepository.searchByName("%" + name + "%");
    }
 
    @Override
    public int batchSave(List<ProductEntity> list) {
        return productRepository.saveAll(list).size();
    }
 
}

2.7 添加Controller

package com.jingai.jpa.controller;
 
import com.jingai.jpa.dao.entity.ProductEntity;
import com.jingai.jpa.service.ProductService;
import com.jingai.jpa.util.ResponseUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
 
import javax.annotation.Resource;
import javax.persistence.EntityNotFoundException;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.util.Map;
 
@Slf4j
@Validated
@RestController
@RequestMapping("product")
public class ProductController {
 
    @Resource
    private ProductService productService;
 
    @GetMapping("get")
    public Map<String, Object> get(@NotNull(message = "id不能为空") @Min(value = 1, message = "id无效") Long id) {
        ProductEntity entity = productService.getById(id);
        try {
            if (entity == null || !StringUtils.hasText(entity.getSecurityCode())) {
                log.info(String.format("id为%d的记录不存在", id));
            }
        } catch(EntityNotFoundException e) {
            return ResponseUtil.fail(String.format("id为%d的记录不存在", id));
        }
        return ResponseUtil.success(entity);
    }
 
}

在Controller类中引入Service,访问Service的提供的接口,实现对数据库的操作。其他接口的访问也是使用类似的方法,此处就不在贴代码了。

说明:@Validated、@NotNull、@Min为参数校验,详见:Spring validation参数校验基本使用_spring validate 参数-CSDN博客

Repository方法命名规则

规则:findBy(关键字)+ 属性名称(属性名称的首字母大写)+ 查询条件(首字母大写)

方法名词命名规范表

 

posted @ 2024-09-24 17:42  苹果芒  阅读(88)  评论(0编辑  收藏  举报