如何通过QueryCriteria按userName查询UserState以规避数量限制报错?
Yes, you absolutely can use QueryCriteria to filter UserState by userName directly at the database level—and this is the right approach to fix your large dataset pagination issue. Your current method pulls all states into memory first, which hits Corda's default 200-record page limit when you have more than 201 users. Using a custom criteria pushes the filtering to the database, making the query efficient and avoiding the error.
Here's how to implement it step by step:
1. Ensure your UserState is persistently mapped
First, you need to map your UserState to a database entity so Corda can query its fields directly. Add JPA annotations and implement QueryableState:
import net.corda.core.contracts.BelongsToContract import net.corda.core.contracts.LinearState import net.corda.core.contracts.QueryableState import net.corda.core.contracts.UniqueIdentifier import net.corda.core.schemas.MappedSchema import net.corda.core.schemas.PersistentState import javax.persistence.Column import javax.persistence.Entity import javax.persistence.Table import java.util.UUID @BelongsToContract(UserContract::class) @Entity @Table(name = "user_states") data class UserState( val userName: String, // Add your other state fields here override val linearId: UniqueIdentifier = UniqueIdentifier() ) : LinearState, QueryableState { override fun generateMappedObject(schema: MappedSchema): PersistentState { return when (schema) { is UserSchemaV1 -> PersistentUserState( userName = this.userName, linearId = this.linearId.id, externalId = this.linearId.externalId ) else -> throw IllegalArgumentException("Unrecognized schema: $schema") } } override fun supportedSchemas(): Iterable<MappedSchema> { return listOf(UserSchemaV1) } } // Define the database schema for UserState object UserSchemaV1 : MappedSchema( schemaFamily = UserSchema::class.java, version = 1, mappedTypes = listOf(PersistentUserState::class.java) ) @Entity @Table(name = "user_states") class PersistentUserState( @Column(name = "user_name", nullable = false) var userName: String, @Column(name = "linear_id", nullable = false) var linearId: UUID, @Column(name = "external_id") var externalId: String? ) : PersistentState() // Dummy contract class (replace with your actual contract) class UserContract
2. Build the custom QueryCriteria
Use VaultCustomQueryCriteria with a JPA criteria query to filter by userName:
val userName = "Bob" // Create a JPA criteria query targeting the persistent UserState entity val criteriaBuilder = proxy.entityManager.criteriaBuilder val query = criteriaBuilder.createQuery(PersistentUserState::class.java) val root = query.from(PersistentUserState::class.java) query.select(root).where(criteriaBuilder.equal(root.get("userName"), userName)) // Wrap the JPA query in a Corda VaultCustomQueryCriteria val customCriteria = VaultCustomQueryCriteria(query) // Execute the query val userStates = proxy.vaultQueryBy<UserState>(criteria = customCriteria).states
Key Notes
- This approach filters records at the database level, so you only fetch the states matching your
userName—no more pulling hundreds of records into memory. - Make sure your node's configuration includes the schema (it should auto-detect if your state is in a deployed CorDapp).
- You can extend this to add additional filters (like state status) by combining criteria with
QueryCriteria.and()orQueryCriteria.or().
内容的提问来源于stack exchange,提问作者pozita

