Showing posts with label hibernate. Show all posts
Showing posts with label hibernate. Show all posts

Thursday, 3 May 2012

Binding spring form using bean with map attribute retrieved using hibernate

Another one exception bit me today. Spring forms nicely support maps as bean properties using 'mapProperty[key].mapElementProperty' paths, but each time I tried to submit form, exception was thrown (see below). As it turns out, problem was caused by hibernate which returned bean proxy. When setting map element property values after form submit, Spring tries to find out what type of elements map contains using generics information (... using some kind of magic), but in the end it didn't work. Not sure, but I suppose it could be because javassist can't handle generics because of type erasure. Solution was simple - modify DAO method which returned editable object (getById(id)) so that it would not return a hibernate-managed object, but a cloned object. As soon as those changes were applied, everything started to work.
java.lang.NullPointerException
 at org.springframework.beans.BeanWrapperImpl.createDefaultPropertyValue(BeanWrapperImpl.java:620)
 at org.springframework.beans.BeanWrapperImpl.setDefaultValue(BeanWrapperImpl.java:614)
 at org.springframework.beans.BeanWrapperImpl.getNestedBeanWrapper(BeanWrapperImpl.java:579)
 at org.springframework.beans.BeanWrapperImpl.getBeanWrapperForPropertyPath(BeanWrapperImpl.java:553)
 at org.springframework.beans.BeanWrapperImpl.setPropertyValue(BeanWrapperImpl.java:914)
 at org.springframework.beans.AbstractPropertyAccessor.setPropertyValues(AbstractPropertyAccessor.java:76)
 at org.springframework.validation.DataBinder.applyPropertyValues(DataBinder.java:692)
 at org.springframework.validation.DataBinder.doBind(DataBinder.java:588)
 at org.springframework.web.bind.WebDataBinder.doBind(WebDataBinder.java:191)
 at org.springframework.web.portlet.bind.PortletRequestDataBinder.bind(PortletRequestDataBinder.java:113)
 at org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter$PortletHandlerMethodInvoker.doBind(AnnotationMethodHandlerAdapter.java:570)
 at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.doBind(HandlerMethodInvoker.java:813)
 at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.resolveHandlerArguments(HandlerMethodInvoker.java:367)
 at org.springframework.web.bind.annotation.support.HandlerMethodInvoker.invokeHandlerMethod(HandlerMethodInvoker.java:171)
 at org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter.invokeHandlerMethod(AnnotationMethodHandlerAdapter.java:362)
 at org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter.doHandle(AnnotationMethodHandlerAdapter.java:349)
 at org.springframework.web.portlet.mvc.annotation.AnnotationMethodHandlerAdapter.handleAction(AnnotationMethodHandlerAdapter.java:283)
 at org.springframework.web.portlet.DispatcherPortlet.doActionService(DispatcherPortlet.java:641)
 at org.springframework.web.portlet.FrameworkPortlet.processRequest(FrameworkPortlet.java:519)
 at org.springframework.web.portlet.FrameworkPortlet.processAction(FrameworkPortlet.java:460)
 at org.jasig.portal.portlet.container.FilterChainImpl.doFilter(FilterChainImpl.java:130)
 at org.jasig.portal.portlet.container.FilterChainImpl.processFilter(FilterChainImpl.java:92)
 at org.jasig.portal.portlet.container.FilterManagerImpl.processFilter(FilterManagerImpl.java:119)
 at org.apache.pluto.container.driver.PortletServlet.dispatch(PortletServlet.java:359)
 at org.apache.pluto.container.driver.PortletServlet.doPost(PortletServlet.java:267)
 at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
 at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
 at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
 at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
 at org.apache.catalina.core.ApplicationDispatcher.invoke(ApplicationDispatcher.java:646)
 at org.apache.catalina.core.ApplicationDispatcher.doInclude(ApplicationDispatcher.java:551)
 at org.apache.catalina.core.ApplicationDispatcher.include(ApplicationDispatcher.java:488)
 at org.apache.pluto.driver.container.DefaultPortletInvokerService.invoke(DefaultPortletInvokerService.java:233)
 at org.apache.pluto.driver.container.DefaultPortletInvokerService.action(DefaultPortletInvokerService.java:101)
 at sun.reflect.GeneratedMethodAccessor295.invoke(Unknown Source)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:597)
 at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:318)
 at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
 at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:90)
 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
 at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:80)
 at org.jasig.portal.portlet.dao.jpa.ThreadContextClassLoaderAspect.doThreadContextClassLoaderUpdate(ThreadContextClassLoaderAspect.java:56)
 at sun.reflect.GeneratedMethodAccessor105.invoke(Unknown Source)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:597)
 at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:621)
 at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:610)
 at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:65)
 at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
 at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:202)
 at $Proxy207.action(Unknown Source)
 at org.apache.pluto.container.impl.PortletContainerImpl.doAction(PortletContainerImpl.java:251)
 at org.jasig.portal.portlet.rendering.PortletRendererImpl.doAction(PortletRendererImpl.java:165)
 at org.jasig.portal.portlet.rendering.worker.PortletActionExecutionWorker.callInternal(PortletActionExecutionWorker.java:46)
 at org.jasig.portal.portlet.rendering.worker.PortletActionExecutionWorker.callInternal(PortletActionExecutionWorker.java:31)
 at org.jasig.portal.portlet.rendering.worker.PortletExecutionWorker$1.call(PortletExecutionWorker.java:145)
 at java.util.concurrent.FutureTask$Sync.innerRun(FutureTask.java:303)
 at java.util.concurrent.FutureTask.run(FutureTask.java:138)
 at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
 at java.lang.Thread.run(Thread.java:662)

