Corda:如何实现状态数据之间持久化到H2的层次关系 [英] Corda: How to implement hierarchical relationships between state data persisted to H2

查看:146
本文介绍了Corda:如何实现状态数据之间持久化到H2的层次关系的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

摘要

Summary

我修改了基本的令牌发行Corda Bootcamp应用程序来演示此问题.我想在TokenStates与TokenChildren之间建立一对多关系的双向映射.

I've adapted the basic Token Issuance Corda Bootcamp application to demonstrate this issue. I want to create a bidirectional mapping between TokenStates and TokenChildren where the relationship is one-to-many.

持久存储分层数据的最佳实践是什么?是否可以在状态模式中使用JPA注释来实现这一点?

我有一个状态-TokenState,其中包含一些任意数据以及类TokenChild的对象的Collection.该列表的目的是促进H2中的记录之间的一对多关系.该州的关联架构具有相应的JPA批注(@OneToMany和@ManyToOne-请参见下面的代码段). TokenState类在supportedSchemasgenerateMappedObject方法中引用了适当的架构-TokenSchemaV1.

I have one state - TokenState, that contains some arbitrary data as well as a Collection of objects with the class TokenChild. The purpose of this list is to facilitate a one-to-many relationship between the records in H2. The state's associated schema has corresponding JPA annotations (@OneToMany and @ManyToOne - see code snippet below). The TokenState class references the appropriate schema - TokenSchemaV1 in the supportedSchemas and generateMappedObject methods.

当我在部署和运行节点后从控制台运行TokenIssueFlow(也包含在代码段中)时,事务成功完成,但没有token_child_states表持久化到h2.

When I run TokenIssueFlow (also included as a snippet) from the console after deploying and running the nodes, the transaction succeeds but no token_child_states table is persisted to h2.

其他说明

Other Notes

  • 我还尝试了实施不同的策略,其中两个令牌
    和TokenChildren是唯一状态(而不是一个整体
    状态).有关更多详细信息,请参见此Github问题.

  • I've also tried to implement a different strategy where both Tokens
    and TokenChildren are unique states (rather than one monolithic
    state). See this Github issue for more details.

另一个解决方案可能是将Tokens和TokenChildren作为 分离状态并手动将外键保留在h2中以 促进这种关系,但这似乎是一种解决方法, 解决方案.

Another solution might be having Tokens and TokenChildren as separate states and manually persisting foreign keys in h2 to facilitate this relationship but that seems like a work-around more than a solution.

对于更深层嵌套的关系有什么影响 上课之间? (例如-TokenChildren的伪造示例 TokenGrandChildren等).我如何使用generateMappedObject()supportedSchemas()创建我需要的数据模型?

What are the ramifications for an even more deeply nested relationship between classes? (e.g. - a contrived example of TokenChildren having TokenGrandChildren and so forth). How do I use generateMappedObject() and supportedSchemas() to create the data model I need?

TokenState

TokenState

public class TokenState implements LinearState, QueryableState {

    private final Party owner;
    private final Party issuer;
    private final int amount;
    private final UniqueIdentifier linearId;
    private List<TokenSchemaV1.PersistentChildToken> listOfPersistentChildTokens;

    public TokenState (Party issuer, Party owner, int amount, UniqueIdentifier linearId, List<TokenSchemaV1.PersistentChildToken> listOfPersistentChildTokens) {
        this.owner = owner;
        this.issuer = issuer;
        this.amount = amount;
        this.linearId = linearId;
        this.listOfPersistentChildTokens = listOfPersistentChildTokens;
    }

    public Party getOwner() {
        return owner;
    }

    public Party getIssuer() {
        return issuer;
    }

    public int getAmount() {
        return amount;
    }

    @Override
    public UniqueIdentifier getLinearId() {
        return linearId;
    }

    public List<TokenSchemaV1.PersistentChildToken> getListOfPersistentChildTokens() {
        return listOfPersistentChildTokens;
    }

    @Override
    public PersistentState generateMappedObject(MappedSchema schema) {
        if (schema instanceof TokenSchemaV1) {
            return new TokenSchemaV1.PersistentToken(
                    this.getOwner().getName().toString(),
                    this.getIssuer().getName().toString(),
                    this.getAmount(),
                    this.linearId.getId(),
                    this.getListOfPersistentChildTokens()
            );
        } else {
            throw new IllegalArgumentException("Unrecognised schema $schema");
        }
    }

    @Override
    public Iterable<MappedSchema> supportedSchemas() {
        return ImmutableList.of(new TokenSchemaV1());
    }

    @NotNull
    @Override
    public List<AbstractParty> getParticipants() {
        return ImmutableList.of(issuer, owner);
    }

}

TokenSchemaV1

TokenSchemaV1

@CordaSerializable
public class TokenSchemaV1 extends MappedSchema {

    public TokenSchemaV1() {
        super(TokenSchema.class, 1, ImmutableList.of(PersistentToken.class, PersistentChildToken.class));
    }

    @Entity
    @Table(name = "token_states")
    public static class PersistentToken extends PersistentState {
        @Column(name = "owner") private final String owner;
        @Column(name = "issuer") private final String issuer;
        @Column(name = "amount") private final int amount;
        @Column(name = "linear_id") private final UUID linearId;
        @OneToMany(mappedBy = "persistentToken") private final List<PersistentChildToken> listOfPersistentChildTokens;
        //get() = field

        public PersistentToken(String owner, String issuer, int amount, UUID linearId, List<PersistentChildToken> listOfPersistentChildTokens) {
            this.owner = owner;
            this.issuer = issuer;
            this.amount = amount;
            this.linearId = linearId;
            this.listOfPersistentChildTokens = listOfPersistentChildTokens;
        }

