JPA入门

简介:

  • JPA的英文全称是“Java Persistence API”,表示Java持久层API,是Java持久化规范。

  • 它为Java开发人员提供了一种对象、关联映射工具来管理Java应用中的关系数据。

  • 具有标准化、简单易用、集成方便等优势。

作用:

它的作用是在关系型数据库和对象之间形成一个映射,这样,我们在具体的操作数据库的时候,就不需要再去和复杂的SQL语句打交道,只要像平时操作对象一样操作它就可以了。

Mybatis使用带来的问题:

通过直接编写对应的SQL语句来实现数据访问,我们发现实际上我们在Java中大部分操作数据库的情况都是读取数据并封装为一个实体类,因此,为什么不直接将实体类直接对应到一个数据库表呢?

也就是说,一张表里面有什么属性,那么我们的对象就有什么属性,所有属性跟数据库里面的字段一一对应,而读取数据时,只需要读取一行的数据并封装为我们定义好的实体类既可以,而具体的SQL语句执行,完全可以交给框架根据我们定义的映射关系去生成,不再由我们去编写,因为这些SQL实际上都是千篇一律的。

基本使用步骤:

  1. 配置数据源

    spring.datasource.url=jdbc:mysql://localhost:3306/demo?useUnicode=true&useSSL=false&serverTimezone=UTC
    spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
    spring.datasource.username=root
    spring.datasource.password=Admin@123
  2. 导入依赖

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

  3. 创建实体类

    package uyun.demo.pojo;
    import lombok.Data;
    import javax.persistence.*;
    ​
    @Data
    @Entity   //表示这个类是一个实体类
    @Table(name = "students")    //对应的数据库中表名称
    public class Account {
    ​
        @GeneratedValue(strategy = GenerationType.IDENTITY)   //生成策略,这里配置为自增
        @Column(name = "id")    //对应表中id这一列
        @Id     //此属性为主键
        Integer id;
    ​
        @Column(name = "stu_name")   //对应表中username这一列
        String StuName;
    ​
        @Column(name = "password")   //对应表中password这一列
        String password;
    }

    这里我们还需要设置自动表定义ddl-auto

    #开启SQL语句执行日志信息
    spring.jpa.show-sql=true
    #配置为自动创建
    spring.jpa.hibernate.ddl-auto=create

    ddl-auto是设置自动表定义,可以实现自动在数据库中为我们创建一个表,表的结构会根据我们定义的实体类决定

    • create :启动时删数据库中的表,然后创建,退出时不删除数据表

    • create-drop:启动时删数据库中的表,然后创建,退出时删除数据表 如果表不存在报错

    • update :如果启动时表格式不一致则更新表,原有数据保留

    • validate :项目启动表结构进行校验 如果不一致则报错

  4. 创建好实体类之后,只需要启动一个测试类,即启动Spring容器,则会自动在数据库中创建表

  5. 访问创建的表(CRUD)

    使用接口自带的方法

    • 自定义接口继承JpaRepository类,JpaRepository有两个泛型:

      • 具体操作的对象实体,也就是对应的表

      • ID的类型)

      package uyun.demo.service;
      import org.springframework.data.jpa.repository.JpaRepository;
      import org.springframework.stereotype.Repository;
      import uyun.demo.pojo.Student;
      ​
      /**
       * JpaRepository有两个泛型
       * 1)具体操作的对象实体,也就是对应的表
       * 2)ID的类型
       */
      @Repository
      public interface AccountRepository extends JpaRepository<Student,Integer> {
      ​
      }

    • 在测试类中自动注入自定义的接口,并直接使用封装好的方法

      @SpringBootTest
      public class JPATests {
          @Autowired
          AccountRepository repository;
          //根据id查找
          @Test
          public void findById(){
              Optional<Student> byId = repository.findById(1);
              System.out.println(byId);
          }
          //新增操作
          @Test
          public void save(){
              Student student = new Student();
              student.setStuName("Auto-add");
              student.setPassword("admin");
              student = repository.save(student);
              System.out.println("自动生产的主键为:"+student.getId());
          }
          //根据Id删除
          @Test
          public void deleteById(){
              repository.deleteById(7);
          }
          //分页操作
          @Test
          public void pageStudent(){
              Page<Student> all = repository.findAll(PageRequest.of(0, 2));
              for (Student student : all) {
                  System.out.println(student);
              }
              System.out.println(all);
          }
      }

  6. 使用方法名称拼接自定义SQL语句

    这个是重点,根据表的定义,自定义方法来进行查询

    • 在接口中按照表关键字进行函数名的拼接,框架会自动的生成对应的SQL

    • 拼接不需要强制记忆,IDEA有提示,具体规则请看:JPA方法命名规则

      注意:拼接的时候,By后面跟的一定是要数据库字段对应的,例如:ByName,那么数据库中一定要有一个字段是name

      @Repository
      public interface AccountRepository extends JpaRepository<Student,Integer> {
          //根据Name模糊查询
          List<Student> findAllByNameLike(String str);
          
          //根据Id和name查询
          Student findByIdAndName(Integer id,String name);
      ​
          //根据id判断Student是否存在
          boolean existsStudentsById(Integer id);
      }
      ​
          @Test
          public void like(){
              for (Student student : repository.findAllByNameLike("%a%")) {
                  System.out.println(student);
              }
          }
          @Test
          public void findByIdAndName(){
              System.out.println(repository.findByIdAndName(1, "test"));
          }
          @Test
          public void existsStudentsById(){
              System.out.println(repository.existsStudentsById(2));
          }

  7. 不使用方法名称拼接(使用SQL自由度更高)

    这里有两种SQL,一种是自定义的,就是对实体类进行操作然后框架自动生成对应的原生SQL,并且IDEA有提示,只需要SQL语句中的字段名字跟类名对应就行,不需要跟数据库中的表名对应。另外一种是原生SQL,就是直接对数据库中表对应,SQL语句中的字段名必须要跟数据库中的字段名对应

    • 使用自定义SQL,对实体类操作

      自定义SQL语句必须在事务环境下运行 必须有DML支持(Modifying)

      //?2表示下面的形参的第二个位置,?1代表第一个形参的位置。这里不对表进行操作,直接对实体类进行操作,然后实体类映射到表中
          @Transactional
          @Modifying
          @Query("update Student set password = ?2 where id = ?1")
          int updatePasswordById(int id,String newPassword);
      ​
          @Test
          public void updatePasswordById() {
              repository.updatePasswordById(1,"wangpeng");
          }

    • 使用原生SQL,对表进行操作

      nativeQuery = true:开启原生SQL

          
      @Transactional
          @Modifying
          @Query(value = "update students set password = ?2 where name = ?1",nativeQuery = true)
          int updatePasswordByName(String name,String newPassword);
      ​
          @Test
          public void updatePasswordByName() {
              repository.updatePasswordByName("one", "testTest");
          }

  8. 关联查询(一对一)

    我们会在表中添加一个外键字段,而此外键字段又指向了另一个表中的数据,当我们查询数据时,可能会需要将关联数据也一并获取

    • 首先定义一个实体,用来表示被关联的表

      这里设计的是一个学生详细信息表

      @Data
      @Entity
      @Table(name = "student_detail")
      public class StudentDetail {
      ​
          @Id
          @GeneratedValue(strategy = GenerationType.IDENTITY)
          @Column(name = "id")
          Integer id;
      ​
          @Column(name = "address")
          String address;
          @Column(name = "email")
          String email;
          @Column(name = "phone")
          String phone;
          @Column(name = "real_name")
          String realName;
      }

    • 然后在Student实体中,新增加一个属性(StudentDetail)表示和另外一个表关联,需要在这个属性上面添加两个注解:

      @JoinColumn:设置新增的外键名字,JPA自动关联另外一个实体的主键

      • 参数name:设置自己实体类对应数据库的表用来存外键的列名

      @OneToOne:声明为一对一的关联

      • 参数fetch:设置加载形式,这里设置为懒加载完成想查什么就查什么功能

      • 参数cascade:置关联级别完成同时操作两张表。这里的关联级别也是有多个,一般设置为all就行

        • ALL:所有操作都进行关联操作

        • PERSIST:插入操作时才进行关联操作

        • REMOVE:删除操作时才进行关联操作

        • MERGE:修改操作时才进行关联操作

         
       /*这段代码是增加在Student这个实体类的*/
      ​
          //
          @JoinColumn(name = "detail_id")
          @OneToOne(fetch = FetchType.LAZY,cascade = CascadeType.ALL)
          StudentDetail detail;

    • 测试:直接调用Student实体的方法,就能自动关联StudentDetail实体。

      懒加载属性需要在事务环境下获取,因为repository方法调用完后Session会立即关闭。

      //根据id查询,返回结果会自带detail中的内容
          @Transactional
          @Test
          void pageAccount() {
              Optional<Student> student = repository.findById(1);
              System.out.println(student.get());
          }
      ​
      //添加
          @Test
          public void add(){
              Student student = new Student();
              student.setName("dyt");
              student.setPassword("123456");
      ​
              StudentDetail detail = new StudentDetail();
              detail.setAddress("重庆市万州区");
              detail.setEmail("dyt@qq.com");
              detail.setPhone("173");
              detail.setRealName("邓玉婷");
              student.setDetail(detail);
              repository.save(student);
      ​
          }
      ​
      //删除
          @Test//同时删除(建立在cascade = CascadeType.ALL的基础上)
          void delete(){
              repository.deleteById(4);
          }

  9. 关联查询(一对多)

    • 这里新建一个成绩实体(score)一个学生对应多个成绩,成绩表又一对一对应课程表

      @Data
      @Entity
      @Table(name = "student_score")
      public class Score {
      ​
          @Id
          @GeneratedValue(strategy = GenerationType.IDENTITY)
          @Column(name = "id")
          Integer id;
      ​
          @OneToOne//成绩表一对一到课程表
          @JoinColumn(name = "cid")
          Course course;
      ​
          @Column(name = "score")
          Double score;
      ​
          //学生id,被用来对应的列
          @Column(name = "sid")
          Integer sid;
      }

    • 然后在Student实体中,新增加一个List列表的属性(scores)表示关联属性表的多条数据;

          @OneToMany(fetch = FetchType.EAGER,cascade = CascadeType.REMOVE)
          @JoinColumn(name = "sid")//这里的sid是指student_score里的sid字段
          List<Score> scores;//成绩有多个,用列表存储

    • 测试

    • @Test
          void selectScore() {
              repository.findById(1).ifPresent(student -> student.getScores().forEach(score -> System.out.println(score)));
          }

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值