Wednesday, 21 March 2012

Bytecode instrumentation for Hibernate using Maven

Lazy property loading for hibernate is nice when you're using complex formulas for calculating values. But it requires bytecode instrumentation or otherwise hibernate will ignore 'lazy="true"' on property element and load the value eagerly. Here's the snippet of my pom.xml that does the instrumentation:
    <build>
        <plugins>
            ...
            <!-- Bytecode insturmentation in order to enable lazy property loading by Hibernate. -->
            <plugin>
                <artifactId>maven-antrun-plugin</artifactId>
                <version>1.3</version>
                <executions>
                    <execution>
                        <id>Instrument domain classes</id>
                        <configuration>
                            <tasks>
                                <taskdef name="instrument" classname="org.hibernate.tool.instrument.javassist.InstrumentTask">
                                    <classpath>
                                        <path refid="maven.dependency.classpath" />
                                        <path refid="maven.plugin.classpath" />
                                    </classpath>
                                </taskdef>
                                <instrument verbose="true">
                                    <fileset dir="${project.build.outputDirectory}">
                                        <include name="com/example/domain/*.class" />
                                    </fileset>
                                </instrument>
                            </tasks>
                        </configuration>
                        <phase>process-classes</phase>
                        <goals>
                            <goal>run</goal>
                        </goals>
                    </execution>
                </executions>
                <dependencies>
                    <dependency>
                        <groupId>org.hibernate</groupId>
                        <artifactId>hibernate-core</artifactId>
                        <version>${hibernate.version}</version>
                    </dependency>
                    <dependency><!-- bytecode instrumentation library to use... note that cglib is deprecated -->
                        <groupId>org.javassist</groupId>
                        <artifactId>javassist</artifactId>
                        <version>${javassist.version}</version>
                    </dependency>
                    <dependency><!-- so that slf4j won't barf about missing binding -->
                        <groupId>org.slf4j</groupId>
                        <artifactId>slf4j-simple</artifactId>
                        <version>${slf4j.version}   </version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>
Note that you'll have to define the versions of libraries that you're using (here are my values):
  <properties>
    <hibernate.version>3.6.7.Final</hibernate.version>
    <javassist.version>3.14.0-GA</javassist.version>
    <slf4j.version>1.6.1</slf4j.version>
  </properties>

Monday, 14 March 2011

Hibernate and native SQL fetching

Today it took me about 3 hours to find a problem in my code that was caused by HHH-2831. Unfortunately Hibernate documentation is vague regarding 'return-join' elements. By reading comments of bug, i realized that i'm not the only one facing the same problem and in all cases it was intuition of developers which proved to be wrong.

I'll explain briefly the essence of problem. Consider two simple hibernate class mappings and named native SQL query:

  <class name="ObjectA" table="OBJECT_A">
<id column="ID" name="id">
<generator class="native" />
</id>
<property name="title" column="TITLE" type="string" length="255" not-null="true" />
<list name="children" cascade="all-delete-orphan">
<key column="OBJECT_A_ID" not-null="true" />
<list-index column="ORDER_INDEX" base="0" />
<one-to-many class="ObjectB" />
</list>
</class>
<class name="ObjectB" table="OBJECT_B">
<id column="ID" name="id">
<generator class="native" />
</id>
<property name="title" column="TITLE" type="string" length="255" not-null="true" /> </class>
<sql-query name="getAll" read-only="true">
<return alias="a" class="com.example.ObjectA" />
<return-join alias="b" property="a.children" />
select {a.*}, {b.*}
from OBJECT_A a
inner join OBJECT_B b on b.OBJECT_A_ID = b.ID
</sql-query>

Now... what do you expect of when executing something like:

List result = session.getNamedQuery("getAll").list()

I was expecting it to be a list of objects of class ObjectA without any duplicates. Unfortunately this is not the case (like it would've been when using HQL). In fact it returns a list of Object[2] elements and with as many duplicates of ObjectA as it has children. The second object in array is ObjectB (child) object. Hence you must filter the list for duplicate elements:

        List finalResult = new ArrayList();
for (Object[] objs : result) {
ObjectA obj = (ObjectA) objs[0];
if (!finalResult.contains(obj)) {
finalResult.add(obj);
}
}

I wonder what will happen in case of another level... They cannot be filtered if those collections are not inverse, because it would automatically update the list of children (remove them).

Friday, 24 April 2009

HIbernate equals/hashCode

Just to write this down:
When implementing equals() or hashCode() methods wich will be used in Hibernate, you should always use properties with real "business value" (keep your hands off the ID).
See also: http://www.hibernate.org/109.html