Skip to content

Command methods implementation details

Command method extensions applied to all command extensions (@Query, @Function, @Script etc.)

Tip

Read repository internals first for better understanding

Common command features

Commands generally support:

  • Positional parameters
  • Named parameters
  • Command string el variables

Most commands use CommandMethodDescriptor as descriptor, except @Script which use ScriptCommandMethodDescriptor (to store script language). So when you target CommandMethodDescriptor (in generic), extension will be compatible with all command methods.

All command extensions use AbstractCommandExtension as base class. It's very easy to add your own custom command annotation using it (look bundled extensions sources).

Command extensions have three execution phases:

  • SqlCommandDescriptor composition
  • OCommandRequest composition (orient command)
  • And command execution

Amend extension shoudl implement CommandExtension insterface (to affect execution).

SqlCommandDescriptor phase

SqlCommandDescriptor contains query, prepared parameters (composed from method arguments) and el variables (also composed fro arguments). Extensions could modify query (for example, @Skip appends "SKIP" part to query string. Extensions could add (or modify) query parameters. And extensions could add/modify el variables.

Positional parameters is a core feature (its parameters without any extension), anyway they could be modified too (for example, @DynamicParams modifies them).

Named parameters covered with bundled extension @Param. It is not mandatory to use it: you can write your own extension to define named parameters.

El variables defined with @ElVar extension, but again, its not mandatory - you can use your own extension to define variables.

OCommandRequest phase

First of all, el variables replaced in query, producing target query string. Then command object created using prepared query.

Extensions could modify resulted command object (for example, @Timaout, @Listen).

After all command object wrapped with connection object (using selected RepositoryExecutor) and executed, using composed parameters. Command wrapping is required to bind command to specific connection type. This will affect resulting objects.

Extensions lifecycle

  • Create SqlCommandDescriptor
  • Run extensions to amend descriptor
  • Apply el vars and create OCommandRequest
  • Run extensions to amend request object
  • Wrap command with connection and execute with prepared parameters

Command parameters context

Command extensions use CommandParamsContext during parameters processing.

Parameter extensions are called after all parameters processing (to collect all parameters, marked with the same annotation)

If you need to modify positional parameters, you can obtain them through paramsContext.getOrdinals(). Usually, there is no need to modify them, because if you need special parameter handling you'll define custom annotation.

If your extension defines named parameter you may register it in context: paramsContext.addNamedParam(). After registration, it will be applied automatically. You may omit registration if your parameter must be converted or something like this (in this case you'll use amend extension to manually apply parameter value).

If your extension handles el variable you must declare it either as static paramsContext.addStaticElVarValue() if value is the same for all method executions, or declare it as dynamic paramsContext.addDynamicElVarValue(). In case of dynamic el var, you'll need to use amend extension to manually apply value during execution.

Registration of el variables is required, because they are strictly checked and if some not declared variable found in query or declared variable not used in query error will be thrown to indicate method definition error.

If you need access to method, type, know root repository type or use generics info, use paramsContext.getDescriptorContext(). Note that if parameter type is generified, you will receive resolved generic in extension.

Implementing command parameter extension

Suppose you want to implement @MyCustomParam parameter extension.

Declaring annotation:

@Target(PARAMETER)
@Retention(RUNTIME)
@MethodParam(MyCustomParamExtension.class)
public @interface MyCustomParam { ... }

If required, define extension options as annotation attributes. @MethodParam marks extension as parameter extension and it will be found and MyCustomParamExtension class obtained from guice context and executed.

Now implement extension:

@Singleton
public class MyCustomParamExtension implements 
        MethodParamExtension<CommandMethodDescriptor, CommandParamsContext, MyCustomParam >{ 

    @Override
    public void processParameters(final CommandMethodDescriptor descriptor, final CommandParamsContext context,
                                  final List<ParamInfo<MyCustomParam>> paramsInfo) {
       ...
    }
}

This extension will be called once, during descriptor creation. Here you can validate parameters and, for example, register named parameter or el variable in paramsContext

Note

We declare MethodParamExtension<CommandMethodDescriptor ...>, this means extension is compatible only with command descriptor and can't be used with delegate (or some other) method extensions. Also, because @Script's ScriptCommandMethodDescriptor extends CommandMethodDescriptor, this extension is compatible with scripts too.

If you need some execution time modification, you'll need to implement amend extension interface:

@Singleton
public class MyCustomParamExtension implements 
        MethodParamExtension<CommandMethodDescriptor, CommandParamsContext, MyCustomParam>,
        CommandExtension<CommandMethodDescriptor> { 

    public static final String KEY = MyCustomParamExtension .class.getName();

    ...
    @Override
    public void amendCommandDescriptor(final SqlCommandDescriptor sql, final CommandMethodDescriptor descriptor,
                                       final Object instance, final Object... arguments) {
        ...
    }

    @Override
    public void amendCommand(final OCommandRequest query, final CommandMethodDescriptor descriptor,
                             final Object instance, final Object... arguments) {
        ...
    }

By implementing CommandExtension we did our extension compatible with command methods amend extension. Now parameter extension will be registered as amend extension afeter parameters processing.

Most likely you'll need some information from parameters processing method inside executino methods. If it's not a simple value (int, string etc), define your value object MyCustomParamDescriptor. Compose descriptor on processing phase and store inside main method descriptor:

descriptor.extDescriptors.put(KEY, yourCustomDescriptor);

Now in execution method you can obtain it and use:

MyCustomParamDescriptor desc = (MyCustomParamDescriptor) descriptor.extDescriptors.get(KEY);

Note

KEY is public. This will allow you to use it in unit tests and possibly, other extensions also could use it (for example, @AsyncQuery must know about @Listen extension).

That's all, extension could be used in command method:

@Query("select from Model")
List select(@MyCustomParam String param);

Look bundled extensions sources for advanced examples.

Implementing command method amend extension

First of all, you can register custom amend extension globally:

@Inject AmendExtensionsService amendExtensions;
...
amendExtensions.addGlobalExtension(new CommandExtension() {...});

Or you can use custom annotation, for example:

@Target({METHOD, TYPE})
@Retention(RUNTIME)
@AmendMethod(MyCustomAmendExtension.class)
public @interface MyCustomAmend{ ... }

Amend extensions are searched on method, on type and on root repository type. You can limit usage scope with @Target annotation (e.g. allow usage only on methods). @AmendMethod marks annotation as amend annotation, now it will be resolved.

As with parameters, you eaither implement just parsing interface, to apply extension in descriptor creation time, or implement also amend extension interface, to use extension during method call.

@Singleton
public class MyCustomAmendExtension implements 
        AmendMethodExtension<CommandMethodDescriptor, MyCustomAmend>,
        CommandExtension<CommandMethodDescriptor> {

    @Override
    public void handleAnnotation(final CommandMethodDescriptor descriptor, final MyCustomAmend annotation) {
        ...
    }

    ...
}

Implementation guideline is the same as with params: use public KEY field and custom descriptor object.

Extension could be used like this:

@Query("select from Model")
@MyCustomAmend
List select();

See @Timeout as implementation example.