0

In my application I am using @Transactional annotation to mark service methods to be transactional and service beans are created with @Component - nothing special a generic annotation based SpringFramework app.

It usually was working fine and I have been very happy with it, until I moved several methods of the service to superclass... @Transactional annotation does not seem to work there. I have noticed this, because these methods use @Transactional(readOnly = false) and commit can not get through. In result I get an error with an exception which is usually got when you forget to mark a method readOnly = false.

I have double checked everything and YES, the problem appears only if I move the methods to the superclass. Any ideas on how to solve it (and YES, I googled a lot. They say there is a problem, but no one really has a solution)?

UPDATE: here is the exception as requested:

org.springframework.dao.InvalidDataAccessApiUsageException: Write operations are not allowed in read-only mode (FlushMode.MANUAL): Turn your Session into FlushMode.COMMIT/AUTO or remove 'readOnly' marker from transaction definition.
    at org.springframework.orm.hibernate4.HibernateTemplate.checkWriteOperationAllowed(HibernateTemplate.java:1135)
    at org.springframework.orm.hibernate4.HibernateTemplate$24.doInHibernate(HibernateTemplate.java:789)
    at org.springframework.orm.hibernate4.HibernateTemplate.doExecute(HibernateTemplate.java:340)
    at org.springframework.orm.hibernate4.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:308)
    at org.springframework.orm.hibernate4.HibernateTemplate.delete(HibernateTemplate.java:786)
    at org.springframework.orm.hibernate4.HibernateTemplate.delete(HibernateTemplate.java:781)
    at com.shop.server.common.dao.impl.AbstractDaoImpl.delete(AbstractDaoImpl.java:82)
    at com.shop.server.common.api.das.DeleteDataAccessService$class.delete(DeleteDataAccessService.scala:20)
    at com.shop.server.auth.api.UserService.delete(UserService.scala:24)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:606)
    at org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory$1.invoke(ResourceMethodInvocationHandlerFactory.java:81)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run(AbstractJavaResourceMethodDispatcher.java:151)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke(AbstractJavaResourceMethodDispatcher.java:171)
    at org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$TypeOutInvoker.doDispatch(JavaResourceMethodDispatcherProvider.java:195)
    at org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch(AbstractJavaResourceMethodDispatcher.java:104)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:387)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:331)
    at org.glassfish.jersey.server.model.ResourceMethodInvoker.apply(ResourceMethodInvoker.java:103)
    at org.glassfish.jersey.server.ServerRuntime$1.run(ServerRuntime.java:271)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:271)
    at org.glassfish.jersey.internal.Errors$1.call(Errors.java:267)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:315)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:297)
    at org.glassfish.jersey.internal.Errors.process(Errors.java:267)
    at org.glassfish.jersey.process.internal.RequestScope.runInScope(RequestScope.java:297)
    at org.glassfish.jersey.server.ServerRuntime.process(ServerRuntime.java:254)
    at org.glassfish.jersey.server.ApplicationHandler.handle(ApplicationHandler.java:1025)
    at org.glassfish.jersey.servlet.WebComponent.service(WebComponent.java:372)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:382)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:345)
    at org.glassfish.jersey.servlet.ServletContainer.service(ServletContainer.java:220)
    at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:684)
    at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:503)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:137)
    at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:533)
    at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:231)
    at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1086)
    at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:429)
    at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:193)
    at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1020)
    at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:135)
    at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:255)
    at org.eclipse.jetty.server.handler.HandlerCollection.handle(HandlerCollection.java:154)
    at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:116)
    at org.eclipse.jetty.server.Server.handle(Server.java:370)
    at org.eclipse.jetty.server.AbstractHttpConnection.handleRequest(AbstractHttpConnection.java:494)
    at org.eclipse.jetty.server.AbstractHttpConnection.headerComplete(AbstractHttpConnection.java:971)
    at org.eclipse.jetty.server.AbstractHttpConnection$RequestHandler.headerComplete(AbstractHttpConnection.java:1033)
    at org.eclipse.jetty.http.HttpParser.parseNext(HttpParser.java:644)
    at org.eclipse.jetty.http.HttpParser.parseAvailable(HttpParser.java:235)
    at org.eclipse.jetty.server.AsyncHttpConnection.handle(AsyncHttpConnection.java:82)
    at org.eclipse.jetty.io.nio.SelectChannelEndPoint.handle(SelectChannelEndPoint.java:696)
    at org.eclipse.jetty.io.nio.SelectChannelEndPoint$1.run(SelectChannelEndPoint.java:53)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:608)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:543)
    at java.lang.Thread.run(Thread.java:744)

