Spring data jpa 学习记录 CRUD 条件 分页 一对多 多对多

在这里插入图片描述

配置文件

maven 的配置


  <dependencies>
        <!-- 加密解密-->
        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.13</version>
        </dependency>

        <!--junit 插件-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <!--swagger 插件-->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>2.4.0</version>
        </dependency>
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>2.4.0</version>
        </dependency>
        <!--spring web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!--spring data jpa -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <!--mysql  -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <!-- devtools -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

spring boot 的配置文件

server:
  port: 8001

spring:
  datasource:
    url: jdbc:mysql:// ip /jpa?useUnicode=true&characterEncoding=utf-8&zeroDateTimeBehavior=convertToNull
    username: root
    password: 123456
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    # 打印sql
    show-sql: true
    properties:
      hibernate:
        # hibernate 的配置格式化 sql
        format_sql: true
        # 打印 sql 语句
        show_sql: true
        # 懒加载
        enable_lazy_load_no_trans: true
        # 时间设置
        jdbc:
          time-zone: GMT+8
    # 对数据库表的配置
    # create 启动时删数据库中的表,然后创建,退出时不删除数据表
    # create-drop 启动时删数据库中的表,然后创建,退出时删除数据表 如果表不存在报错
    # update 如果启动时表格式不一致则更新表,原有数据保留
    # validate 项目启动表结构进行校验 如果不一致则报错
    hibernate:
      ddl-auto: none
  # 允许重复的 bean 覆盖
  main:
    allow-bean-definition-overriding: true

  # jack son 配置
  jackson:
    date-format: yyyy-MM-dd HH:mm:ss
    time-zone: GMT+8




swagger 的配置

@Configuration
@Component
public class SwaggerConfig {

    @Bean
    public Docket api() {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(this.apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("cn.dianhun.om.jpa"))
                .paths(PathSelectors.any())
                .build();
    }


    private ApiInfo apiInfo() {
        @SuppressWarnings("deprecation")
        ApiInfo info = new ApiInfo(
                "Test Spring data jpa  Swagger",
                "study spring data jpa",
                "1.0",
                "study spring data jpa",
                "study spring data jpa",
                "Test Spring data jpa  Swagger",
                "Test Spring data jpa  Swagger");
        return info;
    }
}

项目结构

在这里插入图片描述

普通 crud

spring data jpa 是基于hiberate 的一个 全自动的orm框架, 他能够实现不用编写sql就对数据库实现基本的增删改功能, 其根本的缘由是jpa 为我们提供了很多的 Repository 我们在实现具体的业务时候不需要考虑底层怎么实现,是需要编写我们自己的数据库服务类直接继承 Repository 即可

Repository

要想使用spring data jpa 做出我们的查询。 首先需要 编写我们的 PersonRepository 来继承 JpaRepository , 注意 JpaRepository 需要添加两个泛型的参数, 一个是绑定的实体类, 一个是实体类对应的主键, 这里的实体类就是一个和数据库表对应的类, 它通过注解来和数据库表做到绑定! JpaRepository 里面封装了很多常用的方法。

public interface PersonRepository extends JpaRepository<PersonEntity, Integer> {
    /**
     * @param specification
     * @param page
     * @return
     */
    Page<PersonEntity> findAll(Specification<PersonEntity> specification, Pageable page);
}

PersonEntity

  • @Entity 这个注解就代表的是与我们数据库表所绑定的实体类
  • @Table(name = “person”, schema = “op_upms”, catalog = “”) 这个注解代表了和数据库中的那个表做到的绑定,
  • @Id 数据库中的主键
  • @GeneratedValue(strategy = GenerationType.IDENTITY) 主键的生成策略
  • @Column(name = “id”) 属性和表字段的映射
@Entity
@Table(name = "person", schema = "op_upms", catalog = "")
public class PersonEntity {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id")
    private int id;
    private String name;
    private String address;

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        PersonEntity that = (PersonEntity) o;
        return id == that.id &&
                Objects.equals(name, that.name) &&
                Objects.equals(address, that.address);

    }
    @Override
    public int hashCode() {
        return Objects.hash(id, name, address);
    }
}

CRUD

1插入数据库

    @Autowired
    private PersonRepository personRepository;

    @PostMapping("/insert")
    public void insert() {
        for (int i = 0; i < 30; i++) {
            PersonEntity person = new PersonEntity();
            person.setName("name" + UUID.randomUUID().toString().substring(0, 2)).setId(i).setAddress("address" + UUID.randomUUID().toString().substring(0, 2));
            personRepository.save(person);
        }
    }

2 查询数据库

    /**
     * 安装 id 查询
     * @param id
     * @return
     */
    @GetMapping("/getById/{id}")
    public ResultVO getById(@PathVariable("id") Integer id) {
        Optional<PersonEntity> byId = personRepository.findById(id);
        PersonEntity entity = byId.get();
        return ResultVOUtil.successWith(entity);
    }

3删除

    /**
     * 删除
     *
     * @param id
     * @return
     */
    @DeleteMapping("/delete/{id}")
    public ResultVO delete(@PathVariable("id") Integer id) {
        personRepository.deleteById(id);
        return ResultVOUtil.success();
    }

4更新

    /**
     * 更新
     * @param personEntity
     * @return
     */
    @PostMapping("/update")
    public ResultVO update(@RequestBody PersonEntity personEntity) {
        Optional<PersonEntity> byId = personRepository.findById(personEntity.getId());
        BeanUtils.copyProperties(personEntity, byId.get());
        personRepository.save(byId.get());
        return ResultVOUtil.success();
    }

条件查询、分页查询

分页查询

    /**
     * 分页查询
     * @param page
     * @param size
     * @return
     */
    @GetMapping("/query/{page}/{size}")
    public ResultVO query(@PathVariable("page") Integer page, @PathVariable("size") Integer size) {
        Page<PersonEntity> repositoryAll = personRepository.findAll(getPage(page, size));
        return ResultVOUtil.successWith(repositoryAll);
    }
    static Pageable getPage(Integer page, Integer size) {
        Pageable of = PageRequest.of(
                page - 1, size,
                Sort.by(Sort.Direction.ASC, "id")
        );
        return of;
    }

带有条件的分页

    @PostMapping("/queryVo/{page}/{size}")
    public ResultVO queryVo(@PathVariable("page") Integer page, @PathVariable("size") Integer size, @RequestBody PersonEntity person) {
        log.info("search info:  " + person.toString());

        Specification<PersonEntity> specification = (Specification<PersonEntity>) (root, query, builder) -> {
            List<Predicate> predicateList = new ArrayList<>();
            if (!StringUtils.isEmpty(person.getName())) {
                predicateList.add(builder.like(root.get("name"),
                        "%" + StringUtils.trimWhitespace(person.getName()) + "%"));
            }
            if (!StringUtils.isEmpty(person.getAddress())) {
                predicateList.add(builder.like(root.get("address"),
                        "%" + StringUtils.trimWhitespace(person.getAddress()) + "%"));
            }
            return builder.and(predicateList.toArray(new Predicate[0]));
        };

        Page<PersonEntity> repositoryAll = personRepository.findAll(specification, getPage(page, size));
        return ResultVOUtil.successWith(repositoryAll);
    }

Repository使用的规范

jpa 回根据我们的方法名来解析成相应的sql ,在实际的解析过程中会遵循一定的规范, 只用按照这样的规范才能够发货出jpa动态Sql的功能。

  • findBy
  • findAll
  • And
    在这里插入图片描述

一对多

数据库中的外键设计

一对多的例子中,模拟的关系为: 一个老师可以拥有多个学生、一个学生只有一个老师。主外键关系: 学生表中包含外键 老师id, 老师id在老师表中是主键。
在这里插入图片描述

实体类中配置主外键关系

学生的实体中应该是包含一个老师的, 并且添加上 @ManyToOne 表面一个多对一的关系, 然后在通过
@JoinColumn(name = “teacher_id”, referencedColumnName = “teacher_id”) 注解

  • name 代表的是外键的名字
  • referencedColumnName 代表的是参看栏目的名字
    @Id
    @Column(name = "stu_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int stuId;
    private String stuName;
    private String stuClass;

    @JoinColumn(name = "teacher_id", referencedColumnName = "teacher_id")
    @ManyToOne
    private TeacherEntity teacherEntity;

老师实体中, 一个老师拥有多个学生, 这就要在实体类中配置一个学生的集合, 并且通过添加注解的方式来实现标识

  • @JoinColumn(name = “teacher_id”, referencedColumnName = “teacher_id”)

  • @OneToMany(targetEntity = StuEntity.class)

targetEntity 代表的是一个目标实体。

    @Id
    @Column(name = "teacher_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int teacherId;
    private String teacherName;
    private String teacherCourse;

    /**
     * 一个老师对应多个学生,
     * 一对多关系, 此时老师的主键是学生的外键
     * 1、配置一对多关系
     * 2、配置外键
     */
/*    @JoinColumn(name = "teacher_id", referencedColumnName = "teacher_id")
    @OneToMany(targetEntity = StuEntity.class)
    private Set<StuEntity> stuEntities = new HashSet<>();*/

测试

主要的测试就是新建实体类, 然后为实体类添加上关系, 最后开始保存。 可以看到我们在保存学生的时候会直接保存上老师的id。

    @ApiOperation("测试一对多")
    @PostMapping("/test")
    public R testOneToMany() {

        StuEntity stuEntity = new StuEntity();
        stuEntity.setStuName("小崔");
        stuEntity.setStuClass("一班");

        StuEntity stuEntity2 = new StuEntity();
        stuEntity2.setStuName("小崔");
        stuEntity2.setStuClass("二班");

        TeacherEntity teacherEntity = new TeacherEntity();
        teacherEntity.setTeacherName("王老师");
        teacherEntity.setTeacherCourse("高数");

        // 设置关系
        // teacherEntity.getStuEntities().add(stuEntity);

        // 设置关系
        stuEntity.setTeacherEntity(teacherEntity);
        stuEntity2.setTeacherEntity(teacherEntity);

        teacherRepository.save(teacherEntity);
        stuRepository.save(stuEntity);
        stuRepository.save(stuEntity2);
        return R.ok();
    }

在这里插入图片描述

在这里插入图片描述

测试删除老师, 回自动删除下面的所有学生


    @ApiOperation("级联删除")
    @PostMapping("/test4")
    public R testOneToMany4() {
        // 1、查询数据
        Optional<TeacherEntity> byId = teacherRepository.findById(15);
        TeacherEntity teacherEntity = byId.get();
        //2 、删除数据
        teacherRepository.delete(teacherEntity);
        return R.ok();
    }

观察打印出来的sql

    select
        stuentitie0_.teacher_id as teacher_4_0_0_,
        stuentitie0_.stu_id as stu_id1_0_0_,
        stuentitie0_.stu_id as stu_id1_0_1_,
        stuentitie0_.stu_class as stu_clas2_0_1_,
        stuentitie0_.stu_name as stu_name3_0_1_,
        stuentitie0_.teacher_id as teacher_4_0_1_ 
    from
        stu stuentitie0_ 
    where
        stuentitie0_.teacher_id=?

补充

对于一对多, 多对一的操作, 在实体类关系的设置上:无论是在一的一放设置、还是在多的一方都不会妨碍最后的结果。 并且还可以设置级联操作,举例: 删除老师, 就会把老师下面的所有学生给全部删除。
更多的配置:

    /**
     * 一个老师对应多个学生,
     * 一对多关系, 此时老师的主键是学生的外键
     * 1、配置一对多关系
     * 2、配置外键
     */
/*    @JoinColumn(name = "teacher_id", referencedColumnName = "teacher_id")
    @OneToMany(targetEntity = StuEntity.class)
    private Set<StuEntity> stuEntities = new HashSet<>();*/
    /*
    放弃外键维护
    mappedBy: 对方的属性名称
     */
    //配置级联操作
    @OneToMany(mappedBy = "teacherEntity", cascade = CascadeType.ALL)
    // @OneToMany(mappedBy = "teacherEntity")
    private Set<StuEntity> stuEntities = new HashSet<>();

多对多

实体类设置

在多对多的测试中, 考虑的是一个用户拥有多个角色、一个角色拥有多个用户,并且做了一个用户

角色

    @Id
    @Column(name = "role_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long roleId;
    private String roleName;
    private String remark;
    private Long createUserId;
    private Timestamp createTime;


    /**
     * 中间表名
     * <p>
     * 当前对象在中间表的外键
     * 对方在中间表的外键
     */
     @JoinTable(name = "sys_user_role",
            joinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "role_id")},
            inverseJoinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "user_id")})
     // @ManyToMany(targetEntity = SysUserEntity.class, cascade = CascadeType.ALL)
     @ManyToMany(mappedBy = "roleEntities", cascade = CascadeType.ALL)
    private Set<SysUserEntity> userEntitySet = new HashSet<>();

