1. 程式人生 > >JPA配置,簡單使用以及常見問題

JPA配置,簡單使用以及常見問題

option 用戶 場景 類屬性 let 兩種 diag specific 自定義查詢

1.引入pom依賴

<!--springboot-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>
</dependency>

2.配置數據源

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/joe?serverTimezone=UTC
    username: root
    password: root
    driver-class-name: com.mysql.cj.jdbc.Driver


  jpa:
    show-sql: true

2.1 driver-class-name(驅動類)

根據mysql版本不同不一樣,有的是com.mysql.cj.jdbc.Driver,有的是com.mysql.jdbc.Driver,不會報錯,但是會有提示信息。

提示:Loading class com.mysql.jdbc.Driver‘. This is deprecated. The new driver class is com.mysql.cj.jdbc.Driver‘.



2.2 time zone 異常

異常:java.sql.SQLException: The server time zone value ‘?D1ú±ê×?ê±??‘ is unrecognized or represents more than one time zone.

解決:在datasource-url後拼接參數 serverTimezone=UTC

3.生成數據庫表實體類domain

@Data
@Entity
@Table(name = "user")
public class User implements Serializable {

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

    @Column(name = "user_name")
    private String userName;

    private Long age;

    private String gender;

    private String address;
}

3.1 實體類建議用工具生成,因為jpa要求實體類中的字段都必須在數據庫中找到對應列,即類屬性只能比表字段少不能多,否則會報異常:
org.springframework.dao.InvalidDataAccessResourceUsageException

如何生成 IDEA自動生成JPA實體

3.2 數據庫所有的表必須要有主鍵,jpa要求所有的表都必須有主鍵列,實體類必須有@Id標註,可以是聯合主鍵,但是不能沒有,沒有的話會報異常:
org.hibernate.AnnotationException: No identifier specified for entity: com.joe.jpa.domain.User
聯合主鍵如何使用 JPA聯合主鍵

4.編寫dao層

@Repository
public interface UserRepository extends JpaRepository<User, Integer> {
}

1.jpa已經給我們提供了單表的增刪改查操作,只需要在dao接口上實現 JpaRepository<T, ID>接口,就可以通過接口裏的方法完成crud操作。
其中JpaRepository裏的泛型,T是表的實體類,ID是表的主鍵對應的實體類屬性數據類型,我這裏是<User, Integer>。

5.測試類

即使自定義的UserRepository沒有編寫任何代碼,註入後一樣可以能使用 save(),findXXX(),delete(),count()等方法,並且測試有效,其實是JPA內部 CrudRepository 提供, SimpleJpaRepository 實現的,而 JpaRepository 是CrudRepository的子類,我們又實現了 JpaRepository所以就可以直接用。

詳細類圖在 JpaRepository 中 右鍵>Diagrams>show Diagrams...>java class Diagrams 可以查看。 圖文步驟:IDEA查看類繼承關系

5.1 保存

//保存和批量保存
@Test
public void testSave() {

    //保存
    User user = new User();
    user.setUserName("張三");
    user.setAge(23);
    user.setGender("男");
    userRepository.save(user);
    log.info("保存成功,主鍵:{}", user.getUserId());

    //批量保存
    User user2 = new User();
    user2.setUserName("李四");
    user2.setAge(27);
    user2.setGender("男");

    User user3 = new User();
    user3.setUserName("王五");
    user3.setAge(25);
    user3.setGender("女");

    User user4 = new User();
    user4.setUserName("趙六");
    user4.setAge(26);
    user4.setGender("女");

    List<User> list = new ArrayList();
    list.add(user2);
    list.add(user3);
    list.add(user4);
    userRepository.saveAll(list);
    log.info("批量保存成功");
}

JPA中提供了save() 單個數據保存和saveAll()批量保存的方法,但是批量保存也是循環執行的單條保存,
這個可以在運行時控制臺打印的SQL或者直接從源碼SimpleJpaRepository.saveAll()看出來,
所以大批量的插入最好不要用自帶的saveAll()去執行。

