繁体   English   中英

@交易服务

[英]@Transactional Services

我经过一天半的时间寻找答案,但是这件事会让我发疯!

我和我的队友正在基于springboot进行一个项目。 我专门在管理部分(网络管理)上工作。

我的项目主要包括三层:使用服务的控制器和使用存储库的服务。 我希望我的项目在服务层使用@Transactional工作(到目前为止,我们已经做出了一些成功的努力,仅使用注释进行配置)。

但是,这似乎不起作用:我的一项服务抛出了RuntimeException,并且没有回滚。 我已经阅读了其他兄弟姐妹主题中的所有命题。 与我的问题有关的唯一一件事是,我不确定是否要整洁地执行上下文配置。 Eventhow,我不确定这真的是我的问题。

我向您展示实际配置:

@SpringBootApplication
@EnableScheduling
@EnableTransactionManagement
public class Application extends SpringBootServletInitializer {

    @Value("${ajp.port}")
    private int ajpPort;

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }

    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(Application.class);
    }

    @Bean
    public EmbeddedServletContainerFactory servletContainer() {
        TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory() {};
        tomcat.addAdditionalTomcatConnectors(createConnector(ajpPort));
        return tomcat;
    }

    @Bean
    public EmbeddedServletContainerCustomizer containerCustomizer() {
        return container -> {

                ErrorPage error401Page = new ErrorPage(HttpStatus.UNAUTHORIZED, "/static/401.html");
                ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/static/404.html");

                container.addErrorPages(error401Page, error404Page);
        };
    }

    @Bean
    public EmailValidator emailValidator() {
        return EmailValidator.getInstance();
    }

    private static Connector createConnector(int ajpPort) {
        Connector connector = new Connector("AJP/1.3");
        connector.setPort(ajpPort);
        return connector;
    }
}

网络配置:

@Configuration
public class MvcConfig extends WebMvcConfigurerAdapter {

    @Autowired
    private RequestProcessingTimeInterceptor requestProcessingTimeInterceptor;

    @Autowired
    private CertificateInterceptor certificateInterceptor;

    @Autowired
    private ProfilesAuthorizationInterceptor profilesAuthorizationInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(requestProcessingTimeInterceptor);
        registry.addInterceptor(certificateInterceptor);
        registry.addInterceptor(profilesAuthorizationInterceptor);
    }

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }

    @Bean
    public InternalResourceViewResolver viewResolver() {
        InternalResourceViewResolver resolver = new InternalResourceViewResolver();
        resolver.setExposeContextBeansAsAttributes(true);
        resolver.setPrefix("/WEB-INF/");
        resolver.setSuffix(".jsp");

        return resolver;
    }

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        registry.addResourceHandler("/admin/css/**").addResourceLocations("/WEB-INF/admin/css/").setCachePeriod(CACHE_PERIOD);
        registry.addResourceHandler("/admin/img/**").addResourceLocations("/WEB-INF/admin/img/").setCachePeriod(CACHE_PERIOD);
        registry.addResourceHandler("/admin/js/**").addResourceLocations("/WEB-INF/admin/js/").setCachePeriod(CACHE_PERIOD);
        registry.addResourceHandler("/admin/plugins/**").addResourceLocations("/WEB-INF/admin/plugins/").setCachePeriod(CACHE_PERIOD);
    }

}

像控制器一样:

@RestController
@RequestMapping("/pathA")
public class ControlerA {

    @Autowired
    public ServiceA serviceA;

    @RequestMapping(value = "{id}", method = RequestMethod.GET)
    @ResponseStatus(HttpStatus.OK)
    public A getA(@PathVariable long id) {
        return serviceA.getA(id);
    }

}

类似于服务(接口+实现):

public interface ServiceA {

    A getA(long id);

}

@Service
@Transactional
public class ServiceAImpl implements ServiceA {

    @Autowired
    public RepositoryA repositoryA;

    public A getA(long id) {
        (...)
        A a = repositoryA.findOne(id);
        a.updatesomething(something);
        repositoryA.update(a);
        doOtherThing(a); //throw RuntimeException
        (...)
        return a;
    }

}

和存储库:

@Repository
public interface RepositoryA extends JpaRepository<A, Long> {
    (...)
}

这是MySQL数据库的配置:

# Configuration de la base de donnée
spring.datasource.url=jdbc:mysql://localhost/name_innodb
spring.datasource.username=username
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.testOnBorrow=true
spring.datasource.validationQuery=SELECT 1

我知道默认情况下,存储库事务有效(当SQLException发生时,我看到了)。 但是在服务层中,什么也没有发生(参见throwing exception line); 当引发异常时,更新完成且不回滚。 这意味着我的@Transactional被忽略。

编辑:我设法获得想要的交易,在Controller的方法getA(...)上添加@Transactional。 它有效,但不是管理事务的地方。

然后我的问题是:如何使它起作用?

好吧,经过几天的头脑风暴,我发现了!

唯一合理的答案是照顾您的Configuration类。 我的问题只是一个交叉配置问题,导致分配混乱的DispatcherServlet配置。

相关主题: 对于Web MVC Spring应用程序,@ Transactional应该在控制器或服务上运行吗?

编辑:我添加一些细节,因为很难找到一些信息以分隔上下文。 而且我仍在校准配置,因为没有关于所有弹簧注释的完整而详尽的信息。

您可以这样创建父级和子级上下文:

@SpringBootApplication
@ComponentScan({"com.mycompany.service", "com.mycompany.interceptors","com.mycompany.manager"})
@PropertySource("file:config/application.properties")
public class ParentConfig{

    public static void main(String[] args) {
        new SpringApplicationBuilder()
                .parent(ParentConfig.class)
                .child(ChildConfig1.class, ChildConfig2.class, ChildConfig3.class, ..., ChildConfigN.class)
                .run(args);
    }

    (...)

}

我仍然想知道为什么我必须添加@PropertySource以便孩子知道属性值,为什么“ classpath:path”在@PropertySource中不起作用,为什么我必须添加一个静态PropertySourcesPlaceholderConfigurer以便在我的孩子中使用@Value(在我这样做之前,即没有这种分层上下文,每个上下文都知道属性)

@Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
    return new PropertySourcesPlaceholderConfigurer();
}

而且我仍在按顺序进行注释,以便进行每个配置工作。

编辑:我终于找到了一些可以正常使用spring配置的东西:不同的配置必须尊重包装的层次结构。

我停止使用父母和孩子的配置,而让spring工作。 我像这样整理不同的配置类:

主配置
|
| __________ my.package.mvc.MVCConfig
|
| __________ my.package.schedulers.SchedulerConfig
|
|
等等..

然后在我的MainConfig中添加:
@ComponentScan({"my.package.mvc", "my.package.services", "my.package.interceptors","my.package.managers", "my.package.schedulers"})

现在一切都很好! 通常,由于层次结构不同,MVCConfig无法与服务产生冲突。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM