千家信息网

Java持久化和命令行怎么用

发表于:2026-11-03 作者:千家信息网编辑
千家信息网最后更新 2026年11月03日,这篇文章主要讲解了"Java持久化和命令行怎么用",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"Java持久化和命令行怎么用"吧!数据库选择我们这里使用
千家信息网最后更新 2026年11月03日Java持久化和命令行怎么用

这篇文章主要讲解了"Java持久化和命令行怎么用",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"Java持久化和命令行怎么用"吧!

数据库选择

我们这里使用的是Java来实现,BoltDB不支持Java,这里我们选用 Rocksdb 。

数据结构

在我们开始实现数据持久化之前,我们先要确定我们该如何去存储我们的数据。为此,我们先来看看比特币是怎么做的。

简单来讲,比特币使用了两个"buckets(桶)"来存储数据:

  • blocks. 描述链上所有区块的元数据.

  • chainstate. 存储区块链的状态,指的是当前所有的UTXO(未花费交易输出)以及一些元数据.

"在比特币的世界里既没有账户,也没有余额,只有分散到区块链里的UTXO。"

详见:《精通比特币》第二版 第06章节 -- 交易的输入与输出

此外,每个区块数据都是以单独的文件形式存储在磁盘上。这样做是出于性能的考虑:当读取某一个单独的区块数据时,不需要加载所有的区块数据到内存中来。

在 blocks 这个桶中,存储的键值对:

  • 'b' + 32-byte block hash -> block index record

    区块的索引记录

  • 'f' + 4-byte file number -> file information record

    文件信息记录

  • 'l' -> 4-byte file number: the last block file number used

    最新的一个区块所使用的文件编码

  • 'R' -> 1-byte boolean: whether we're in the process of reindexing

    是否处于重建索引的进程当中

  • 'F' + 1-byte flag name length + flag name string -> 1 byte boolean: various flags that can be on or off

    各种可以打开或关闭的flag标志

  • 't' + 32-byte transaction hash -> transaction index record

    交易索引记录

在 chainstate 这个桶中,存储的键值对:

  • 'c' + 32-byte transaction hash -> unspent transaction output record for that transaction

    某笔交易的UTXO记录

  • 'B' -> 32-byte block hash: the block hash up to which the database represents the unspent transaction outputs

    数据库所表示的UTXO的区块Hash(抱歉,这一点我还没弄明白……)

由于我们还没有实现交易相关的特性,因此,我们这里只使用 block 桶。另外,前面提到过的,这里我们不会实现各个区块数据各自存储在独立的文件上,而是统一存放在一个文件里面。因此,我们不要存储和文件编码相关的数据,这样一来,我们所用到的键值对就简化为:

  • 32-byte block-hash -> Block structure (serialized)

    区块数据与区块hash的键值对

  • 'l' -> the hash of the last block in a chain

    最新一个区块hash的键值对

(查看更加详细的解释)

序列化

RocksDB的Key与Value只能以byte[]的形式进行存储,这里我们需要用到序列化与反序列化库 Kryo,代码如下:

package one.wangwei.blockchain.util;import com.esotericsoftware.kryo.Kryo;import com.esotericsoftware.kryo.io.Input;import com.esotericsoftware.kryo.io.Output;/** * 序列化工具类 * * @author wangwei * @date 2018/02/07 */public class SerializeUtils {    /**     * 反序列化     *     * @param bytes 对象对应的字节数组     * @return     */    public static Object deserialize(byte[] bytes) {        Input input = new Input(bytes);        Object obj = new Kryo().readClassAndObject(input);        input.close();        return obj;    }    /**     * 序列化     *     * @param object 需要序列化的对象     * @return     */    public static byte[] serialize(Object object) {        Output output = new Output(4096, -1);        new Kryo().writeClassAndObject(output, object);        byte[] bytes = output.toBytes();        output.close();        return bytes;    }}

持久化

