【Web系列二十四】使用JPA简化持久层接口开发

目录

环境配置

1、引入依赖

配置文件

代码编写

实体类创建

JPA常用注解

Service与ServiceImpl

Service

ServiceImpl

Controller

Dao

三种实现Dao功能方式

1.继承接口,使用默认接口+实现

2.根据接口命名规则默认生成实现

3.自定义接口+实现(类似MyBatis)

多表关联

1.一对一关联

2.一对多、多对一

3.多对多

参考资料


Spring Data JPA

Spring Data JPA 是Spring提供的一套简化JPA开发的持久层框架,根据实体类自动生成表 (注意库仍旧自己创建),按照约定好的【方法命名规则】写dao层接口,就可以在不写接口实现的情况下,实现对数据库的访问和操作。同时提供了很多除了CRUD之外的功能,如分页、排序、复杂查询等等。

Spring Data JPA 可以理解为 JPA 规范的再次封装抽象,底层还是使用了 Hibernate 的 JPA 技术实现。

SpringBoot集成新框架环境往往很容易:引入依赖,编写配置、[启用]、代码编写。

环境配置

1、引入依赖

首先要引入jpa的依赖

XML 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

配置文件

javascript 复制代码
spring:
  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true
  • ddl-auto:自动创建表,有4个选项
    • create:每次启动将之前的表和数据都删除,然后重新根据实体建立表。
    • create-drop:比上面多了一个功能,就是在应用关闭的时候,会把表删除。
    • update:最常用的,第一次启动根据实体建立表结构,之后重启会根据实体的改变更新表结构,不删除表和数据。
    • validate:验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值,运行程序会校验实体字段与数据库已有的表的字段类型是否相同,不同会报错
  • show-sql:指运行时,是否在控制台展示sql

代码编写

和mybatis主要的区别在于JPA可以根据实体类自动创建表,并且会提供默认的DAO方法。

实体类创建

创建一个models文件夹,并新建文件algo.java

java 复制代码
package com.xxx.xxx.xxx.models;

import lombok.Data;
import org.springframework.data.annotation.CreatedDate;

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

@Entity
@Table(name = "algo")
@Data
public class Algo {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private Integer id;

    @Column(name = "name", length = 200)
    private String name;

    @CreatedDate     
    @Column(name = "create_time", updatable = false, nullable = false)
    private Date createTime;

}

JPA常用注解

注解 作用
@Entity 声明类为实体或表.
@Table 声明表名。
@Basic 指定非约束明确的各个字段。
@Embedded 指定类或它的值是一个可嵌入的类的实例的实体的属性。
@Id 指定的类的属性,用于识别(一个表中的主键)。
@GeneratedValue 指定如何标识属性可以被初始化,参数strategy有以下选项: TABLE:使用一个特定的数据库表格存放主键。 SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。(Oracle) IDENTITY:主键有数据库自动生成(主要是自动增长类型)。(MySQL) AUTO:主键由程序控制。(默认)
@Transient 指定该属性为不持久属性,即:该值永远不会存储在数据库中。
@AccessType 这种类型的注释用于设置访问类型。如果设置@AccessType(FIELD),则可以直接访问变量并且不需要getter和setter,但必须为public。如果设置@AccessType(PROPERTY),通过getter和setter方法访问Entity的变量。
@JoinColumn 指定一个实体组织或实体的集合。这是用在多对一和一对多关联。
@UniqueConstraint 指定的字段和用于主要或辅助表的唯一约束。
@ColumnResult 参考使用select子句的SQL查询中的列名。
@ManyToMany 定义了连接表之间的多对多一对多的关系。
@ManyToOne 定义了连接表之间的多对一的关系。
@OneToMany 定义了连接表之间存在一个一对多的关系。
@OneToOne 定义了连接表之间有一个一对一的关系。
@NamedQueries 指定命名查询的列表。
@NamedQuery 指定使用静态名称的查询。

Service与ServiceImpl

Service

java 复制代码
public interface AlgoService {
    //查询全部
    List<Algo> findAlgoList();
    //查询一条
    User findAlgoById(int id);
    //添加
    void insertAlgo(Algo algo);
    //删除
    void deleteAlgo(int id);
    //修改
    void updateAlgo(Algo algo);
}

ServiceImpl

查询一条数据时没有直接使用User而是使用Optional< User >,这是由于Dao层直接使用了默认的方法。Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。

修改和添加都是save方法,修改时对象id有值,添加时id无值。

java 复制代码
@Service
public class AlgoServiceImpl implements AlgoService {

    @Autowired
    private AlgoRepository algoRepository;

    @Override
    public List<Algo> findAlgoList() {
        return algoRepository.findAll();
    }

    @Override
    public AlgofindAlgoById(int id) {
        //Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用get()方法会返回该对象。
        Optional<Algo> ao = algoMapper.findById(id);
        return ao.orElse(null);
    }

