天天看點

SpringBoot整合JPA(六)一. SpringBoot 整合 JPA前期準備二. 整合JPA應用

二八佳人體似酥,腰間仗劍斬愚夫。雖然不見人頭落,暗裡教君骨髓枯。

上一章簡單介紹了SpringBoot整合JdbcTemplate(五),如果沒有看過,

請觀看上一章

日常生活中,我們并不使用 JdbcTemplate, 而是使用 JPA和MyBatis,MyBatis-Plus. 這一章節,我們講解一下, JPA的相關操作。

一. SpringBoot 整合 JPA前期準備

按照老蝴蝶以前講解的方式,采用Maven 建構SpringBoot項目。

一.一 pom.xml 添加依賴

<!--引入MySql的驅動-->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
<!--引入springboot與jpa整合的依賴-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>           

一.二 application.yml 添加JPA的配置

# 引入 資料庫的相關配置
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/springboot?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8&useSSL=false
    username: root
    password: abc123
  # JPA的相關配置
  jpa:
    # 設定資料庫平台
    database-platform: org.hibernate.dialect.MySQLDialect
    # 設定資料庫
    database: mysql
    # 是否展示SQL語句
    show-sql: true
    hibernate:
      # 持久化規則是 update
      ddl-auto: update
      naming:
        # 實體命名政策類的全限定名稱
        physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl           

一.三 建立 User 表

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(15) DEFAULT NULL,
  `sex` varchar(20) DEFAULT NULL,
  `age` int(6) DEFAULT NULL,
  `description` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=18 DEFAULT CHARSET=utf8;           

二. 整合JPA應用

SpringBoot整合JPA時,需要建立相應的 實體類,工廠接口。

工廠接口有 Crud接口,有 PagingAndSorting,有 Jpa接口,也有 Specification 動态查詢接口。

每一種接口,都有其特殊的功能。

二.一 建立 POJO 類和業務類

在 pojo 包下,建立 User.java 類

@Data
@NoArgsConstructor
@AllArgsConstructor
@Table(name = "user")
@Entity
public class User implements Serializable {
    /**
     * @param id id編号
     * @param name 姓名
     * @param sex 性别
     * @param age 年齡
     * @param description 描述
     */
    @Id
    //指定生成政策
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    private Integer id;
    @Column(name = "name")
    private String name;
    @Column(name="sex")
    private String sex;
    @Column(name="age")
    private Integer age;
    @Column(name="description")
    private String description;
}           

在 service包下,建立相應的接口和實作類

public interface UserService {
    
}           
@Service
public class UserServiceImpl implements UserService {
    
}           

二.二 Crud 工廠接口

二.二.一 接口的相關定義

org.springframework.data.repository.CrudRepository

@NoRepositoryBean
public interface CrudRepository<T, ID> extends Repository<T, ID> {

}           
方法簽名 方法解釋
<S extends T> S save(S entity); 插入或者更新一個對象
<S extends T> Iterable<S> saveAll(Iterable<S> entities); 插入或者更新多個對象,批量操作
Optional findById(ID id); 根據id 查詢對象
boolean existsById(ID id); 是否存在此id的記錄
Iterable findAll(); 查詢所有的記錄
Iterable findAllById(Iterable ids); 根據id集合查詢相關的記錄
long count(); 統計數目
void deleteById(ID id); 根據id進行删除
void delete(T entity); 根據實體對象進行删除
void deleteAll(Iterable<? extends T> entities); 根據實體對象集合進行删除,批量操作
void deleteAll(); 删除所有的記錄

二.二.二. Crud 接口實作

在 repository 包下,建立 UserCrudRepository 接口。

public interface UserCrudRepository extends CrudRepository<User,Integer> {

}           

二.二.三 Crud 測試

在 test測試目錄下,建立對應的測試類和測試方法,進行測試。

@SpringBootTest
@Log4j2
public class CrudRepositoryTests {
    @Autowired
    private UserService userService;
}           

二.二.三.一 插入 save 方法

測試方法:

@Test
    public void addTest(){
        //1. 建構對象
        User user=new User();
        user.setName("歡歡");
        user.setAge(22);
        user.setSex("女");
        user.setDescription("一個非常可愛的女孩紙");
        //2. 添加方法
        userService.addUser(user);
       log.info("添加成功,{}",user);
    }           

