摘要
我修改了基本的令牌发行Corda Bootcamp应用程序来演示此问题。我想在TokenStates与TokenChildren之间建立一对多关系的双向映射。
持久保存分层数据的最佳实践是什么? 是否可以在状态模式中使用JPA注释来实现此目的?
我有一个状态- TokenState,其中包含一些任意数据以及Collection带有类的对象TokenChild。该列表的目的是促进H2中的记录之间的一对多关系。该州的关联架构具有相应的JPA批注(@OneToMany和@ManyToOne- 请参见下面的代码段)。本TokenState类引用适当的模式- TokenSchemaV1在supportedSchemas和generateMappedObject方法。
TokenState
Collection
TokenChild
TokenSchemaV1
supportedSchemas
generateMappedObject
当我TokenIssueFlow在部署和运行节点之后从控制台运行(也包含在代码段中)时,事务成功完成,但没有任何token_child_states表持久化到h2。
TokenIssueFlow
token_child_states
其他注意事项
我还尝试实现一种不同的策略,其中Token 和TokenChildren都是唯一状态(而不是一个整体 状态)。有关更多详细信息,请参见此Github问题。
另一个解决方案可能是将Tokens和TokenChildren作为单独的状态,并在h2中手动保留外键以促进这种关系,但这似乎是一种解决方法,而不是解决方案。
类之间更深层嵌套的关系有哪些后果?(例如-具有TokenGrandChildren的TokenChildren的人为示例)。如何使用generateMappedObject() 和supportedSchemas()创建所需的数据模型?
generateMappedObject()
supportedSchemas()
令牌状态
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); } }
令牌模式V1
@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; } } }
令牌发行流程
@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)批注。
看一下下面的工作代码片段:
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 } }
请根据以上内容调整您的代码,然后报告。