Spring-Data-JPA初體驗(內含demo)

前言

我一直在使用Mybatis作爲持久化框架,並且覺得Mybatis十分的不錯,足夠靈活,雖說需要自己手寫sql,但是這也是我覺得的一個優點,直觀並且優化方便.

但是我覺得JPA規範也有其優點,比如說簡單,在一些基本的CRUD操作時,完全無需手寫SQL.

因此趁着空閒,對Spring Data JPA做一個瞭解,並簡單的寫一個Demo來學習使用.

定義

在本文可能會涉及一下幾個概念,這裏統一講一下定義.

JPA

JPA,即Java Persistence API.是Sun公司在Java EE 5規範中提出的Java持久化接口,即一種規範.

ORM

對象關係映射,即Object Relational Mapping,簡稱ORM.是一種程序技術,用於實現面向對象編程語言裏不同類型系統的數據之間的轉換.

Hibernate

Hibernate是一種ORM框架,Hibernate在3.2版本開始,已經完全兼容JPA標準.

Mybatis

Mybatis是另外一種ORM框架.使用它構建項目可以看Spring Boot Mybatis Web 開發環境搭建

Spring Data JPA

Spring Data JPA是Spring基於Hibernate開發的一個JPA框架,實現了JPA規範.

Spring Data JPA 實現原理

前文說過,JPA的一個優點就是不用寫簡單的CRUD的SQL語句,那麼怎麼做到的呢?

JPA可以通過如下兩種方式指定查詢語句:

  1. Spring Data JPA 可以訪問 JPA 命名查詢語句。開發者只需要在定義命名查詢語句時,爲其指定一個符合給定格式的名字,Spring Data JPA 便會在創建代理對象時,使用該命名查詢語句來實現其功能。
  2. 開發者還可以直接在聲明的方法上面使用 @Query 註解,並提供一個查詢語句作爲參數,Spring Data JPA 在創建代理對象時,便以提供的查詢語句來實現其功能。

第一種功能基本可以滿足日常所需,當需要連表查詢或者一些更加複雜的操作時,可以使用@Query註解來使用自己編寫的sql進行查詢.

方法名和sql的對應關係在文末附錄

環境搭建

首先使用Spring Boot 及Maven搭建一個項目,這部分不再贅述,有興趣可以移步上面的鏈接.

添加依賴

在pox.xml中添加以下依賴,分別爲:

  1. spring-data-jpa
  2. Hibernate-core
  3. Hibernate–annotations
  4. HikariCP

其中第四點爲我使用的連接池,Spring Boot官方也比較推薦這個,當然,可以換成C3P0或者其他連接池.

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

<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-core</artifactId>
  <version>5.3.7.Final</version>
</dependency>

<!-- https://mvnrepository.com/artifact/org.hibernate/hibernate-annotations -->
<dependency>
  <groupId>org.hibernate</groupId>
  <artifactId>hibernate-annotations</artifactId>
  <version>3.5.6-Final</version>
</dependency>

<dependency>
  <groupId>com.zaxxer</groupId>
  <artifactId>HikariCP</artifactId>
  <version>3.2.0</version>
</dependency>

配置文件

application.yaml文件中加入以下內容,設置服務啓動端口,使用配置爲local以及數據源的一些配置,最後是JPA的配置.


server:
  port: 9999

spring:
  profiles:
    active: local
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    hikari:
      minimum-idle: 0
      initial-size: 5
      maximum-pool-size: 15
      auto-commit: true
      pool-name: NezhaHikariCP
      test-connection-on-checkout: true

  jpa:
    database: MYSQL
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect

同時,新建application-local.yaml中加入以下內容,設置mysql數據源的相關信息.

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://127.0.0.1/test?characterEncoding=utf8&useSSL=false
    username: root
    password: root
  jpa:
    show-sql: true

環境的搭建就是上面那麼簡單,第一步添加依賴,第二步添加一些配置就ok.

剩下的就是編寫一些業務代碼,而各種配置類什麼的完全沒有!!XML配置也沒有!!

Demo創建

創建數據表

首先在數據庫中創建表,本文測試表爲(在test數據庫中):

mysql> desc student;
+------------+-------------+------+-----+---------------------+-----------------------------+
| Field      | Type        | Null | Key | Default             | Extra                       |
+------------+-------------+------+-----+---------------------+-----------------------------+
| id         | int(10)     | NO   |     | NULL                |                             |
| name       | varchar(45) | NO   | PRI | NULL                |                             |
| class_num  | int(12)     | YES  |     | NULL                |                             |
| age        | int(3)      | YES  |     | 18                  |                             |
| created_at | timestamp   | NO   |     | CURRENT_TIMESTAMP   |                             |
| updated_at | timestamp   | NO   |     | 2018-01-01 00:00:00 | on update CURRENT_TIMESTAMP |
+------------+-------------+------+-----+---------------------+-----------------------------+
6 rows in set (0.01 sec)

建表語句爲:

