【问题标题】:Why @Transactional not working in my code?为什么@Transactional 在我的代码中不起作用?
【发布时间】:2019-01-07 07:25:09
【问题描述】:
@Service
public class SingleCityService implements ICityService {

private static final Logger logger = LoggerFactory.getLogger(SingleCityService.class);

@Autowired
CityMmtRepo cityMmtRepo;

@Autowired
MmtCityService mmtCityService;

@Autowired
RestCityService restCityService;

@PostConstruct
public void init() {
    CityServiceFactory.getInstance().registor(EnumCity.SINGLE_CITY, this);
}

/**
 * Method to validate if action type is to Create city or Update city
 * 
 * @param request
 * @return CityServiceResponse
 */
@Override
public CityServiceResponse serveRequest(CityServiceRequest request) throws ResourceException, InvalidCityCreateException, SQLException {

    ValidatorFactory.getInstance().getRequestValidator(Validator.SINGLE_CITY_VALIDATOR).validate(request);

    if (CityActionType.CREATE.toString().equals(request.getEventType().getActionType())) {
        return createCity(request);
    }
    return updateCity(request);
}

/**
 * Method to create city and fetch matched cities from cache.
 * 
 * @param request
 * @return CityServiceResponse
 * @throws ResourceException
 */
private CityServiceResponse createCity(CityServiceRequest request) throws ResourceException {
    logger.info("Inside SingleCityService: Starting createCity for request: {}", request.toString());

    /**
     * forceUpdate signifies if user still want to create city after getting conflicts.
     */
    if (ResourceConstants.TRUE.equalsIgnoreCase(request.getCityDetails().get(0).getForceUpdate())) {
        return saveCity(request, ResourceConstants.CREATED);
    }

    CityCacheServiceResponse cityCacheServiceResponse = restCityService.getCityMatchingFromCityCache(request);
    CityServiceResponse cityServiceResponse = new CityServiceResponse();
    if (ResourceConstants.FAILURE.equalsIgnoreCase(cityCacheServiceResponse.getStatus())) {
        cityServiceResponse.setStatus(ResourceConstants.FAILURE);
        cityServiceResponse.setDescription(cityCacheServiceResponse.getDescription());
    } else {
        if (cityCacheServiceResponse.getCity().get(0).getMatchedCity().isEmpty()) {
            return saveCity(request, ResourceConstants.SUCCESS);
        } else {
            cityServiceResponse.setStatus(ResourceConstants.CONFLICTING_CITIES_UI_DISPLAY_MSG);
            cityServiceResponse.setCity(CityServiceMapperUtil.cacheResponse(cityCacheServiceResponse));
        }
    }
    return cityServiceResponse;
}

/**
 * To update city.
 * 
 * @param request
 * @return CityServiceResponse
 * @throws ResourceException
 */
private CityServiceResponse updateCity(CityServiceRequest request) throws ResourceException, InvalidCityCreateException {
    logger.info("Inside SingleCityService, Starting updateCity for request: {}", request.toString());
    CityMmtEntity cityMmtEntity = mmtCityService.fetchCityToEdit(request.getCityDetails().get(0).getCityCd());
    request = CityServiceMapperUtil.mapCityToBeUpdated(cityMmtEntity, request);
    return saveCity(request, ResourceConstants.UPDATE);
}

/**
 * To create or update city in DB and Push city in Cache.
 * 
 * @param request
 * @param status
 * @return CityServiceResponse
 * @throws ResourceException
 */
@Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRED)
public CityServiceResponse saveCity(CityServiceRequest request, String status) throws ResourceException {
    CityMmtEntity cityMmtEntity = CityServiceMapperUtil.getCityEntity(request);
    logger.info("Inside SingleCityService: saveCity: Saving city data in DB for cityCode: {}", cityMmtEntity.getChtCitycd());
    cityMmtEntity = mmtCityService.SaveToDb(cityMmtEntity);
    int i = 1/0;
    int k = i;
    logger.info("Inside SingleCityService: saveCity: Syncing city to ES API for cityCode: {}", cityMmtEntity.getChtCitycd());
    request.getCityDetails().get(0).setCityCd(cityMmtEntity.getChtCitycd());
    request.getCityDetails().get(0).setLastUpdatedOn(cityMmtEntity.getChtCityLastUpdDt());
    SyncCityCacheRequest syncCityCacheRequest = CityServiceMapperUtil.mapCityCacheRequest(request);
    SyncCityCacheResponse syncCityCacheResponse = restCityService.syncCityToCityCache(syncCityCacheRequest);
    CityServiceResponse cityServiceResponse = new CityServiceResponse();
    cityServiceResponse.setStatus(status);
    cityServiceResponse.setDescription(syncCityCacheResponse.getSyncCityDetails().get(0).getDescription());
    return cityServiceResponse;
}

}

// 我的服务类中有一个方法 saveCity,如您所见,我在其上放置了@transactional,但它不起作用,数据库不会在异常时回滚。 // saveToDb 是另一个基于 Jpa Repository 构建的服务(下)中的方法

