MyBatis Performance with OSGi
I’m currently working on a Java OSGi-based application using MyBatis 3.2 for persistence, and ran into performance problems. Loading a few thousand rows from the database (ie creating a few thousand objects based on the matched rows) was running far slower than expected.
The cause turned out to be interesting. MyBatis is configured with xml ResultMap
entries specifying what object to instantiate to represent each matched row, and how to map the columns into the object’s properties. The actual instantiating of the object is done via an “object factory”. The default implementation (DefaultObjectFactory) uses the following (for each instance, ie each db row):
Constructor c = Class.getDeclaredConstructor(....)
c.newInstance(args)
However method Class.getDeclaredConstructor calls into the java.lang.SecurityManager which calls into each ProtectionDomain on the callstack - which is inefficient in all cases, but particularly bad for OSGi when a ConditionalPermissionConstraints service is enabled. The OSGi checks aren’t particularly fast, and calling them multiple times for each object being instantiated quickly adds up.
To fix, just create a custom ObjectFactory which caches the constructor. At first, I was concerned about having a cache with strong references to Constructor objects (and therefore to the related class) locking the containing OSGi bundle into memory (ie interfering with runtime unloading or refreshing of OSGi bundles). However the ObjectFactory instance is held by the MyBatis Configuration instance; as long as that is held by an OSGi service which is in a bundle that explicitly imports the package for every class that the mybatis config files reference then for every class in the cache, OSGi will automatically stop the service holding the Configuration instance when the bundle for that class is unloaded. So a normal map can be used.
While tracking this issue down, I also learnt some new things about MyBatis caching which I have written up here.