Search code examples
springspring-bootspring-mvctransactionaltransactional-database

Manage Transactions in Spring Applications


I'm using springboot to develop my application, In the service I'm calling a method by the name submitPostDetails() --> This method sends request to method - X() the Dao along with some parameters and inserts the post details into the system.

Once, This is done, the same method in dao calls another method Y() to update some records in another table. Now, my concern is what if the first method executes and the second method throws an exception. I would like to rollback the insert done by the first method if any exception occurs. Tried using @Transactional but it did not work

[This is the service method named as submit Post details ][1]

public String submitPost( SubmitPostDetailsDto submitPostDetailsDto) throws IOException {
    // TODO Auto-generated method stub
    LOGGER.debug("Request received from controller to submit the post details and upload the scanned document");
    LOGGER.debug("Sending request to get the unique application ID");
    String uniqueApplicationId = applicationUtilityClass.getUniqueApplicationId(submitPostDetailsDto);
    LOGGER.debug("Checking if the applicaiton id is null");
    if(uniqueApplicationId==null||uniqueApplicationId.isEmpty())
    {
        LOGGER.error("The unique application id is null or empty");
        LOGGER.error("Returning message -  Application Id could not be generated, Please contact the administrator");
        return readApplicationConstants.getApplicationIdGenerationErrorMessage(); //Returns error message - Id could not be generated, Please contact the administrator

    }
    LOGGER.debug("Unique application id generated is: "+uniqueApplicationId);
    LOGGER.debug("Sending Request to determine the document type and return the uploadPath");
    String uploadPath = applicationUtilityClass.uploadFile(submitPostDetailsDto.getDocumentType(),submitPostDetailsDto.getFile(),uniqueApplicationId );

    if(uploadPath==null)
    {
        LOGGER.error("An error occured in uploadFile method in ApplicationUtilityClass");
        LOGGER.error("ERROR! The file cannot be uploaded");
        LOGGER.error("Returning ERROR message to controller");
        return readApplicationConstants.getApplicationIdGenerationErrorMessage();
    }
    else
    {
        LOGGER.debug("Path of uploaded file returned is: "+uploadPath);
        LOGGER.debug("Sending request to Dao to submit postDetailsDto along with applicationId and uploadPath into the database");

        return registerApplicationDao.submitPostDetails(submitPostDetailsDto,uniqueApplicationId,uploadPath);
    }
}

