You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Corda中创建自定义查询的可行方法及DataState状态查询咨询

Custom Queries for Corda's DataState (Create-Only QueryableState)

Great question! Since your DataState implements both LinearState and QueryableState (and it's a create-only, never-consumed state), you have three solid approaches to implement custom queries in Corda. Let's start with the critical prerequisite—finishing your QueryableState implementation—then dive into each query method.

Prerequisite: Complete the QueryableState Mapping

First, you need to define how your DataState maps to a persistent database entity (since QueryableState requires this for database queries). Here's the updated DataState class with the necessary schema and persistent entity mapping:

import net.corda.core.contracts.LinearState;
import net.corda.core.contracts.QueryableState;
import net.corda.core.identity.AbstractParty;
import net.corda.core.identity.Party;
import net.corda.core.schemas.MappedSchema;
import net.corda.core.schemas.PersistentState;
import net.corda.core.utilities.UniqueIdentifier;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Table;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;

public class DataState implements LinearState, QueryableState {
    private Party partyA;
    private Party partyB;
    private String partyAId;
    private String partyBId;
    private String sharedIdentifierNumber;
    private UniqueIdentifier linearId;

    public DataState(Party partyA, Party partyB, String partyAId, String partyBId, String sharedIdentifierNumber) {
        this.partyA = partyA;
        this.partyB = partyB;
        this.partyAId = partyAId;
        this.partyBId = partyBId;
        this.sharedIdentifierNumber = sharedIdentifierNumber;
        this.linearId = new UniqueIdentifier();
    }

    // LinearState required methods
    @Override
    public List<AbstractParty> getParticipants() {
        return Arrays.asList(partyA, partyB);
    }

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

    // QueryableState required methods
    @Override
    public PersistentState generateMappedObject(MappedSchema schema) {
        if (schema instanceof DataStateSchemaV1) {
            // Convert Party objects to their X500 string representations for persistence
            return new PersistentDataState(
                partyA.getName().toString(),
                partyB.getName().toString(),
                partyAId,
                partyBId,
                sharedIdentifierNumber,
                linearId.getId()
            );
        } else {
            throw new IllegalArgumentException("Unsupported schema: " + schema);
        }
    }

    @Override
    public Iterable<MappedSchema> supportedSchemas() {
        return Arrays.asList(new DataStateSchemaV1());
    }

    // Define the schema version
    public static class DataStateSchemaV1 extends MappedSchema {
        public DataStateSchemaV1() {
            super(DataState.class, 1, Arrays.asList(PersistentDataState.class));
        }
    }

    // Persistent entity mapped to the database table
    @Entity
    @Table(name = "data_states")
    public static class PersistentDataState extends PersistentState {
        @Column(name = "party_a")
        private String partyA;

        @Column(name = "party_b")
        private String partyB;

        @Column(name = "party_a_id")
        private String partyAId;

        @Column(name = "party_b_id")
        private String partyBId;

        @Column(name = "shared_identifier_number")
        private String sharedIdentifierNumber;

        @Column(name = "linear_id")
        private UUID linearId;

        // JPA requires a no-arg constructor
        public PersistentDataState() {}

        public PersistentDataState(String partyA, String partyB, String partyAId, String partyBId, String sharedIdentifierNumber, UUID linearId) {
            this.partyA = partyA;
            this.partyB = partyB;
            this.partyAId = partyAId;
            this.partyBId = partyBId;
            this.sharedIdentifierNumber = sharedIdentifierNumber;
            this.linearId = linearId;
        }

        // Getters for all fields (required for JPA)
        public String getPartyA() { return partyA; }
        public String getPartyB() { return partyB; }
        public String getPartyAId() { return partyAId; }
        public String getPartyBId() { return partyBId; }
        public String getSharedIdentifierNumber() { return sharedIdentifierNumber; }
        public UUID getLinearId() { return linearId; }
    }
}

Approach 1: Use Vault Query API with Custom Criteria

This is the most straightforward method for simple to moderately complex queries. You'll build QueryCriteria objects to filter against your state's fields, then execute the query via Corda's VaultService.

Example: Query by Party A and Unconsumed Status

Since your state is never consumed, you can filter for UNCONSUMED status to avoid irrelevant results:

// Inside a Flow or Corda Service
val vaultService = serviceHub.vaultService

// Filter for states where partyA matches a specific Party
val partyACriteria = QueryCriteria.VaultCustomQueryCriteria(
    Builder.equal(PersistentDataState::partyA, targetPartyA.name.toString())
)

// Filter for only unconsumed states (since your state is never consumed)
val unconsumedCriteria = QueryCriteria.VaultQueryCriteria(Vault.StateStatus.UNCONSUMED)

// Combine criteria
val combinedCriteria = partyACriteria.and(unconsumedCriteria)

// Execute the query and get the results
val matchingStates = vaultService.queryBy<DataState>(combinedCriteria).states

Example: Query by Shared Identifier Number

val sharedIdCriteria = QueryCriteria.VaultCustomQueryCriteria(
    Builder.equal(PersistentDataState::sharedIdentifierNumber, "SHARED-12345")
)
val results = vaultService.queryBy<DataState>(sharedIdCriteria.and(unconsumedCriteria)).states

Approach 2: Custom JPA Queries (For Complex Logic)

If you need advanced queries (like fuzzy matches, multi-field joins, or aggregate functions), use JPA directly with a custom repository interface. Corda supports Spring Data JPA-style repositories for this.

Step 1: Define a Repository Interface

import net.corda.core.schemas.CordaJpaRepository
import java.util.UUID

interface DataStateRepository : CordaJpaRepository<PersistentDataState, UUID> {
    // Find states by partyBId and sharedIdentifierNumber
    fun findByPartyBIdAndSharedIdentifierNumber(partyBId: String, sharedId: String): List<PersistentDataState>

    // Fuzzy search for shared identifiers containing a substring
    fun findBySharedIdentifierNumberContaining(partialSharedId: String): List<PersistentDataState>
}

Step 2: Execute the Query

// Inside a Flow or Service
val repo = serviceHub.cordappSchemaService.getSchemaRepository(DataStateRepository::class.java)

// Run the custom query
val matchingStates = repo.findByPartyBIdAndSharedIdentifierNumber("B-9876", "SHARED-12345")

// Convert persistent entities back to DataState if needed
val dataStates = matchingStates.map { serviceHub.vaultService.toState(it) }

Approach 3: Named Queries (Predefined Reusable Queries)

For frequently used complex queries, define named queries directly on your persistent entity. This keeps query logic centralized and easy to maintain.

Step 1: Add Named Query to PersistentDataState

Update the PersistentDataState class with an @NamedQuery annotation:

import javax.persistence.NamedQuery;

@Entity
@Table(name = "data_states")
@NamedQuery(
    name = "PersistentDataState.findByPartyAId",
    query = "SELECT p FROM PersistentDataState p WHERE p.partyAId = :partyAId"
)
public static class PersistentDataState extends PersistentState {
    // ... existing fields and methods ...
}

Step 2: Execute the Named Query

val entityManager = serviceHub.jpaEntityManagerFactory.createEntityManager()
val query = entityManager.createNamedQuery(
    "PersistentDataState.findByPartyAId",
    PersistentDataState::class.java
).setParameter("partyAId", "A-1234")

val results = query.resultList

Key Notes for Your Use Case

  • Since your state is never consumed, always include Vault.StateStatus.UNCONSUMED in your criteria to avoid redundant data.
  • As a LinearState, you can also combine custom criteria with LinearStateQueryCriteria to filter by linearId or participants.
  • Ensure all fields in PersistentDataState are properly mapped to your DataState fields (e.g., converting Party to X500 strings for persistence).

内容的提问来源于stack exchange,提问作者Ben Tan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:33:36