省略接口方法,其對應的實作方法

添加Crud接口注入

@Autowired
private UserCrudRepository userCrudRepository;           
@Override
    public void addUser(User user) {
        userCrudRepository.save(user);
    }           

控制台列印輸出:

SpringBoot整合JPA(六)一. SpringBoot 整合 JPA前期準備二. 整合JPA應用

二.二.三.二 更新 save 方法

測試方法

@Test
    public void updateTest(){
        //1. 建構對象
        User user=new User();
        user.setId(1); //id不存在,會添加
        user.setName("歡歡");
        user.setDescription("嶽澤霖最好的朋友");
        //2. 修改方法
        userService.updateUser(user);
        log.info("修改成功,{}",user);
    }           

接口實作方法

@Override
    public void updateUser(User user) {
        userCrudRepository.save(user);
    }           

控制台列印輸出

SpringBoot整合JPA(六)一. SpringBoot 整合 JPA前期準備二. 整合JPA應用

先查詢,發現有,就更新,如果沒有的話,就插入。

二.二.三.三 删除 delete 方法

@Test
    public void deleteTest(){
        userService.deleteUser(1);
    }           
@Override
    public void deleteUser(Integer id) {
        userCrudRepository.deleteById(id);
    }           
SpringBoot整合JPA(六)一. SpringBoot 整合 JPA前期準備二. 整合JPA應用

資料庫裡面,沒有此條資料了。

二.二.三.四 批量更新資料 saveAll

重新執行一下, save() 方法,插入一條資料, id=2.

@Test
    public void batchAddTest(){
        //1. 建構對象
        User user=new User();
        user.setName("小歡歡");
        user.setAge(22);
        user.setSex("女");
        user.setDescription("一個小壞蛋");

        User user1=new User();
        user1.setName("小澤霖");
        user1.setAge(25);
        user1.setSex("男");
        user1.setDescription("一個大壞蛋");

        //這是修改的操作,id=2已經存在這條記錄了。
        User user2=new User();
        user2.setName("嶽澤霖");
        user2.setId(2);
        user2.setAge(25);
        user2.setSex("男性");
        user2.setDescription("一個快樂的程式員");

        //2. 放置到集合裡面
        List<User> userList=new ArrayList<>();
        userList.add(user);
        userList.add(user1);
        userList.add(user2);
        userService.batchAddUser(userList);
    }           

接口實作方法:

@Override
    public void batchAddUser(List<User> userList) {
        userCrudRepository.saveAll(userList);
    }           
SpringBoot整合JPA(六)一. SpringBoot 整合 JPA前期準備二. 整合JPA應用

會插入前兩條,更新第三條記錄。

二.二.三.五 根據id 進行查詢 findById

@Test
    public void findByIdTest(){
        User user=userService.findById(3);
        log.info(user);
    }           
@Override
    public User findById(Integer id) {
        Optional<User> optional= userCrudRepository.findById(id);
        if(optional.isPresent()){
            return optional.get();
        }
        return null;
    }           
SpringBoot整合JPA(六)一. SpringBoot 整合 JPA前期準備二. 整合JPA應用

二.二.三.六 查詢所有的資料 findAll

@Test
    public void findAllTest(){
        List<User> userList=userService.findAll();
        userList.forEach(n->log.info(n));
    }           
@Override
    public List<User> findAll() {
        Iterable<User> iterator= userCrudRepository.findAll();
        List<User> userList=new ArrayList<>();
        //将 Iterable 轉換成 list
        iterator.forEach(n->userList.add(n));
        return userList;

    }           
SpringBoot整合JPA(六)一. SpringBoot 整合 JPA前期準備二. 整合JPA應用

二.二.三.七 根據id集合進行查詢 findAllById

@Test
    public void findByIdsTest(){
        List<Integer> ids= Arrays.asList(3,4,6);
        List<User> userList=userService.findAllByIds(ids);
        userList.forEach(n->log.info(n));
    }           
@Override
    public List<User> findAllByIds(List<Integer> ids) {
        Iterable<User> iterator= userCrudRepository.findAllById(ids);
        List<User> userList=new ArrayList<>();
        iterator.forEach(n->userList.add(n));
        return userList;
    }           
SpringBoot整合JPA(六)一. SpringBoot 整合 JPA前期準備二. 整合JPA應用