    @Override
    public void insertAlgo(Algo algo) {
        algoRepository.save(algo);
    }

    @Override
    public void deleteAlgo(int id) {
        algoRepository.deleteById(id);
    }

    @Override
    public void updateAlgo(Algo algo) {
        Algo emp = algoRepository.findById(algo.getId()).orElse(null);
        assert emp != null;
        BeanUtils.copyProperties(algo,emp); //属性值拷贝
        algoRepository.save(emp);
    }
}

Controller

java 复制代码
@RestController
public class JpaController {

    @Autowired
    private AlgoService algoService;

    @PostMapping("/add")
    public Map<String,String> addAlgo(){
        Algo algo = new Algo();
        algo.setName("张三");
        algo.setPassword("123456");
        algo.setSex("男");
        algoService.insertAlgo(algo);
        Map<String,String> map = new HashMap<>();
        map.put("msg","操作成功");
        return map;
    }
}

Dao

继承JpaRepository,它默认的提供了一些常见dao方法,主要是完成一些增删改查的操作。

java 复制代码
@Repository
public interface AlgoRepository extends JpaRepository<Algo, Integer> {
	//约束1为实体类类型、约束2为主键类型
}

三种实现Dao功能方式

1.继承接口,使用默认接口+实现

接口 作用
CrudRepository 提供默认增删改查方法
PagingAndSortingRepository CRUD方法+分页、排序
JpaRepository 针对关系型数据库优化

2.根据接口命名规则默认生成实现

默认提供了常见方法,但仍可以根据命名规则自动生成方法。

此表内容来源于官网:

Spring Data JPA - Reference Documentation

关键词 示例 JPQL片段
Distinct findDistinctByLastnameAndFirstname select distinct ...​ where x.lastname = ?1 and x.firstname = ?2
And findByLastnameAndFirstname ... where x.lastname = ?1 and x.firstname = ?2
Or findByLastnameOrFirstname ... where x.lastname = ?1 or x.firstname = ?2
Is, Equals findByFirstname,findByFirstnameIs,findByFirstnameEquals ... where x.firstname = ?1
Between findByStartDateBetween ... where x.startDate between ?1 and ?2
LessThan findByAgeLessThan ... where x.age < ?1
LessThanEqual findByAgeLessThanEqual ... where x.age <= ?1
GreaterThan findByAgeGreaterThan ... where x.age > ?1
GreaterThanEqual findByAgeGreaterThanEqual ... where x.age >= ?1
After findByStartDateAfter ... where x.startDate > ?1
Before findByStartDateBefore ... where x.startDate < ?1
IsNull, Null findByAge(Is)Null ... where x.age is null
IsNotNull, NotNull findByAge(Is)NotNull ... where x.age not null
Like findByFirstnameLike ... where x.firstname like ?1
NotLike findByFirstnameNotLike ... where x.firstname not like ?1
StartingWith findByFirstnameStartingWith ... where x.firstname like ?1
EndingWith findByFirstnameEndingWith ... where x.firstname like ?1
Containing Containing ... where x.firstname like ?1
OrderBy findByAgeOrderByLastnameDesc ... where x.age = ?1 order by x.lastname desc
Not findByLastnameNot ... where x.lastname <> ?1
In findByAgeIn(Collection ages) ... where x.age in ?1
NotIn findByAgeNotIn(Collection ages) ... where x.age not in ?1
True findByActiveTrue() ... where x.active = true
False findByActiveFalse() ... where x.active = false
IgnoreCase findByFirstnameIgnoreCase ... where UPPER(x.firstname) = UPPER(?1)

3.自定义接口+实现(类似MyBatis)

使用注解方式 @Query

使用 @Query()注解来生成sql语句,注意此注解默认value属性值与myBatis有点不同,它使用的是JPQL。

如果想使value值为原生SQL,则添加属性:nativeQuery = true 即可。

表名映射: 可以直接使用表对应类名,如果想用表名:#{#entityName}
参数映射:?n表示第n个参数、:参数名(参数可用@Param指定)
#{#entityName}:SPEL表达式,实体类使用了@Entity后,它的值为实体类名,如果@Entity的name属性有值,则它的值为该name值。

@Modifying:标记仅映射参数的方法。

@Transactional:开启事务,并将只读改为非只读。

java 复制代码
@Repository
//约束1为实体类、约束2为主键
public interface AlgoRepository extends JpaRepository<Algo,Integer> {

    //添加:使用了原生sql
    @Transactional//开启事务为非只读
    @Modifying
    @Query(value = "insert into jpa_test(name, userId) values(:#{#algo.name}, :#{#algo.userId}) ", nativeQuery = true)
    void addAlgo(@Param("algo") Algo algo);

    //删除
    @Transactional(timeout = 10)
    @Modifying
    @Query("delete from Algo where id=:id")
    void deleteAlgoById(@Param("id") Integer id);

