企業級應用中,通常一個業務系統並不是孤立存在的,而是需要與企業、部門或者是外部的已有系統進行整合。一般而言,系統整合的資料和介面互動方式通常有以下幾種:
不論使用那種方式進行通訊和傳輸,在主系統(即,正在實施的系統)中,還需要考慮的一個問題是,第三方系統過來的資料需不需要儲存?如果主系統要基於接入的資料進行進一步處理,則通常需要儲存資料。而有時候由於資料安全方面的原因,亦或是考慮到本地儲存資料後還存在資料同步與重複儲存的問題,第三方系統的資料過來後,主系統並不需要儲存資料,只是提供展示和操作介面。
本文中,我們以常見的 REST API 通訊為例,看看 Jmix 應用是如何直接使用外部資料的(這裡我們不儲存外部資料)。
我們假設外部資料來源通過 REST API 提供關於專案(project)和任務(task)的 CRUD 介面。
首先,我們在主系統中定義兩個 DTO 實體:Project
和 Task
,用 Jmix Studio 可以直接建立 DTO 實體:
然後,在主系統中我們需要定義兩個 Services,專門用來對 Project
和 Task
實體進行 CRUD 操作,而這些操作裡面,其實是呼叫了外部系統提供的 REST 介面,以 TaskService
為例:
@Component
public class TaskService {
public static final String TASKS_BASE_URL = "http://localhost:18080/tasks";
@Autowired
private RestTemplate restTemplate;
public List<Task> loadTasks() {
Task[] tasks = restTemplate.getForObject(TASKS_BASE_URL, Task[].class);
return Arrays.asList(tasks);
}
public Task saveTask(Task task) {
String url = task.getId() != null ?
TASKS_BASE_URL + "/" + task.getId() :
TASKS_BASE_URL;
ResponseEntity<Task> response = restTemplate.postForEntity(url, task, Task.class);
return response.getBody();
}
public void deleteTask(Task task) {
restTemplate.delete(TASKS_BASE_URL + "/" + task.getId());
}
}
Project
DTO 的建立過程和 ProjectService
的內容與上面步驟類似,這裡就不再贅述。
第一種方式是使用資料載入代理和提交代理方法,將原本使用 DataManager
進行資料載入和寫入的相應方法替換為使用我們自定義的服務:
這裡,我們選擇 Task
DTO 和它的列表頁和編輯頁作為範例。
首先,在列表頁新增資料載入的代理,在介面選中資料載入器後,雙擊代理方法中的 <empty>
標籤,Studio 會自動生成方法並跳轉到方法定義,新增自定義邏輯:
然後,在編輯頁新增資料提交代理,這裡需要在 XML 中選中 data
節點,然後雙擊生成 commitDelegate
:
這樣就完成了我們需要實現的功能。是不是很簡單?
Jmix 中,資料儲存可以進行自定義,通過自定義的資料儲存,可以像處理 JPA 實體一樣,使用 DataManager
處理 DTO 實體。在檢測到 DTO 實體關聯到某個自定義儲存後,DataManager
會將 CRUD 操作都通過代理執行,並且能處理對 DTO 實體的參照。具體實現框架如下:
可以看到,使用這種方式不需要對介面中的實體操作進行攔截,而是將所有對於外部系統的介面呼叫都交給 DataManager
通過資料儲存進行分發。
這裡,我們選擇 Project
DTO 作為範例,建立相應的資料儲存:
ProjectDataStore
實現 DataStore
介面:@Component("sample_ProjectDataStore")
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class ProjectDataStore implements DataStore {
// 注入 ProjectService 用於具體的操作
@Autowired
private ProjectService projectService;
// 後面需要實現介面中的方法,主要是通過 projectService 對資料進行 CRUD,這裡省略。
...
}
StoreDescriptor
介面的類。必須是一個 Spring 單例 bean,其中 getBeanName()
方法必須返回上一步建立的 bean 的名稱:@Component("sample_ProjectDataStoreDescriptor")
public class ProjectDataStoreDescriptor implements StoreDescriptor {
@Override
public String getBeanName() {
return "sample_ProjectDataStore";
}
@Override
public boolean isJpa() {
return false;
}
}
application.properties
中新增對資料儲存的設定:# 如果有多個,則以逗號分隔
jmix.core.additional-stores = projectds
# 設定名稱為 jmix.core.storeDescriptor_<store_name>
jmix.core.store-descriptor_projectds = sample_ProjectDataStoreDescriptor
Project
實體新增 @Store
註解:@Store(name = "projectds")
@JmixEntity
public class Project {
...
}
通過這幾步,我們完成了資料儲存的實現和設定。Project
的列表頁和編輯也不需要做任何改動,並且,任何通過 DataManager
對 Project
DTO 的操作就像操作 JPA 實體一樣方便,可以在服務層和 UI 層呼叫。
如果外部 API 提供了豐富的操作介面,比如 CRUD、分頁、排序甚至支援某種查詢語言,那麼我們推薦建立一個自定義的資料儲存。這種為資料操作 Service 提供自定義資料儲存的方式,更貼近 Jmix 原生的開發方式。另外,如果需要的話,自定義的資料儲存也可以繼承 AbstractDataStore
類,這個類是 Jmix 內建 JpaDataStore
的父類別。通過這個類派生可以使用框架提供的一些機制,比如資料存取安全和對外部資料的審計。
但是如果外部 API 只提供了幾個簡單的介面,這種情況我們建議直接在 UI 層使用資料讀寫代理的方式。
範例的完整程式碼請存取 GitHub。