This is the Dao method submit post details - @Transactional public String submitPostDetails(SubmitPostDetailsDto submitPostDetailsDto, String uniqueApplicationId,String uploadPath) {

    // TODO Auto-generated method stub

    LOGGER.debug("Request received from registerApplicationService - submitPost method to submit document details, uniqueApplicationId,uploadPath into database");
    LOGGER.debug("Creating hashmap of objects");
    SqlParameterSource postDetailsParams = new MapSqlParameterSource();
    LOGGER.debug("Hashmap created successfully");
    LOGGER.debug("Putting values in the hashmap");
    ((MapSqlParameterSource) postDetailsParams).addValue("applicationId", uniqueApplicationId);
    ((MapSqlParameterSource) postDetailsParams).addValue("senderName", submitPostDetailsDto.getSenderName());
    ((MapSqlParameterSource) postDetailsParams).addValue("senderPoc", submitPostDetailsDto.getPointOfContact());
    ((MapSqlParameterSource) postDetailsParams).addValue("senderContact",submitPostDetailsDto.getContactNumber());
    ((MapSqlParameterSource) postDetailsParams).addValue("dateReceived", submitPostDetailsDto.getDateReceived());
    ((MapSqlParameterSource) postDetailsParams).addValue("priority", submitPostDetailsDto.getPriority());
    ((MapSqlParameterSource) postDetailsParams).addValue("subject", submitPostDetailsDto.getSubject());
    ((MapSqlParameterSource) postDetailsParams).addValue("documentType", submitPostDetailsDto.getDocumentType());
    ((MapSqlParameterSource) postDetailsParams).addValue("documentPath", uploadPath);
    ((MapSqlParameterSource) postDetailsParams).addValue("documentRemarks", submitPostDetailsDto.getAdditionalComments());
    LOGGER.debug("Values succcessfully inserted into hashmap");
    LOGGER.debug("Creating object of KeyHolder to return the auto generated key after insertion of the post details");
    KeyHolder holder = new GeneratedKeyHolder();
    try {
        LOGGER.debug("Executing the query to submit post details");
        getJdbcTemplate().update(registerApplicationConfig.getSubmitPostDetails(),postDetailsParams, holder);
        LOGGER.debug("The key which is generated is: "+holder.getKey().intValue());
        Integer docId = holder.getKey().intValue();//To get the primary key (Generated by Auto-Increment) of the document details table
        LOGGER.debug("Details inserted successfully, Key of inserted application id is: "+holder.getKey().intValue());
        LOGGER.debug("Calling method get the Id of the owner to whom the document is assigned");
        Integer documentOwnerId = getDocumentOwnerId(submitPostDetailsDto.getOwnerName());//Auto-incremented id of the DH in the users table
        LOGGER.debug("The document owner id corresponding to ownerName: "+submitPostDetailsDto.getOwnerName()+" and applicationId: "+uniqueApplicationId+" is: "+documentOwnerId);
        LOGGER.debug("Calling method to fill the document status table");
        LOGGER.debug("Sending control to fillDocumentStatusTable");




        Integer submitStatus = fillDocumentStatusTable(docId,documentOwnerId);

        if(submitStatus == -1)
        {
            LOGGER.error("Details cannot be inserted in document status table");
            LOGGER.error("Returning ERROR Message");
            return readApplicationConstants.getPostDetailsSubmissionFailure();//Returns - Post cannot be submitted

        }
        LOGGER.debug("The document has been submitted successfully ");
        LOGGER.debug("Calling method to return application id on the basis of id(PK) :" +docId);
        String uniqueId = getApplicationIdById(docId);



        LOGGER.debug("The unique id corresponding to the generated primary key is: "+docId);
        return uniqueId;
    }
    catch(RuntimeException e)
    {
        LOGGER.error("The exception is: "+e);
        LOGGER.error("Returning NULL");
        return null;
    }


}

This method calls a method - fillDocumentStatusTable

private Integer fillDocumentStatusTable(Integer docId, Integer documentOwnerId) {
    // TODO Auto-generated method stub
    LOGGER.debug("In fillDocumentStatusTable to submit the details in the documentStatus table");
    LOGGER.debug("Creating hashmap of objects");
    Map<String,Object>docStatusParams = new HashMap<>();
    LOGGER.debug("Hashmap successfully created");
    LOGGER.debug("Putting values in the hashmap");
    docStatusParams.put("ownerId", documentOwnerId);
    docStatusParams.put("status",readApplicationConstants.getDocumentNotStartedStatus());
    docStatusParams.put("documentId", docId);
    try
    {
        LOGGER.debug("In try block of fillDocumentStatusTable to submit details in the doc status table");
        LOGGER.debug("Executing query to submit the details in document status table");
        return getJdbcTemplate().update(registerApplicationConfig.getSubmitDocumentStatus(), docStatusParams);

    }
    catch(Exception e)
    {
        LOGGER.error("An error occured while submitting the details in doc status table: "+e);
        LOGGER.error("returning -1");
        return -1;

    }
}

Solution

  • Every method invoked should be in the same transaction -> should have @Transactional annotation.

    If you decide to catch your exception and want a rollback, you should rethrow it, for example:

    try {
        doSomething();
    catch (Exception e) {
        logger.error("exception occurred", e);
        throw new MyException(e);
    }
    

    Then you can use in your annotation:

    @Transactional(rollbackFor=MyExceptoin.class)

    That should do the trick.