简体   繁体   English

Spring @Transactional和@Async

[英]Spring @Transactional and @Async

In my application, on the creation of a task, I need to make an API call to Google to create a google calendar event. 在我的应用程序中,在创建任务时,我需要向Google发出API调用以创建Google日历活动。

I decided to make that API call on a separate thread so that our client doesn't have to wait longer for the response. 我决定在单独的线程上进行API调用,以便我们的客户端不必等待更长的响应。

@Override
@Transactional( rollbackFor = DataException.class )
public TaskResponseBean createTask( TaskCreationBean taskCreationBean, UserAccessDetails accessDetails )
        throws DataException
{
    String googleEventId = "";
    try
    {
        TaskServiceUtil.validateInputBeforeCreatingTask(taskCreationBean, accessDetails);

        MatterModel matterModel = matterService.giveMatterIfExistElseThrowException(taskCreationBean.getMatterId(),
                owner);

        //A task is unique for a user for a matter
        taskCommons.throwExceptionIfTaskNameAlreadyExistForTheMatter(taskCreationBean.getTaskName().trim(), owner,
                matterModel);

        TaskModel savedTask = taskModelRepository.save(savableTask);

        if( !NullEmptyUtils.isNull(savableTask.getDueDate()) )
        {
            final CreateEventBean createEventBean = getCreateEventBean(getEventParticipants(savedTask), savedTask);
            calendarTrigerer.triggerEventCreation(createEventBean, savedTask.getId(), null,
                    GoogleCalendarTrigerer.EVENT_TYPE_CREATE);
        }
        // Keep track of the list of assignees of a task
        if( taskCreationBean.getHaveAssignee() || taskCreationBean.getIsSelfAssigned() )
        {
            saveTaskAssignedHistory(savedTask, owner, savedTask.getAssignedTo(), false);
        }

    }
    catch( DataException e )
    {
        LOGGER.error(GeneralConstants.ERROR, e);
        if( !NullEmptyUtils.isNullOrEmpty(googleEventId) )
        {
            LOGGER.info("Deleting google event id {}", googleEventId);
            googleCalendarService.deleteGoogleCalendarEvent(googleEventId);
        }
        throw e;
    }
    catch( Exception e )
    {
        LOGGER.error(GeneralConstants.ERROR, e);
        if( !NullEmptyUtils.isNullOrEmpty(googleEventId) )
        {
            LOGGER.info("Deleting google event id {}", googleEventId);
            googleCalendarService.deleteGoogleCalendarEvent(googleEventId);
        }
        throw new DataException(GeneralConstants.EXCEPTION, GeneralConstants.SOMETHING_WENT_WRONG,
                HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

@Async
void triggerEventCreation( CreateEventBean createEventBean, Long taskId, String eventId, String eventType )
        throws DataException
{
    try
    {

        TaskModel taskModel = null;

        if( !NullEmptyUtils.isNullOrEmpty(taskId) )
        {
            int retryCount = 0;
            Optional<TaskModel> taskModelOptional = taskModelRepository.findByIdAndIsActiveTrue(taskId);
            while( !taskModelOptional.isPresent() )
            {
                System.out.println("NOT PRESENT***********************************************");
                taskModelOptional = taskModelRepository.findByIdAndIsActiveTrue(taskId);
                if( retryCount++ > 50 )
                {
                    throw new DataException(GeneralConstants.EXCEPTION, "Transaction is unable to commit",
                            HttpStatus.INTERNAL_SERVER_ERROR);
                }
            }

            taskModel = taskModelOptional.get();
        }

        switch ( eventType )
        {
            case EVENT_TYPE_CREATE :

                eventId = googleCalendarService.addGoogleCalendarEvent(createEventBean);
                System.out.println("ADDED EVENT***********************************************" + eventId);

                System.out.println("PRESENT***********************************************");
                taskModel.setGoogleEventId(eventId);
                taskModelRepository.save(taskModel);
                break;
            case EVENT_TYPE_DELETE :
                NullEmptyUtils.throwExceptionIfInputIsNullOrEmpty(eventId);
                googleCalendarService.deleteGoogleCalendarEvent(eventId);
                taskModel.setGoogleEventId(null);
                taskModel.setIsActive(false);
                taskModelRepository.save(taskModel);
                break;
            case EVENT_TYPE_UPDATE :
                NullEmptyUtils.throwExceptionIfInputIsNullOrEmpty(eventId);
                NullEmptyUtils.throwExceptionIfInputIsNullOrEmpty(createEventBean);

                taskModel.setGoogleEventId(
                        googleCalendarService.updateGoogleCalendarEvent(eventId, createEventBean));
                taskModelRepository.save(taskModel);
                break;
            default :
                throw new DataException(GeneralConstants.EXCEPTION, "Invalid eventType", HttpStatus.BAD_REQUEST);
        }

    }
    catch( DataException e )
    {
        log.error(GeneralConstants.ERROR, e);
        throw e;
    }
    catch( Exception e )
    {
        log.error(GeneralConstants.ERROR, e);
        throw new DataException(GeneralConstants.EXCEPTION,
                "Something went wrong while trigering create event action", HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

How I solved (make sure it works) while creating an event In the separate thread, I will iterate and wait till the task gets created and then update it with the event id and save it. 我在创建事件时如何解决(确保它有效)在单独的线程中,我将迭代并等待任务被创建,然后使用事件id更新它并保存它。

But, a new problem arises, when updating the task, I already have its details in the database. 但是,出现了一个新问题,在更新任务时,我已经在数据库中有了详细信息。 In the update method, I will set updated values to TaskModel and do taskModelRepo.save() and in a separate thread I am calling google calendar update API and after successful API call, I have to update the corresponding TaskModel and save it. 在更新方法中,我将更新值设置为TaskModel并执行taskModelRepo.save()并在单独的线程中调用google calendar update API,并且在成功调用API之后,我必须更新相应的TaskModel并保存它。

The issue here is, sometimes when I fetch task by id after google API call is successful, I will get the TaskModel with non-updated values as the previous transaction is not committed yet. 这里的问题是,有时当我在谷歌API调用成功后通过id获取任务时,我将获得具有未更新值的TaskModel ,因为之前的事务尚未提交。

So How to ensure the new thread runs only after the transaction of the method from which it is called is committed? 那么如何确保新线程只在提交调用它的方法的事务之后运行?

you can use the Google Guava Event Bus to solve this problem. 您可以使用Google Guava Event Bus解决此问题。 It's a publish-subscribe model in which the producer is responsible for emitting the events, these events are then passed on to the event bus and are sent to all listeners that are subscribed to that event. 它是一个发布 - 订阅模型 ,其中生产者负责发出事件,然后将这些事件传递给事件总线并发送给订阅该事件的所有侦听器。

The listener, subscribes to an event and it is triggered when that event is posted from the producer, you could have a listener method run Synchronously or Asynchronously depending on the kind of event bus you use. 侦听器订阅一个事件,当从生产者发布该事件时触发该事件,您可以根据您使用的事件总线的类型, 同步异步运行侦听器方法。

Here is the link : https://github.com/google/guava/wiki/EventBusExplained 这是链接: https//github.com/google/guava/wiki/EventBusExplained

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM