精华内容
下载资源
问答
  • 2.需要开启事务的mapping添加 DefaultTransactionDefinition def=new DefaultTransactionDefinition(); def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); TransactionStatus status = ...

    1.首先需要引入

    PlatformTransactionManager类

    2.需要开启事务的mapping添加

    DefaultTransactionDefinition def=new DefaultTransactionDefinition();
    def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
    TransactionStatus status = platformTransactionManager.getTransaction(def);

    3.提交时commit(status)

    4.回滚时rollback(status)

    展开全文
  • 如何在Controller层实现事务管理?

    万次阅读 热门讨论 2017-03-04 06:45:39
    如何在Controller层实现事务管理?

    spring aop 事务管理中发现,我们是在service层实现的事务管理。
    现在有如下场景,大家讨论下看如何实现?
    ControllerA、ControllerB、ControllerC….共同依赖ServiceA、ServiceB,上述Controller的save操作需要把数据同步ServiceA和ServiceB。
    由于每个Controller保存ServiceB的extraData字段是通过Json组装的,所以每个Controller具有独特性。如果在Service层实现事务管理,ServiceA将会变的异常庞大,需要判断是哪个Controller过来的数据,然后组装ServiceB的extraData字段。
    另一种思路,我们是否可以把每个Controller组装ServiceB的extraData字段过程放在各自的Controller,然后在Controller实现事务管理呢?
    经过测试,在Controller层加事务,在spring.xml的aop:config添加对Controller的声明式事务拦截,结果未生效。在Controller的class加上@Transactional也未生效。最后采取的编程式事务实现的。
    我们在Spring.xml配置sessionFactory和transactionManager,如果已经配置声明式事务,这步可以忽略。

    <bean id="sessionFactory"
            class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
            <property name="dataSource" ref="c3p0DataSource" />
            <property name="hibernateProperties">
                <props>
                    <prop key="hibernate.dialect">#{ nplat['db.dialect'] }</prop>
                    <prop key="hibernate.hbm2ddl.auto">none</prop>
                    <prop key="hibernate.connection.release_mode">after_transaction</prop>
                    <prop key="hibernate.show_sql">false</prop>
                    <prop key="hibernate.format_sql">false</prop>
                    <prop key="hibernate.max_fetch_depth">3</prop><!-- 抓取的级联深度 -->
                    <prop key="hibernate.jdbc.fetch_size">50</prop><!-- 批量抓取的数量.mysql不支持 -->
                    <prop key="hibernate.jdbc.batch_size">30</prop><!-- 批量写入的数量 -->
                    <prop key="javax.persistence.validation.mode">none</prop><!-- HiberV3.5以上需配置该项 -->
                    <!-- <prop key="hibernate.cache.use_second_level_cache">true</prop> <prop 
                        key="hibernate.cache.use_query_cache">false</prop> <prop key="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</prop> 
                        强制Hibernate以更人性化的格式将数据存入二级缓存 <prop key="hibernate.cache.use_structured_entries">true</prop> -->
                </props>
            </property>
            <property name="packagesToScan">
                <list>
                    <value>com.gina.gc</value>
                </list>
            </property>
        </bean>
    
        <bean id="transactionManager"
            class="org.springframework.orm.hibernate3.HibernateTransactionManager">
            <property name="sessionFactory">
                <ref bean="sessionFactory" />
            </property>
        </bean>

    然后在每个Controller注入transactionManager:

    @Resource
    private PlatformTransactionManager transactionManager;

    下面讲解如何在Controller的save方法加上编程式事务:

    @RequestMapping("/save")
    @ResponseBody
    public String save(@Validated BaseSetting info) {
        DefaultTransactionDefinition defaultTransactionDefinition = new DefaultTransactionDefinition();
        defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
        TransactionStatus status = transactionManager.getTransaction(defaultTransactionDefinition);
    
        try {
            serviceA.save(A);
            serviceB.save(B);
            ...
    
            transactionManager.commit(status);
        } catch (Exception e) {
            transactionManager.rollback(status);
            e.printStackTrace();
            log.error("sava *** error" + e.toString());
            return ERROR(e.toString());
        }
        return OK();
    }

    这样我们便实现了在Controller层加上事务管理。
    虽说大家建议把事务加在Service,但不同情况不同处理方案,真正到项目中还得综合考虑,灵活运用。

    展开全文
  • 按照之前的学习,事务都是配置在service层中的,但是我的项目模块里一个service对应一个数据表,所以想在controller层加一个针对多个表的数据修改以及添加的事务配置。悲惨的是,在controller层配置事务出错没有回滚...

    问题背景:

    在写项目过程中遇到了多表联合修改数据时的事务问题,按照之前的学习,事务都是配置在service层中的,但是我的项目模块里一个service对应一个数据表,所以想在controller层加一个针对多个表的数据修改以及添加的事务配置。悲惨的是,在controller层配置事务出错没有回滚!

    • 按照我已所接触的逻辑,控制层是不建议写业务逻辑的,所以在里面调用的是多个service层的接口(使用Autowired)来调用多个表的业务操作。但是多个表形成一个事务,所以我没找在service层里单独添加事务的合适的方法。如果有前辈想到合适的方法,望赐教!叩谢!

    解决

    原来的配置

    • 首先是在service层上添加事务的配置,我这里的事务处理采用的是注解的方式,所以配置文件相较于配置事务的方式大大简化了。
      首先命名空间中加入:

      xmlns:tx="http://www.springframework.org/schema/tx"
      
      http://www.springframework.org/schema/tx
      http://www.springframework.org/schema/tx/spring-tx.xsd
      
    • 然后是xml文件的配置:

        <!-- service除了业务(操作dao)还要有事务 -->
        <tx:annotation-driven
        transaction-manager="txManager" />
        <!-- 配置Spring的声明式事务管理器 -->
        <bean id="txManager"
        class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
        </bean>
      

    其中,数据源我是配置在了dao层的配置文件中,由于都在spring的管理之下,所以在service直接使用是能够找到的。

    • 以下是我的maven依赖的jar包版本:

        <!-- https://mvnrepository.com/artifact/org.springframework/spring-tx -->
        <dependency>
        	<groupId>org.springframework</groupId>
        	<artifactId>spring-tx</artifactId>
        	<version>5.1.5.RELEASE</version>
        </dependency>
      
        <!-- Spring jdbc事务管理 -->
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
        <dependency>
        	<groupId>org.springframework</groupId>
        	<artifactId>spring-jdbc</artifactId>
        	<version>5.1.5.RELEASE</version>
        </dependency>
      

    以上是我起初的配置。但是仅仅这样是无法在controller层添加事务的。

    修正后的配置

    在service层的配置文件不变的情况下,我们想要在controller层添加事务,只需要在spring-mvc.xml中引入事务的注解驱动标签即可。

    <!--在xml文件头部引入命名空间,参考serviice层-->
    <tx:annotation-driven/>
    

    为什么会这样?

    • 首先我们来看配置文件的加载:

        <!-- 配置前端控制器 -->
        <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
        	<param-name>contextConfigLocation</param-name>
        	<param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        </servlet>
        <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <url-pattern>*.action</url-pattern>
        </servlet-mapping>
        <!-- 配置spring容器加载的监听器 -->
        <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
        </listener>
        <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:spring-*.xml</param-value>
        </context-param>
      
    • 以上是我的web.xml的部分配置。在项目启动过程中,加载spring-mvc.xml是使用DispatcherServlet加载的,而加载spring-service.xml与spring-dao.xml使用的是ContextLoaderListener。
      然后我们需要知道的是,ContextLoaderListener是早于DispatcherServlet启动的,而在ContextLoaderListener加载service层配置时controller并没有加载到容器中,但是此时事务的动态代理已经切入到了service层,所以后续的controller层并没有被增强。
      因此,我们需要在controller层再次加入 <tx:annotation-driven/>。


    最后

    • 祝大家中秋节快乐!所有人!
    • 然后还是希望帆帆姐不要烦烦,离家多远都不要怕!
    展开全文
  • Transaction在Controller层的探索

    千次阅读 热门讨论 2018-03-23 18:43:22
    Transaction在Controller层的探索 ...那么放到Controller层事务会生效吗?会产生什么问题呢?下面一起来看看 I、透过现象看本质 第一种情况 Controller层代码如下 @RestController @Request...

    Transaction在Controller层的探索

    一般开发中事务要求我们放在Service层,可是有些情况,我们可能会要求放在Controller层,你有没有碰到过这样的需求呢?那么放到Controller层事务会生效吗?会产生什么问题呢?下面一起来看看

    I、透过现象看本质

    第一种情况

    • Controller层代码如下

      @RestController
      @RequestMapping("/city")
      public class CityControllerImpl implements CityController {
      
        @Autowired
        private CityService cityService;
      
        @Override
        @RequestMapping(value = "getCity",method = RequestMethod.GET,produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
          @Transcational
        public BaseResult<City> getCity(@RequestParam("id") Integer id) {
            City one = cityService.getOne(id);
            BaseResult<City> baseResult=new BaseResult<>();
            baseResult.setData(one);
            return baseResult;
        }
      }
    • 运行结果

      Transaction ERROR controller

    • 对的,你没有看错,当Transactional加载Controller层时出现404异常

     第二种情况

    • Controller层代码如下

      @RestController
      @RequestMapping("/city")
      public class CityControllerImpl  {
        @Autowired
        private CityService cityService;
      
        @RequestMapping(value = "getCity",method = RequestMethod.GET,produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
        @Transactional
        public BaseResult<City> getCity(@RequestParam("id") Integer id) {
            City one = cityService.getOne(id);
            BaseResult<City> baseResult=new BaseResult<>();
            baseResult.setData(one);
            return baseResult;
        }
      }
    • 跟上面的区别,就是没有实现CityController接口了,那么我们运行一下,会有什么结果呢?

    • 运行结果如下:

      {
      data: null,
      message: null,
      status: 0
      }
    • 第二种情况居然没有啥问题,那么Transactional是否正常回滚呢?这里答案我直接告诉大家了,即使是换成有数据更改的接口,我们的事务是生效的。

    • 下面我为大家看源码解释一下

    第三种情况

    • 笔者测试使用支持==JAX-RS 2.0==的 Resteasy 测试,发现是没有这个问题的,大家可以自测一下Jersey是不是存在这个问题,推断应该没有

    II、熟悉本质解现象

    1. 区别

    可以看出,我们两个Controller的区别就是一个有实现接口,一个没有实现,为什么差别会这么大呢?

    2. 事务的本质

    我们知道事务是基于代理实现的,目前Spring中有JDK动态代理和CGLIB代理两种代理,那么跟Spring选择的代理有没有关系呢?我们看一下Spring在代理类的时候选择使用何种代理的源代码。如下:

    @Override
        public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
            if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
                Class<?> targetClass = config.getTargetClass();
                if (targetClass == null) {
                    throw new AopConfigException("TargetSource cannot determine target class: " +
                            "Either an interface or a target is required for proxy creation.");
                }
                if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                    return new JdkDynamicAopProxy(config);
                }
                return new ObjenesisCglibAopProxy(config);
            }
            else {
                return new JdkDynamicAopProxy(config);
            }
        }

    这是Spring创建代理比较核心的一段代码,在类 DefaultAopProxyFactory 中,不管加没有加接口,Spring看到了@Transactional注解都会给我们的Controller注册为一个代理对象。注意:Spring并非对所有的Controller都会创建代理类,假如我们的Controller没有暴露任何切面,Spring并不会创建一个代理类,这里可能大家会感到奇怪,我们这里打个TAG,文末讲解。

    继续刚刚的话题,第一种情况,由于我们的Controller有接口,所以就走了JDK代理,相反第二种走了Cglib代理。OK, 我们的CityControllerImpl现在是一个代理类。那么为什么会发生404异常呢?

    3. SpringMvc的原理

    为什么Controller变成代理之后,就会404异常了,肯定跟我们的SpringMVC有关,我们看一下SpringMVC的核心类 AbstractHandlerMethodMapping 这个类可以绑定URL和需要执行处理器的哪个方法。这个抽象类实现了initializingBean接口,其实主要的注册URL操作则是通过这个接口的afterPropertiesSet()接口方法来调用的。然后调用initHandlerMethods 方法进行绑定URL。方法详细如下:

    protected void initHandlerMethods() {
            if (logger.isDebugEnabled()) {
                logger.debug("Looking for request mappings in application context: " + getApplicationContext());
            }
            String[] beanNames = (this.detectHandlerMethodsInAncestorContexts ?
                    BeanFactoryUtils.beanNamesForTypeIncludingAncestors(getApplicationContext(), Object.class) :
                    getApplicationContext().getBeanNamesForType(Object.class));
    
            for (String beanName : beanNames) {
                if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX)) {
                    Class<?> beanType = null;
                    try {
                        beanType = getApplicationContext().getType(beanName);
                    }
                    catch (Throwable ex) {
                        // An unresolvable bean type, probably from a lazy bean - let's ignore it.
                        if (logger.isDebugEnabled()) {
                            logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
                        }
                    }
                    if (beanType != null && isHandler(beanType)) {
                        detectHandlerMethods(beanName);
                    }
                }
            }
            handlerMethodsInitialized(getHandlerMethods());
        }

    beanType中取出来是 CityControllerImpl 代理类,这里大家注意,代码第21行,有一个isHandler方法,这个方法用于判定这个类是不是Handler,其中代码如下:

    @Override
        protected boolean isHandler(Class<?> beanType) {
            return (AnnotatedElementUtils.hasAnnotation(beanType, Controller.class) ||
                    AnnotatedElementUtils.hasAnnotation(beanType, RequestMapping.class));
        }

    看到这里相信大家已经很明白了,这里就是看你这个类上面有没有Controller注解和RequestMapping注解。如果有,就建立相关的映射关系(URL->Handler)

    • 其中有接口的是被JDK代理的,生成的是JDK代理类

      JDK的动态代理是靠多态和反射来实现的,它生成的代理类需要实现你传入的接口,并通过反射来得到接口的方法对象,并将此方法对象传参给增强类的invoke方法去执行,从而实现了代理功能。

      CityController生成的代理类文件如下:

      public final class cityControllerImpl extends Proxy implements Proxy86 {
        private static Method m1;
        private static Method m32;
        private static Method m7;
      
        public cityControllerImpl(InvocationHandler var1) throws  {
            super(var1);
        }
      
        public final TargetSource getTargetSource() throws  {
            try {
                return (TargetSource)super.h.invoke(this, m8, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
      
        public final void addAdvice(int var1, Advice var2) throws AopConfigException {
            try {
                super.h.invoke(this, m21, new Object[]{var1, var2});
            } catch (RuntimeException | Error var4) {
                throw var4;
            } catch (Throwable var5) {
                throw new UndeclaredThrowableException(var5);
            }
        }
      
        public final BaseResult getCity(Integer var1) throws  {
            try {
                return (BaseResult)super.h.invoke(this, m27, new Object[]{var1});
            } catch (RuntimeException | Error var3) {
                throw var3;
            } catch (Throwable var4) {
                throw new UndeclaredThrowableException(var4);
            }
        }
      
      }

      类已经被精简过,我们看到生成的代理类中完全没有@Controller @RequestMapping 注解,所以isHandler方法执行失败,所以根本不会加到SpringMvc的控制器处理方法中去,当URL请求过来的时候,找不到对应的处理器处理,所以就报404错误啦

    • 没有接口的是被CGLIB代理的,生成的是CGlib代理类

      CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础

      public class CityControllerImpl$$EnhancerBySpringCGLIB$$8cae5808 extends CityControllerImpl implements SpringProxy, Advised, Factory {
        private boolean CGLIB$BOUND;
        public static Object CGLIB$FACTORY_DATA;
        private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
        private static final Callback[] CGLIB$STATIC_CALLBACKS;
      
        final BaseResult CGLIB$getCity$0(Integer var1) {
            return super.getCity(var1);
        }
        public final BaseResult getCity(Integer var1) {
            MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
            if (this.CGLIB$CALLBACK_0 == null) {
                CGLIB$BIND_CALLBACKS(this);
                var10000 = this.CGLIB$CALLBACK_0;
            }
            return var10000 != null ? (BaseResult)var10000.intercept(this, CGLIB$getCity$0$Method, new Object[]{var1}, CGLIB$getCity$0$Proxy) : super.getCity(var1);
        }
      }

      ==其实isHandler方法会代理类的接口和父类进行扫描==,看你有没有这个注解,JDK代理中cityControllerImpl接口和父类都没有注解,而CGlib代理的父类是CityControllerImpl 这个原始的类, 所以返回为真

    4. 思考

    如果Controller层不加@Transcational注解的时候,为什么又不会产生404异常呢?其实如果你Controller不加任何织入代码的话(自定义aop切面等,有兴趣的可以用AspectJ试一下织入Controller层的Method方法会发生什么事情),Spring是不会给你的类生成代理的,也就是在AbstractHandlerMethodMapping 绑定的时候,这个类不是一个代理,所以才会匹配成功。

    展开全文
  • 关于spring在controller层事务操作

    千次阅读 2017-09-01 14:15:44
    以下是代码:package cn.hr.controller;import java.io.PrintWriter; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; ...
  • controller层使用@Transactional事务注解

    千次阅读 2020-08-05 14:57:33
    但是可以在controller层方法的catch语句中增加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();语句,手动回滚,这样上层就无需去处理异常 @RequestMapping(value = "/delrecord", ...
  • 事务加在controller层和service有什么区别, 说是在controller层事务针对安全性要求较高的系统,为什么?
  • 如题,我在service层加了事务,并在controller层捕获了service层的异常并处理掉,那么service层的事务会回滚吗?
  • Transaction在Controller层的探索...那么放到Controller层事务会生效吗?会产生什么问题呢?下面一起来看看I、透过现象看本质第一种情况Controller层代码如下@RestController @RequestMapping("/city") ...
  • 能将事务管理配置到Controller层吗?

    千次阅读 2017-08-01 15:34:59
    我们通常会在Service进行事务管理,难道不能在Contoller实现? (我觉得这个问题,对于每一个不仅仅是为了编程而编程的程序员在最初接触切面编程时都是一个比较大的疑问,尤其是强迫症患者或者懒得写Service...
  • Spring的@Transactional源码中写道: By default, a transaction will be ...默认情况下,如果在事务中抛出了未检查异常(继承自 RuntimeException 的异常)或者 Error,则 Spring 将回滚事务;除此之外,Spring 不
  • Spring MVC中,事务是否可以加在Controller层  一般而言,事务都是加在Service层的,但是爱钻牛角尖的我时常想:事务加在Controller层可不可以。我一直试图证明事务不止可以加在Service层,还可以加在Controller层...
  • --配置事务增强,事务如何切入--> <tx:advice id="txAdvice"> <tx:attributes> <!--所有方法都是事务方法--> <tx:method name="*"/> <!--以get开始的所有方法
  • 在Spring MVC中,事务可以加在Controller层  一般而言,事务都是加在Service层的,但是爱钻牛角尖的我时常想:事务加在Controller层可不可以。我一直试图证明事务不止可以加在Service层,还可以加在Controller...
  • 第一个: 事务的声明是在spring里面进行声明的. &lt;bean id = "transactionManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager"&gt;&lt;...
  • 今天面试的时候, 一个面试官问我,,, 事务一般是加在 service 的,为什么不加在 controller ? 我 突然懵了,没听过 要加 web的,而且觉得 这样加上去有用? 没有想过这样的问题: 而其实 事务是可以 加在...
  • 1、Dao Dao,又称为数据访问(Data Access Object),其中规定这的目的就在于让这个下面类只和数据库的增删改查相关,不做多余的业务逻辑判断。 2、Service Service叫服务,Service对一个或多...
  • 1.事务一般要放在Service,放在Controller也可以,。 2.在springmvc的配置文件中扫描controller时要忽略service,因为在springmvc的配置文件加载的service事务不起作用。所以在springmvc.xml中:   &lt;!-...
  • spring事务-可以使用在controller @Autowired private PlatformTransactionManager ... * 基于controller层事务 * @since 2019-10-25 * @return */ private TransactionStatus transaction(){ Defau...
  • 一般而言,事务都是加在Service层的,也可以加在Controller层  在spring-framework-reference.pdf文档中有这样一段话: &lt;tx:annotation-driven/&gt; only looks for @Transactional on beans in the ...
  • Spring boot 三层框架dao层、service层、controller层+实体model层model层dao层service层controller层 首先创建一个springboot项目。 model层 model层也叫pojo层或者entity层,个人比较喜欢pojo层。 一般数据库的...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 56,519
精华内容 22,607
关键字:

controller层事务