No existing transaction found for transaction marked with propagation 'mandatory' Exception in DAO classes
Asked Answered
H

3

7

I have an activity class that is annotated as a component that calls an action class:

 @Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = NonRetryableException.class)
 public ExecuteTemplateResponse executeTemplate(ExecuteTemplateRequest request)
 {
      actionExecutionContext = action.execute(actionExecutionContext);
 }

My action class is also annotated with @Component and has the following execute method:

@Transactional(propagation = Propagation.MANDATORY)
@Override
public ActionExecutionContext execute(ActionExecutionContext actionExecutionContext)
{
    iogl = ioglDao.create(iogl);
    return actionExecutionContext;
}

The ioglDao class is annotated as @Repository and has the following create method:

@Transactional(propagation = Propagation.MANDATORY)
@Override
public InventoryOwnerGroupLocation create(InventoryOwnerGroupLocation iogl)
{
    // injected
    Session session = sessionFactory.getCurrentSession();

    session.save(iogl);

    return iogl;
}

I believe that the Transaction should propagate from the Service Layer to the dao class, but it seems it's not. I get the No existing transaction found for transaction marked with propagation 'mandatory' Exception.

Why isn't the transaction propagating to my DAO classes?

EDIT: added all of the activity class

@Service("FASelfServiceMappingService")
@Component
public class ExecuteTemplateActivity extends Activity
{
    private final static Logger logger = Logger.getLogger(ExecuteTemplateActivity.class);


// mapper framework to interact with DynamoDB database
private final DynamoDBMapper dynamoDBMapper;

// class to convert external models to internal models
private final InternalModelToDynamoDBModelConverter internalToDynamoDBConverter;
private final InternalModelToOracleModelConverter internalToOracleConverter;
private final CoralModelToInternalModelConverter coralToInternalConverter;

// class to generate list of actions
private final ActionGenerator actionGenerator;

// status constants
private static final String STATUS_COMPLETED = "COMPLETED";
private static final String STATUS_FAILED = "FAILED";

@Inject
public ExecuteTemplateActivity(InternalModelToDynamoDBModelConverter internalToDynamoDBConverter,
                               InternalModelToOracleModelConverter internalToOracleConverter,
                               CoralModelToInternalModelConverter coralToInternalConverter,
                               ActionGenerator actionGenerator,
                               DynamoDBMapper dynamoDBMapper)
{
    this.internalToDynamoDBConverter = internalToDynamoDBConverter;
    this.internalToOracleConverter = internalToOracleConverter;
    this.coralToInternalConverter = coralToInternalConverter;
    this.actionGenerator = actionGenerator;
    this.dynamoDBMapper = dynamoDBMapper;
}


@Transactional(propagation = Propagation.REQUIRED, readOnly = false, rollbackFor = NonRetryableException.class)
 @Operation("ExecuteTemplate")
 public ExecuteTemplateResponse executeTemplate(ExecuteTemplateRequest request) throws RetryableException, NonRetryableException
 {
     try
     {
         logger.info("Input given: " + request);

         // convert request input to an internal request
         Request internalRequest = coralToInternalConverter.coralRequestToInternal(request);
         logger.info("Successfully converted External Request to internal Request.");

         String templateName = getTemplateName(internalRequest);
         logger.info("Template Name extracted from the request: " + templateName);

         Template template = getTemplateFromDynamo(internalRequest, templateName);
         logger.info("Template read from dynamoDB table: " + template);

         // Generate a map from string to Action objects associated with the retrieved template
         List<Action> listOfActions = actionGenerator.generateActions(template.getActions());
         logger.info("Actions generated for template " + templateName + ": " + listOfActions);

         // Generate the action context for actions to pass to each other to keep track of state
         ActionExecutionContext actionExecutionContext = internalToOracleConverter.inputsToActionExecutionContext(internalRequest.getInputs());
        logger.info("Built ActionExecutionContext:" + actionExecutionContext);

         // execute the actions
         for (Action action : listOfActions)
         {
             actionExecutionContext = action.execute(actionExecutionContext);
         }
         logger.info("All actions executed successfully.");
         // request was completed successfully, create request in Request table
         String requestId = createRequestInDynamo(internalRequest, STATUS_COMPLETED);

         ExecuteTemplateResponse executeTemplateResponse = new ExecuteTemplateResponse();
         executeTemplateResponse.setRequestId(requestId);

         logger.info("Service call "+ this.getClass() +" succeeded.");
         return executeTemplateResponse;
         }
     catch (RetryableException re)
     {
         logger.error("Retryable Exception occurred in activity.", re);
         throw re;
     }
     catch (NonRetryableException nre)
     {
         logger.error("NonRetryable Exception occurred in activity.", nre);
         throw nre;
     }
     catch (Exception e)
     {
         logger.error("Unknown Exception occurred in activity.", e);
         throw new NonRetryableException("Unexpected error", e);
     }
 }

/**
 * extracts the templateName from the internalRequest
 * @param internalRequest internal model of the request
 * @return templateName
 */
private String getTemplateName(Request internalRequest)
{
    Validate.notNull(internalRequest, "internalRequest must not be null.");

    String templateName;
    try
    {
        // extract template name from request
        templateName = internalRequest.getTemplateName();
        Validate.notNull(templateName, "templateName must not be null.");
    }
    catch (IllegalArgumentException iae)
    {
        createRequestInDynamo(internalRequest, STATUS_FAILED);
        logger.error("Invalid input: templateName is null.");
        throw new NonRetryableException("Invalid input: templateName is null.", iae);
    }

    return templateName;
}

/**
 * Retrieves the template object associated with given templateName
 * @param internalRequest internal model of request
 * @param templateName name of template to retrieve
 * @return Template object
 */
private Template getTemplateFromDynamo(Request internalRequest, String templateName)
{
    Validate.notNull(internalRequest, "internalRequest must not be null.");
    Validate.notNull(templateName, "templateName must not be null.");

    Template template;
    try
    {
        // read the template with given template name from Templates table
        template = dynamoDBMapper.load(Template.class, templateName);
    }
    catch (DynamoDBMappingException ddbme)
    {
        createRequestInDynamo(internalRequest, STATUS_FAILED);
        logger.error("Reading template from dynamoDB table failed.", ddbme);
        throw new NonRetryableException("Incorrect class annotation or incompatible with class", ddbme);
    }
    catch (AmazonClientException ace)
    {
        createRequestInDynamo(internalRequest, STATUS_FAILED);
        logger.error("Reading template from dynamoDB table failed.", ace);
        throw new RetryableException("Error when loading template from dynamoDB", ace);
    }

    return template;
}

EDIT:

Transaction Manager configuration:

   <tx:annotation-driven transaction-manager="txManager"
                          mode="proxy" proxy-target-class='true' />

    <bean id="txManager"
          class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="sessionFactory" />
        <property name="dataSource" ref="dataSource" />
    </bean>
Hydrolysis answered 3/8, 2016 at 4:50 Comment(3)
Can you show more of your activity class (esp. the part, where you inject your action class)?Anatolia
And also the part where you call the method executeTemplate. Do you call it from another class or from inside your activity class?Anatolia
Added all of my activity class. I'm testing the activity class in a unit test and calling the executeTemplate from a unit test.Hydrolysis
S
-3

It is maybe a problem related to your Spring configuration. Did you follow the guide at http://docs.spring.io/autorepo/docs/spring/4.2.x/spring-framework-reference/html/transaction.html? Can you show how you configure Spring transaction management ?

Selfregulated answered 3/8, 2016 at 5:51 Comment(3)
Yes, I followed the guide. I have updated the question with my Spring transaction management.Hydrolysis
Thank you. Some more questions: does injection of sessionFactory in activity class work? Is component scanning configured correctly (<context:component-scan base-package="com.youpackage..." />)? Since you already have '@Service', is the '@Component' annotation on the activity class necessary?Selfregulated
I have not tried injection of sessionFactory in the activity class as it's not needed there. It is injected directly into the DAO classes. Should I try that? Yes, component scanning is configured correctly. The @Service annotation on the activity class is from another internal framework, not spring.Hydrolysis
H
2

I had the same exception with Spring WebFlux.

Issue :

repository.findByUserId(id);

Solution:

@Autowired
private TransactionTemplate transactionTemplate;

transactionTemplate.execute(transactionStatus -> identInformationRepository.findByUserId(id));
Hesse answered 22/3, 2022 at 11:26 Comment(0)
S
0

In some cases it can be solved with the following:

Try to separate the code from the transaction you are getting the error in, try to do the injection in a separate method, preferably at startup.

And add @Transactional before begin void

Example:

BEFORE

public void mainProces(){
  System.out.println("Hello, i save my name");
  SQL(select)
  SQL(save)
}

AFTER

@Transactional
    public void secondProces(){
            SQL(save)
    }

public void mainProces(){
  System.out.println("Hello, i save my name");
  secondProces();
  SQL(select)
}
Stenophagous answered 7/12, 2022 at 18:46 Comment(0)
S
-3

It is maybe a problem related to your Spring configuration. Did you follow the guide at http://docs.spring.io/autorepo/docs/spring/4.2.x/spring-framework-reference/html/transaction.html? Can you show how you configure Spring transaction management ?

Selfregulated answered 3/8, 2016 at 5:51 Comment(3)
Yes, I followed the guide. I have updated the question with my Spring transaction management.Hydrolysis
Thank you. Some more questions: does injection of sessionFactory in activity class work? Is component scanning configured correctly (<context:component-scan base-package="com.youpackage..." />)? Since you already have '@Service', is the '@Component' annotation on the activity class necessary?Selfregulated
I have not tried injection of sessionFactory in the activity class as it's not needed there. It is injected directly into the DAO classes. Should I try that? Yes, component scanning is configured correctly. The @Service annotation on the activity class is from another internal framework, not spring.Hydrolysis

© 2022 - 2024 — McMap. All rights reserved.