二.二.三.八 查詢數目 count

@Test
    public void countTest(){
        Long count=userService.count();
       log.info("總數目{}",count);
    }           
@Override
    public Long count() {
        return userCrudRepository.count();
    }           
SpringBoot整合JPA(六)一. SpringBoot 整合 JPA前期準備二. 整合JPA應用

這就是 Crud倉庫接口的基本用法。

二.三 PagingAndSorting 工廠接口

二.三.一 接口的相關定義

org.springframework.data.repository.PagingAndSortingRepository

分頁和排序的接口,繼承了 Crud的接口。

@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {

    Iterable<T> findAll(Sort sort);

    Page<T> findAll(Pageable pageable);
}           
Iterable findAll(Sort sort); 排序
Page findAll(Pageable pageable); 分頁

二.三.二 PagingAndSorting 接口實作

在 repository 包下,建立 UserPagingAndSortingRepository 接口

public interface UserPagingAndSortingRepository extends PagingAndSortingRepository<User,Integer> {

}           

二.三.三 PagingAndSorting 測試

在 test目錄下,建立 PagingAndSortingRepositoryTests 測試類和測試方法。

多添加幾條資料,好便于分頁和排序。

SpringBoot整合JPA(六)一. SpringBoot 整合 JPA前期準備二. 整合JPA應用

二.三.三.一 排序,按照性别和年齡

@Autowired
    private UserPagingAndSortingRepository userPagingAndSortingRepository;

@Test
    public void sortTest(){
        List<User> userList=userService.findAllOrderBySexAndAge();
        userList.forEach(n->log.info(n));
    }           

業務接口方法

@Override
    public List<User> findAllOrderBySexAndAge() {
        Sort.Order sort1= Sort.Order.desc("sex");
        Sort.Order sort2 = Sort.Order.asc("age");
        Sort sort=Sort.by(sort1,sort2);
        Iterable<User> userIterable=userPagingAndSortingRepository.findAll(sort);
        List<User> userList=new ArrayList<>();
        userIterable.forEach(n->userList.add(n));
        return userList;
    }           
SpringBoot整合JPA(六)一. SpringBoot 整合 JPA前期準備二. 整合JPA應用

二.三.三.二 排序并分頁

@Test
    public void pageTest(){
        Page<User> page=userService.pageAll();
        log.info("總頁數:{}",page.getTotalPages());
        log.info("總的數目:{}",page.getTotalElements());
        log.info("目前頁數:{}",page.getNumber()+1);
        log.info("目前頁的數目:{}",page.getNumberOfElements());
        List<User> userList= page.getContent();
        userList.forEach(n->log.info(n));
    }           
@Override
    public Page<User> pageAll() {
        Sort sort=Sort.by(Sort.Direction.DESC,"id");
        // 預設從0開始的
        Pageable pageable= PageRequest.of(2-1, 4, sort);
        Page<User> userPage=userPagingAndSortingRepository.findAll(pageable);
        return userPage;
    }           
SpringBoot整合JPA(六)一. SpringBoot 整合 JPA前期準備二. 整合JPA應用

這就是 PageAndSorting倉庫接口的基本用法。

二.四 JpaRepository 工廠接口

二.四.一接口的相關定義

@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {

}           

繼承了分頁,還有一個查詢的接口。

List findAll(); 查詢所有,傳回對象集合
List findAll(Sort sort); 查詢所有,可排序,傳回對象集合
List findAllById(Iterable ids); 根據id集合,查詢所有的記錄,傳回對象集合
<S extends T> List*lt;S> saveAll(Iterable<S> entities); 批量儲存資料
void flush(); 重新整理緩存
S saveAndFlush(S entity); 儲存并重新整理
void deleteInBatch(Iterable entities); 根據對象集合
void deleteAllInBatch(); 批量删除所有
T getOne(ID id); 根據id查詢目前對象,如果沒有的話,傳回null
<S extends T> List<S> findAll(Example<S> example); 根據條件,查詢資料,傳回對象集合
<S extends T> List<S> findAll(Example<S> example, Sort sort); 根據條件,支援排序,查詢資料,傳回對象集合

二.四.二 Jpa 接口實作

在repository包下,建立 UserJpaRepository 接口。 通常使用的是這個Jpa的接口,會在這接口内添加相應的接口定義。