@Service
public class MmtCityServiceImpl implements MmtCityService {

private static final Logger logger = LoggerFactory.getLogger(MmtCityServiceImpl.class);

@Autowired
private CityMmtRepo cityMmtRepo;

/**
 * To save city in DB
 * 
 * @param cityMmtEntity
 * @return {@link CityMmtEntity}
 */
public CityMmtEntity SaveToDb(CityMmtEntity cityMmtEntity) throws ResourceException {
    try {
        logger.info("Inside MmtCityServiceImpl: Starting SaveToDb to save city for CityMmtEntity: {}", cityMmtEntity.toString());
        return cityMmtRepo.save(cityMmtEntity);
    } catch (Exception e) {
        logger.error("MmtCityServiceImpl : Exception while saving/updating city in database, Exception: {}", e);
        throw new ResourceException(ResourceErrors.SAVE_CITY_ERROR.getErrorCode(), ResourceErrors.SAVE_CITY_ERROR.getErrorDescription());
    }
}

//我把 k = 1/0 放在我们将实体保存到 db 的步骤旁边抛出运行时异常,只是为了检查 @Transactional 是否工作,但异常实体不会从数据库回滚。 //实际上我正在从另一个服务(其方法是SyncCityToCache)抛出异常服务是:-

@Service
public class RestCityServiceImpl implements RestCityService {

private static final Logger logger = LoggerFactory.getLogger(RestCityService.class);

@Autowired
RestTemplate restTemplate;

@Value("${rest.elatic-cache-sync.end.point}")
private String cacheSyncEndPoint;

@Value("${rest.elatic-search-cache.end.point}")
private String cacheSearchEndPoint;

@Value("${rest.elatic-cache.end.point}")
private String cacheEndPoint;

public SyncCityCacheResponse syncCityToCityCache(SyncCityCacheRequest syncCityCacheRequest) throws ResourceException {
    SyncCityCacheResponse syncCityCacheResponse = null;
    try {
        logger.info("RestCityService: Hitting CityCacheServiceAPI to push city for request: {}", syncCityCacheRequest.toString());
        syncCityCacheResponse = restTemplate.postForObject(new URI(cacheSyncEndPoint), syncCityCacheRequest.getSyncCityDetails(), SyncCityCacheResponse.class);
        if (ResourceConstants.FAILURE.equalsIgnoreCase(syncCityCacheResponse.getStatus())) {
            throw new ResourceException(ResourceErrors.SYNC_API_ERROR.getErrorCode(),
                    ResourceErrors.SYNC_API_ERROR.getErrorDescription() + " " + syncCityCacheResponse.getSyncCityDetails().get(0).getDescription());
        }
    } catch (RestClientException | URISyntaxException e) {
        logger.error("RestCityService : Exception while hitting cache to push city, Exception:{}", e);
        throw new ResourceException(ResourceErrors.SYNC_API_ERROR.getErrorCode(),
                ResourceErrors.SYNC_API_ERROR.getErrorDescription() + " " + syncCityCacheResponse.getSyncCityDetails().get(0).getDescription());
    }
    return syncCityCacheResponse;
}

【问题讨论】:

  • 我希望您正在创建 ArithmeticException 并且在方法 Signature 中您正在抛出 ResourceException。您可以使用 try(with resources) 并在 catch 块中抛出 Exception for Rollback
  • @Transactional(rollbackFor= Exception.class) public CityServiceResponse saveCity(CityServiceRequest request, String status){ try { // 你的逻辑 int i = 1/0; } catch(Exception e) { throw new Exception("保存时发生异常"); } }
  • @sgewraks,这是 SpringBoot 项目还是 Spring MVC 项目?
  • @SpringBootApplication 在 ApplicationStartup.java 文件中被提及。
  • @KathirvelSubramanian 也尝试过......,我怀疑配置中是否有问题......或者我应该提到事务的地方......

标签: java database spring exception transactional


【解决方案1】:

Spring 使用 AOP 将事务性应用于使用 @Transactional 注释的方法。 Spring,by default,使用代理向现有类添加行为。通常这不是问题,但代理的一个问题是只有外部方法调用通过代理,来自对象内部的方法调用不通过代理。 (另见understanding AOP proxies)。

现在,当您从同一对象内部调用 @Transactional 方法时,它不会通过代理,因此会忽略 @Transactional。要修复您的顶级方法应该是@TransactionalserveRequest 方法)。

该方法也是您想要完全完成或完全失败的工作单元。因此,简单的解决方案是注释您的 serveRequest 方法。

【讨论】:

  • 我已按照您的建议在 MmtCityServiceImpl 服务中添加了 @Transactional 在 serveRequest 方法和 saveToDb 方法之上,但仍然无法正常工作。
  • 你最外层的方法应该是事务性的。否则它根本不起作用(尤其是内部方法调用)。
  • 还要确保您的配置中有 @EnableTransactionManagement<tx:annotation-driven />
  • 在我的 ApplicationStartup.java 文件上添加 @EnableTransactionManagement 之后。无法为我们正在使用事务的这个文件创建 Bean
猜你喜欢
  • 1970-01-01
  • 2014-01-23
  • 2013-08-06
  • 2021-03-17
  • 2018-04-24
  • 2014-04-28
  • 2017-06-18
  • 2019-03-12
相关资源
最近更新 更多