[英]@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.