當接口的方法名稱定義符合相應的規範的時候,不用寫相應的實作,Jpa會幫我們自動比對生成相應的sql語句。

public interface UserJpaRepository extends JpaRepository<User,Integer> {
    
}           
SpringBoot整合JPA(六)一. SpringBoot 整合 JPA前期準備二. 整合JPA應用
SpringBoot整合JPA(六)一. SpringBoot 整合 JPA前期準備二. 整合JPA應用

二.四.三 JPA測試

在test目錄下,建立JpaRepositoryTests 進行測試。

二.四.三.一 查詢全部

@Test
    public void findAllTest(){
        List<User> userList=userService.jpaFindAll();
        userList.forEach(n->log.info(n));
    }           

業務方法

@Autowired
    private UserJpaRepository userJpaRepository;

@Override
    public List<User> jpaFindAll() {
        return userJpaRepository.findAll();
    }           

不用往 UserJpaRepository 添加方法。

SpringBoot整合JPA(六)一. SpringBoot 整合 JPA前期準備二. 整合JPA應用

二.四.三.二 Example 查詢 (通常不用)

@Test
    public void findByExampleTest(){
        User user=new User();
        user.setName("澤霖");
        user.setAge(25);
        user.setSex("男");
        //1.建立比對器
        ExampleMatcher exampleMatcher=ExampleMatcher.matching()
                .withMatcher("sex",matcher -> matcher.contains())
                .withMatcher("age",matcher -> matcher.exact())
                .withMatcher("name",matcher ->matcher.contains());
        //2. 生成Example 對象
        Example example=Example.of(user,exampleMatcher);
        //3. 進行查詢
        List<User>userList=userService.findByExample(example);
        userList.forEach(n->log.info(n));
    }           

業務實作接口

@Override
    public List<User> findByExample(Example example) {
        return userJpaRepository.findAll(example);
    }           
SpringBoot整合JPA(六)一. SpringBoot 整合 JPA前期準備二. 整合JPA應用

二.四.三.三 根據名稱進行查詢

@Test
    public void findByNameTest(){
       List<User> userList=userService.findByName("小歡歡");
        userList.forEach(n->log.info(n));
    }           
@Override
    public List<User> findByName(String name) {
        return userJpaRepository.findByName(name);
    }           

需要往 UserJpaRepository 裡面添加方法,需要符合一定的規則

List<User> findByName(String name);           
SpringBoot整合JPA(六)一. SpringBoot 整合 JPA前期準備二. 整合JPA應用

二.四.三.四 根據性别和年齡

@Test
    public void findBySexAndAgeTest(){
        List<User> userList=userService.findBySexAndAge("男",25);
        userList.forEach(n->log.info(n));
    }           
@Override
    public List<User> findBySexAndAge(String sex, Integer age) {
        return userJpaRepository.findBySexAndAge(sex,age);
    }           
List<User> findBySexAndAge(String sex, Integer age);           
SpringBoot整合JPA(六)一. SpringBoot 整合 JPA前期準備二. 整合JPA應用

二.四.三.五 根據性别查詢,年齡排序

@Test
    public void findAllOrderByTest(){
        List<User> userList=userService.findBySexOrderByAge("女");
        userList.forEach(n->log.info(n));
    }           
@Override
    public List<User> findBySexOrderByAge(String sex) {
        return userJpaRepository.findBySexOrderByAgeDesc(sex);
    }           
List<User> findBySexOrderByAgeDesc(String sex);           
SpringBoot整合JPA(六)一. SpringBoot 整合 JPA前期準備二. 整合JPA應用

二.四.三.六 使用原始SQL進行查詢部分字段

@Test
    public void findQueryNameTest(){
        List<Map<String,Object>> userMapList=userService.findQueryByName("小歡歡");
        for(Map<String,Object> map:userMapList){
            log.info("id是:{},name是{}",map.get("id"),map.get("name"));
        }
    }           
@Override
    public  List<Map<String,Object>> findQueryByName(String name) {
        return userJpaRepository.findQueryByName(name);
    }           
@Query(value="select id as id,name as name from user where name=:name",nativeQuery = true)
    List<Map<String,Object>> findQueryByName(@Param("name") String name);           
