Pregunta

I have developed a library that generates at runtime especialization classes of an abstract class given certain annotations in the abstract class. So my library is used a bit like:

X x = newInstance(X.class) //X is an abstract class

This library uses Javassist for the generation of a subclass at runtime that will be instantiated by the newInstance method.

As far as I understand Hibernate also makes use of Javassist to instrument at runtime entity classes (please someone correct me if that is not the case).

My question is if it is possible to make the two frameworks work together ?. I mean, can I tell Hibernate that every time that it needs an instance of an entity class (an abstract class) it should use a specific factory method from my library ?.

I do not know if Hibernate works behind the courtains by also generating subclasses at runtime of entity classes (assuming Hibernate also needs to instrument them). If that is the case the solution of passing to Hibernate a factory for certain classes will not work. In that case, does Hibernate have any kind of support for working with abstract entity classes ?. I mean, it should be possible to work with abstract (or interface?) entity classes and somehow tell to Hibernate which is the right concrete class it should instrument when required to work with one of these abstract entity classes.

But an additional complication is that the concrete class especializing the abstract entity class does not exist at compile-time.

¿Fue útil?

Solución

Before heading to the your asnwer:

  • Yes you are correct, Hibernate currently uses Javassist (in the past it used GCLib, but it was deprecated) to instrument classes at runtime.
  • Hibernate does creates subclasses at runtime that work has proxy for you persistent entities.

Short answer

Sadly, I don't think you will be able to configure Hibernate to use your own factory. For details I invite you to read the long answer part.

Long answer

For what I know, currently, Hibernate 4.x only supports Javassist as its bytecode manipulation provider. Although, it used to allow you to switch between GClib and Javassist in the 3.x versions. Back in those versions you could modify the factory to be used by configuring an hibernate global setting called hibernate.bytecode.provider.

This setting no longer shows up in the Hibernate 4.1 documentation but you still can find a bit about it in the documentation for Hibernate 3.2 under the optional configuration properties.

Being myself a developer I know that sometimes we are some tricky fellows and just because something is out of the docs doesn't mean necessarily it's out of the code :-) So, I though maybe if the setting still exists we could try to exploit it in order to do what you want (in a non-supported way, though).

For the sake of curiosity since I had Hibernate 4.0.1 code (notice that is not the latest, though) in my machine I did some digging... And suprise, suprise the property still existed! After tracking for used references (thank you Eclipse) I ended up in the class org.hibernate.cfg.Environment (code for version 4.2.0.CR2) where I found the following code (code was the same both in my version and 4.2.0CR2):

 public static BytecodeProvider buildBytecodeProvider(Properties properties) {
    String provider = ConfigurationHelper.getString( BYTECODE_PROVIDER, properties, "javassist" );
    LOG.bytecodeProvider( provider );
    return buildBytecodeProvider( provider );
}

private static BytecodeProvider buildBytecodeProvider(String providerName) {
    if ( "javassist".equals( providerName ) ) {
        return new org.hibernate.bytecode.internal.javassist.BytecodeProviderImpl();
    }

    LOG.unknownBytecodeProvider( providerName );
    return new org.hibernate.bytecode.internal.javassist.BytecodeProviderImpl();
}

So as far I can tell it's Javassist implementation for your proxy factory and there won't be a standard way to change it.


The madman sidenote

What I'm about to say is pure madness and it should not be taken into account for production code but only in a would it actually work/academic/lets bend things scenario.

<Hammer Hacking>

  • You could try extend your own framework to instrument the classes in order not only to add the bytecode for your needs but also the bytecode that hibernate needs - which I would say would be something like merging your manipulations with org.hibernate.bytecode.internal.javassist.BytecodeProviderImpl manipulations.
  • You could then rename your extension class to BytecodeProviderImpl put it in the same package org.hibernate.bytecode.internal.javassist and finally put it somewhere in the classpath where the classloader would find it before the one in the jar (or maybe using a custom class loader)

Then you would just enjoy listening to Hibernate, your framework and probably the entire JVM scream in panic not knowing what to do, or maybe it could work...

</Hammer Hacking>

Anyway, if you have the time and will to try it, let me know if it worked out.


UPDATE

After talking a bit in the comments section I had this idea to use a custom EntityPersister. Since I wasn't very sure about this I googled a bit to see if I could find something that could tell me that what I was thinking would work or not.

Even better than finding out if my gut feeling was right or not, I found a question in Stackoverflow that seems pretty similar to yours. Sadly, there's no accepted answer there.

But the first answer to that question, gives a link to something similar to what I was thinking. Quoting Pascal Thivent:

a custom EntityPersister implementation (that you can register for a particular entity during Hibernate initialization using a custom Configuration)

It's true that the example is for Hibernate in Grails but it's pretty much the same in plain Java:

 public void registerCustomEntityPersister(Configuration configuration) {
     final Iterator<PersistentClass> classesIterator = configuration.getClassMappings();
 while (classesIterator.hasNext()) {
    final PersistentClass persistentClass = classesIterator.next();
        if (checkIfIsOneTheClassesThatMatters(persistentClass)) {
          persistentClass.etEntityPersisterClass(CustomEntityPersister.class); 
        }
 }

Even though this seems doable, it looks like way too much work because implementing the EntityPersister doesn't look that trivial... way too many things. You could try to extend the default one Hibernate uses (which I really don't know which one is) and try to override the getProxy() method in order to return one of your instrumented classes.

Sorry if it's still not a spot on answer, but sadly I'm no Hibernate expert I just use it usually out of the box, I actually landed on your question due to the javassist tag and found it quite interesting.

I hope that at least I gave you information that can help you out.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top