批量插入的SQL語句
技術分享圖片

批量插入saveAll()源碼
技術分享圖片

5.2 查詢

JpaRepository提供的可以直接使用的查詢雖然只有find()和findAll()兩種,但是進行了多次重載,可以滿足很多種場景的查詢。

5.2.1 查詢全部和主鍵查詢

List<T> findAll();

Optional<T> findById(ID var1);

List<T> findAllById(Iterable<ID> var1);

示例:

    @Test
    public void testFindAll() {

        List<User> userList = userRepository.findAll();
        log.info("查詢所有:{}", userList);
    }

    @Test
    public void testFindById() {

        //主鍵查詢-查詢一個
        Optional<User> userOptional = userRepository.findById(2);
        if (userOptional.isPresent()) {
            log.info("根據主鍵查詢:{}", userOptional.get());
        }

        //主鍵查詢-查詢多個
        List<Integer> userIdList = Arrays.asList(new Integer[]{2, 3});
        List<User> userListByIds = userRepository.findAllById(userIdList);
        log.info("根據多個主鍵查詢:{}", userListByIds);
    }
排序查詢

List<T> findAll(Sort var1);
排序查詢需要借助Sort類實現
Sort類實例化 Sort ageSort = Sort.by(Sort.Direction.DESC, "age");
第一個參數可以省略,默認為Sort.Direction.ASC,第二個參數為要排序的字段對應的實體類屬性,例如

@Test
public void testFindSort() {

    //Sort ageSort = Sort.by("age");
    Sort ageSort = Sort.by(Sort.Direction.DESC, "age");//倒敘
    List<User> userListSortByAge = userRepository.findAll(ageSort);
    log.info("查詢所有按照age倒敘:{}", userListSortByAge);
}
條件查詢

<S extends T> List<S> findAll(Example<S> var1);
條件查詢需要借助Example類實現
Example實例化 Example<User> userExample = Example.of(user);
參數為裝有帶條件的數據庫實體。例如:

@Test
public void testFindByExample() {

    //等同條件查詢
    User user = new User();
    user.setGender("男");
    Example<User> userExample = Example.of(user);
    List<User> userExampleList = userRepository.findAll(userExample);
    log.info("性別為男的用戶為:{}", userExampleList);

    //等同條件查詢唯一記錄,如果查到兩條會報錯
    User user2 = new User();
    user2.setUserName("張三");
    Example<User> oneUserExample = Example.of(user2);
    Optional<User> oneUser = userRepository.findOne(oneUserExample);
    log.info("名字叫張三的記錄:{}", oneUser);
}
分頁查詢

分頁查詢需要借助PageRequest類實現
PageRequest實例化: PageRequest pageParam = PageRequest.of(1, 2);
第一個參數為頁碼,第二個參數為頁行數,註意jpa的頁碼是從0開始算的,傳入0查詢的是第一頁數據。例如

@Test
public void testFindByPage() {
    //分頁查詢  jpa頁碼是從0開始的,傳入1的話,返回的是第二頁的數據
    PageRequest pageParam = PageRequest.of(1, 2);
    Page<User> userListByPage = userRepository.findAll(pageParam);
    long totalElements = userListByPage.getTotalElements();
    int totalPages = userListByPage.getTotalPages();
    List<User> content = userListByPage.getContent();
    log.info("分頁查詢結果,總記錄數:{},總頁數:{},選定頁數據:{}", totalElements, totalPages, content);
}

以上所以的查詢是JPA提供的可以直接使用的查詢接口,所有的查詢都是等值查詢,即column=xxx ,
如要想實現 like, <,> ,isnull ,in等操作,需要手動寫SQL或者借助 特殊查詢類JpaSpecificationExecutor.findAll()和 查詢條件類Specification實現。
jpa排序分組條件查詢 jpa自定義查詢和復雜條件查詢

JPA配置,簡單使用以及常見問題