用户

    @Id
    @Column(name = "user_id")
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long userId;
    private String username;
    private String password;
    private String salt;
    private String email;
    private String mobile;
    private Byte status;
    private Long createUserId;

    // 自动填充时间
    @CreatedDate
    private Timestamp createTime;

    /**
     * 中间表名
     * <p>
     * 当前对象在中间表的外键
     * 对方在中间表的外键
     */
    @JoinTable(name = "sys_user_role",
            joinColumns = {@JoinColumn(name = "user_id", referencedColumnName = "user_id")},
            inverseJoinColumns = {@JoinColumn(name = "role_id", referencedColumnName = "role_id")})
    @ManyToMany(targetEntity = SysRoleEntity.class, cascade = CascadeType.ALL)
    private Set<SysRoleEntity> roleEntities = new HashSet<>();


中间表

    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Id
    @Column(name = "id")
    private long id;
    private Long roleId;
    private Long userId;

测试

    @GetMapping("/manyTomany")
    public R manyTomany() {
        SysUserEntity userEntity = new SysUserEntity();
        userEntity.setUsername("测试2").setEmail("1721310909@qq.com");

        SysUserEntity userEntity2 = new SysUserEntity();
        userEntity2.setUsername("测试3").setEmail("1721310909@qq.com");


        SysRoleEntity roleEntity2 = new SysRoleEntity();
        roleEntity2.setRoleName("管理1").setRemark("测试");

        SysRoleEntity roleEntity = new SysRoleEntity();
        roleEntity.setRoleName("管理2").setRemark("测试");
        // 设置关系
        userEntity.getRoleEntities().add(roleEntity);
        userEntity.getRoleEntities().add(roleEntity2);

        roleEntity.getUserEntitySet().add(userEntity2);
        roleEntity.getUserEntitySet().add(userEntity);

        userRepository.save(userEntity);
        roleRepository.save(roleEntity);

        return R.ok();
    }

测试级联操作

    @ApiOperation("级联删除操作")
    @GetMapping("/manyTomany4")
    public R manyTomany4() {
        //1、 查出 角色
        SysRoleEntity one = roleRepository.getOne(9L);
        //2、 删除角色
        roleRepository.delete(one);
        return R.ok();
    }

常用的注解

标注在类上

@DynamicInsert

再插入数据的时候动态生成的sql中只包含要插入字段的部分, 其他的内容不会一起产生。

@DynamicUpdate

再更新数据的时候动态生成的sql中只包含要更新字段的部分, 其他的内容不会一起产生。

标注在字段上面

Column

  /**
数据库表上的字段名字
     */
    String name() default "";

    /**
 标明这个是唯一标识
     */
    boolean unique() default false;

    /**
 标明这个是可以为空的
     */
    boolean nullable() default true;

    /**
 在动态的插入sql中会产生个片段
     */
    boolean insertable() default true;

    /**
 在动态的更新sql中会产生个片段
     */
    boolean updatable() default true;

    /**
     * (Optional) The SQL fragment that is used when 
     * generating the DDL for the column.
 创建表的时候会创建这个字段的sql
     */
    String columnDefinition() default "";

    /**
 
 包含列的表的名称。
     */
    String table() default "";

    /**
 这个字段的长度
     */
    int length() default 255;

    /**
 字段的精度
     */
    int precision() default 0;
 

标注在方法上面

@PrePersist

再插入实体之前可以做的一个回调事件, 比如可以添加创建时间、指定字段的默认值等。

@PreUpdate

再插入更新之前可以做的一个回调事件, 比如: 可以实现修改时间。

    @PrePersist
    public void prePersist() {
        this.createdAt = this.updatedAt = new Timestamp(System.currentTimeMillis());
    }

    @PreUpdate
    public void preUpdate() {
        this.updatedAt = new Timestamp(System.currentTimeMillis());
    }

@PrePersist- 在新实体持久化之前(添加到EntityManager)
@PostPersist- 在数据库中存储新实体(在commit或期间flush)
@PostLoad - 从数据库中检索实体后。
@PreUpdate- 当一个实体被识别为被修改时EntityManager
@PostUpdate- 更新数据库中的实体(在commit或期间flush)
@PreRemove - 在EntityManager中标记要删除的实体时
@PostRemove- 从数据库中删除实体(在commit或期间flush)

码云仓库

https://gitee.com/yu_tian_shui/spring-data-jpa-study

码云仓库

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值