The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

快速上手 Spring Data JPA 與 SQL Server 資料庫開發

我們在開發任何 Web 應用程式時,幾乎可以說 99% 的情境都需要存取資料庫。而在 Java 世界裡有個 JPA 標準規範,幫助你大幅簡化資料存取所需的程式碼。而 Spring 框架整合了 JPA 標準,幫助你更好的在應用程式中使用 JPA 來開發資料存取層的邏輯。今天這篇文章我就來好好釐清快速上手的過程。

Spring Data JPA

簡介 JPA 標準

JPA 全名為 Jakarta Persistence API,早期名稱叫 Java Persistence API,主要用來實現一致的 ORM 的開發介面 (API Interface),讓你通過簡單的標注(Annotation)來對應出物件資料之間的關聯關係。

Spring Boot 內建一個 spring-boot-starter-data-jpa 相依套件,裡面定義了更多種相依套件,主要有:

  1. Hibernate: 這是 JPA 標準中最知名的實作
  2. Spring Data JPA: 幫助你實現以 JPA 為主的 Repositories
  3. Spring ORM: 由 Spring 框架核心支援的 ORM 架構

準備 SQL Server 容器

開發資料庫應用之前,當然要先準備好資料庫,這裡我就拿我擅長的 SQL Server 來做示範。在 Docker 的輔助之下,一個命令就可以在 10 秒內啟動一個 SQL Server 資料庫伺服器,以下是啟動的命令與匯入範例資料的步驟:

  1. 啟動 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 密碼登入。

  2. 下載 ContosoUniversity 範例資料庫

    curl -SLO https://gist.github.com/doggy8088/2a2f7075d49b3814d19513426ede3549/raw/ba8c4e7d46597188a17a39de7906d358f18d834d/ContosoUniversity.sql
    
  3. 建立範例資料庫 (含資料匯入)

    docker cp ContosoUniversity.sql sql1:/
    
    docker exec -it sql1 /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P "Ver7CompleXPW" -i /ContosoUniversity.sql
    
  4. 測試連接 ContosoUniversity 資料庫 (列出所有表格清單)

    docker exec sql1 /opt/mssql-tools/bin/sqlcmd -S localhost -U SA -P "Ver7CompleXPW" -d "ContosoUniversity" -W -Q "SELECT name FROM sys.tables"
    

    你也可以透過 SSMSosqlsqlcmdmssql-cli 連接資料庫。

建立 Spring Boot 應用程式

你可以透過 Spring Boot CLI 建立專案,也可以使用 IDE 內建的 Spring Initializr 工具建立專案。

以下我以 Spring Boot CLI 為例進行解說:

  1. 安裝 Spring Boot CLI 命令列工具

    若在 Windows 可用 Chocolatey 進行安裝:

    choco install spring-boot-cli -y
    
  2. 查詢 Spring Boot CLI 可用的參數與清單

    spring init --list
    
  3. 建立 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
    
  4. 使用任何 IDE 開啟專案 (以下使用 VSCode 為例)

    code demojpa
    

開發 Spring Data JPA 的 Spring Boot 應用程式

因為我們在建立專案時,已經加入了 data-jpasqlserver 相依套件,

  1. 設定 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
    
  2. 建立 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;
    }
    
  3. 建立 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> {
    }
    
  4. 撰寫 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 ORMSpring Data JPA 都不是個很小的主題,還有許多內涵等著大家去發掘,建議多閱讀官網,學習更進階的各種用法。

相關連結