UPDATE: here is the spring config for data:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

    <tx:annotation-driven/>

    <bean id="interfaceEntityFactory" class="com.shop.server.common.hibernate.InterfaceEntityFactory"/>

    <bean id="interfaceInstantiator" class="com.shop.server.common.hibernate.InterfaceInstantiator">
        <property name="factory" ref="interfaceEntityFactory"/>
    </bean>

    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location">
            <value>/properties/database.properties</value>
        </property>
    </bean>

    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="${jdbc.driverClassName}" />
        <property name="url" value="${jdbc.url}" />
        <property name="username" value="${jdbc.username}" />
        <property name="password" value="${jdbc.password}" />
    </bean>

    <bean id="sessionFactory" class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="entityInterceptor" ref="interfaceInstantiator"/>

        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
                <prop key="hibernate.hbm2ddl.auto">update</prop>
            </props>
        </property>

        <property name="mappingLocations">
            <list>
                <value>classpath*:com/shop/server/**/*.hbm.xml</value>
            </list>
        </property>
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>

    <bean id="abstractDao" class="com.shop.server.common.dao.impl.AbstractDaoImpl" abstract="true">
        <property name="sessionFactory" ref="sessionFactory"/>
    </bean>

</beans>

OK, the base class:

package com.shop.server.auth.api;

import com.shop.server.common.api.model.ContentResponse;
import com.shop.server.common.api.model.SuccessResponse;
import com.shop.server.common.dao.IdentifiableDao;
import com.shop.server.common.entity.Identifiable;
import org.springframework.transaction.annotation.Transactional;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;

/**
 * Created by daniel on 6/29/14.
 */
public abstract class BaseService<TEntity extends Identifiable> {
    protected abstract IdentifiableDao<TEntity> getDao();

    @GET
    @Produces({(MediaType.APPLICATION_JSON)})
    @Path("{id}")
    @Transactional
    public ContentResponse<TEntity> read(@PathParam("id") Long id) {
        return new ContentResponse<TEntity>(getDao().get(id));
    }

    @DELETE
    @Produces({(MediaType.APPLICATION_JSON)})
    @Path("{id}")
    @Transactional(readOnly = false)
    public SuccessResponse delete(@PathParam("id") Long id) {
        getDao().delete(id);
        return SuccessResponse.RESPONSE;
    }
}

the ancestor:

package com.shop.server.auth.api;

import com.shop.server.auth.entity.EMail;
import com.shop.server.auth.entity.User;
import com.shop.server.common.api.model.ContentResponse;
import com.shop.server.common.dao.IdentifiableDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.ws.rs.*;
import javax.ws.rs.core.MediaType;
import java.util.Set;

/**
 * Created by daniel on 6/29/14.
 */
@Component
public class UsersService extends BaseService<User>  {
    @Autowired
    @Qualifier("userDao")
    private IdentifiableDao<User> dao;

    protected IdentifiableDao<User> getDao() {
        return dao;
    }

    @GET
    @Produces({(MediaType.APPLICATION_JSON)})
    @Path("{id}/emails")
    @Transactional
    public ContentResponse<Set<EMail>> emails(@PathParam("id") Long id) {
        return new ContentResponse<Set<EMail>>(getDao().get(id).emails());
    }
}
strannik
  • 1,595
  • 1
  • 13
  • 22
  • *I get an error with an exception which is usually got when you forget to mark a method readOnly = false*. Don't you think it would be much clearer, and easier for us to tell you how to fix the problem, if you posted your code, and the stack trace of the exception you're getting? When you go to the doctor, you don't tell him "please heal me, I suffer the same way as when I play football". – JB Nizet Jun 29 '14 at 11:39
  • @JBNizet I have added exception, but it does not add a lot of info to the question. The problem is just that spring somehow does not take annotations from superclass. – strannik Jun 29 '14 at 11:47
  • @Daniel Can you share with us your code and spring configuration? – Skizzo Jun 29 '14 at 12:31
  • Post the relevant bits of code as well – Jukka Jun 29 '14 at 15:06
  • OK, posted super and service – strannik Jun 29 '14 at 15:48

1 Answers1

0

To enable spring to process the annotations following tag must be specified:

<tx:annotation-driven proxy-target-class="true"/>

Using just annotation-driven without proxy-target-class does not make spring to process supers.

strannik
  • 1,595
  • 1
  • 13
  • 22