我們在開發任何 Web 應用程式時,幾乎可以說 99% 的情境都需要存取資料庫。而在 Java 世界裡有個 JPA 標準規範,幫助你大幅簡化資料存取所需的程式碼。而 Spring 框架整合了 JPA 標準,幫助你更好的在應用程式中使用 JPA 來開發資料存取層的邏輯。今天這篇文章我就來好好釐清快速上手的過程。
簡介 JPA 標準
JPA 全名為 Jakarta Persistence API,早期名稱叫 Java Persistence API,主要用來實現一致的 ORM 的開發介面 (API Interface),讓你通過簡單的標注(Annotation)來對應出物件與資料之間的關聯關係。
Spring Boot 內建一個 spring-boot-starter-data-jpa
相依套件,裡面定義了更多種相依套件,主要有:
Hibernate
: 這是 JPA 標準中最知名的實作
Spring Data JPA
: 幫助你實現以 JPA 為主的 Repositories
Spring ORM
: 由 Spring 框架核心支援的 ORM 架構
準備 SQL Server 容器
開發資料庫應用之前,當然要先準備好資料庫,這裡我就拿我擅長的 SQL Server 來做示範。在 Docker 的輔助之下,一個命令就可以在 10 秒內啟動一個 SQL Server 資料庫伺服器,以下是啟動的命令與匯入範例資料的步驟:
-
啟動 SQL Server 容器
docker run --rm -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=Ver7CompleXPW" -p 1433:1433 --name sql1 -d mcr.microsoft.com/mssql/server:2019-latest
上述命令能讓你從本機連接 localhost:1433
伺服器,並使用 sa
帳號以 Ver7CompleXPW
密碼登入。
-
下載 ContosoUniversity 範例資料庫
curl -SLO https://gist.github.com/doggy8088/2a2f7075d49b3814d19513426ede3549/raw/ba8c4e7d46597188a17a39de7906d358f18d834d/ContosoUniversity.sql
-
建立範例資料庫 (含資料匯入)
docker cp ContosoUniversity.sql sql1:/
docker exec -it sql1 /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P "Ver7CompleXPW" -i /ContosoUniversity.sql
-
測試連接 ContosoUniversity 資料庫 (列出所有表格清單)
docker exec sql1 /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P "Ver7CompleXPW" -d "ContosoUniversity" -W -Q "SELECT name FROM sys.tables"
你也可以透過 SSMS、osql、sqlcmd 或 mssql-cli 連接資料庫。
建立 Spring Boot 應用程式
你可以透過 Spring Boot CLI 建立專案,也可以使用 IDE 內建的 Spring Initializr 工具建立專案。
以下我以 Spring Boot CLI 為例進行解說:
-
安裝 Spring Boot CLI 命令列工具
若在 Windows 可用 Chocolatey 進行安裝:
choco install spring-boot-cli -y
-
查詢 Spring Boot CLI 可用的參數與清單
spring init --list
-
建立 Spring Boot 專案
這裡我們選擇四個相依套件,分別是 Spring Web (web
), Lombok (lombok
), Spring Data JPA (data-jpa
), MS SQL Server Driver (sqlserver
) 這四個:
spring init '-dweb,lombok,data-jpa,sqlserver' demojpa
-
使用任何 IDE 開啟專案 (以下使用 VSCode 為例)
code demojpa
開發 Spring Data JPA 的 Spring Boot 應用程式
因為我們在建立專案時,已經加入了 data-jpa
與 sqlserver
相依套件,
-
設定 JPA 的 SQL Server Driver
編輯 src/main/resources/application.properties
屬性檔,加入連接字串與相關參數
spring.datasource.url=jdbc:sqlserver://127.0.0.1:1433;databaseName=ContosoUniversity;sslProtocol=TLS;Encrypt=false
spring.datasource.username=sa
spring.datasource.password=Ver7CompleXPW
spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver
spring.jpa.database=sql-server
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
-
建立 Entity 實體類別
傳統的 JPA 實作都需要定義一個 persistence.xml
檔案,但在 Spring 框架中不需要定義這個檔,而是只要你的類別有套用 @Entity
標注(Annotation),就可以透過 "Entity Scanning" (實體掃描) 機制自動註冊到 Spring 的 DI 容器中。預設只要是在有標注 @EnableAutoConfiguration
或 @SpringBootApplication
的類別,Spring 框架都會自動找出所有啟動類別的 sub-package 下的所有實體類別(Entity Class)。
定義實體類別應該是使用 JPA 最繁瑣的部分,因為要定義物件與資料的對應關係,所以可以藉由一些 IDE 提供的工具自動產生,或透過 Lombok 簡化程式碼。以下是實體類別的範例:
import javax.persistence.*;
import lombok.Data;
@Data
@Entity
@Table(name = "Course", schema = "dbo", catalog = "ContosoUniversity")
public class CourseEntity {
@Id
@Column(name = "CourseID", nullable = false)
private int courseId;
@Basic
@Column(name = "Title", nullable = true, length = 50)
private String title;
@Basic
@Column(name = "Credits", nullable = false)
private int credits;
}
-
建立 Spring Data JPA Repositories 類別
所謂的 Spring Data JPA Repositories 只是一個介面(interface)而已,有趣的地方來了,Spring 設計成只要在這些 Repository 介面中撰寫特定命名規則的方法名稱(Method Name),就可以自動查詢到結果,非常酷!
以下是一份最精簡的 Repository 範例程式,在 CrudRepository<T,ID> 介面的幫助下,預設已經提供許多常用的資料存取操作:
import org.springframework.data.repository.CrudRepository;
import org.springframework.stereotype.Repository;
import com.example.demojpa.entity.CourseEntity;
@Repository
public interface CourseEntityRepository extends CrudRepository<CourseEntity, Integer> {
}
-
撰寫 API Controller 控制器
以下僅提供簡易使用範例,實務上我們的 Controller 應該盡可能的結構清晰、語意清楚,所以可能會多包一層 Service 回應 DTO 結果,例外處理也可能透過 @ControllerAdvice
來進行集中處理,請自行斟酌調整程式碼結構,本文主要介紹 JPA 的存取方法。
@RestController
@RequestMapping("/")
public class HomeController {
private CourseEntityRepository repository;
public HomeController(CourseEntityRepository repository) {
this.repository = repository;
}
@GetMapping
public ResponseEntity<List<CourseEntity>> getAllCourseEntity() {
try {
List<CourseEntity> items = new ArrayList<CourseEntity>();
repository.findAll().forEach(items::add);
return ResponseEntity.ok(items);
} catch (Exception e) {
return ResponseEntity.internalServerError().build();
}
}
}
總結
其實 Hibernate ORM 與 Spring Data JPA 都不是個很小的主題,還有許多內涵等著大家去發掘,建議多閱讀官網,學習更進階的各種用法。
相關連結