SpringBoot整合JPA(六)一. SpringBoot 整合 JPA前期準備二. 整合JPA應用

二.四.三.七 使用原始SQL進行查詢全部字段

@Test
    public void findQueryNameTest(){
        List<Map<String,Object>> userMapList=userService.findQueryByName("小歡歡");
        for(Map<String,Object> map:userMapList){
            log.info("id是:{},name是{}",map.get("id"),map.get("name"));
        }
    }           
@Override
    public List<User> jpaFindAllSql(String name) {
        return userJpaRepository.findAllSql(name);
    }           
@Query(value="select * from user where name=:name",nativeQuery = true)
    List<User> findAllSql(@Param("name")String name);           
SpringBoot整合JPA(六)一. SpringBoot 整合 JPA前期準備二. 整合JPA應用

這就是 jpa的一些基本的用法。

二.五 JpaSpecificationExecutor 動态查詢接口

二.五.一 接口的相關定義

public interface JpaSpecificationExecutor<T> {
    
}           
Optional findOne(@Nullable Specification spec); 根據條件,查詢最多隻有一條記錄
List findAll(@Nullable Specification spec); 根據條件,查詢多條記錄
Page findAll(@Nullable Specification spec, Pageable pageable); 根據條件,分頁查詢,可包含排序
List findAll(@Nullable Specification spec, Sort sort); 根據條件,排序查詢
long count(@Nullable Specification spec); 查詢數目

二.五.二. JpaSpecificationExecutor實作

通常都是與 JpaRepository 一起使用的。

public interface UserSpecificationRepository extends JpaRepository<User, Integer>,
        JpaSpecificationExecutor<User>{
            
}           

二.五.三 Specification 動态查詢測試

@Test
    public void nameAndSexAndDescTest(){
        User user=new User();
        user.setName("小歡歡1");
        user.setSex("女");
        user.setAge(27);
        user.setDescription("小壞蛋");
        List<User> userList=userService.findByNameSexAndDesc(user);
        userList.forEach(n->log.info(n));
    }           
@Override
    public List<User> findByNameSexAndDesc(User user) {
        //1. 根據條件建立 Specification 對象資訊
        Specification<User> specification=new Specification<User>(){
            @Override
            public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder criteriaBuilder) {
                //1. 用于接收封裝的查詢對象
                List<Predicate> predicateList = new ArrayList<>();
                if(user!=null){
                    //1.如果name 不為空的話,對name 進行精确比對
                    if(!StringUtils.isEmpty(user.getName())){
                        Predicate namePredicate = criteriaBuilder.equal(root.get("name"), user.getName());
                        predicateList.add(namePredicate);
                    }
                    //2.如果sex 不為空的話,也是精确比對
                    if(!StringUtils.isEmpty(user.getSex())){
                        Predicate sexPredicate=criteriaBuilder.equal(root.get("sex"),user.getSex());
                        predicateList.add(sexPredicate);
                    }
                    //3.如果age不為空的話,就是 < 比對
                    if(!StringUtils.isEmpty(user.getAge())){
                        Predicate agePreDicate=criteriaBuilder.lt(root.get("age"),user.getAge());
                        predicateList.add(agePreDicate);
                    }
                    //4. 如果description 不為空的話,進行模糊比對
                    if(!StringUtils.isEmpty(user.getDescription())){
                        Predicate descPredicate=criteriaBuilder.like(root.get("description"),"%"+user.getDescription()
                        +"%");
                        predicateList.add(descPredicate);
                    }
                }
                return criteriaBuilder.and(predicateList.toArray(
                        new Predicate[predicateList.size()]
                ));

            }
        };
        //傳入條件,也可以傳入分頁資訊。這兒就不舉例分頁了。
        return userSpecificationRepository.findAll(specification);

    }           

如果将 user條件的屬性全部去掉,是一種sql查詢,sex去掉,又是一種sql查詢,會根據屬性的不同,動态的處理。

SpringBoot整合JPA(六)一. SpringBoot 整合 JPA前期準備二. 整合JPA應用

動态查詢,也可以傳入分頁和排序的相關資訊。

這個非常重要,需要重點掌握一下。

本章節的代碼放置在 github 上:

https://github.com/yuejianli/springboot/tree/develop/Jpa

謝謝您的觀看,如果喜歡,請關注我,再次感謝 !!!