上面已经说过,我们这里使用RocksDB,我们先写一个相关的工具类RocksDBUtils,主要的功能如下:

  • putLastBlockHash:保存最新一个区块的Hash值

  • getLastBlockHash:查询最新一个区块的Hash值

  • putBlock:保存区块

  • getBlock:查询区块

注意:BoltDB 支持 Bucket 的特性,而RocksDB 不支持,所以需要我们自己使用Map来做一个映射。

RocksDBUtils

package one.wangwei.blockchain.store;import com.google.common.collect.Maps;import one.wangwei.blockchain.block.Block;import one.wangwei.blockchain.util.SerializeUtils;import org.rocksdb.RocksDB;import org.rocksdb.RocksDBException;import java.util.Map;/** * 存储工具类 * * @author wangwei * @date 2018/02/27 */public class RocksDBUtils {    /**     * 区块链数据文件     */    private static final String DB_FILE = "blockchain.db";    /**     * 区块桶前缀     */    private static final String BLOCKS_BUCKET_KEY = "blocks";    /**     * 最新一个区块     */    private static final String LAST_BLOCK_KEY = "l";    private volatile static RocksDBUtils instance;    public static RocksDBUtils getInstance() {        if (instance == null) {            synchronized (RocksDBUtils.class) {                if (instance == null) {                    instance = new RocksDBUtils();                }            }        }        return instance;    }    private RocksDB db;    /**     * block buckets     */    private Map blocksBucket;    private RocksDBUtils() {        openDB();        initBlockBucket();    }    /**     * 打开数据库     */    private void openDB() {        try {            db = RocksDB.open(DB_FILE);        } catch (RocksDBException e) {            throw new RuntimeException("Fail to open db ! ", e);        }    }    /**     * 初始化 blocks 数据桶     */    private void initBlockBucket() {        try {            byte[] blockBucketKey = SerializeUtils.serialize(BLOCKS_BUCKET_KEY);            byte[] blockBucketBytes = db.get(blockBucketKey);            if (blockBucketBytes != null) {                blocksBucket = (Map) SerializeUtils.deserialize(blockBucketBytes);            } else {                blocksBucket = Maps.newHashMap();                db.put(blockBucketKey, SerializeUtils.serialize(blocksBucket));            }        } catch (RocksDBException e) {            throw new RuntimeException("Fail to init block bucket ! ", e);        }    }    /**     * 保存最新一个区块的Hash值     *     * @param tipBlockHash     */    public void putLastBlockHash(String tipBlockHash) {        try {            blocksBucket.put(LAST_BLOCK_KEY, SerializeUtils.serialize(tipBlockHash));            db.put(SerializeUtils.serialize(BLOCKS_BUCKET_KEY), SerializeUtils.serialize(blocksBucket));        } catch (RocksDBException e) {            throw new RuntimeException("Fail to put last block hash ! ", e);        }    }    /**     * 查询最新一个区块的Hash值     *     * @return     */    public String getLastBlockHash() {        byte[] lastBlockHashBytes = blocksBucket.get(LAST_BLOCK_KEY);        if (lastBlockHashBytes != null) {            return (String) SerializeUtils.deserialize(lastBlockHashBytes);        }        return "";    }    /**     * 保存区块     *     * @param block     */    public void putBlock(Block block) {        try {            blocksBucket.put(block.getHash(), SerializeUtils.serialize(block));            db.put(SerializeUtils.serialize(BLOCKS_BUCKET_KEY), SerializeUtils.serialize(blocksBucket));        } catch (RocksDBException e) {            throw new RuntimeException("Fail to put block ! ", e);        }    }    /**     * 查询区块     *     * @param blockHash     * @return     */    public Block getBlock(String blockHash) {        return (Block) SerializeUtils.deserialize(blocksBucket.get(blockHash));    }    /**     * 关闭数据库     */    public void closeDB() {        try {            db.close();        } catch (Exception e) {            throw new RuntimeException("Fail to close db ! ", e);        }    }}

