Core concepts
This document walks through the core classes and concepts behind the Lovrabet Java OpenSDK, so you can understand how the SDK works.
1. The SDK client (LovrabetSDKClient)
LovrabetSDKClient is the heart of the SDK — it wraps every OpenAPI endpoint.
1.1 Creating the client
public LovrabetSDKClient(String accessKey, String baseUrl);Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
| accessKey | String | Yes | Access key, generated in the app admin console |
| baseUrl | String | Yes | API server URL: https://runtime.lovrabet.com/openapi |
Example:
String accessKey = "ak-OD8xWhMOsL5ChQ3Akhv4uYYiu1fPOFQGVF9BULIeov8";
String baseUrl = "https://runtime.lovrabet.com/openapi";
LovrabetSDKClient sdkClient = new LovrabetSDKClient(accessKey, baseUrl);1.2 Client features
Thread safety
LovrabetSDKClient is thread-safe and can be shared across threads:
// 推荐:创建单例复用
public class SdkClientManager {
private static volatile LovrabetSDKClient instance;
public static LovrabetSDKClient getInstance() {
if (instance == null) {
synchronized (SdkClientManager.class) {
if (instance == null) {
instance = new LovrabetSDKClient(
ConfigManager.getAccessKey(),
ConfigManager.getBaseUrl()
);
}
}
}
return instance;
}
}
// 在应用中使用
LovrabetSDKClient client = SdkClientManager.getInstance();Automatic authentication
The SDK signs every request automatically, so you never have to deal with the signing algorithm yourself:
// SDK 会自动完成:
// 1. 生成时间戳
// 2. 构建签名字符串
// 3. 计算 HMAC-SHA256 签名
// 4. 添加认证头到 HTTP 请求
LovrabetResult<?> result = sdkClient.getList(request); // 自动签名2. The request object (LovrabetRequest)
LovrabetRequest holds the parameters for an API call.
2.1 Class definition
public class LovrabetRequest {
private Long id; // 主键ID
private String appCode; // 应用编码(必填)
private String modelCode; // 数据集编码
private Map<String, Object> paramMap; // 业务参数
// Getters and Setters
}2.2 Field reference
| Field | Type | Required | Used by | Description |
|---|---|---|---|---|
| appCode | String | Yes | All methods | App code, found in the app admin console |
| modelCode | String | Some methods | Data operations | Dataset code identifying the target dataset |
| id | Long | Some methods | getOne, update | Primary key ID of the record |
| paramMap | Map | No | Various | Pagination parameters, query conditions, data fields, etc. |
2.3 Usage examples
Basic request (appCode only)
For app-level endpoints such as listing datasets:
LovrabetRequest request = new LovrabetRequest();
request.setAppCode("app-c2dd52a2");Dataset request
For dataset-level endpoints such as querying records:
LovrabetRequest request = new LovrabetRequest();
request.setAppCode("app-c2dd52a2");
request.setModelCode("b460abdbb0fb49e1865110d9dfbbc9b4");
// 添加分页参数
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("pageSize", 10);
paramMap.put("currentPage", 1);
request.setParamMap(paramMap);Single-record request
For fetching or updating a specific record:
LovrabetRequest request = new LovrabetRequest();
request.setAppCode("app-c2dd52a2");
request.setModelCode("32c036010c504757b80d438e3c0ec8b7");
request.setId(513L);
// 更新操作还需要设置要修改的字段
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("TEXT_1", "新的文本内容");
request.setParamMap(paramMap);2.4 paramMap guide
paramMap is a flexible container that serves several purposes:
Use case 1: pagination parameters
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("currentPage", 1); // 当前页码,从 1 开始
paramMap.put("pageSize", 20); // 每页显示的记录数Use case 2: data fields (create/update)
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("TEXT_1", "订单编号-001"); // 文本字段
paramMap.put("NUMBER_3", 100); // 数字字段
paramMap.put("DATETIME_1", System.currentTimeMillis()); // 时间戳
paramMap.put("SELECT_4", "china-north"); // 选择字段Use case 3: query conditions (if the API supports them)
Map<String, Object> paramMap = new HashMap<>();
paramMap.put("status", "active"); // 根据状态筛选
paramMap.put("category", "product"); // 根据分类筛选3. The response object (LovrabetResult)
LovrabetResult<T> is the uniform response object for every API call.
3.1 Class definition
public class LovrabetResult<T> {
private boolean success; // 执行是否成功
private String resultCode; // 结果代码
private String resultMsg; // 结果消息
private T data; // 返回数据
private LovrabetPage page; // 分页信息(部分接口)
private List<LovrabetField> fieldList; // 字段列表(部分接口)
// Getters and Setters
}3.2 Field reference
| Field | Type | Description |
|---|---|---|
| success | boolean | Whether the request succeeded — true for success, false for failure |
| resultCode | String | Result code — "0000" on success, a specific error code on failure |
| resultMsg | String | Result message; contains error details on failure |
| data | T | The returned business data; the type varies by method |
| page | LovrabetPage | Pagination info (returned only by paginated queries) |
| fieldList | List | Field list (returned by some methods) |
3.3 Standard response-handling patterns
Pattern 1: basic check
LovrabetResult<?> result = sdkClient.someMethod(request);
if (result.isSuccess()) {
// 成功:处理业务数据
Object data = result.getData();
System.out.println("操作成功: " + data);
} else {
// 失败:处理错误信息
System.err.println("操作失败 [" + result.getResultCode() + "]: " + result.getResultMsg());
}Pattern 2: type-safe handling
// 获取单条数据
LovrabetResult<?> result = sdkClient.getOne(request);
if (result.isSuccess()) {
Map<String, Object> data = (Map<String, Object>) result.getData();
// 访问具体字段
String text = (String) data.get("TEXT_1");
Integer number = (Integer) data.get("NUMBER_3");
System.out.println("文本字段: " + text);
System.out.println("数字字段: " + number);
}Pattern 3: list handling
// 获取列表数据
LovrabetResult<?> result = sdkClient.getList(request);
if (result.isSuccess()) {
List<Map<String, Object>> dataList = (List<Map<String, Object>>) result.getData();
System.out.println("数据量: " + dataList.size());
// 遍历处理
for (Map<String, Object> record : dataList) {
System.out.println("记录: " + record);
}
}Pattern 4: error handling with retries
int maxRetries = 3;
int retryCount = 0;
LovrabetResult<?> result = null;
while (retryCount < maxRetries) {
result = sdkClient.getOne(request);
if (result.isSuccess()) {
// 成功,跳出循环
break;
} else {
retryCount++;
System.err.println("尝试 " + retryCount + " 失败: " + result.getResultMsg());
if (retryCount < maxRetries) {
try {
Thread.sleep(1000); // 等待 1 秒后重试
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
}
}
// 最终处理
if (result != null && result.isSuccess()) {
System.out.println("数据: " + result.getData());
} else {
System.err.println("多次重试后仍然失败");
}4. Authentication
The SDK authenticates every request with an HMAC-SHA256 signature, protecting both the security and integrity of the call.
4.1 Signing flow (automatic)
The SDK handles signing for you, but knowing the flow helps when troubleshooting:
Step 1: collect the signing parameters
The SDK collects these parameters (sorted lexicographically):
accessKey: the access keytimeStamp: timestamp in millisecondsappCode: the app codedatasetCode: the dataset code
Step 2: build the string to sign
accessKey=ak-xxx&appCode=app-xxx&datasetCode=xxx&timeStamp=1234567890Step 3: compute the HMAC-SHA256 signature
It computes the HMAC-SHA256 of the signing string with a fixed key, then Base64-encodes the result.
Step 4: add to request headers
The SDK adds the following information to the HTTP request headers:
X-Time-Stamp: timestampX-App-Code: app codeX-Dataset-Code: dataset codeX-Token: the generated signature
4.2 Security best practices
✅ Do this
// 1. 使用环境变量
String accessKey = System.getenv("LOVRABET_ACCESS_KEY");
// 2. 使用配置文件
@Value("${lovrabet.accessKey}")
private String accessKey;
// 3. 使用密钥管理服务
String accessKey = secretManager.getSecret("lovrabet-access-key");❌ Don't do this
// ❌ 不要硬编码在代码中
String accessKey = "ak-OD8xWhMOsL5ChQ3Akhv4uYYiu1fPOFQGVF9BULIeov8";
// ❌ 不要提交到代码仓库
// git add config.properties (包含 accessKey)
// ❌ 不要在日志中打印
logger.info("AccessKey: " + accessKey); // 危险!4.3 Common authentication issues
Issue 1: "Token invalid"
Causes:
- The AccessKey is incorrect
- The system clock is more than 10 minutes off from the server time
Fix:
// 检查系统时间
System.out.println("当前时间戳: " + System.currentTimeMillis());
// 确认 AccessKey
System.out.println("AccessKey 前 10 位: " + accessKey.substring(0, 10));Issue 2: "AccessKey not found or expired"
Fix:
- Log in to the Lovrabet platform
- Check that the AccessKey has been generated and hasn't expired
- Confirm that the OpenAPI feature is enabled
- Regenerate the AccessKey if needed
5. The full request-response flow
The diagram below shows one complete API call:
┌─────────────────┐
│ 创建 Request │
│ 设置参数 │
└────────┬────────┘
│
▼
┌─────────────────┐
│ SDK 自动签名 │ ← 自动添加时间戳、计算签名
└────────┬────────┘
│
▼
┌─────────────────┐
│ 发送 HTTP 请求 │ ← 携带签名头
└────────┬────────┘
│
▼
┌─────────────────┐
│ 服务器验证签名 │
└────────┬────────┘
│
▼
┌─────────────────┐
│ 处理业务逻辑 │
└────────┬────────┘
│
▼
┌─────────────────┐
│ 返回 Result │ ← 包含 success、data 等
└────────┬────────┘
│
▼
┌─────────────────┐
│ 应用处理结果 │ ← if (result.isSuccess()) { ... }
└─────────────────┘Example code
// 1. 创建 Request
LovrabetRequest request = new LovrabetRequest();
request.setAppCode("app-c2dd52a2");
request.setModelCode("32c036010c504757b80d438e3c0ec8b7");
request.setId(513L);
// 2-4. SDK 自动完成签名、发送、验证
// 5. 服务器处理并返回 Result
LovrabetResult<?> result = sdkClient.getOne(request);
// 6. 应用处理结果
if (result.isSuccess()) {
Map<String, Object> data = (Map<String, Object>) result.getData();
System.out.println("查询成功: " + data);
} else {
System.err.println("查询失败: " + result.getResultMsg());
}Next steps
Now that you understand the core concepts, we suggest continuing in this order:
- 📖 Quick start — build your first CRUD program in 5 minutes
- 📚 Core concepts ← you are here — understand how the SDK works
- 📋 API reference ← recommended next — the complete API documentation
- 💡 Examples — 5 real-world implementations
- 🚀 Best practices — production optimization tips
- ❓ FAQ — answers to common questions
Need help? See the FAQ or contact service@lovrabet.com