2016-11-16 67 views
0

我過了一天半尋找答案,但這件事會讓我發瘋!@Transactional Services

我和我的隊友正在基於springboot開展一個項目。我專門研究管理部分,這是一個Web管理。

我的項目主要有三層:使用使用存儲庫的服務的控制器。 我希望我的項目能在服務層使用@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; 
    } 
} 

web配置:

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

} 

A服務類(接口+實現) :

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被忽略。

編輯: 我設法讓像我想要一個交易,在控制器的方法getA(...)加入@Transactional。它可以工作,但它不是管理交易的地方。

然後我的問題是:我如何使它工作?

+0

哪個數據庫?顯示你的配置(屬性等)。另外你使用的是Spring Boot,那麼爲什麼你需要一個Web配置類(Spring引導已經配置好了) –

+0

最初的配置不是來自我,我認爲配置的分離是爲了更好的可讀性。 ... – Mohicane

+1

你不需要這樣的配置,因爲spring boot已經爲你配置了MVC,你的實際服務方法還是你有一個try/catch塊或者其他東西?如果@ Transactional沒有工作 –

回答

0

好吧,經過幾天的頭腦風暴,我發現了!

唯一合理的答案是關心你的Configuration類。我的問題只是交叉配置問題,導致DispatcherServlet配置導致混亂。

相關主題:For web MVC Spring app should @Transactional go on controller or service?

編輯: 我補充一些細節,因爲這將是很難找到以單獨的上下文的一些信息。而且我仍在校準配置,因爲沒有關於所有Spring註釋的完整和詳盡的信息。

您可以創建父母和孩子方面是這樣的:

@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知道屬性值的,爲什麼「類路徑:路徑」沒有在@PropertySource,我爲什麼要添加靜態PropertySourcesPlaceholderConfigurer爲我的孩子們使用@Value工作(之前我做到這一點,即沒有這種分層的上下文,每個上下文知道屬性)

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

和我米仍在玩annotatio ns,以便進行每項配置工作。

編輯:不同的配置必須尊重包裝層次:爲了與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不能與服務產生衝突。