我必须创建一个异步方法来更新数据库和文件系统。在此方法中,某些操作必须是Transactional而其他操作不是。从我的服务
@Service
public class FleetAndCarServicesImpl implements FleetAndCarServices {
@Autowired
private DatabaseFleetsAndCarsServices databaseFleetsAndCarsServices;
@Autowired
private DatabaseAcquisitionServices databaseAcquisitionServices;
@Autowired
private DatabaseAdministrationServices databaseAdministrationServices;
@Autowired
private MakeName makeName;
@Autowired
private MakeAcquisitionPath makeAcquisitionPath;
@Autowired
private Environment env;
private static String PROPERTY_NAME_FILESYSTEM_BASEPATH = "filesystem.basepath";
//OTHER CODES
@Override
@SetEditingFleet
public void modifyFleet(User user, FleetForm fleetForm) throws Exception{
databaseFleetsAndCarsServices.modifyFleet(user, fleetForm);
}
SetEditingFleet
注释将boolean fieldediting
设置为true,仅当此布尔值为false时才允许某些操作。
这是设置和取消设置此字段的代码:
@Before("@annotation(SetEditingFleet) && args(user, fleetForm)")
public void setEditingFleet(User user, FleetForm fleetForm) throws QueryException {
utils.setEditingFleet(user, fleetForm);
}
@After("@annotation(UnSetEditingFleet) && args(user, fleetForm)")
public void unSetEditingFleet(User user, FleetForm fleetForm) throws QueryException {
utils.unSetEditingFleet(fleetForm.getIdFleet());
}
Utils
类:
@Service
public class Utils {
@Autowired
private FleetServices fleetServices;
@Autowired
private CarServices carServices;
@Transactional(rollbackFor=Exception.class)
public void unSetEditingFleet(Integer idFleet) throws QueryException {
try {
fleetServices.setEditingFleet(idFleet, false);
for(Car car : carServices.findByFleetIdFleet(idFleet)){
carServices.setEditingCar(car.getIdCar(), false); //Unset cars associated with the fleet
}
}catch(Exception e){
throw new QueryException(e);
}
}
@Transactional(rollbackFor=Exception.class)
public void setEditingFleet(User user, FleetForm fleetForm) throws QueryException {
try{
fleetServices.setEditingFleet(fleetForm.getIdFleet(), true);
for(Car car : carServices.findByFleetIdFleet(fleetForm.getIdFleet())){
carServices.setEditingCar(car.getIdCar(), true); //Set cars associated with the fleet
}
}catch(Exception e){
throw new QueryException(e);
}
}
}
从异步方法和unSetEditingFleet
调用class MyAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler
方法,以在异步方法结束时或在某些异常的情况下将布尔字段editing
设置为false。异步方法(我删除了所有无用的代码):
@Override
@Async
@Transactional(rollbackFor=Exception.class)
public void modifyFleet(User currentUser, FleetForm fleetForm) throws Exception {
LOG.info("Editing fleet with id: "+ fleetForm.getIdFleet());
//Keep the progress status on DB
fleetServices.setEditingProgress(fleetForm.getIdFleet(), "Start editing fleet");
utils.unSetEditingFleet(oldFleet.getIdFleet());
}catch(Exception e){
ErrorResponse errorResponse= ErrorResponseBuilder.buildErrorResponse(e);
LOG.error("Threw exception in DatabaseFleetsAndCarsServicesImpl::modifyFleet: " + errorResponse.getStacktrace());
//Keep the progress status on DB
fleetServices.setEditingProgress(oldFleet.getIdFleet(), "Sorry an error occured during the procedure, wait until restore is ended!");
//Restore the file system procedure
restoreProcedure(oldFleet.getIdFleet(), fleetFile, backupFolderFile);
//Keep the progress status on DB
fleetServices.setEditingProgress(oldFleet.getIdFleet(), "");
//Even with this exception set the fleet as not in editing
throw new EditingException("fleet has been restored!");
}finally{
LOG.info("Editing end for fleet with id: "+ fleetForm.getIdFleet());
}
}
fleetServices.setEditingProgress()
必须立即写入数据库,因为该值必须显示给用户。为此,我将此方法放在另一个类中:
@Service
@Transactional
public class FleetServicesImpl implements FleetServices{
@Resource
private FleetRepository fleetRepository;
@Override
@Transactional(propagation = Propagation.REQUIRES_NEW) //necessary to set immediately the text into the database inside a transactional method. This annotation create a new transaction
public void setEditingProgress(Integer idFleet, String editingProgress) {
fleetRepository.setEditingProgress(idFleet, editingProgress);
}
}
我有两个问题:
setEditingFleet
方法不会回滚setEditingProgress
方法不会立即写入数据库昨天我使setEditingProgress
正确,但更改@Transactional
注释我做了一些错误,现在甚至它不起作用。
你知道我弄错了什么吗?
配置类:
@EnableWebSecurity
@EnableAspectJAutoProxy
@EnableAsync
@EnableWebMvc
@Configuration
@PropertySource(value = { "classpath:application.properties" })
@ComponentScan({ "com.*" })
@EnableTransactionManagement
@Import({ SecurityConfig.class, SpringMvcInitializer.class})
@EnableJpaRepositories("com.repository")
public class AppConfig extends WebMvcConfigurerAdapter implements AsyncConfigurer{
@Autowired
private Environment env;
@Autowired
private MyAsyncUncaughtExceptionHandler myAsyncUncaughtExceptionHandler;
private static final String PROPERTY_NAME_DATABASE_DRIVER = "db.driver";
private static final String PROPERTY_NAME_DATABASE_PASSWORD = "db.password";
private static final String PROPERTY_NAME_DATABASE_URL = "db.url";
private static final String PROPERTY_NAME_DATABASE_USERNAME = "db.username";
private static final String PROPERTY_NAME_HIBERNATE_DIALECT = "hibernate.dialect";
// private static final String PROPERTY_NAME_HIBERNATE_SHOW_SQL = "hibernate.show_sql";
private static final String PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN = "entitymanager.packages.to.scan";
private static final String PROPERTY_NAME_HIBERNATE_FORMAT_SQL = "hibernate.format_sql";
/**
* This method allows use of . with pathparam into web services, otherwise it truncate after dot.
* @param configurer
*/
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.setUseSuffixPatternMatch(false);
}
/**
* This and the next methods are used to avoid exception while jackson mapping the entity, so fields are setted with null value
* unless use Hibernate.initialize
* @return
*/
public MappingJackson2HttpMessageConverter jacksonMessageConverter(){
MappingJackson2HttpMessageConverter messageConverter = new MappingJackson2HttpMessageConverter();
ObjectMapper mapper = new ObjectMapper();
//Registering Hibernate4Module to support lazy objects
mapper.registerModule(new Hibernate4Module());
messageConverter.setObjectMapper(mapper);
return messageConverter;
}
/**
* Used for spring security
* @return
*/
@Bean
public SpringSecurityDialect springSecurityDialect() {
SpringSecurityDialect dialect = new SpringSecurityDialect();
return dialect;
}
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
//Here we add our custom-configured HttpMessageConverter
converters.add(jacksonMessageConverter());
super.configureMessageConverters(converters);
}
private Properties getHibernateProperties() {
Properties properties = new Properties();
properties.put(PROPERTY_NAME_HIBERNATE_DIALECT, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_DIALECT));
// properties.put(PROPERTY_NAME_HIBERNATE_SHOW_SQL, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_SHOW_SQL));
properties.put(PROPERTY_NAME_HIBERNATE_FORMAT_SQL, env.getRequiredProperty(PROPERTY_NAME_HIBERNATE_FORMAT_SQL));
properties.put("hibernate.enable_lazy_load_no_trans",true);
return properties;
}
@Bean(name = "dataSource")
public BasicDataSource dataSource() {
BasicDataSource ds = new BasicDataSource();
ds.setValidationQuery("SELECT 1");
ds.setDriverClassName(env.getRequiredProperty(PROPERTY_NAME_DATABASE_DRIVER));
ds.setUrl(env.getRequiredProperty(PROPERTY_NAME_DATABASE_URL));
ds.setUsername(env.getRequiredProperty(PROPERTY_NAME_DATABASE_USERNAME));
ds.setPassword(env.getRequiredProperty(PROPERTY_NAME_DATABASE_PASSWORD));
return ds;
}
@Bean
public ServletContextTemplateResolver TemplateResolver(){
ServletContextTemplateResolver resolver = new ServletContextTemplateResolver();
resolver.setPrefix("/WEB-INF/templates/pages/");
resolver.setSuffix(".html");
resolver.setTemplateMode("LEGACYHTML5");
resolver.setCacheable(false);
return resolver;
/*ServletContextTemplateResolver resolver = new ServletContextTemplateResolver();
resolver.setPrefix("/WEB-INF/pages/");
resolver.setSuffix(".html");
resolver.setTemplateMode("HTML5");
return resolver;*/
}
@Bean
public SpringTemplateEngine templateEngine(){
SpringTemplateEngine templateEngine = new SpringTemplateEngine();
templateEngine.setTemplateResolver(TemplateResolver());
templateEngine.addDialect(springSecurityDialect());
return templateEngine;
}
@Bean
public ThymeleafViewResolver viewResolver() {
ThymeleafViewResolver resolver = new ThymeleafViewResolver();
resolver.setTemplateEngine(templateEngine());
resolver.setOrder(1);
resolver.setViewNames(new String[]{"*", "js/*", "template/*"});
return resolver;
}
/**
* Register multipartResolver for file upload
* @return
*/
@Bean
public CommonsMultipartResolver multipartResolver() {
CommonsMultipartResolver resolver=new CommonsMultipartResolver();
resolver.setDefaultEncoding("utf-8");
return resolver;
}
/**
* Allow use of bootstrap
*/
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("/static/");
}
/**
* Allow use of JPA
*/
@Bean
public JpaTransactionManager transactionManager() {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory().getObject());
return transactionManager;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory() {
LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
entityManagerFactoryBean.setDataSource(dataSource());
entityManagerFactoryBean.setPersistenceProviderClass(HibernatePersistenceProvider.class);
entityManagerFactoryBean.setPackagesToScan(env.
getRequiredProperty(PROPERTY_NAME_ENTITYMANAGER_PACKAGES_TO_SCAN));
entityManagerFactoryBean.setJpaProperties(getHibernateProperties());
return entityManagerFactoryBean;
}
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(100);
executor.setQueueCapacity(100);
executor.initialize();
return executor;
}
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return myAsyncUncaughtExceptionHandler;
}
//Concurrent Session Control. Necessary for maximumSessions(1) into securityConfig
@Bean
public ServletListenerRegistrationBean<HttpSessionEventPublisher> httpSessionEventPublisher() {
return new ServletListenerRegistrationBean<HttpSessionEventPublisher>(new HttpSessionEventPublisher());
}
}
MVC初始化程序包:com.config.core包;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class SpringMvcInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new Class[] { AppConfig.class };
}
@Override
protected Class<?>[] getServletConfigClasses() {
return null;
}
@Override
protected String[] getServletMappings() {
return new String[] { "/" };
}
}
在使用@Transactional之前使用注释@Test。试试这个:
@Service
public class Utils {
@Autowired
private FleetServices fleetServices;
@Autowired
private CarServices carServices;
public static long stream(InputStream input, OutputStream output) throws IOException {
try (
ReadableByteChannel inputChannel = Channels.newChannel(input);
WritableByteChannel outputChannel = Channels.newChannel(output);
) {
ByteBuffer buffer = ByteBuffer.allocateDirect(10240);
long size = 0;
while (inputChannel.read(buffer) != -1) {
buffer.flip();
size += outputChannel.write(buffer);
buffer.clear();
}
return size;
}
}
@Test
@Transactional
public void unSetEditingFleet(Integer idFleet) throws QueryException {
try {
fleetServices.setEditingFleet(idFleet, false);
for(Car car : carServices.findByFleetIdFleet(idFleet)){
carServices.setEditingCar(car.getIdCar(), false); //Unset cars associated with the fleet
}
}catch(Exception e){
throw new QueryException(e);
}
}
@Test
@Transactional
public void setEditingFleet(User user, FleetForm fleetForm) throws QueryException {
try{
fleetServices.setEditingFleet(fleetForm.getIdFleet(), true);
for(Car car : carServices.findByFleetIdFleet(fleetForm.getIdFleet())){
carServices.setEditingCar(car.getIdCar(), true); //Set cars associated with the fleet
throw new Exception("fleet has been restored Exception");
}
}catch(Exception e){
throw new QueryException(e);
}
}
}