Spring Data JPA介紹
Spring Boot中支持的數(shù)據(jù)庫交互方式多種多樣,今天咱就來玩一下Spring Data JPA好了葫录,因為其他的阀湿,咱也還不會呀8鲜臁?陷嘴!
JPA全稱為Java Persistence API(Java持久層API)映砖,它是Sun公司在JavaEE 5中提出的Java持久化規(guī)范。它為Java開發(fā)人員提供了一種對象/關(guān)聯(lián)映射工具灾挨,來管理Java應(yīng)用中的關(guān)系數(shù)據(jù)邑退,JPA吸取了目前Java持久化技術(shù)的優(yōu)點,旨在規(guī)范劳澄、簡化Java對象的持久化工作地技。很多ORM框架都是實現(xiàn)了JPA的規(guī)范,如:Hibernate浴骂、EclipseLink乓土。
Spring Data JPA旨在通過減少實際需要的工作量來顯著改善數(shù)據(jù)訪問層的實現(xiàn)。它在JPA的基礎(chǔ)上做了一些封裝溯警,可以輕松實現(xiàn)基于JPA的存儲庫趣苏。 此模塊處理對基于JPA的數(shù)據(jù)訪問層的增強支持。 它使構(gòu)建使用數(shù)據(jù)訪問技術(shù)的Spring驅(qū)動應(yīng)用程序變得更加容易梯轻。
需要注意的是JPA統(tǒng)一了Java應(yīng)用程序訪問ORM框架的規(guī)范
JPA為我們提供了以下規(guī)范:
- ORM映射元數(shù)據(jù):JPA支持XML和注解兩種元數(shù)據(jù)的形式食磕,元數(shù)據(jù)描述對象和表之間的映射關(guān)系,框架據(jù)此將實體對象持久化到數(shù)據(jù)庫表中喳挑;
- JPA 的API:用來操作實體對象彬伦,執(zhí)行CRUD操作,框架在后臺替我們完成所有的事情伊诵,開發(fā)人員不用再寫SQL了(只能說在一定程度上不用寫SQL单绑,實際情況可能還是會用一些SQL);
- JPQL查詢語言:通過面向?qū)ο蠖敲嫦驍?shù)據(jù)庫的查詢語言查詢數(shù)據(jù)曹宴,避免程序的SQL語句緊密耦合搂橙;
以上的定義引用自網(wǎng)絡(luò)技術(shù)文章,我還在不斷理解與學(xué)習(xí)中笛坦,我們先來Demo一個例子:
P.S.:本演示是基于上期文章創(chuàng)建的項目演進的:
集成Spring Data JPA
以連接Oracle為例(網(wǎng)上有很多Mysql的案例区转,除了配置不一樣,其實使用差不多版扩,有機會我們可以Demo Mysql废离、Mongo的案例)
在項目pom.xml中的dependencies節(jié)點加入以下依賴:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc8</artifactId>
<version>12.2.0.1.0</version>
</dependency>
準(zhǔn)備數(shù)據(jù)庫配置
在src/main底下創(chuàng)建resources文件夾;
-
在resources文件夾內(nèi)新建application.properties文件礁芦;
resources 在application.properties文件內(nèi)輸入數(shù)據(jù)庫的配置信息蜻韭,如:
spring.datasource.url=jdbc:oracle:thin:@//xxdb-scan:xxxx/XXXX
spring.datasource.username=xxxx
spring.datasource.password=xxxxxxxx
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
spring.jpa.show-sql=true
spring.jackson.serialization.indent_output=true
-
啟動項目,驗證數(shù)據(jù)庫配置正確與否;
啟動項目 1
啟動項目 2
創(chuàng)建實體類entity
假設(shè)我們的數(shù)據(jù)庫中有個表叫LEAD表(也叫l(wèi)ead表)湘捎,我們想查詢LEAD表中的數(shù)據(jù)诀豁;
- 創(chuàng)建entity包;
-
在entity包內(nèi)創(chuàng)建實體類Lead.java;
實體類 - 編寫實體類窥妇;
(LEAD 表有多個字段舷胜,我們本例只使用其中的2個字段,一個是lead_id, 一個是email;)
package com.mycompany.sample.entity;
import javax.persistence.*;
/**
* @author : dylanz
* @since : 06/30/2020
**/
@Entity
@Table(name = "LEAD")
public class Lead {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "lead_id")
private int leadId;
@Column(name = "email")
private String email;
public Lead() {
}
public Lead(Integer leadId, String email) {
this.leadId = leadId;
this.email = email;
}
public int getLeadId() {
return leadId;
}
public void setLeadId(int leadId) {
this.leadId = leadId;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
#@Table(name = "LEAD")這個非必需活翩,當(dāng)未提供時烹骨,說明本實體類的表名是實體類類名轉(zhuǎn)換后的名字,如:
# 1.實體類名為Lead時材泄,默認(rèn)表名為:lead表沮焕;
# 2.實體類名為UserPermission時,默認(rèn)表名為:user_permission表拉宗;
#@Column(name = "lead_id")中name為非必需峦树,當(dāng)未提供時,說明數(shù)據(jù)庫的列名為對應(yīng)成員變量轉(zhuǎn)換后的名字旦事,如:
# 1.當(dāng)對應(yīng)的成員變量為private int leadId; 時魁巩,默認(rèn)數(shù)據(jù)庫列名為lead_id;
# 2.當(dāng)對應(yīng)的成員變量為private int email; 時,默認(rèn)數(shù)據(jù)庫列名為email;
#可以看到姐浮,實體類和成員變量均為駝峰結(jié)構(gòu)谷遂,而對應(yīng)的表明和數(shù)據(jù)庫列名為下劃線結(jié)構(gòu);
#盡管如此卖鲤,還是建議補全@Table和@Column肾扰;
#Spring Boot還有很多注解和屬性,可以用于規(guī)范實體類及實體類的列蛋逾,比如長度限制集晚、數(shù)據(jù)類型限制等,
#這里不再詳細(xì)介紹区匣,畢竟我們本例的重點不是介紹這個甩恼。
創(chuàng)建repository
有了實體類之后,我們接下來要創(chuàng)建一個repository接口類沉颂,并在repository接口類內(nèi)實現(xiàn)數(shù)據(jù)庫交互,并且把交互結(jié)果映射到實體類Lead.java
- 創(chuàng)建repository包悦污;
-
在repository包內(nèi)創(chuàng)建repository接口類LeadRepository.java铸屉;
repository接口類 - 編寫repository接口類;
package com.mycompany.sample.repository;
import com.mycompany.sample.entity.Lead;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.stereotype.Repository;
import javax.transaction.Transactional;
/**
* @author : dylanz
* @since : 06/30/2020
**/
@Repository
public interface LeadRepository extends JpaRepository<Lead, Integer> {
@Query(value = "select l from Lead l where leadId=?1")
Lead getLeadByLeadId(Integer leadId);
@Query(value = "select lead_id,email from lead where lead_id=?1", nativeQuery = true)
Lead getLeadByLeadIdWithNativeQuery(Integer leadId);
@Modifying
@Query(value = "update lead set email=?1 where lead_id=?2", nativeQuery = true)
@Transactional
void updateLeadEmail(String email, Integer leadId);
}
#增切端、刪彻坛、改的數(shù)據(jù)庫交互,必須搭配@Modifying使用,并且建議也使用注解@Transactional來處理事務(wù)昌屉,
#即交互失敗钙蒙,Spring Boot會自動幫我們進行回滾。
創(chuàng)建service
repository創(chuàng)建完之后间驮,我們就可以與數(shù)據(jù)庫進行交互了躬厌,接下來我們寫個service調(diào)用repository
- 創(chuàng)建service包;
-
創(chuàng)建service類竞帽;
service類 - 編寫service類扛施;
package com.mycompany.sample.service;
import com.mycompany.sample.entity.Lead;
import com.mycompany.sample.repository.LeadRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author : dylanz
* @since : 06/30/2020
**/
@Service
public class LeadService {
@Autowired
private LeadRepository leadRepository;
public Lead getLead(Integer leadId) {
return leadRepository.getLeadByLeadId(leadId);
}
public Lead getLeadByLeadIdWithNativeQuery(Integer leadId) {
return leadRepository.getLeadByLeadIdWithNativeQuery(leadId);
}
public Lead updateEmail(com.mycompany.sample.domain.Lead lead) {
int leadId = lead.getLeadId();
String email = lead.getEmail();
leadRepository.updateLeadEmail(email, leadId);
Lead updatedLead = leadRepository.getLeadByLeadId(leadId);
if (updatedLead.getEmail() != null && !updatedLead.getEmail().equals(email)) {
throw new InternalError("Unable to update email for leadId: " + leadId + " with email: " + email);
}
return updatedLead;
}
}
開發(fā)帶數(shù)據(jù)庫交互功能的API
service開發(fā)完成后,我們要把該service暴露給客戶端使用屹篓,于是就要創(chuàng)建API了
-
創(chuàng)建Controller疙渣;
Controller - 編寫Controller;
package com.mycompany.sample.controller;
import com.mycompany.sample.entity.Lead;
import com.mycompany.sample.service.LeadService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
/**
* @author : dylanz
* @since : 06/30/2020
**/
@RestController
public class LeadController {
@Autowired
private LeadService leadService;
@GetMapping("/getLead/{leadId}")
@ResponseBody
public Lead getLead(@PathVariable Integer leadId) {
return leadService.getLead(leadId);
}
@GetMapping("/getLead")
@ResponseBody
public Lead getLeadById(@RequestParam("leadId") Integer leadId) {
return leadService.getLeadByLeadIdWithNativeQuery(leadId);
}
@PutMapping(value = "/updateLeadEmail")
@ResponseBody
public Lead updateLeadEmail(@RequestBody com.mycompany.sample.domain.Lead lead) {
return leadService.updateEmail(lead);
}
}
- 為了演示PUT請求堆巧,我還建了個updateLeadEmail API妄荔,并且引入lombok;
- pom.xml中dependencies節(jié)點內(nèi)添加lombok依賴
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
- 建立domain包谍肤;
- 編寫Lead domain;
package com.mycompany.sample.domain;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data;
import java.io.Serializable;
/**
* @author : dylanz
* @since : 06/30/2020
**/
@Data
@JsonIgnoreProperties(ignoreUnknown = true)
public class Lead implements Serializable {
private static final long serialVersionUID = 1L;
private Integer leadId;
private String email;
}
運行Spring Boot項目啦租,見證奇跡
調(diào)用Get API,返回執(zhí)行結(jié)果:
http://127.0.0.1:8080/getLead?leadId=10xxxx46 或 http://127.0.0.1:8080/getLead/10xxxx46
調(diào)用Put API谣沸,返回執(zhí)行結(jié)果:
- 同時項目log中刷钢,我們可以找到被執(zhí)行的sql:
為了能看到sql,關(guān)鍵一步是src/main/resources/application.properties內(nèi)的配置項:spring.jpa.show-sql=true
后臺log
其中第一條SQL是非nativeQuery的乳附,第二内地、三條SQL是nativeQuery的,nativeQuery的SQL就是我們平常寫的sql赋除,而非nativeQuery的SQL阱缓,是Spring Boot JPA幫我們生成的。
到此為止举农,我們已經(jīng)完成了Spring Boot項目中采用Spring Boot JPA方式與數(shù)據(jù)庫交互的實現(xiàn)荆针!
碼字不容易,點贊需積極
謝謝0湓恪:奖场!