使用超类的Spring框架基于注释的事务

时间:2014-06-29 11:34:24

标签: java spring hibernate transactions annotations

在我的应用程序中,我使用@Transactional注释将服务方法标记为事务性,服务bean使用@Component创建 - 没有什么特别的基于通用注释的SpringFramework应用程序。

它通常工作正常,我对它非常满意,直到我将多个服务方法移动到超类... @Transactional注释似乎不适用于那里。我注意到了这一点,因为这些方法使用@Transactional(readOnly = false)并且提交无法通过。结果我得到一个异常的错误,这通常是在您忘记标记方法readOnly = false时获得的。

我已经仔细检查了所有内容,是的,只有将方法移动到超类时才会出现问题。关于如何解决它的任何想法(是的,我搜索了很多。他们说有问题,但没有人真的有解决方案)?

更新:这是所要求的例外:

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)

更新:这是数据的弹簧配置:

<?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>

好的,基类:

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;
    }
}

祖先:

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());
    }
}

1 个答案:

答案 0 :(得分:0)

要启用spring来处理标记后面的注释必须指定:

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

仅使用annotation-driven而不使用proxy-target-class并不会使弹簧处理超级。