我过了一天半寻找答案,但这件事会让我发疯!
我的队友和我正在开发一个基于springboot的项目。我专门负责管理部分,这是一个网络管理。
我的项目主要有三个层:控制器使用使用存储库的服务。 我希望我的项目与@Transactional一起用于服务层(到目前为止我们已经做了一些成功的努力,只使用注释进行配置)。
但是,似乎它不起作用:我的一个服务抛出RuntimeException并且没有完成回滚。我已经阅读了其他兄弟科目中的所有命题。与我的问题相关的唯一一件事,我不确定整齐地做了上下文配置。事件上,我不确定这是我的问题。
我告诉你实际的配置:
@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);
}
}
类似于Controler:
@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);
}
}
类似服务(界面+implémentation):
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发生时看到它)。但是在服务层中,没有任何事情发生(参见抛出异常行);抛出异常时,更新完成而不是回滚。那就意味着我的@Transactional会被忽略。
修改:
我设法获得我想要的事务,在Controller的方法getA(...)
上添加@Transactional。它有效,但它不是管理交易的地方。
然后我的问题是:我怎样才能让它发挥作用?
答案 0 :(得分:0)
好的,经过几天的头脑风暴,我发现了!
唯一可靠的答案是关注您的Configuration类。我的问题只是一个交叉配置问题,导致DispatcherServlet配置导致混乱。
相关主题:For web MVC Spring app should @Transactional go on controller or service?
修改强> 我添加了一些细节,因为要分离上下文很难找到一些信息。我仍在校准配置,因为没有关于所有弹簧注释的完整和详尽的信息。
您可以像这样创建父和子上下文:
@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 in我的孩子们(在我这样做之前,即没有这种分层的情境,每个情境都知道这些属性)
@Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
return new PropertySourcesPlaceholderConfigurer();
}
我仍在使用注释来完成每个配置工作。
修改强> 我终于找到了一些东西,以便能够正确使用spring配置:不同的配置必须尊重包装层次结构。
我停止使用父级和子级配置,让spring工作。我正在尝试使用这样的不同配置类:
MainConfig
|
| __________ 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不能与服务产生冲突,因为层次结构不同。