Announcement Announcement Module
Collapse
No announcement yet.
Hibernate AOP Question Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Hibernate AOP Question

    Hi All,

    I am having a problem using the HibernateInterceptor and TransactionInterceptor for declarative transaction demarcation.
    Basically, what I am trying to do looks like this:


    Code:
    public class MyService
    {
        public void A ()
        {
            // Retrieve data from a DAO
            ..
    
            // Iterate through a list of entities. Call method B to store each 
            //  entity in the database (in its own transaction)
            for (Iterator i = entityList.iterator(); i.hasNext();){  
                this.B((DataItem) i.next());
            }
        }
    
        protected void B (DataItem entity)
        {
            // Load some reference data from the database
            ..
    
            // Set fields, do some logic
            ..
                 
            // Save entity to the database using a DAO
            dao.save(entity);
        }
    }
    I am trying to set up interceptors that will:

    a) wrap method A in a hibernate session, thus ensuring that for the duration of a call to A and all sub-methods, the same Hibernate session will be used (ie. an OpenSessionInView style pattern).
    b) wrap method B in a hibernate transaction, thus ensuring that for each call to B, a new transaction is used. Any errors that occur within that transaction should cause the transaction to roll-back, but should not affect subsequent calls to B.



    The xml configuration file for this service bean looks like this:

    Code:
    <beans>
      <bean id="myService" class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="proxyTargetClass"><value>true</value></property>
        <property name="target"><ref local="myServiceTarget"/></property>
        <property name="interceptorNames">
          <list>
            <value>hibernateSessInterceptor</value>
            <value>hibernateTxInterceptor</value>
          </list>
        </property>
      </bean>
    
      <bean id="myServiceTarget" class="mypackage.MyService">
        <!-- property configurationsdefined here -->
      </bean>
    
      <bean id="hibernateSessInterceptor" class="org.springframework.orm.hibernate.HibernateInterceptor">
        <property name="sessionFactory"><ref bean="sessionFactory"/></property>
      </bean>
    
      <bean id="hibernateTxInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
        <property name="transactionManager"><ref bean="hibernateTxManager"/></property>
        <property name="transactionAttributeSource">
          <value>mypackage.MyService.B=PROPAGATION_REQUIRED</value>
        </property>
      </bean>
    
    </beans>

    The problem I am having is that upon running this, no data is inserted in the database. It appears that the problem is that no transactions are being opened, and as such the saved data is not being inserted into the database. If an error occurs, the stack trace I get looks something like this:

    Code:
    java.lang.Exception&#58; 
    
    ... &#40;etc, etc&#41;
    
    	at mypackage.MyService.B&#40;MyService.java&#58;92&#41;
    	at mypackage.MyService.A&#40;MyService.java&#58;70&#41;
    	at mypackage.MyService$$FastClassByCGLIB$$a36e139c.invoke&#40;<generated>&#41;
    	at net.sf.cglib.proxy.MethodProxy.invoke&#40;MethodProxy.java&#58;149&#41;
    	at org.springframework.aop.framework.Cglib2AopProxy$MethodInvocationImpl.invokeJoinpoint&#40;Cglib2AopProxy.java&#58;392&#41;
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed&#40;ReflectiveMethodInvocation.java&#58;114&#41;
    	at org.springframework.transaction.interceptor.TransactionInterceptor.invoke&#40;TransactionInterceptor.java&#58;169&#41;
    	at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed&#40;ReflectiveMethodInvocation.java&#58;134&#41;
    	at org.springframework.aop.framework.Cglib2AopProxy.intercept&#40;Cglib2AopProxy.java&#58;144&#41;
    	at mypackage.MyService$$EnhancerByCGLIB$$b02f66a1.A&#40;<generated>&#41;
    
    ... &#40;etc, etc&#41;

    It appears from this stack trace that only method A is being intercepted by the CGLIB interceptor, and that method B is not being intercepted, hence has no transaction demarcation. Which brings me to my question, which is, what is going wrong with this configuration, that method B is not being intercepted?

    Thanks in advance to anyone who might have tips that could help here.

    Regards,
    Scott Russell

  • #2
    Method B() will not be advised and intercepted in this configuration. Because Spring does not change the "target object" itself at all, if a target object invokes methods on itself they're not advised.

    The approach is to either refactor B into a separate class, or invoke B not using "this", but using AopContext.currentProxy(), with the exposeProxy flag on your proxy config set to true. This should be covered in the AOP Chapter of the Reference Manual. This is similar to calling getEJBObject() for an EJB.

    Rgds
    Rod

    Comment


    • #3
      Originally posted by Rod Johnson
      Method B() will not be advised and intercepted in this configuration. Because Spring does not change the "target object" itself at all, if a target object invokes methods on itself they're not advised.

      The approach is to either refactor B into a separate class, or invoke B not using "this", but using AopContext.currentProxy(), with the exposeProxy flag on your proxy config set to true. This should be covered in the AOP Chapter of the Reference Manual. This is similar to calling getEJBObject() for an EJB.
      Thanks Rod, I'll give that a go.

      Regards
      Scott

      Comment


      • #4
        futher info

        Using Spring 1.0.2/Hibernate 2.1.6/JTA

        I found this post and it helped me out. I am getting things to work like I want (same type of loop/process one thing structure). However, I want to take the example a little farther because my situation is more complex. Say there's a utility method C that needs to be called from B to break things up nicely. Just calling C from B works fine. However, I thought that I would also call this via the proxy in case in the future the transactional attributes on B or C needed to change. Only using currentProxy() on methods you want to define tx scope seems to reduce the advantage of having declarative tx management elsewhere because you've coupled the code to the tx declarations.

        So, attempted to invoke C from B via the proxy also was my idea, but that didn't work with my classes.

        Code:
        public class MyService
        &#123;
           private MyService me&#40;&#41; &#123;
               return AopContext.currentProxy&#40;&#41;;
           &#125;
        
            public void A &#40;&#41;
            &#123;
                // Retrieve data from a DAO
                ..
        
                // Iterate through a list of entities. Call method B to store each
                //  entity in the database &#40;in its own transaction&#41;
                for &#40;Iterator i = entityList.iterator&#40;&#41;; i.hasNext&#40;&#41;;&#41;&#123; 
                    me&#40;&#41;.B&#40;&#40;DataItem&#41; i.next&#40;&#41;&#41;;
                &#125;
            &#125;
        
            protected void B &#40;DataItem entity&#41;
            &#123;
                // Load some reference data from the database
                ..
        
                // Set fields, do some logic
                ..
                //me&#40;&#41;.C&#40;entity&#41;; //doesn't work
                C&#40;entity&#41;;
        
                // Save entity to the database using a DAO
                dao.save&#40;entity&#41;;
            &#125;
        
            protected void C &#40;DataItem entity&#41;
            &#123;
               //utility stuff
            &#125;
        &#125;
        There's base classes with helper methods and common dependencies and such, etc. The reason it doesn't work seems to be related to how current proxies get pushed on a stack each time there's a call through the proxy, so in C you're actually in MyService$$ByCGLib instead of MyService. When this happens then the way the class is extended with respect to private/protected variables and methods starts getting confusing and broken.

        Anyway, what I don't understand is why the generated proxy for MyService isn't something like this:

        Code:
        public class MyService$$ByCGLib
        &#123;
            public void A &#40;&#41;
            &#123;
                //tx setup
                super.A&#40;&#41;
                //tx end
            &#125;
        
            protected void B &#40;DataItem entity&#41;
            &#123;
                //tx setup
                super.B&#40;entity&#41;
                //tx end
            &#125;
        
            protected void C &#40;DataItem entity&#41;
            &#123;
                //tx setup
                super.C&#40;entity&#41;
                //tx end
            &#125;
        &#125;
        Calls from outside to A go through the proxy, which checks and sees no tx attributes. The call to super.A() from the outside is handled by the proxy and checks for tx. However, the call within the MyService to B from A resolves to the subclasses B, which checks tx code and then calls the real B to do the work. The subsequent call to C does the same thing.

        When I first read about proxyTargetClass and the CGLib class generation, that's what I figured this did. Then all methods on a service that are protected are automatically overridden and can support declarative tx without modifying the source. That's not what it really does, however, as far as I can tell. The generated class seems to have all of the private members for dependencies overridden (and null), and just doesn't fit what I expect.

        Am I missing something? Is there a way to accomplish this another way, or am I totally off base? Interface-based programming really doesn't help here, either, since the calls to A, B, and C have to come from outside the interface to work. In fact, that almost seems worse, because unless you want the caller to the interface to loop through items and call B for each one, you have to have the tx around the main method A and can't break it up into smaller bits with B.

        Thanks,
        John

        Comment


        • #5
          The cglib based variant is ultimately still a proxy based approach. It just gets around the limitations of the JDK Proxy mechanism, which can only deal with interfaces.

          Short of doing a bunch of bytecode modification on the original class itself, these usage semantics are basically what you're stuck with when you use a proxy approach...

          Comment


          • #6
            I don't really understand how it works currently, but I don't see how it can just be a proxy. It seems like in order for all the calling code to work the classes generated by cglib would have to be subclasses of the real class. If that's true, then I don't see why something like my example wouldn't work. Do you know if this is even possible with cglib? It seems like it can generated any class at runtime (ignoring security considerations), so it should be.

            Actually in my example before, the generated class should have been like:
            Code:
            public class MyService$$ByCGLib extends MyService
            &#123; 
            &#125;

            Comment


            • #7
              I agree. It seems to me that the way bungle describes it is the way things *should* work. Did anyone figure this out?

              Comment


              • #8
                Originally posted by Colin Sampaleanu
                The cglib based variant is ultimately still a proxy based approach. It just gets around the limitations of the JDK Proxy mechanism, which can only deal with interfaces.

                Short of doing a bunch of bytecode modification on the original class itself, these usage semantics are basically what you're stuck with when you use a proxy approach...
                CGLib is a popular approach to proxying, but have you considered other libraries like javassist, bcel, ast, etc? From what little I know of javassist, I think it allows you to create a subclass with over-ridden methods, as opposed to the CGLib approach which tunnels all (external) method calls through a single proxy access point. A javassist-created subclass would not require byte-code modification of the original class file, but would resolve such problems as internally calling proxied methods.

                Just a thought, anyway...

                regards,
                Scott

                Comment


                • #9
                  I don't think your analysis of CGLib is quite right. I don't think that only external calls are proxied. I looked into things a bit more and it *looks* like the reason that protected methods aren't proxied is because Cglib2AopProxy.ProxyCallbackFilter's accept() method has an explicit condition to ignore protected methods. Of course, there could still be other hindrances.

                  I assume the above approach was taken in order to keep the operational behavior consistent between the JDK-based proxies and the CGLib-based proxies. I'm going to try to implement a custom proxy factory bean to get around this 'limitation'.

                  Now if someone could only explain why everything is protected/private in Cglib2AopProxy!?

                  Comment

                  Working...
                  X