Corda's JPA classes should not be final or have final methods


The JEE7 specification stipulates the following for @Entity and @Embeddable objects (and presumably @MappedSuperclass ones too):

  1. The class must be annotated with the javax.persistence.Entity annotation.

  2. The class must have a public or protected, no-argument constructor. The class may have other constructors.

  3. The class must not be declared final. No methods or persistent instance variables must be declared final.

  4. If an entity instance is passed by value as a detached object, such as through a session bean's remote business interface, the class must implement the Serializable interface.

However, the net.corda.core.schemas.PersistentStateRef class and both entities in are declared as final, as are almost all of their methods. Similarly, while net.corda.core.schemas.PersistentState, net.corda.core.schemas.CommonSchemaV1$LinearState and net.corda.core.schemas.CommonSchemaV1$FungibleState are not final, most of their methods are. This means that JPA cannot create proxies for any of these classes.

The solution is to apply the kotlin-allopen plugin to the core and finance modules.

To be fair, we should probably also ask whether these classes should implement as well.


Chris Rankin
March 27, 2018, 2:00 PM

JPA cannot create proxies for any of our entities because they are either final classes or have final methods. This means Hibernate must always be EAGER when fetching entities from the database, even when the user explicitly asks for a LAZY proxy. For example, consider the following code using EntityManager.getReference():

At the moment, this results in the following SQL being generated:

However, if we fix the entities to allow Hibernate to create proxies, we get this instead:

You will notice that in this case, the SELECT has been deferred until we try to read the owner, currency and pennies properties. And this is how we asked Hibernate to work, because this is the point of getReference() vs find().

Chris Rankin
March 27, 2018, 2:03 PM

Yes, I also saw the kotlin-jpa plugin. But that plugin is just a wrapper around the kotlin-noarg plugin with configuration for JPA entities. It does not perform every JPA-related tweak (unfortunately).

Jose Coll
March 27, 2018, 2:33 PM

Thanks for the clear explanation .
This is a good performance optimisation which we should ask team to attempt to benchmark (Eg. to quantify the before / after effect of the optimisation).
Are there any specific paths (calling sequences) within our code that led you to report this issue?

Chris Rankin
March 27, 2018, 3:28 PM

No, my "performance cluster" at home didn't spot any issues. I simply observed that proxy-generation could not possibly work with the current code, proved it to myself and then raised an issue.

However, you will notice that all occurrences of FetchType.LAZY are meaningless without working proxies. And also that FetchType.LAZY is the default mode for OneToMany and ManyToMany relationships, c.f. NodeInfoSchemaV1.PersistentNodeInfo.

Katelyn Baker
March 29, 2018, 11:44 AM

Merged onto the V3 branch so setting fixed version to reflect


Michele Sollecito


Chris Rankin





Epic Link






CVSS Score


CVSS Vector


Due Date


Engineering Teams


Fix versions

Affects versions


Ported to...


Story Points / Dev Days