创建区块链

现在我们来优化 Blockchain.newBlockchain 接口的代码逻辑,改为如下逻辑:

代码如下:

/**  * 

创建区块链

* * @return */public static Blockchain newBlockchain() throws Exception { String lastBlockHash = RocksDBUtils.getInstance().getLastBlockHash(); if (StringUtils.isBlank(lastBlockHash)) { Block genesisBlock = Block.newGenesisBlock(); lastBlockHash = genesisBlock.getHash(); RocksDBUtils.getInstance().putBlock(genesisBlock); RocksDBUtils.getInstance().putLastBlockHash(lastBlockHash); } return new Blockchain(lastBlockHash);}

修改 Blockchain 的数据结构,只记录最新一个区块链的Hash值

public class Blockchain {        @Getter    private String lastBlockHash;    private Blockchain(String lastBlockHash) {        this.lastBlockHash = lastBlockHash;    }}

每次挖矿完成后,我们也需要将最新的区块信息保存下来,并且更新最新区块链Hash值:

/** * 

添加区块

* * @param data */public void addBlock(String data) throws Exception { String lastBlockHash = RocksDBUtils.getInstance().getLastBlockHash(); if (StringUtils.isBlank(lastBlockHash)) { throw new Exception("Fail to add block into blockchain ! "); } this.addBlock(Block.newBlock(lastBlockHash, data));}/** *

添加区块

* * @param block */public void addBlock(Block block) throws Exception { RocksDBUtils.getInstance().putLastBlockHash(block.getHash()); RocksDBUtils.getInstance().putBlock(block); this.lastBlockHash = block.getHash();}

到此,存储部分的功能就实现完毕,我们还缺少一个功能:

检索区块链

现在,我们所有的区块都保存到了数据库,因此,我们能够重新打开已有的区块链并且向其添加新的区块。但这也导致我们再也无法打印出区块链中所有区块的信息,因为,我们没有将区块存储在数组当中。让我们来修复这个瑕疵!

我们在Blockchain中创建一个内部类 BlockchainIterator ,作为区块链的迭代器,通过区块之前的hash连接来依次迭代输出区块信息,代码如下:

