Spring Data Jpa初體驗(內含demo)

前言

我一直在使用Mybatis做爲持久化框架,而且以爲Mybatis十分的不錯,足夠靈活,雖然說須要本身手寫sql,可是這也是我以爲的一個優勢,直觀而且優化方便.html

可是我以爲JPA規範也有其優勢,好比說簡單,在一些基本的CRUD操做時,徹底無需手寫SQL.java

所以趁着空閒,對Spring Data JPA作一個瞭解,並簡單的寫一個Demo來學習使用.mysql

定義

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

JPA

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

ORM

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

Hibernate

Hibernate是一種ORM框架,Hibernate在3.2版本開始,已經徹底兼容JPA標準.數據庫

Mybatis

Mybatis是另一種ORM框架.使用它構建項目能夠看Spring Boot Mybatis Web 開發環境搭建編程

Spring Data JPA

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

Spring Data JPA 實現原理

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

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)

參考連接

www.ibm.com/developerwo… segmentfault.com/a/119000000…

完。





ChangeLog

2018-12-22 完成

以上皆爲我的所思所得,若有錯誤歡迎評論區指正。

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

聯繫郵箱:huyanshi2580@gmail.com

更多學習筆記見我的博客------>呼延十

相關文章
相關標籤/搜索