1. 簡介
本教程將介紹如何使用 Jmix Studio 和 Jmix 框架 與 IntelliJ IDEA 配合使用。我們將構建一個完整的 Spring Boot 應用程序,用於跟蹤員工費用。從快速設置項目環境到生成響應式 UI 以及實現基於角色的訪問控制,我們將看到該框架如何加速開發的同時,保持靈活性。
2. 項目概述與設置
我們的 MVP 是一個 Web 應用程序,員工可以在其中註冊他們的費用。管理員可以定義一些基本類型的費用,例如午餐、出租車和機票,而員工則從中選擇幷包含金額、日期和其他必要信息等細節。 我們還將包含授權角色,以區分管理員和員工,以便員工只能註冊自己的費用,而管理員擁有對任何用户的費用訪問權限。
只需在 Jmix Studio 中幾步操作,我們就能包含豐富的響應式 UI,用於管理用户和費用。 這些 UI 將包含高級搜索過濾器和開箱即用的基本表單驗證:
2.1. 環境搭建
Jmix 需要一個賬户。因此,在 創建一個免費賬户後,我們將安裝以下工具來 設置我們的環境:
- IntelliJ IDEA
- Jmix 插件(用於 IntelliJ)
- Java 17 或更高版本
2.2. 在 IntelliJ 中創建 Jmix 項目
使用我們的工具準備就緒後,我們將打開 IntelliJ 並看到一個名為“Jmix 項目”的新選項可用:
我們選擇“全棧應用程序(Java)”並點擊“下一步”。然後,我們選擇項目名稱和基礎包,就完成了。必須等待所有依賴項下載完畢,項目索引完成。
最重要的是,首次創建 Jmix 項目,我們會收到登錄對話框。登錄後,點擊 IntelliJ 側邊欄上的 Jmix 按鈕,以查看項目結構:
該結構包括諸如 User 實體(將代表員工)、幾個 UI 視圖和基本的身份驗證/授權功能等基本功能。它還包括用於國際化的消息包以及基本的 CSS 主題。通過 Jmix 插件訪問項目結構還允許對各種操作(如新視圖或實體)進行快捷方式。
此外,它是一個標準的 Spring Boot 應用,因此對 Spring Boot 新手來説既熟悉又容易修改。最重要的是,對於不熟悉 Spring Boot 的新用户來説,可以輕鬆地瀏覽項目並瞭解其工作原理。
3. 創建新實體
創建一個新實體非常簡單,只需關注插件面板,點擊加號,然後選擇“新建 JPA 實體…”。我們需要選擇類名,並確保“實體”選項在實體類型字段中被選中。插件還允許我們選擇 ID 類型,默認是 UUID。Jmix 建議檢查“版本化”選項,因為它會為實體啓用樂觀鎖。
創建實體後,我們可以在屬性部分點擊加號添加新屬性。 讓我們從 費用實體來表示常見的費用類型:
由於 Jmix 已經添加了 id 和 version 屬性,我們現在只需添加 name 屬性,類型為 String。
最重要的是,通過 Studio 創建實體也會生成 Liquibase 變更日誌,以便為我們創建或修改數據庫對象。
添加新屬性時,大多數選項都具有自解釋性,但以下選項值得注意:
- 只讀:不為屬性創建 getter,也不會在 UI 中進行編輯
- 必需:在數據庫中創建非空約束,並在 UI 中進行驗證
- 瞬態:不創建數據庫中的列,也不會在 UI 中顯示
在 費用實體仍然打開時,我們將切換到“文本”選項卡以查找生成的代碼,該代碼將 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
在 Designer 中專注於一個實體,點擊“視圖”、“創建視圖”、“實體列表和詳細視圖”,會創建一個用於列出和過濾現有項目以及創建/查看/編輯項目的頁面。 我們可以開發具有完整 CRUD 功能的實體 UI,而無需修改任何代碼:
嚮導會提供合理的默認設置,並會引導您選擇一些關鍵選項:
- 實體:任何帶有 @JmixEntity 註解的類。 默認情況下,選擇當前所選的類
- 包名:默認情況下,設置為項目創建時選擇的基包、視圖和實體的名稱
- 父菜單項:默認情況下,設置為與項目創建時創建的菜單項
- 表單操作:默認情況下,包含所有 CRUD 操作
- 創建通用過濾器:允許我們在列表視圖中執行基本的搜索過濾
- 獲取計劃:默認情況下,從行中獲取所有列(不包括外鍵)
- 本地化消息:允許我們更改創建的兩個頁面的標題
3.2. 創建枚舉
我們將限制開支到幾個類別。為此,我們將創建一個名為 ExpenseCategory 的枚舉,其中包含幾個選項:
- 教育
- 食品
- 醫療
- 住房
- 交通
讓我們使用“新建枚舉”選項來創建它:
然後,我們可以將 ExpenseCategory 添加到我們的 Expense 實體中作為一個新的強制屬性,並選擇“ENUM”作為屬性類型:
由於我們創建了這個屬性是在創建視圖之後,我們可以使用“添加到視圖”按鈕將其添加到視圖中,並選擇要在哪些視圖中查看此屬性。這會生成一個添加到詳細視圖中的 select 標籤:
<formLayout id="form" dataContainer="expenseDc">
<select id="categoryField" property="category"/>
<textField id="nameField" property="name"/>
</formLayout>此外,在列表視圖中添加了一個“列”標籤。
<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 = admin3.3. 創建唯一約束
為支出名稱創建唯一約束是一個好主意,可以避免重複。 通過插件添加時,它會被添加到我們的 Java 類中,帶有 <em @UniqueConstraint 標註,同時還包括 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 existsJmix 識別幾個特定的前綴用於本地化消息。對於 唯一約束違反,我們的消息鍵必須以 “databaseUniqueConstraintViolation” 開頭,後跟數據庫中的約束名稱,IDX_EXPENSE_UNQ。
4. 使用引用屬性添加實體
我們需要創建一個新的實體來表示員工的 費用。該插件支持添加引用屬性,因此我們創建一個 用户費用 實體並將其與對 費用、用户以及相關屬性的引用關聯起來:
| NAME | ATTRIBUTE TYPE | TYPE | CARDINALITY | MANDATORY |
|---|---|---|---|---|
| user | ASSOCIATION | User | Many to One | true |
| expense | ASSOCIATION | Expense | Many to One | true |
| amount | DATATYPE | Double | – | true |
| date | DATATYPE | LocalDate | – | true |
| details | DATATYPE | 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. 添加日期驗證
當我們前往“屬性”部分,並在 Designer 中選擇日期屬性時,我們會注意到出現了一些驗證選項。點擊“未設置”以將其設置為 PastOrPresent。我們可以通過點擊地球圖標而不是使用字面消息來包含本地化消息:
此驗證阻止用户創建具有未來日期的費用。 同時,啓用此註釋還會關閉 Jmix 在我們的視圖中創建的日期選擇器組件中選擇未來日期的功能。
4.2. 創建主-詳情視圖
我們已為 費用 實體創建了列表視圖和詳情視圖。現在,對於 用户費用 實體,讓我們嘗試 主-詳情視圖,它將兩者合併到一個 UI 中。在此過程中,在實體列表/詳情獲取計劃中,我們將添加 費用 屬性,以便將其包含在初始查詢中,該查詢列出所有 用户 費用。 並且,僅在詳情獲取計劃中,我們將添加 用户 屬性,以便我們可以查詢所有必要的信息以打開 費用 的詳情:
如前所述,我們可以在應用程序菜單中找到我們的新視圖,在“用户費用”下,準備添加一些項目:
檢查生成的代碼 user-expense-list-view.xml,我們注意到插件已經為我們包含了一些複雜的組件,例如日曆選擇器,因此我們不必擔心前端工作:
<datePicker id="dateField" property="date"/>然而,此頁面為應用程序中的任何用户提供全方位訪問權限。稍後我們將學習如何控制訪問權限。
4.3. 添加組合屬性
現在,讓我們回到 User 實體,並添加 expenses 屬性,以便在我們的詳情視圖中快速參考用户的支出。 我們將通過添加具有“組合”類型的全新屬性,並選擇 UserExpense 具有一對多關係來實現:
要將其添加到用户詳情視圖中,請在 Designer 中選擇 expenses 屬性,然後單擊“添加到視圖”按鈕,並在 User.detail 佈局視圖中選中其複選框:
如果我們在 user-detail-view.xml 中檢查,我們會看到 Jmix 添加了一個 fetchPlan 用於 expenses 屬性:
<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 屬性:
除了數據網格接收到新的列元素之外,fetch 計劃現在還包括 expense 對象:
<fetchPlan extends="_base">
<property name="expenses" fetchPlan="_base">
<property name="expense" fetchPlan="_base"/>
</property>
</fetchPlan>因此,我們現在可以直接在訪問 User 詳情視圖時看到用户開支的數據網格。
4.5. 熱部署 (Hot Deploy)
使用 Jmix Studio,大部分簡單的更改——例如我們添加的新列——都支持熱部署,因此我們無需重新啓動應用程序即可查看更改;只需頁面刷新即可:
在本地使用 Jmix 開發時,更新會自動應用。
5. 設置用户權限
當訪問應用程序時,在“安全”下的“資源角色”菜單中,我們會看到與項目一同創建的初始安全角色。其中包括一個具有完整訪問權限的角色和一個僅用於登錄應用程序的最小角色。
我們將創建一個角色,允許員工訪問 <em >UserExpense</em> 菜單並管理他們的費用。然後,我們將創建一個行級訪問角色,以限制用户只能檢索與其 ID 匹配的項。 這種限制確保用户只能查看自己的費用。
5.1. 創建新的資源角色
資源角色控制對項目中的特定對象(如實體、實體屬性和 UI 視圖)以及通過 UI 或 API 請求的訪問和操作。由於我們僅使用視圖,讓我們創建一個名為 EmployeeRole 的角色,通過在 Jmix 中右鍵單擊“Security”節點,選擇“New”,並確保將安全範圍標記為“UI”:
角色初始為空,僅包含將在 UI 中使用的代碼:
@ResourceRole(name = "Employee Role", code = EmployeeRole.CODE, scope = "UI")
public interface EmployeeRole {
String CODE = "employee-role";
}因此,在我們的 EmployeeRole 頁面上,點擊 Entities 以定義該角色允許對實體執行的操作。 我們需要對 UserExpense 執行的所有操作,除了刪除,以及對 User 的讀取和更新,以及對 Expense 的讀取,以便我們在註冊自定義費用類型時可以選擇可用費用類型:
| 實體 | 創建 | 讀取 | 更新 |
|---|---|---|---|
| 費用 | X | ||
| 用户 | X | X | |
| UserExpense | X | X | X |
然後,在 用户界面 選項卡中,選擇該角色可以訪問的視圖。 由於視圖可以相互連接,該頁面允許選擇視圖訪問(通過頁面上的組件)或菜單訪問。 我們的 UserExpense 視圖與 User 視圖(用於顯示其詳細信息)和 Expense 視圖(用於列出可用費用類型)相關聯,因此我們將選中“查看”選項對於 User.list、Expense.list 和 UserExpense.list。 最後,對於 UserExpense.list,我們還將選中“菜單”,使其可通過 UI 訪問:
5.2. 創建具有 JPQL 策略的新行級別角色
為了確保員工在訪問菜單時只能看到自己的費用,我們將創建一個 行級別策略,它將限制每個查詢僅檢索與已登錄 User 對象的 ID 匹配的行。
要做到這一點,我們還需要添加一個 JPQL 策略。因此,讓我們創建一個名為 EmployeeRowLevelAccessRole 的行級別角色,通過右鍵單擊 Jmix 中的“Security”節點並選擇“New”來完成。然後在我們創建的新角色中,單擊“Add Policy”並選擇“Add JPQL Policy”以添加用於引用 User 的實體的策略:
- 實體:UserExpense;where 子句:{E}.user.id = :current_user_id
- 實體:User;where 子句:{E}.id = :current_user_id
在添加 JPQL 策略時,{E}表示當前實體。我們還獲得了一些獨特的變量,例如 current_user_id,它解析為當前已登錄的用户 ID。 插件中提供自動補全功能,以便我們輕鬆找到所需的內容。
最終,我們可以通過訪問“Resource roles”和“Row-level roles”在“Security”菜單中將這些角色分配給任何用户,或者更方便地,通過列表視圖中的“Role assignments”按鈕,將這兩個角色添加到列表中中的任何用户:
擁有這些角色的用户現在可以訪問“Expense”列表視圖並註冊他們的費用。最重要的是,他們將無法訪問其他人的費用。
6. 結論
在本文中,我們展示瞭如何輕鬆創建功能完善且安全的 Web 應用程序,突出了 Jmix Studio 在顯著加速我們的開發週期同時保持高功能性和用户體驗標準方面的潛力。對 CRUD 操作、基於角色的訪問控制和驗證的全面支持,確保了安全性和可用性。
如往常一樣,源代碼可在 GitHub 上找到 上。