尝试在WSL Ubuntu上运行ScalarDB(创建示例应用程序部分)
本篇文章是有关创建样本应用程序部分,在此我将运行 ScalarDB 的样本应用程序。
“Environment setup section is here.”
「环境搭建部分在这里。」
使用ScalarDB进行操作。
ScalarDB的操作组件有两个,分别是Storage组件和Transaction组件。
存储组件
这个组件具有以下功能。
-
- アトミックなCRUD操作
-
- 逐次一貫性のサポート
-
- 原子性/線形性条件下でのデータ変更(Create/Update/Delete)
- レコードごとにユーザ定義のメタデータを含める
此外,此处的键的作用是与Cassandra的分区键和聚簇键对应,可以通过列表或逗号分隔的形式进行多个定义。
此外,ScalarDB的存储组件为CRUD操作提供了四个函数。
-
- Get:1件のエントリーを取得する
-
- Put:1件のエントリーを挿入/更新する
-
- Delete:1件のエントリーを削除する
- Scan:特定のパーティション内のエントリーを複数件取得する
交易组件
该组件不仅包含Storage组件的功能,还提供了无主事务功能。
如果要使用事务功能,首先需要获取TransactionService实例。然后,在TransactionService实例上执行Get、Put、Delete和Scan操作之后,将事务实例提交并将操作内容反映到数据库中。
样本应用程序
我们将在样例应用程序中验证ScalarDB的实际应用运行情况。
应用程序的概述
-
- ユーザごとに口座を持つ
-
- 口座に指定した金額をチャージ
-
- 口座から別の口座へ支払い
- 口座の残高を確認
我們將實施具有這些功能的樣例應用程式。
定义模式
KEYSPACE: emoney
TABLE: account
列:
– group_id: 分组ID(分区键)
– id: 用户ID(聚簇键)
– balance: 用户的余额
处理的步骤
充电
-
- 获取指定用户的信息
-
- 在获取的用户信息中添加充值金额
- 将更新后的信息写入
在一个事务内执行上述一系列操作。
付款
-
- 获取发送人和接收人的金钱信息
-
- 确认发送人的余额大于发送金额
-
- 将发送金额从发送人减少,将发送金额增加到接收人
- 将更新的信息写入
在一个事务中执行以上的一系列动作。
查询账户余额
-
- 获取所有用户信息
-
- 使用循环处理逐个提取用户信息
- 从用户信息中获取余额,并显示在控制台上
建立环境
假设已经安装了执行所需操作的应用程序,我们可以使用Gradle来获取运行用的jar文件。
设定Scalar DB模式工具的路径。
在.bash_profile中添加到Schema Tools的路径设置
export SCHEMATOOL=/home/(自己环境的用户名)/scalardb/tools/schema
将设置更改生效。示例:$ source ~/.bash_profile
2. 设定样本项目目录配置
创建并移动到目录
$ mkdir ~/scalardb_sample
$ cd ~/scalardb_sample
执行gradle初始化命令
$ gradle init –type java-application
创建Gradle定义文件
$ 使用vi命令打开build.gradle文件
# mainClassNameの指定を変更(18行目)
# 変更前
mainClassName = 'App'
# 変更後
mainClassName = 'sample.emoney.ElectronicMoneyMain'
# dependenciesにScalar DB定義を追加(22行目)
dependencies {
// This dependency is found on compile classpath of this component and consumers.
compile 'com.google.guava:guava:23.0'
compile group: 'com.scalar-labs', name: 'scalardb', version: '1.0.0'
// Use JUnit test framework
testCompile 'junit:junit:4.12'
通过gradle构建获取可执行的jar文件。
如果显示“构建成功”,那就表示OK。
完成
1. 创建模式文件
$ vi emoney.sdbql
REPLICATION FACTOR 1;
CREATE NAMESPACE emoney;
CREATE TRANSACTION TABLE emoney.account (
group_id TEXT PARTITIONKEY,
id TEXT CLUSTERINGKEY,
balance INT,
);
使用Schema Tool,在Cassandra中创建模式。
$SCHEMATOOL/loader emoney.sdbql
使用cqlsh工具来确认数据库的架构
cqlsh> 查看所有keyspace,确认存在emoney
emoney system_auth coordinator system_traces
system_schema system system_distributed
确认表中存在account
cqlsh> 使用emoney数据库;
cqlsh:emoney> 查看所有表;
account
结束cqlsh。
cqlsh:emoney> 退出
我们将创建两个文件:一个包含构造函数和函数的类文件,另一个是用于接收参数并调用函数的执行类文件。
创建目录和Java文件
$ mkdir -p src/main/java/sample/emoney
$ cd src/main/java/sample/emoney
$ vi ElectronicMoney.java
创建文件夹和Java文件
$ mkdir -p src/main/java/sample/emoney
$ cd src/main/java/sample/emoney
$ vi ElectronicMoney.java
建立目录和Java文件
$ mkdir -p src/main/java/sample/emoney
$ cd src/main/java/sample/emoney
$ vi ElectronicMoney.java
package sample.emoney;
//インポート追加
import com.google.inject.Guice;
import com.google.inject.Injector;
import com.scalar.database.config.DatabaseConfig;
import com.scalar.database.service.StorageModule;
import com.scalar.database.service.StorageService;
import com.scalar.database.service.TransactionModule;
import com.scalar.database.service.TransactionService;
import java.io.File;
import java.io.IOException;
import com.scalar.database.api.DistributedTransaction;
import com.scalar.database.api.Get;
import com.scalar.database.api.Put;
import com.scalar.database.api.Delete;
import com.scalar.database.api.Result;
import com.scalar.database.io.IntValue;
import com.scalar.database.io.Key;
import com.scalar.database.io.TextValue;
import com.scalar.database.exception.storage.ExecutionException;
import com.scalar.database.exception.transaction.CommitException;
import com.scalar.database.exception.transaction.CrudException;
import com.scalar.database.exception.transaction.UnknownTransactionStatusException;
import java.util.Optional;
import com.scalar.database.api.Scan;
import com.scalar.database.api.Scanner;
public class ElectronicMoney {
// クラス変数を定義
private final String NAMESPACE = "emoney";
private final String TABLE_NAME = "account";
private final String ID = "id";
private final String GROUP_ID = "group_id";
private final String BALANCE = "balance";
private final StorageService storageService;
private final TransactionService transactionService;
// コンストラクタを実装
public ElectronicMoney() throws IOException {
File prop_file = new File("/etc/scalar/database.properties");
DatabaseConfig config = new DatabaseConfig(prop_file);
Injector injector = Guice.createInjector(new StorageModule(config));
storageService = injector.getInstance(StorageService.class);
storageService.with(NAMESPACE, TABLE_NAME);
injector = Guice.createInjector(new TransactionModule(config));
transactionService = injector.getInstance(TransactionService.class);
transactionService.with(NAMESPACE, TABLE_NAME);
}
public void charge(String groupId, String id, int amount) throws CrudException, CommitException, UnknownTransact ionStatusException {
//トランザクション開始
DistributedTransaction tx = transactionService.start();
Key partitionKey = new Key(new TextValue(GROUP_ID, groupId));
Key clusteringKey = new Key(new TextValue(ID, id));
Get get = new Get(partitionKey, clusteringKey);
Optional<Result> result = tx.get(get);
int balance = amount;
if (result.isPresent()) {
int current = ((IntValue) result.get().getValue(BALANCE).get()).get();
balance += current;
}
// 残高を更新
Put put = new Put(partitionKey, clusteringKey).withValue(new IntValue(BALANCE, balance));
tx.put(put);
// トランザクションコミット
tx.commit();
}
public void pay(String groupId, String fromId, String toId, int amount) throws CrudException, CommitException, UnknownTransactionStatusException {
// トランザクション開始
DistributedTransaction tx = transactionService.start();
// 送金元、送金先のアカウント情報を取得
Key partitionKey = new Key(new TextValue(GROUP_ID, groupId));
Key fromKey = new Key(new TextValue(ID, fromId));
Key toKey = new Key(new TextValue(ID, toId));
Get fromGet = new Get(partitionKey, fromKey);
Get toGet = new Get(partitionKey, toKey);
Optional<Result> fromResult = tx.get(fromGet);
Optional<Result> toResult = tx.get(toGet);
if (!fromResult.isPresent()) {
throw new RuntimeException(fromId + " doesn't exist.");
}
if (!toResult.isPresent()) {
throw new RuntimeException(toId + " doesn't exist.");
}
int newFromBalance = ((IntValue) (fromResult.get().getValue(BALANCE).get())).get() - amount;
int newToBalance = ((IntValue) (toResult.get().getValue(BALANCE).get())).get() + amount;
if (newFromBalance < 0) {
throw new RuntimeException(fromId + " doesn't have enough balances.");
}
// 残高を更新
Put fromPut = new Put(partitionKey, fromKey).withValue(new IntValue(BALANCE, newFromBalance));
Put toPut = new Put(partitionKey, toKey).withValue(new IntValue(BALANCE, newToBalance));
tx.put(fromPut); tx.put(toPut);
// トランザクションコミット
tx.commit();
}
public void balances(String groupId) throws ExecutionException {
Key partitionKey = new Key(new TextValue(GROUP_ID, groupId));
Scan scan = new Scan(partitionKey);
Scanner scanner = storageService.scan(scan);
scanner.forEach(r -> {
r.getValue(ID).ifPresent(v -> System.out.print(((TextValue) v).getString().get()));
System.out.print(" : ");
r.getValue(BALANCE).ifPresent(v -> System.out.println(((IntValue) v).get()));
});
}
public void deleteUser(String groupId, String id) throws CrudException, CommitException, UnknownTransactionStatu sException {
// トランザクション開始
DistributedTransaction tx = transactionService.start();
Key partitionKey = new Key(new TextValue(GROUP_ID, groupId));
Key clusteringKey = new Key(new TextValue(ID, id));
Get get = new Get(partitionKey, clusteringKey);
Optional<Result> result = tx.get(get);
if (!result.isPresent()) {
tx.abort();
return;
}
// 残高を更新
Delete delete = new Delete(partitionKey, clusteringKey);
tx.delete(delete);
// トランザクションコミット
tx.commit();
}
public void close() {
storageService.close();
transactionService.close();
}
}
使用vi编辑器打开ElectronicMoneyMain.java文件
package sample.emoney;
public class ElectronicMoneyMain {
public static void main(String[] args) throws Exception {
String action = null;
int amount = 0;
String group = null;
String to = null;
String from = null;
for (int i = 0; i < args.length; ++i) {
if ("-action".equals(args[i])) {
action = args[++i];
} else if ("-group".equals(args[i])) {
group = args[++i];
} else if ("-amount".equals(args[i])) {
amount = Integer.parseInt(args[++i]);
} else if ("-to".equals(args[i])) {
to = args[++i];
} else if ("-from".equals(args[i])) {
from = args[++i];
} else if ("-help".equals(args[i])) {
printUsageAndExit();
}
}
ElectronicMoney eMoney = new ElectronicMoney();
if (action.equalsIgnoreCase("charge")) {
eMoney.charge(group, to, amount);
} else if (action.equalsIgnoreCase("pay")) {
if (from == null) {
printUsageAndExit();
}
eMoney.pay(group, from, to, amount);
} else if (action.equalsIgnoreCase("balances")) {
eMoney.balances(group);
} else if (action.equalsIgnoreCase("delete")) {
eMoney.deleteUser(group, to);
}
eMoney.close();
}
private static void printUsageAndExit() {
System.err.println(
"ElectronicMoneyMain -action charge/pay/balances/delete -group id -to id [-amount number (needed for charge/pay)] [-from id (needed for pay)]"
);
System.exit(1);
}
}
执行
1. 充电
给用户1充值1000,请使用以下命令行参数运行Gradle:
$ gradle run –args=”-action 充值 -amount 1000 -group groupA -to user1″
以原生中文转述如下:
给用户2充值0
$ gradle run –args=”-action charge -amount 0 -group groupA -to user2″
2. 付款
用户1向用户2支付300元。
请输入以下命令执行:gradle run –args=”-action pay -amount 300 -group groupA -to user2 -from user1″。
3. 用户删除
删除用户1
$ gradle run –args=”-action delete -group groupA -to user1″
确认交易功能
为了确认交易功能正常运行,需要修改ElectronicMoney.java文件。
$ vi 电子货币.java
public void pay(String groupId, String fromId, String toId, int amount) throws CrudException, CommitException, UnknownTransactionStatusException {
// トランザクション開始
DistributedTransaction tx = transactionService.start();
// 送金元、送金先のアカウント情報を取得
Key partitionKey = new Key(new TextValue(GROUP_ID, groupId));
Key fromKey = new Key(new TextValue(ID, fromId));
Key toKey = new Key(new TextValue(ID, toId));
Get fromGet = new Get(partitionKey, fromKey);
Get toGet = new Get(partitionKey, toKey);
Optional<Result> fromResult = tx.get(fromGet);
Optional<Result> toResult = tx.get(toGet);
if (!fromResult.isPresent()) {
throw new RuntimeException(fromId + " doesn't exist.");
}
if (!toResult.isPresent()) {
throw new RuntimeException(toId + " doesn't exist.");
}
int newFromBalance = ((IntValue) (fromResult.get().getValue(BALANCE).get())).get() - amount;
int newToBalance = ((IntValue) (toResult.get().getValue(BALANCE).get())).get() + amount;
if (newFromBalance < 0) {
throw new RuntimeException(fromId + " doesn't have enough balances.");
}
// 残高を更新
Put fromPut = new Put(partitionKey, fromKey).withValue(new IntValue(BALANCE, newFromBalance));
// ----------------ここから追加行-----------------------
// 実験用に必ずエラーをthrowする文の追加
if (newFromBalance >= 0){
throw new RuntimeException("test error.");
}
// ----------------ここまで追加行-----------------------
// 送金先の残高は更新されない
Put toPut = new Put(partitionKey, toKey).withValue(new IntValue(BALANCE, newToBalance));
tx.put(fromPut); tx.put(toPut);
// トランザクションコミット
tx.commit();
}
使用者1充值1000。
$ gradle run –args=”-action charge -amount 1000 -group groupA -to user1″
以原生的中文方式表达上述句子(只提供一个选项):
用户2充值0元
$ gradle run –args=”-action 充值 -amount 0 -group 组A -to 用户2″
以上就是示例应用的执行情况。非常感谢您的阅读。