public class Blockchain {     ....        /**     * 区块链迭代器     */    public class BlockchainIterator {        private String currentBlockHash;        public BlockchainIterator(String currentBlockHash) {            this.currentBlockHash = currentBlockHash;        }        /**         * 是否有下一个区块         *         * @return         */        public boolean hashNext() throws Exception {            if (StringUtils.isBlank(currentBlockHash)) {                return false;            }            Block lastBlock = RocksDBUtils.getInstance().getBlock(currentBlockHash);            if (lastBlock == null) {                return false;            }            // 创世区块直接放行            if (lastBlock.getPrevBlockHash().length() == 0) {                return true;            }            return RocksDBUtils.getInstance().getBlock(lastBlock.getPrevBlockHash()) != null;        }                /**         * 返回区块         *         * @return         */        public Block next() throws Exception {            Block currentBlock = RocksDBUtils.getInstance().getBlock(currentBlockHash);            if (currentBlock != null) {                this.currentBlockHash = currentBlock.getPrevBlockHash();                return currentBlock;            }            return null;        }    }           ....    }

测试

/** * 测试 * * @author wangwei * @date 2018/02/05 */public class BlockchainTest {    public static void main(String[] args) {        try {            Blockchain blockchain = Blockchain.newBlockchain();            blockchain.addBlock("Send 1.0 BTC to wangwei");            blockchain.addBlock("Send 2.5 more BTC to wangwei");            blockchain.addBlock("Send 3.5 more BTC to wangwei");            for (Blockchain.BlockchainIterator iterator = blockchain.getBlockchainIterator(); iterator.hashNext(); ) {                Block block = iterator.next();                if (block != null) {                    boolean validate = ProofOfWork.newProofOfWork(block).validate();                    System.out.println(block.toString() + ", validate = " + validate);                }            }        } catch (Exception e) {            e.printStackTrace();        }    }}/*输出*/Block{hash='0000012f87a0510dd0ee7048a6bd52db3002bae7d661126dc28287bd6c23189a', prevBlockHash='0000024b2c23c4fb06c2e2c1349275d415efe17a51db24cd4883da0067300ddf', data='Send 3.5 more BTC to wangwei', timeStamp=1519724875, nonce=369110}, validate = trueBlock{hash='0000024b2c23c4fb06c2e2c1349275d415efe17a51db24cd4883da0067300ddf', prevBlockHash='00000b14fefb51ba2a7428549d469bcf3efae338315e7289d3e6dc4caf589d79', data='Send 2.5 more BTC to wangwei', timeStamp=1519724872, nonce=896348}, validate = trueBlock{hash='00000b14fefb51ba2a7428549d469bcf3efae338315e7289d3e6dc4caf589d79', prevBlockHash='0000099ced1b02f40c750c5468bb8c4fd800ec9f46fea5d8b033e5d054f0f703', data='Send 1.0 BTC to wangwei', timeStamp=1519724869, nonce=673955}, validate = trueBlock{hash='0000099ced1b02f40c750c5468bb8c4fd800ec9f46fea5d8b033e5d054f0f703', prevBlockHash='', data='Genesis Block', timeStamp=1519724866, nonce=840247}, validate = true

命令行界面

CLI 部分的内容,这里不做详细介绍,具体可以去查看文末的Github源码链接。大致步骤如下:

配置

添加pom.xml配置

       ...                            commons-cli                commons-cli                1.4                ...                            org.apache.maven.plugins                maven-assembly-plugin                3.1.0                                                                                                                true                                        lib/                                        one.wangwei.blockchain.cli.Main                                                                                                                jar-with-dependencies                                                                                                                make-assembly                                                                package                                                                                                        single                                                                                        ...   
项目工程打包
$ mvn clean && mvn package
执行命令
# 打印帮助信息$ java -jar blockchain-java-jar-with-dependencies.jar -h # 添加区块$ java -jar blockchain-java-jar-with-dependencies.jar -add "Send 1.5 BTC to wangwei"$ java -jar blockchain-java-jar-with-dependencies.jar -add "Send 2.5 BTC to wangwei"$ java -jar blockchain-java-jar-with-dependencies.jar -add "Send 3.5 BTC to wangwei"# 打印区块链$ java -jar blockchain-java-jar-with-dependencies.jar -print

感谢各位的阅读,以上就是"Java持久化和命令行怎么用"的内容了,经过本文的学习后,相信大家对Java持久化和命令行怎么用这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

区块 数据 存储 序列 文件 命令 信息 数据库 交易 代码 查询 输出 内容 功能 工具 索引 学习 支持 迭代 对象 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网科技有限公司 天津市摩尔互联网科技 互联网科技工作委员会主任 科技股互联网 深圳科技互联网 互联网科技金融公司 广州宏创互联网科技 兼蜜互联网科技 新华互联网科技校企合作 济南和联互联网科技有限公司 华服互联网科技官网 互联网科技创新服务 青岛科技大学互联网大赛研究生 广西互联网科技孵化中心 科技类公司包括互联网吗 燊美集互联网科技有限公司 互联网新科技动漫ppt素材 长沙茂晟互联网科技有限公司 知域互联网科技有限公司官网 小牛互联网科技有限公司地址 互联网是第几次科技革命的成果 互联网科技有限公司经营范围 互联网下的科技发展 互联网养老 桔豆科技 全球顶尖互联网科技公司 深圳市罗太互联网科技有限公司 互联网在线教育科技创业 中科明德北京互联网科技 吉林省码上互联网科技电话 同城互联网络科技
0