資料分類
為維護資料的可讀性,區塊鏈預先配置了不同業務情境下對應的資料分類,作為所有使用者的存證資料格式約定。上鏈資料必須滿足規範要求。
前置條件
在閱讀此 開發樣本 前,請與您所在的區塊鏈的 管理員 溝通確定已配置的資料分類,如果您是管理員,請參考 配置資料分類 一節完成資料分類的配置;管理員完成資料分類配置後,就可以下載並解壓SDK包,完成本文各樣本講解的功能開發。
SDK包下載方式為,登入BaaS平台,在左側導覽列選擇 區塊鏈列表,在列表中找到您希望訪問的區塊鏈,滑鼠選中右側 更多,並點擊 下載簽署憑證 和 下載SDK。
您可以在client-sdk.zip包內的 schema.txt 檔案中查看區塊鏈的資料分類。一條區塊鏈可以包含多個業務資料分類(Category),鏈上會為每個分類分配一個Category ID,分類配置完成後,使用者可以通過識別資料的Category ID在鏈上檢索和過濾存證資料。
開發樣本
這裡通過一個簡單的schema.txt檔案樣本來示範如何構造、讀取存證資料,請對照您自己的檔案內容,將GuestInfo類型替換為您實際的資料分類。檔案聲明了一個名為GuestInfo的Category,包含三個成員,分別是姓名、生日和郵件地址,均為字串類型,注釋中說明了成員資料須滿足的約束條件。
請注意:實際開發時,請將GuestInfo替換為您本地schema.txt中實際定義的Category。
// schema.txt定義:商家顧客資訊
GuestInfo {
String userName; // 姓名,要求非空,長度在20以內
String birthday; // 生日,要求非空
String email; // 郵件地址,要求非空,符合電子郵件格式
}對照規範要求,在client代碼中調用GuestInfoBuilder構造一條存證資料。
// client代碼
// 構造存證資料
GuestInfoBuilder builder = new GuestInfoBuilder();
byte[] bizData = builder.buildUserName("Bob")
.buildBirthday("2000-01-01")
.buildEmail("bob@inc.com")
.build();存證資料構造完成了,接下來構造上鏈的payload,這裡使用者需要瞭解一個枚舉類——BizCategory。SDK根據schema.txt的約定,預先產生了這一枚舉類型,協助使用者標識不同的Category。本例中的BizCategory如下所示。
// SDK的BizCategory聲明
public class BizCategory {
public static final int GuestInfo = 0x00010001;
...
}接下來我們把存證資料傳入payload,指定Category為BizCategory.GuestInfo,構造一個資料存證交易,並通過client發送出去,存證上鏈的過程就完成了。
// client代碼
// 建立ContentOnlyNotary Transaction
TransactionDO tx = TransactionBuilder.getContentOnlyNotaryPayloadBuilder()
.setContent(bizData)//設定需要存證的資料
.setTimestamp(System.currentTimeMillis())//設定業務時間
.setCategory(BizCategory.GuestInfo)//通過BizCategory設定業務分類
.build();//build Transaction
// 發送Transaction
Response<TransactionDO> response = client.sendTransaction(tx);存證資料上鏈後,需要等待一段時間才能成塊,出塊後就可以查詢資料了。在讀取資料時,使用者也同樣通過BizCategory.GuestInfo來識別資料物件類型,將存證資料轉換回對象。擷取塊資料有兩種模式,模式一是監聽成塊訊息,處理新資料區塊;模式二是通過定時任務,每隔一段時間拉取新的資料區塊。詳情可參見 最佳實務。
下面展示幾個完整的樣本。
例1. 簡單的存證資料寫入
// 載入client設定檔
Properties p = new Properties();
p.load(new FileInputStream("sdk.properties"));
ClientConfig config = new ClientPropertyConfig(p);
// 使用指定client配置初始化client
Client client = new Client(config);
// 構造存證資料
GuestInfoBuilder builder = new GuestInfoBuilder();
byte[] bizData = builder.buildUserName("Bob")
.buildBirthday("2000-01-01")
.buildEmail("bob@inc.com")
.build();
// 建立ContentOnlyNotary Transaction
TransactionDO tx = TransactionBuilder.getContentOnlyNotaryPayloadBuilder()
.setContent(bizData)//設定需要存證的資料
.setTimestamp(System.currentTimeMillis())//設定業務時間
.setCategory(BizCategory.GuestInfo)//設定業務分類
.build();//build Transaction
// 發送Transaction
Response<TransactionDO> response = client.sendTransaction(tx);
if(response.isSuccess()){
// 發送成功,業務系統保留Transaction Hash與業務資料關聯
return tx.getTxHashValue();
}else{
// 發送失敗,拋異常。業務系統應該重試,rpc調用失敗時區塊鏈的狀態是未知的。
throw new RuntimeException("寫入區塊鏈失敗");
}例2. 存證資料讀取
// 擷取當前最新區塊header
final Response<BlockHeader> blockHeader = client.getLatestBlockHeader();
if(!blockHeader.isSuccess()) {
LOGGER.error("Get block header fail: ", blockHeader.getErrorMsg());
return;
}
// oldHeight = 當前本地區塊高度;
long beginHeight = oldHeight + 1;
long finalHeight = blockHeader.getData().getHeight();
// 發現新塊則開始拉取
while(beginHeight < finalHeight) {
long endHeight = Math.min(beginHeight + FETCH_LIMIT - 1, finalHeight);
Response<List<Block>> response = client.getBlocks((int)beginHeight, (int)endHeight);
if (!response.isSuccess()) {
LOGGER.error("Get response fail: ", response.getErrorMsg());
return;
}
ConstructUtil constructUtil = new ConstructUtil();
List<Block> blocks = response.getData();
for (Block block : blocks) {
// 擷取區塊的存證交易
List<TransactionDO> transactions = block.getTransactions();
for (TransactionDO transaction : transactions) {
// 擷取payload
if (ContentOnlyNotaryPayloadDO.class.isInstance(transaction.getPayload())) {
ContentOnlyNotaryPayloadDO contentOnlyNotaryPayloadDO =
ContentOnlyNotaryPayloadDO.class.cast(transaction.getPayload());
// 判斷payload的業務資料分類
if (contentOnlyNotaryPayloadDO.getCategory() == BizCategory.GuestInfo) {
// 構造資料對象
GuestInfo renter = GuestInfo.class.cast(constructUtil.construct(
contentOnlyNotaryPayloadDO.getContent(), GuestInfo.class));
// 讀取renter資料
System.out.println(renter.getUserName());
}
}
}
beginHeight = block.getHeader().getHeight() + 1;
}
}最後如果使用者的系統要退出,建議在退出前調用shutdown,client會在shutdown中優雅的關閉線程池。
client.shutdown();更多
螞蟻區塊鏈使用Transaction(存證交易模型)進行資料存證,以上樣本使用了ContentOnlyNotaryPayload和EncryptShareNotaryPayload兩種存證模型,業務系統還可以根據具體情境,使用其它類型的Transaction寫資料上鏈,詳情請參考 存證交易模型。