        // Default constructor required by hibernate.
        public PersistentToken() {
            this.owner = "";
            this.issuer = "";
            this.amount = 0;
            this.linearId = UUID.randomUUID();
            this.listOfPersistentChildTokens = null;
        }

        public String getOwner() {
            return owner;
        }

        public String getIssuer() {
            return issuer;
        }

        public int getAmount() {
            return amount;
        }

        public UUID getLinearId() {
            return linearId;
        }

        public List<PersistentChildToken> getChildTokens() { return listOfPersistentChildTokens; }
    }

    @Entity
    @CordaSerializable
    @Table(name = "token_child_states")
    public static class PersistentChildToken {
        @Id
        private final UUID Id;
        @Column(name = "owner")
        private final String owner;
        @Column(name = "issuer")
        private final String issuer;
        @Column(name = "amount")
        private final int amount;
        @Column(name = "child proof")
        private final String childProof;
        @ManyToOne(targetEntity = PersistentToken.class)
        private final TokenState persistentToken;

        public PersistentChildToken(String owner, String issuer, int amount) {
            this.Id = UUID.randomUUID();
            this.owner = owner;
            this.issuer = issuer;
            this.amount = amount;
            this.persistentToken = null;
            this.childProof = "I am a child";
        }

        // Default constructor required by hibernate.
        public PersistentChildToken() {
            this.Id = UUID.randomUUID();
            this.owner = "";
            this.issuer = "";
            this.amount = 0;
            this.persistentToken = null;
            this.childProof = "I am a child";
        }

        public UUID getId() {
            return Id;
        }

        public String getOwner() {
            return owner;
        }

        public String getIssuer() {
            return issuer;
        }

        public int getAmount() {
            return amount;
        }

        public TokenState getPersistentToken() {
            return persistentToken;
        }
    }
}

TokenIssueFlow

TokenIssueFlow

@InitiatingFlow
@StartableByRPC
public class TokenIssueFlow extends FlowLogic<SignedTransaction> {
    private final Party owner;
    private final int amount;

    public TokenIssueFlow(Party owner, int amount) {
        this.owner = owner;
        this.amount = amount;
    }

    private final ProgressTracker progressTracker = new ProgressTracker();

    @Override
    public ProgressTracker getProgressTracker() {
        return progressTracker;
    }

    @Suspendable
    @Override
    public SignedTransaction call() throws FlowException {
        // We choose our transaction's notary (the notary prevents double-spends).
        Party notary = getServiceHub().getNetworkMapCache().getNotaryIdentities().get(0);
        // We get a reference to our own identity.
        Party issuer = getOurIdentity();

        /* ============================================================================
         *         Create our TokenState to represent on-ledger tokens
         * ===========================================================================*/

        List<TokenSchemaV1.PersistentChildToken> listOfPersistentChildTokens = new ArrayList<>();

        for (int count = 0; count <=5; count++) {
            TokenSchemaV1.PersistentChildToken child = new TokenSchemaV1.PersistentChildToken(owner.getName().toString(), issuer.getName().toString(), amount + 2);
            listOfPersistentChildTokens.add(child);
        }

        // We create our new TokenState.
        TokenState tokenState = new TokenState(issuer, owner, amount, new UniqueIdentifier(), listOfPersistentChildTokens);

        /* ============================================================================
         *      Build our token issuance transaction to update the ledger
         * ===========================================================================*/
        // We build our transaction.
        TransactionBuilder txBuilder = new TransactionBuilder();

        txBuilder.setNotary(notary);

        txBuilder.addOutputState(tokenState, TokenContract.ID);

        TokenContract.Commands.Issue commandData = new TokenContract.Commands.Issue();
        List<PublicKey> requiredSigners = ImmutableList.of(issuer.getOwningKey());
        txBuilder.addCommand(commandData, requiredSigners);

        /* ============================================================================
         *          Write our TokenContract to control token issuance!
         * ===========================================================================*/
        // We sign the transaction with our private key, making it immutable.
        SignedTransaction signedTransaction = getServiceHub().signInitialTransaction(txBuilder);

        // We check our transaction is valid based on its contracts.
        txBuilder.verify(getServiceHub());

        // We get the transaction notarised and recorded automatically by the platform.
        return subFlow(new FinalityFlow(signedTransaction));
    }
}

推荐答案

我怀疑您可能需要在@OneToMany关系(在父类中)上添加一个显式的@Cascade(CascadeType.PERSIST)批注.

I suspect you may need to add an explicit @Cascade(CascadeType.PERSIST) annotation on your @OneToMany relationship (in the parent class).

看看下面的工作代码片段:

Have a look at the following working code snippet:

class SchemaFamily

object TestSchema : MappedSchema(SchemaFamily::class.java, 1, setOf(Parent::class.java, Child::class.java)) {
    @Entity
    @Table(name = "Parents")
    class Parent : PersistentState() {
        @OneToMany(fetch = FetchType.LAZY)
        @JoinColumns(JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id"), JoinColumn(name = "output_index", referencedColumnName = "output_index"))
        @OrderColumn
        @Cascade(CascadeType.PERSIST)
        var children: MutableSet<Child> = mutableSetOf()
    }

    @Suppress("unused")
    @Entity
    @Table(name = "Children")
    class Child {
        @Id
        @GeneratedValue
        @Column(name = "child_id", unique = true, nullable = false)
        var childId: Int? = null

        @ManyToOne(fetch = FetchType.LAZY)
        @JoinColumns(JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id"), JoinColumn(name = "output_index", referencedColumnName = "output_index"))
        var parent: Parent? = null
    }
}

请按照上面的代码进行调整,然后报告.

Please adjust your code to the above and report back.

这篇关于Corda:如何实现状态数据之间持久化到H2的层次关系的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