使用 Spring Boot 和 Jmix 進行快速 Web 應用程式開發
一、簡介
在本教學中,我們將學習如何使用Jmix Studio 和Jmix Framework for IntelliJ IDEA。我們將為 Spring Boot 應用程式建立一個全端 MVP,用於追蹤員工費用。從快速設定專案環境到生成響應式 UI 和實施基於角色的訪問,我們將了解該框架如何在保持靈活性的同時加速開發。
2. 項目概況及設置
我們的 MVP 是一個 Web 應用程序,員工可以在其中登記他們的費用。管理員定義一些基本類型的費用,例如午餐、計程車和機票,而員工則從這些費用中進行選擇,並添加諸如花費金額、日期和任何其他必要詳細資訊等詳細資訊。我們還將添加授權角色來區分管理員和員工,以便員工只能為自己登記費用,但管理員可以完全存取任何使用者的費用。
只需在 Jmix Studio 中點擊幾下,我們就會提供豐富且反應迅速的 UI,用於管理使用者和費用。這些將包括開箱即用的高級搜尋過濾器和基本表單驗證:
2.1.環境設定
Jmix 需要一個帳戶。因此,在免費啟動一個之後,我們將安裝這些工具來設定我們的環境:
2.2.在 IntelliJ 中建立 Jmix 項目
工具準備就緒後,我們將開啟 IntelliJ 並看到新的「Jmix Project」選項現已可用:
我們將選擇“Full-Stack Application (Java)”並按一下“下一步”。然後,我們將選擇項目名稱和基礎包,僅此而已。我們必須等到所有依賴項都下載完畢並對專案建立索引。
最重要的是,當我們第一次建立 Jmix 專案時,我們將看到一個登入對話方塊。登入後,我們點擊 IntelliJ 側邊欄中的 Jmix 按鈕來看看我們的專案結構是什麼樣的:
該結構包括基本功能,例如User
實體(代表員工)、一些 UI 視圖和基本身份驗證/授權功能。它還包含一個用於國際化的訊息包和一個基本的 CSS 主題。透過 Jmix 外掛程式存取專案結構還允許快速執行各種操作,例如新視圖或實體。
此外,它是一個常規的 Spring Boot 應用程序,因此很熟悉且易於修改。最重要的是,對於 Spring Boot 新手來說,可以輕鬆瀏覽專案並了解其工作原理。
3. 建立新實體
建立新實體非常簡單,只需專注於外掛程式面板,點擊加號,然後選擇「新 JPA 實體...」我們需要選擇類別名稱並確保為實體類型欄位選擇「實體」。該插件還允許我們選擇 ID 類型,預設為UUID
。 Jmix 也建議檢查「版本化」選項,因為它會啟動實體的樂觀鎖定。
此外,建立實體後,我們可以透過點擊屬性部分中的加號來新增屬性。讓我們從代表常見費用類型的Expense
實體開始:
由於 Jmix 已經新增了id
和version
屬性,因此我們現在只加入String
類型的name
屬性。最重要的是,透過 Studio 建立實體也會產生 Liquibase 變更日誌,以便為我們建立或修改資料庫物件。
在新增屬性時,大多數選項都是不言自明的,但有一些值得注意的提及:
- 唯讀:不為屬性建立 setter,且在 UI 中不可編輯
- 強制:在資料庫中建立非空約束並在 UI 中檢查
- 瞬態:它不會在資料庫中建立列,並且不會在 UI 中顯示
在Expense
實體仍然打開的情況下,我們將切換到「Text」標籤來尋找產生的程式碼,該程式碼將 JPA 註解與 Jmix 特定的註解混合在一起:
@JmixEntity
@Table(name = "EXPENSE")
@Entity
public class Expense {
@JmixGeneratedValue
@Column(name = "ID", nullable = false)
@Id
private UUID id;
@Column(name = "VERSION", nullable = false)
@Version
private Integer version;
@InstanceName
@Column(name = "NAME", nullable = false)
@NotNull
private String name;
// standard getters and setters
}
對程式碼的變更反映在「設計器」標籤中,反之亦然。此外,我們也可以透過程式碼編輯器的內嵌操作使用 JPA 開發工具。讓我們看看在任何行按alt+enter
時出現的選項:
這些選項也出現在頂部面板中。
3.1.為實體新增 CRUD UI
在關注設計器中的實體時,讓我們單擊“視圖”、“建立視圖”,然後單擊“實體列表和詳細資訊視圖”,這將建立一個用於列出和過濾現有項目的頁面以及一個用於建立/檢視/編輯項目的頁面。我們可以為具有完整 CRUD 功能的實體開發 UI,而無需接觸任何程式碼:
此嚮導會建議足夠好的預設值,並瀏覽幾個關鍵選項:
- 實體:任何
@JmixEntity
註解的類別。預設為目前選定的一項 - 套件名稱:預設為專案建立時選擇的基礎套件+視圖+實體名稱
- 父選單項目:預設為與項目一起建立的選單項
- 表操作:預設為所有 CRUD 操作
- 建立通用過濾器:這允許我們在清單視圖中執行基本搜尋過濾
- 取得計劃:預設從行中取得所有列(不包括外鍵)
- 可本地化的訊息:允許我們更改正在建立的兩個頁面的標題
3.2.創建枚舉
我們將把開支限制在幾個類別。為此,我們將建立一個名為ExpenseCategory
的列舉,其中包含一些選項:
- 教育
- 食物
- 健康
- 住房
- 運輸
讓我們使用“新枚舉”選項來創建它:
然後,我們可以將ExpenseCategory
作為新的強制屬性新增到我們的Expense
實體中,並選擇「ENUM」作為屬性類型:
由於我們在建立視圖後創建了此屬性,因此我們可以使用「新增至視圖」按鈕新增它,並選擇我們希望在哪些視圖中看到此屬性。這會產生一個新增到詳細視圖中的選擇標籤:
<formLayout id="form" dataContainer="expenseDc">
<select id="categoryField" property="category"/>
<textField id="nameField" property="name"/>
</formLayout>
此外,在清單檢視中的 columns 標籤中新增了一列:
<columns resizable="true">
<column property="category"/>
<column property="name"/>
</columns>
完成嚮導後,讓我們啟動應用程式。 Jmix Studio 會自動根據資料模式分析資料模型,並產生包含變更的 Liquibase 腳本,因此我們將看到一個對話框,其中顯示要套用的腳本,我們只需執行它們即可繼續:
之後,讓我們看看“應用程式”選單中的“費用”UI:
請注意,我們已經可以管理Expense
項目並且 UI 具有回應能力。此外,我們在登入時填寫了application.properties
。
ui.login.defaultUsername = admin
ui.login.defaultPassword = admin
3.3.建立唯一的約束
為費用名稱建立唯一約束是避免重複的好主意。透過外掛程式添加它時,它會使用@UniqueConstraint
註解添加到我們的 Java 類別中,其中還包括 UI 中的驗證:
同樣,產生的 Liquibase 變更日誌負責將其新增至資料庫:
<databaseChangeLog>
<changeSet id="1" author="expense-tracker">
<addUniqueConstraint columnNames="NAME" constraintName="IDX_EXPENSE_UNQ" tableName="EXPENSE"/>
</changeSet>
</databaseChangeLog>
我們可以透過切換到 Jmix 面板,導航到專案結構中的“使用者介面”,然後打開“訊息包”來個性化違反約束時的錯誤訊息。來自應用程式的所有訊息都在那裡。讓我們新增一個新的:
databaseUniqueConstraintViolation.IDX_EXPENSE_UNQ=An expense with the same name already exists
Jmix 可辨識本地化訊息的一些特定前綴。對於唯一約束違規,我們必須以「 databaseUniqueConstraintViolation
」開始訊息鍵,後面接著資料庫中的約束名稱IDX_EXPENSE_UNQ
。
4. 新增具有引用屬性的實體
我們需要一個新實體來代表員工的Expense
。該外掛支援新增引用屬性,因此讓我們建立一個UserExpense
實體並將其與對Expense,
User
和相關屬性的引用關聯起來:
姓名 | 屬性類型 | 類型 | 基數 | 強制的 |
---|---|---|---|---|
user |
協會 | User |
多對一 | true |
expense |
協會 | Expense |
多對一 | true |
amount |
資料類型 | Double |
– | true |
date |
資料類型 | LocalDate |
– | true |
details |
資料類型 | String |
– | false |
我們的新實體也使用“版本化”選項:
開啟我們新建立的實體,我們可以看到 Jmix 如何使用熟悉的 JPA 註解並索引我們的外鍵:
@JmixEntity
@Table(name = "USER_EXPENSE", indexes = {
@Index(name = "IDX_USER_EXPENSE_USER", columnList = "USER_ID"),
@Index(name = "IDX_USER_EXPENSE_EXPENSE", columnList = "EXPENSE_ID")
})
@Entity
public class UserExpense {
// standard ID and version fields...
}
此外,它使用FetchType.LAZY
進行關聯,而不是預設的EAGER
,因此我們不會意外地影響效能。讓我們檢查產生的User
關聯:
@JoinColumn(name = "USER_ID", nullable = false)
@NotNull
@ManyToOne(fetch = FetchType.LAZY, optional = false)
private User user;
此關聯使用 JPA 註釋,因此以後如有必要可以輕鬆變更。
4.1.新增日期驗證
當我們轉到設計器中的“屬性”部分並選擇日期屬性時,我們注意到出現了一些驗證選項。讓我們點擊“未設定”將其設定為 PastOrPresent。我們可以透過點擊地球圖標而不是使用文字訊息來包含本地化訊息:
此驗證可阻止使用者建立未來日期的費用。包含此註解也會關閉 Jmix 在我們的檢視中建立的日期選擇器元件的未來日期選擇。
4.2.建立主從視圖
我們為Expense
實體建立了清單視圖和詳細視圖。現在,對於UserExpense
實體,我們嘗試一下Master-detail view
,它將兩者結合在一個 UI 中。這次,在實體清單/詳細資訊取得計畫中,我們將新增expense
屬性,以將它們包含在列出所有user
費用的初始查詢中。並且,僅對於詳細資訊獲取計劃,我們將添加user
屬性,以便我們可以查詢打開expense
詳細資訊所需的所有資訊:
和以前一樣,我們將在應用程式選單中的「使用者費用」下找到新視圖,準備包含一些項目:
檢查user-expense-list-view.xml
產生的程式碼,我們會注意到外掛程式已經為我們包含了複雜的元件,例如日期選擇器,因此我們不必費心前端的東西:
<datePicker id="dateField" property="date"/>
但是,此頁面向應用程式中的任何使用者提供完全存取權限。稍後我們將看到如何控制存取。
4.3.新增組合屬性
現在,讓我們回到User
實體並新增一個expenses
屬性,以在詳細資料檢視中列出使用者費用以供快速參考。我們將透過新增一個「組合」類型的新屬性並選擇具有一對多基數的UserExpense
來實現此目的:
若要將其新增至使用者詳細資料視圖,請在設計器中選擇expenses
屬性,然後按一下「新增至視圖」按鈕,選取User.detail
佈局檢視中的方塊:
如果我們檢查user-detail-view.xml
,我們會看到 Jmix 為expenses
屬性新增了一個fetchPlan
:
<fetchPlan extends="_base">
<property name="expenses" fetchPlan="_base"/>
</fetchPlan>
取得計劃透過在初始查詢中包含任何必要的聯接而不是進行額外的查詢來幫助避免 N+1 問題。我們還將找到一個用於綁定視圖物件的資料容器:
<collection id="expensesDc" property="expenses"/>
以及允許對費用進行 CRUD 操作的資料網格元素:
<dataGrid id="expensesDataGrid" dataContainer="expensesDc">
<actions>
<action id="create" type="list_create"/>
<action id="edit" type="list_edit"/>
<action id="remove" type="list_remove"/>
</actions>
<columns>
<column property="version"/>
<column property="amount"/>
...
</columns>
</dataGrid>
這些都是使用 Jmix 的聲明性佈局標籤和元件定義的。
4.4.將取得的列新增至現有視圖
讓我們透過將Expense
類別中的屬性新增到我們建立的expensesDataGrid
元件中來探索這個宣告式佈局定義的工作原理。首先,我們將開啟user-detail-view.xml
並選擇資料網格。然後,在 Jmix UI 面板中,我們按columns
並選擇「新增」和「列」。最後,讓我們從expense
對像中選擇name
和category
屬性:
除了接收新列元素的資料網格之外,獲取計劃現在還包括expense
對象:
<fetchPlan extends="_base">
<property name="expenses" fetchPlan="_base">
<property name="expense" fetchPlan="_base"/>
</property>
</fetchPlan>
因此,我們現在在存取User
詳細資訊視圖時將直接看到用戶費用的資料網格。
4.5.熱部署
使用 Jmix Studio,大多數簡單的更改(例如我們添加的新列)都是熱部署的,因此我們不需要重新啟動應用程式來查看更改;頁面刷新就足夠了:
在本機使用 Jmix 進行開發時,會自動套用更新。
5. 設定使用者權限
存取應用程式時,我們會在「安全」下的「資源角色」選單中看到與專案一起建立的初始安全角色。其中包括完全存取角色和僅接受登入應用程式的最小角色。
我們將創建一個角色,以便員工可以存取UserExpense
選單並管理他們的費用。然後,我們將建立一個行級存取角色來限制用戶,以便他們只能檢索與其 ID 相符的項目。這項限制保證了用戶只能看到自己的支出。
5.1.創造新的資源角色
資源角色控制對特定物件(如實體、實體屬性和 UI 視圖)的存取以及透過 UI 或 API 請求的專案中的操作。由於我們只使用視圖,因此讓我們透過右鍵單擊 Jmix 中的「Security」節點,選擇「New」並確保將安全範圍標記為「UI」來建立一個名為EmployeeRole
的視圖:
該角色開始時為空,僅包含也會出現在 UI 中的程式碼:
@ResourceRole(name = "Employee Role", code = EmployeeRole.CODE, scope = "UI")
public interface EmployeeRole {
String CODE = "employee-role";
}
因此,在我們的EmployeeRole
頁面上,我們按一下Entities
來定義該角色允許對實體執行哪些操作。我們需要對UserExpense
進行所有操作,除了刪除、讀取和更新User
以及讀取Expense
之外,以便我們可以在註冊自己的費用類型時選擇可用的費用類型:
實體 | 創造 | 讀 | 更新 |
---|---|---|---|
Expense |
X |
||
User |
X |
X | |
UserExpense |
X | X |
X |
然後,在User Interface
標籤中,我們將選擇該角色有權存取的視圖。由於視圖可以互連,因此該頁面允許在視圖存取(透過頁面上的元件)或選單存取之間進行選擇。我們的UserExpense
視圖連接到User
視圖(用於顯示其詳細資訊)和Expense
視圖(用於列出可用的費用類型),因此我們將檢查User.list
、 Expense.list
和UserExpense.list
的「View」。最後,對於UserExpense.list
,我們還將檢查“選單”,使其可以透過 UI 進行存取:
5.2.使用 JPQL 策略建立新的行級角色
為了確保員工在存取選單時只能看到自己的費用,我們將建立一個行級策略,該策略將限制每個查詢僅取得與登入User
ID 相符的行。
為此,我們還需要加入 JPQL 策略。因此,讓我們透過右鍵點擊 Jmix 中的「Security」節點並選擇「New」來建立一個名為EmployeeRowLevelAccessRole
的行級角色。然後,在新建立的角色中,我們將按一下“新增策略”,然後按一下“新增 JPQL 策略”為引用User
的實體新增策略:
- 實體:
UserExpense
; where 子句:{E}.user.id = :current_user_id
- 實體:
User
; where 子句:{E}.id = :current_user_id
新增 JPQL 策略時, {E}
代表目前實體。我們還獲得了一些獨特的變量,例如current_user_id
,它解析為當前登入的變數。
最後,我們可以透過存取“安全性”選單中的“資源角色”和“行級角色”,透過 UI 將這些角色指派給任何使用者。或者,更方便的是,透過使用清單檢視中的「角色分配」按鈕,我們可以將這兩個角色新增至清單中的任何人:
https://www.baeldung.com/wp-content/uploads/2024/09/set-employee-role-via-ui.webm
具有這些角色的用戶現在可以訪問Expense
列表視圖並註冊他們的費用。最重要的是,他們無法從其他人那裡獲得費用。
六、結論
在本文中,我們演示瞭如何輕鬆創建功能齊全且安全的 Web 應用程序,強調了 Jmix Studio 在保持高功能和用戶體驗標準的同時顯著加快我們的開發週期的潛力。對 CRUD 操作、基於角色的存取控制和驗證的全面支援確保了安全性和可用性。
與往常一樣,原始碼可以在 GitHub 上取得。