CREATE TABLE `student` (
 `id` int(10) NOT NULL AUTO_INCREMENT COMMENT 'id',
 `name` varchar(45) NOT NULL COMMENT '姓名',
 `class_num` int(12) DEFAULT NULL COMMENT '班級',
 `age` int(3) DEFAULT '18' COMMENT '年齡',
 `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '創建時間',
 `updated_at` timestamp NOT NULL DEFAULT '2018-01-01 00:00:00' ON UPDATE CURRENT_TIMESTAMP COMMENT '修改時間',
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8

創建實體類

在model包下創建Student實體類:

package com.huyan.demo.model;

import java.util.Date;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * created by huyanshi on 2018/12/21
 */
@AllArgsConstructor
@Builder
@Data
@NoArgsConstructor
@Entity
@Table(name = "student")
public class Student {

  @Id
  private int id;

  private String name;

  private int classNum;

  private  int age;

  @Temporal(TemporalType.TIMESTAMP)
  private Date createdAt;

  @Temporal(TemporalType.TIMESTAMP)
  private Date updatedAt;
}

其中,@Entity標識此類是一個實體類,@Table指定關聯的數據表.

創建dao層接口

package com.huyan.demo.dao;

import com.huyan.demo.model.Student;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

/**
 * created by huyanshi on 2018/12/21
 */
@Repository
public interface StudentDao extends JpaRepository<Student,String> {

  List<Student> findAll();
}

在接口上打上@Repository註解並實現JpaRepository接口.該接口使用了泛型,需要爲其提供兩個類型:第一個爲該接口處理的域對象類型,第二個爲該域對象的主鍵類型

好,demo到此就結束了,service層和controller隨意寫,只要調用List<Student> findAll();這個方法,就會查找該表格中的所有數據.

我寫了個很簡單的接口,直接返回拿到的list,數據結果集爲:

注意,在這個過程中,我們是沒有手寫SQL的,如果是在使用mybatis的過程中,我們需要編寫select * from student的SQL語句.

更多方法示例

費勁搞了JPA,當然不可寫一個方法就完事了.這樣在實際應用中沒有多少幫助.因此,我將一些常用的方法類型在這裏測試一遍使用方法,最後,將其整合輸出.

實際測試我才發現,許多的方法在繼承的接口中早已定義,比如查詢全量,根據主鍵嗯增刪改查,排序,分頁等,可謂十分強大,因此簡單測試了大於小於及多參數的查詢.

以下代碼實際運行通過.

@Repository
public interface StudentDao extends JpaRepository<Student, Integer> {

  //查詢所有
  List<Student> findAll();
  //查詢年齡大於傳入值得
  List<Student> findByAgeAfter(int age);
  //查詢年齡和班級等於入參的
  List<Student> findByAgeAndClassNum(int age, int classNum);
  //查詢年齡爲入參的學生,且結果按照創建時間排序
  List<Student> findByAgeOrderByCreatedAt(int age);
  //根據主鍵更新實體的名字年齡及班級
  @Modifying(clearAutomatically = true)
  @Transactional
  @Query(value = "UPDATE Student SET name = :name, classNum = :classNum, "
      + "age = :age WHERE id = :id ")
  int updateNameAndAgeAndClassNumById(@Param("name") String name, @Param("age") int age,
      @Param("classNum") int classNum, @Param("id") int id);

}

繼承哪個接口

在上文中創建dao層接口中,我們要繼承Repository接口,但是在Spring Data JPA中,提供了4個接口,到底該繼承哪個呢?

  1. Repository接口,沒有定義方法.
  2. CrudRepository接口,繼承自Repository接口,定義了基本的增刪改查.
  3. PagingAndSortingRepository接口,繼承自CrudRepository接口,定義了排序及分頁方法.
  4. JpaRepository接口,繼承自PagingAndSortingRepository接口,提供了 flush(),saveAndFlush(),deleteInBatch() 等方法.

但是要說繼承哪個接口呢?這個就見仁見智了,我是在不影響業務(主要是Crudrepository接口會提供刪除方法,有時候你並不想提供刪除)的情況下,我一般使用JPARepository,畢竟功能比較全嘛.

後話

在今天的學習後,對Jpa也算是有一點了解,在我看來,他和Mysql是兩種不同的思路,但是都可以完成同一個任務.

在業務邏輯較爲簡單的時候,使用JPA可以提高開發效率,因爲基本的增刪改查你連方法定義都不需要寫…然後大部分較簡單的查詢你都可以通過定義方法名來完成,實在不行還有@Query手寫sql兜底.

附錄:方法關鍵字和使用方式及生成的SQL列表

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 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 (parameter bound with appended %)
EndingWith findByFirstnameEndingWith … where x.firstname like ?1 (parameter bound with prepended %)
Containing findByFirstnameContaining … where x.firstname like ?1 (parameter bound wrapped in %)
OrderBy findByAgeOrderByLastnameDesc … where x.age = ?1 order by x.lastname desc
Not findByLastnameNot … where x.lastname <> ?1
In findByAgeIn(Collection<Age> ages)</Age> … where x.age in ?1
NotIn findByAgeNotIn(Collection<Age> age)</Age> … 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)

參考鏈接

https://www.ibm.com/developerworks/cn/opensource/os-cn-spring-jpa/index.html
https://segmentfault.com/a/1190000009866465

完。





ChangeLog

2018-12-22 完成

以上皆爲個人所思所得,如有錯誤歡迎評論區指正。

歡迎轉載,煩請署名並保留原文鏈接。

聯繫郵箱:[email protected]

更多學習筆記見個人博客------>呼延十

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章