corda: Unable to implement hierarchical persistence as described in docs

Simple Reference Project Documentation Version: 4.0 Corda Version: 4.0

In the docs a child token is tied to the parent token via:

 @Entity
 @CordaSerializable
 @Table(name = "child_data")
 class PersistentChildToken(
         @Id
         var Id: UUID = UUID.randomUUID(),

         // code for other columns

         @ManyToOne(targetEntity = PersistentParentToken::class)
         var persistentParentToken: TokenState

 ) : PersistentState()

but whenever i annotate a child with @Id i receive the following error:

net.corda.nodeapi.internal.persistence.HibernateConfigException: Could not create Hibernate configuration: net.corda.core.schemas.PersistentStateRef must not have @Id properties when used as an @EmbeddedId

i’ve looked through the source code and saw that a PersistentState is defined as the following:

@KeepForDJVM
@MappedSuperclass
@CordaSerializable
class PersistentState(@EmbeddedId override var stateRef: PersistentStateRef? = null) : DirectStatePersistable

so i guess i understand where the error is coming from, PersistentChildToken implements PersistentState which already has @EmbeddedId. but if i remove the @Id field i get the following error:

javax.persistence.PersistenceException: org.hibernate.id.IdentifierGenerationException: null id generated for:class

it seems like i can’t use the @Id annotation because PersistentState is already annotated with EmbeddedId, but I also need the @Id annotation or else an id will not be properly generated.

there are further errors i have run into while trying to implement the hierarchal relationship which were eventually solved by adding a @OneToMany annotation above @JoinColumns. In the context of the docs it would be:

@OneToMany(
  cascade = [CascadeType.ALL],
  targetEntity = PersistentChildToken::class
)

the project i am attempting implement the hierarchical relationships is more complex than the simple reference project, but the core of the workflows, table creation, and relationship mapping is there.

any help implementing a one to many relationship would be greatly appreciated, thank you.

About this issue

  • Original URL
  • State: open
  • Created 5 years ago
  • Comments: 15 (5 by maintainers)

Most upvoted comments

Internal ticket - CORDA-4212

@amolpednekar you are absolutely correct about the added overhead and i share your frustration with not being able to use Corda’s built in querying apis to handle the situation. unfortunately this approach was the best option available given the circumstances of our use case. if anyone else has found another solution to make this work using the querying api’s, i would be interested in hearing it

@SweeXordious some pseudo-code to get you started - note that this logic does not handle consumed/unconsumed states

 fun getParentAndChildren(parentLinearId: UUID)  {
   val nativeQuery = getNativeQuery(linearId)
   val session= serviceHub.jdbcSession()
   val rs = session.prepareStatement(nativeQuery).executeQuery()
   
   while (rs.next()) {
     // business logic
    }
 }
 
 fun getNativeQuery(linearId: UUID) : String {
    return """
      select 
        *
      from
        parent_table as p
      join
        child_table as c
      on
        c.parent_linear_id = '$linear_id'::uuid
      where
         p.linear_id = '$linear_id'::uuid
    """.trimIndent()
 }

@amolpednekar, I have used the same approach of @jrosmith, pass UUID in constructor, my problem was in CordaSerializable… You can Override method too… If you share the code, we can analyze better…

@amolpednekar @prateektiwari7 we were not able to resolve this using the hierarchical implementation, but did implement a workaround that requires us to write all queries making use of hierarchical relations as native sql. this has been a frustrating experience but at least something can be hacked together.

For a one to many scenario:

// Parent schema implemented as usual
class PersistentParent(
  @Column(name = "linear_id")
  var linearId: UUID
) : PersistentState() {
  contructor() : this( UUID.randomUUID() )
}

// Child has a reference to its parent
class PersistentChild(
  @Column(name = "linear_id")
  var linearId: UUID
  @Column=(name = "parent_linear_id")
  var parentLinearId: UUID
) : PersistentState() {
  contructor() : this( UUID.randomUUID(), UUID.randomUUID() )
}

For many-to-many we had to manually create a through state and schema:

// Parent schema implemented as usual
class PersistentParent(
  @Column(name = "linear_id")
  var linearId: UUID
) : PersistentState() {
  constructor() : this( UUID.randomUUID() )
}

// Child schema implemented as usual
class PersistentChild(
  @Column(name = "linear_id")
  var linearId: UUID
) : PersistentState() {
  constructor() : this( UUID.randomUUID())
}

// ParentChildThrough table schema
class PersistentThroughTable(
  @Column(name = "parent_linear_id")
  var parentLinearId: UUID
  @Column(name="child_linear_id")
  var childLinearId: UUID
) : PersistentState() {
  constructor() : this( UUID.randomUUID(), UUID.randomUUID() )
}