Adrian
Adrian

Reputation: 444

How to query on a field in nested collection of a parent state using VaultCustomQuery

I have a one-to-many relationship where I am trying to add a list of object/class in my state. i.e I have a contract state that has a list of attachments List<Attachment>, where Attachmentis just a class with fields like attachmentHash, uploadedDate, fileType

  1. I wanted to query with something in the child but I get syntax error "AttachmentEntity is not a subtype of PersistentState"

    QueryCriteria.VaultCustomQueryCriteria(
    builder { (ContractSchemaV1.AttachmentEntity::uploadDate).equal(givenDate) })) 
    
  2. I let AttachmentEntity be a subclass of PersistentState and the node started up with the error

    org.hibernate.AnnotationException: net.corda.core.schemas.PersistentStateRef 
    must not have @Id properties when used as an @EmbeddedId: project.schemas.ContractSchemaV1$AttachmentEntity.stateRef
    

Seems like I'm doing something wrong, whats the best way to represent a collection of data classes in the state and translate that in a schema? Or is this already the correct way, but there's no way to query the nested collection using VaultCustomQuery?

The example entity below.

object ContractSchema

 object ContractSchemaV1 : MappedSchema(schemaFamily = ContractSchema.javaClass, version = 1,
    mappedTypes = listOf(ContractEntity::class.java, AttachmentEntity:class.java)) {
@Entity
@Table(name = "contracts")
class ContractEntity(

        @Column(name = "issued_date")
        var issuedDate: Instant,

        @Column(name = "linear_id")
        var linearId: String,

        @OneToMany(fetch = FetchType.LAZY, cascade = arrayOf(CascadeType.PERSIST))
        @JoinColumns(
                JoinColumn(name = "transaction_id", referencedColumnName = "transaction_id"),
                JoinColumn(name = "output_index", referencedColumnName = "output_index"))
        var attachments: MutableSet<AttachmentEntity> = emptyList(),

) : PersistentState()

@Entity
@Table(name = "attachments")
class AttachmentEntity (

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "id", unique = true, nullable = false)
    var id: Long? = null,

    @Column(name = "attachment_hash", nullable = false)
    var attachmentHash: String? = null,

    @Column(name = "attachment_name", nullable = false)
    var attachmentName: String? = null,

    @Column(name = "upload_date", nullable = true)
    var uploadDate: Instant? = null)
}

Upvotes: 1

Views: 654

Answers (1)

Joel
Joel

Reputation: 23140

Your schema definition is correct (and you can see another example here: Querying nested collections in LinearState states).

However, querying nested collections is not supported by VaultCustomQueryCriteria. You have to do direct JDBC queries to query attributes of the nested collections.

Here is an example of a direct JDBC query in Corda:

@Test
fun `test calling an arbitrary JDBC native query`() {
    val nativeQuery = "SELECT v.transaction_id, v.output_index FROM vault_states v WHERE v.state_status = 0"

    database.transaction {
        val jdbcSession = services.jdbcSession()
        val prepStatement = jdbcSession.prepareStatement(nativeQuery)
        val rs = prepStatement.executeQuery()
        var count = 0
        while (rs.next()) {
            val stateRef = StateRef(SecureHash.parse(rs.getString(1)), rs.getInt(2))
            Assert.assertTrue(cashStates.map { it.ref }.contains(stateRef))
            count++
        }
        Assert.assertEquals(cashStates.count(), count)
    }
}

Upvotes: 2

Related Questions