美文网首页SpringBoot全家桶Spring Boot
SpringBoot 全家桶 | JPA实例详解

SpringBoot 全家桶 | JPA实例详解

作者: 码农StayUp | 来源:发表于2020-08-28 15:12 被阅读0次

    本文源码:Gitee·点这里

    参考

    Spring Data JPA

    引入包

    引入jpa的包,同时引入mysql包和test

    <dependencys>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
    </dependencys>
    

    配置文件

    application.yml

    spring:
      datasource:
        driver-class-name: com.mysql.cj.jdbc.Driver
        url: jdbc:mysql://localhost:3306/springboot-familay?charset=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
        username: root
        password: root
        hikari:
          maximum-pool-size: 32
          minimum-idle: 8
      jpa:
        show-sql: true
        hibernate:
          ddl-auto: update
    
    • spring.jpa.show-sql=true 配置是否打印执行的SQL语句
    • spring.jpa.hibernate.ddl-auto=update 配置程序启动时自动创建数据库的类型(注意:此配置很危险,禁止在生产中使用)
      • create 程序重启时先删除表,再创建表
      • create-drop 程序重启时与create一样;程序结束时删除表
      • update 程序启动时若没有表格会创建,表内有数据不会清空,只会更新
      • validate 程序重启时会校验实体类与数据库中的字段是否相同,不同则报错

    实体类

    代码省略了get/set方法

    @Entity
    @Table(name = "AUTH_USER")
    public class User {
        @Id
        @GeneratedValue(strategy = GenerationType.IDENTITY)
        private Long id;
    
        @Column(length = 32)
        private String name;
    
        @Column(length = 32, unique = true)
        private String account;
    
        @Column(length = 63)
        private String pwd;
    
        @Column
        private Date createTime;
    }
    
    • @Entity 声明此类为实体类
    • @Table 声明该实体类对应数据库中的表信息
      • name指定表名,若不指定使用类名
    • @Id 声明实体属性为表主键
    • @GeneratedValue 主键生成策略,通过strategy属性指定
      • TABLE 使用数据库表来模拟序列产生主键
      • SEQUENCE 根据底层数据库的序列来生成主键,通过@SequenceGenerator注解指定序列名,条件是数据库支持序列
      • IDENTITY 使用数据库ID自增主键,条件是数据库支持
      • AUTO 默认,JPA根据数据库类型自动选择适合的策略
    • @Column 声明实体属性的表字段定义
      • name 指定字段名,不指定则使用该实体属性名
      • unique 唯一索引,默认false
      • nullable 可为空,默认true
      • length 长度,默认255
      • precision 精度,默认0
      • columnDefinition 该字段的SQL片段,示例:@Column(name = "account",columnDefinition = "varchar(32) not null default'' unique")

    以上创建好后,程序启动后会自动在数据库中创建表信息

    持久层

    定义持久层接口

    JPA中实现持久层非常简单,创建一个接口,并继承 org.springframework.data.jpa.repository.JpaRepository<T, ID>T为实体类,ID为实体类主键类型;最后在接口上增加 @Repository 注解

    @Repository
    public interface UserDao extends JpaRepository<User, Long> {
    }
    

    JpaRepository继承了CrudRepository,提供了基本的CRUD功能

    public interface CrudRepository<T, ID extends Serializable>
      extends Repository<T, ID> {
    
      <S extends T> S save(S entity);      //(1)
    
      Optional<T> findById(ID primaryKey); //(2)
    
      Iterable<T> findAll();               //(3)
    
      long count();                        //(4)
    
      void delete(T entity);               //(5)
    
      boolean existsById(ID primaryKey);   //(6)
    
      // … more functionality omitted.
    }
    
    • (1) 保存给定的实体,若已存在则更新。
    • (2) 返回由给定 ID 标识的实体。
    • (3) 返回所有实体。
    • (4) 返回实体数。
    • (5) 删除给定的实体。
    • (6) 指示是否存在具有给定 ID 的实体。

    JpaRepository继承了PagingAndSortingRepository,提供分页和排序功能

    public interface PagingAndSortingRepository<T, ID extends Serializable>
      extends CrudRepository<T, ID> {
    
      Iterable<T> findAll(Sort sort);
    
      Page<T> findAll(Pageable pageable);
    }
    

    定义查询方法

    JPA可以通过方法名称派生查询

    • 该机制从方法中剥离前缀find…Byread…Byquery…Bycount…Byget…By,并解析剩余部分
    • 可以包含其他表达式,如Distinct
    • 可以使用AndOr进行串联

    官方示例:

    interface PersonRepository extends Repository<User, Long> {
    
      List<Person> findByEmailAddressAndLastname(EmailAddress emailAddress, String lastname);
    
      // Enables the distinct flag for the query
      List<Person> findDistinctPeopleByLastnameOrFirstname(String lastname, String firstname);
      List<Person> findPeopleDistinctByLastnameOrFirstname(String lastname, String firstname);
    
      // Enabling ignoring case for an individual property
      List<Person> findByLastnameIgnoreCase(String lastname);
      // Enabling ignoring case for all suitable properties
      List<Person> findByLastnameAndFirstnameAllIgnoreCase(String lastname, String firstname);
    
      // Enabling static ORDER BY for a query
      List<Person> findByLastnameOrderByFirstnameAsc(String lastname);
      List<Person> findByLastnameOrderByFirstnameDesc(String lastname);
    }
    

    方法名称中支持的关键字

    Keyword Sample JPQL snippet
    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 findByAgeIsNull … 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 findByFirstnameContaining … 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<Age> ages) … where x.age in ?1
    NotIn findByAgeNotIn(Collection<Age> ages) … where x.age not in ?1
    True findByActiveTrue() … where x.active = true
    False findByActiveFalse() … where x.active = false
    IgnoreCase findByFirstnameIgnoreCase … where UPPER(x.firstame) = UPPER(?1)

    查询返回类型

    Return type Description
    void 表示没有返回值。
    Primitives Java primitives.
    Wrapper types Java 包装器类型。
    T 唯一实体。期望查询方法最多返回一个结果。如果未找到结果,则返回null。一个以上的结果触发IncorrectResultSizeDataAccessException
    Iterator<T> 一个Iterator
    Collection<T> A Collection
    List<T> A List
    Optional<T> Java 8 或 Guava Optional。期望查询方法最多返回一个结果。如果未找到结果,则返回Optional.empty()Optional.absent()。多个结果触发IncorrectResultSizeDataAccessException
    Option<T> Scala 或 Javaslang Option类型。语义上与前面描述的 Java 8 的Optional相同。
    Stream<T> Java 8 Stream
    Future<T> A Future。期望使用@AsyncComments 方法,并且需要启用 Spring 的异步方法执行功能。
    CompletableFuture<T> Java 8 CompletableFuture。期望使用@AsyncComments 方法,并且需要启用 Spring 的异步方法执行功能。
    ListenableFuture A org.springframework.util.concurrent.ListenableFuture。期望使用@AsyncComments 方法,并且需要启用 Spring 的异步方法执行功能。
    Slice 一定大小的数据块,用于指示是否有更多可用数据。需要Pageable方法参数。
    Page<T> Slice以及其他信息,例如结果总数。需要Pageable方法参数。
    GeoResult<T> 具有附加信息(例如到参考位置的距离)的结果条目。
    GeoResults<T> GeoResult<T>列表以及其他信息,例如到参考位置的平均距离。
    GeoPage<T> PageGeoResult<T>,例如到参考位置的平均距离。
    Mono<T> 使用 Reactive 存储库的 Project Reactor Mono发出零或一个元素。期望查询方法最多返回一个结果。如果未找到结果,则返回Mono.empty()。多个结果触发IncorrectResultSizeDataAccessException
    Flux<T> Project Reactor Flux使用 Reactive 存储库发出零,一个或多个元素。返回Flux的查询也可以发出无限数量的元素。
    Single<T> 使用 Reactive 存储库发出单个元素的 RxJava Single。期望查询方法最多返回一个结果。如果未找到结果,则返回Mono.empty()。多个结果触发IncorrectResultSizeDataAccessException
    Maybe<T> 使用 Reactive 存储库的 RxJava Maybe发出零或一个元素。期望查询方法最多返回一个结果。如果未找到结果,则返回Mono.empty()。多个结果触发IncorrectResultSizeDataAccessException
    Flowable<T> RxJava Flowable使用 Reactive 存储库发出零个,一个或多个元素。返回Flowable的查询也可以发出无限数量的元素。

    异步查询结果

    我们在这里重点讲一下异步查询结果。
    JPA查询结果支持异步,这意味着该方法在调用时立即返回,而实际查询的执行已提交给Spring的TaskExecutor的任务中。
    官方示例:

    interface PersonRepository extends Repository<User, Long> {
        @Async
        Future<User> findByFirstname(String firstname);               //(1)
        
        @Async
        CompletableFuture<User> findOneByFirstname(String firstname); //(2)
        
        @Async
        ListenableFuture<User> findOneByLastname(String lastname);    //(3)
    }
    
    • (1) 使用java.util.concurrent.Future作为返回类型。
    • (2) 使用 Java 8 java.util.concurrent.CompletableFuture作为返回类型。
    • (3) 使用org.springframework.util.concurrent.ListenableFuture作为返回类型。

    使用SQL语句查询

    JPA自定义查询语句非常简单,使用@Query注解即可

    public interface UserDao extends JpaRepository<User, Long> {
    
        @Query("select u from User u where u.account = ?1")
        User selectByAccount(String account);
    
        @Transactional
        @Modifying
        @Query("update User u set u.pwd = ?2 where u.id = ?1")
        int updatePassword(Long id, String password);
    
    }
    
    • SQL语句中使用的是实体类名和实体属性,而不是表名和表字段
    • 如果使用update/delete,需要加上@Transactional@Modifying

    实体关系映射

    JPA 实体关系映射:@OneToOne 一对一关系、@OneToMany @ManyToOne 一对多和多对一关系、@ManyToMany 多对多关系。

    OneToOne

    一对一关系,即两个表中的数据是一一对应关系,这种主要应用到对象的扩展信息中。

    用户实体类:

    @Entity
    @Table(name = "AUTH_USER")
    public class User {
    
        /*
            其余代码省略
         */
    
        @OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER, mappedBy = "user")
        private UserDetail userDetail;
    }
    

    用户详情实体类:

    @Entity
    @Table(name = "AUTH_USER_DETAIL")
    public class UserDetail {
    
        /*
            其余代码省略
         */
    
        @OneToOne(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY)
        private User user;
    }
    
    • mappedBy加在了User实体为的userDetail属性上,并指向user表示:在UserDetail实体类映射的表中创建关系字段(user_id),并设为外键,指向User实体类映射的表中主键
    • UserDetail实体类的user属性的CascadeTypePERSIST表示:删除用户详情时,不级联删除用户信息

    OneToMany/ManyToOne

    一对多关系即一个表中的一行数据关联另外一个表中的多行数据,多对一与之相反。

    用户实体类:

    @Entity
    @Table(name = "AUTH_USER")
    public class User {
    
        /*
            其余代码省略
         */
    
        @ManyToOne(cascade = CascadeType.PERSIST, fetch = FetchType.EAGER)
        private Company company;
    }
    

    公司实体类:

    @Entity
    @Table(name = "AUTH_COMPANY")
    public class Company {
    
        /*
            其余代码省略
         */
    
        @OneToMany(cascade = CascadeType.PERSIST, fetch = FetchType.LAZY, mappedBy = "company")
        private Set<User> users = new HashSet<>();
    }
    
    • @mappedBy只能出现在@OneToMany注解中
    • 会在User实体类对应映射表中创建一个外键(company_id),指向Company实体类对应映射表的主键
    • 两边最好不要做级联删除, cascade设为PERSIST

    ManyToMany

    多对多关系即其中一个表中的一行,与另一表中的多行关联,比如本示例中用户和角色的关系。

    用户实体类:

    @Entity
    @Table(name = "AUTH_USER")
    public class User {
    
        /*
            其余代码省略
         */
    
        @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
        private Set<Role> roles = new HashSet<>();
    }
    

    角色实体类:

    @Entity
    @Table(name = "AUTH_ROLE")
    public class Role {
    
        /*
            其余代码省略
         */
    
        @ManyToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, mappedBy = "roles")
        private Set<User> users = new HashSet<>();
    }
    
    • 会建立一个中间表,包含两个实体类的主键关系
    • mappedBy指向哪一个都可以,中途不要修改

    完整代码

    springboot-jpa

    timg (1).jpg

    相关文章

      网友评论

        本文标题:SpringBoot 全家桶 | JPA实例详解

        本文链接:https://www.haomeiwen.com/subject/ajylsktx.html