Custom Mybatis Queries and command context

I’d like to ask, if it is possible to retrieve CommandContext when calling custom mybatis queries, if approach described in example https://github.com/camunda/camunda-consulting/tree/master/snippets/custom-queries or do I need to use approach like described in the documentation(https://docs.camunda.org/manual/7.7/examples/tutorials/custom-queries/)

Unfortunately, although the documentation links the example, the code in the documentation is not aligned with the example. I’d like to retrieve command context in order to be able to access AuthorizationManager instance to fill AuthorizationCheck in TaskQueryImpl, and maybe in future I will need something else from within the command context.
Of course,I could fill the needed data using IdentityService.getAuthentication, but I’d like to stay aligned with Camunda code as much as possible. I am just altering selecttaskbycriteria query a little bit, and I’d like to use all the infrastructure that is already provided in Camunda core. Or is this approach incorrect?

1 Like

So I get this to work.

Basically it was just about extending the TaskQueryImpl and overriding executeList and executeCount methods with implementation, which uses the SqlSession object created by the factory as implemented in the tutorial.

The extended query object is then created with CommandExecutor, which can be obtained from the process engine configuration. Something like this.

processEngineConfiguration.getCommandExecutorTxRequired()

So far, I have not checked, what would the transactions do, but I guess I would just alter the executor chain to whatever my needs would be in future.

Definitely, it would be a very good idea to document this in the documentation.

Hi @tomorrow,

I’m glad you found a solution to your problem. However, I’m not sure what exactly it was :slight_smile:

So in order to decide if we should improve the docs, could you please share the code that achieves what you did along with a description of what you wanted to achieve? Please try to provide a minimal, focused example. Then we can take a look if your solution is indeed the best way and may update the docs accordingly.

Cheers,
Thorben

Hi, ok, so I will try to be more thorough.

First of all, the documentation and the linked example are not aligned. The example is of newer version and the linked classes do no exist anymore, so the documentation needs to be updated in this concern

Now back to my use case. Basically, what I wanted to achieve is something like it is currently mentioned in the outdated documentation linked in the first post, where custom select is run via camunda’s command executor. Normally, running just the select would be enough, however in my use case, I didn’t want to implement completely new select, but just alter select for querying task by task criteria. And for that select I needed the CommandContextInterceptor to be run in order to fill command context, from which is IdentityService retrieved, when filling candidateUserGroups field.

For this I created SqlSessionFactory as is shown in the example in git repository, which btw. is not fully working as mentioned here Custom mybatis queries together with fix.

Now back to extending and creating TaskQueryImpl. Here is a code sample.

public class CustomTaskServiceImpl implements CustomTaskService {

    private SqlSessionFactory sqlSessionFactory;

    private ProcessEngine processEngine;

    private TaskQuery createCustomTaskQuery(){
        ProcessEngineConfigurationImpl processEngineConfiguration = (ProcessEngineConfigurationImpl) processEngine.getProcessEngineConfiguration();
        return new TaskQueryImpl(processEngineConfiguration.getCommandExecutorTxRequired()){
       
        @Override
        public List<Task> executeList(CommandContext commandContext, Page page) {
            ensureVariablesInitialized();
            checkQueryOk();
            List<Task> taskList = null;
            try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
                taskList = sqlSession.selectList("listStatementName", this);
            }

            if (initializeFormKeys) {
                for (Task task : taskList) {
                    // initialize the form keys of the taskList
                    ((TaskEntity) task).initializeFormKey();
                }
            }
            return taskList;
        }


        @Override
        public long executeCount(CommandContext commandContext) {
            try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
                ensureVariablesInitialized();
                checkQueryOk();
                long taskCount = sqlSession.selectOne("countStatementName", this);

                return taskCount;
            }
        }

        };
    }

As I mentioned in my previous post, I am not 100% sure its totally correct approach and if the transactions work correctly, but for selecting data the transaction are not important to me and it works. If I needed transactions in future, I would either alter the command excecutor retrieved from process configuration instance or I would create my own.

Is this better?

It’s still not 100% clear to me. Could you please describe what you are trying to build and not so much how?

I understand so far that you

  • Want to query tasks
  • Have some custom criteria
  • Need to resolve a user’s candidate groups (not sure about this one?)

The reason I’d like to take a step back here, is that pretty much all of the discussed code touches internal API and I’d first like to understand that it is actually necessary to do this.

Hi, so i want to do this.

  • I want to query tasks
  • I want to add some special logic inside the sql query without needing any additional criteria, therefore I want to reuse TaskQueryImpl, ,e.g. I want to query task, that the user is candidate user in, and the tasks are assigned to somebody but the user I have filled in the candidateUser criteria
  • As for some filled criteria TaskQueryImpl needs context, I need the custom select to be run with Context set, which is done in CommandContextInterceptor

Hi,

Thanks for elaboration. I believe there is no good alternative for your case that is less messy.

I’ll try to respond to some aspects as good as possible:

  • The way you adapted the code looks good (i.e. by using processEngineConfiguration#getCommandExecutorTxRequired)
  • It should not be required to alter the command executor in any way
  • If you need additional custom logic executed in a transaction, there are different options. The most safe one (i.e. using public API) would be to use transaction integration via Spring or JTA. The non-public API approach that works in any environment is via CommandExecutor#executeCommand. Inside the command (argument to aforementioned method), you can execute any code that accesses the database or even invoke other commands (e.g. a query is a command). These will all be executed in the same transaction. Again, this is not public API and is therefore not documented and can change any time.
  • Lastly, we would like to remove the tutorial from the official documentation as described here: https://app.camunda.com/jira/browse/CAM-4845. The reason is that this tutorial uses internal API heavily and links to code that is maintained outside of the Camunda product space (https://github.com/camunda/camunda-consulting is not maintained by the Camunda product team). This results in outdated documentation and inconsistencies between docs and code, which is a not a great experience for anyone trying to follow along. That’s also why we won’t update the docs for now. I’m sorry for the messy documentation in this area.

Cheers,
Thorben