    //修改
    @Transactional(timeout = 10)
    @Modifying
    @Query("update Algo u set u.name=:#{#algo.name}, u.createTime=:#{#algo.createTime}, u.userId=:#{#algo.userId} where u.id=:#{#algo.id}")
    void updateAlgo (@Param("algo")Algo algo);

    //查询一条
    @Query("select u from Algo u where u.id=?1 ")
    User findAlgoById(Integer id);

    //查询全部
    @Query("select u from Algo u")
    List<Algo> findAllAlgo();
}

对象属性的绑定:使用 @Param(映射名) 注解 + :#{#映射名.属性}

多表关联

JPA中一般只需要创建关联性即可,默认方法会自动关联查询。

1.一对一关联

两张表a、b,a的每条对应着b最多一条数据。

jpa实现如下:

java 复制代码
/**
 * 表A
 */
@Entity
@Table(name = "a")
public class A{

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	
	private String name;
	
	@OneToOne(cascade = {CascadeType.ALL})//一对一关系,级联删除
	@JoinColumn(name="b",referencedColumnName = "id")//关联 b的id字段
	private B b;
}

/**
 * 表B
 */
@Entity
@Table(name = "b")
public class B{

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

上面是A级联B,即可以通过A查到B,如果想通过B查到A则需要为B添加级联属性。

2.一对多、多对一

一对多: 两张表A、B,A的一条记录对应B的多条记录,B每条只能对应1个A。A对B的关系为一对多;B对 A的关系为多对一。

jpa实现如下:

java 复制代码
/**
 * 球员表
 */
@Entity//球员表
@Table(name = "sportmans")
public class SportMan implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    
    private String sportManName;
    
    @ManyToOne(cascade = {CascadeType.MERGE,CascadeType.PERSIST})
    @JoinColumn(name="duty")  //库中添加的外键字段
    private Duty duty;

}

/**
 * 位置表
 */
@Data
@Entity
@Table(name = "dutys")
public class Duty implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;
    
    private String dutyName;

    @JsonIgnore//不反向查询
    //级联保存、更新、删除,删除时会删除所有球员
    @OneToMany(mappedBy = "duty",cascade = CascadeType.ALL,fetch = FetchType.LAZY)
    private List<SportMan> sportManList;

}

不使用@JsonIgnore注解时,查询球员,球员里关联出位置,位置反向关联球员,会无限递归查询,因此添加此注解,防止此字段被查出来时自动回查。

3.多对多

两张表A、B,一条A记录对应多条B,一条B记录对应多条A。

jpa实现如下:

java 复制代码
/**
 * 表A
 */
@Entity
@Table(name = "a")
public class A{

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	
	private String name;
	
	@ManyToMany(cascade = {CascadeType.ALL})
	@JoinColumn(name="b",referencedColumnName = "id")//关联 b的id字段
	private B b;

}

/**
 * 表B
 */
@Entity
@Table(name = "b")
public class B{

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

	@ManyToMany(cascade = {CascadeType.ALL})
	@JoinColumn(name="a",referencedColumnName = "id")//关联 a的id字段
	private A a;

}

参考资料

SpringBoot 一文搞懂Spring JPA_springboot jpa_马踏飞燕&lin_li的博客-CSDN博客

使用springJpa创建数据库表_jpa可以动态创建数据库表吗_阿圣同学的博客-CSDN博客

【Spring JPA总结】@GeneratedValue注解介绍 - 简书 (jianshu.com)

spring boot 中使用 jpa(详细操作)_springboot jpa_熬菜的博客-CSDN博客

Spring Boot+JPA_springboot+jpa_火恐龙的博客-CSDN博客

相关推荐
芊寻(嵌入式)16 分钟前
C转C++学习笔记--基础知识摘录总结
开发语言·c++·笔记·学习
WaaTong18 分钟前
《重学Java设计模式》之 原型模式
java·设计模式·原型模式
m0_7430484418 分钟前
初识Java EE和Spring Boot
java·java-ee
AskHarries20 分钟前
Java字节码增强库ByteBuddy
java·后端
一颗松鼠25 分钟前
JavaScript 闭包是什么?简单到看完就理解!
开发语言·前端·javascript·ecmascript
有梦想的咸鱼_27 分钟前
go实现并发安全hashtable 拉链法
开发语言·golang·哈希算法
海阔天空_201332 分钟前
Python pyautogui库:自动化操作的强大工具
运维·开发语言·python·青少年编程·自动化
佳佳_34 分钟前
Spring Boot 应用启动时打印配置类信息
spring boot·后端
天下皆白_唯我独黑40 分钟前
php 使用qrcode制作二维码图片
开发语言·php
小灰灰__40 分钟前
IDEA加载通义灵码插件及使用指南
java·ide·intellij-idea