-
Notifications
You must be signed in to change notification settings - Fork 7
Repository command methods
DEPRECATED Documentation was moved into new docuemntation site
Toc
The following methods are using orient commands for execution and called command methods.
- @Query - select/update/insert query
- @Function - function call
- @Script - script call (sql, js etc)
- @AsyncQuery - async query call
- @LiveQuery - live query subscription
All command methods annotations has two options:
- returnAs - defines required collection implementation
- connection - defines required connection
Most of the time they are not needed.
ReturnAs example:
@Query(value = "select from Model", returnAs = TreeSet.class)
Set<Model> select()
Method will return TreeSet as result (for example, we want sorted results).
Connection example:
@Query(value = "select from Model", connection = DbType.OBJECT)
List select()
Result is not generified, so without hint document connection would be selected for method and result will be List. With connection hint actual result will be List.
Another case, when it might be useful is custom converter. For example, you want to convert results to some DTO object, but your converter converts model objects. If you will not use hint:
@Query("select from Model")
@ModelDTOConverter
List<ModelDTO> select()
This will not work, because document connection will be selected and converter expects objects.
When we set connection hint (connection = DbType.OBJECT
), everything will work as planned.
NOTE: @ModelDTOConverter does not exist, it's just hypothetical result converter extension you could write, using extension api.
Commands support positional and named parameters:
@Query("select from Model where name = ?")
List<Model> positional(String name)
@Query("select from Model where name = :name")
List<Model> named(@Param("name") String name)
Positional may be used as named too:
@Query("select from Model where name = :0")
List<Model> positional(String name)
For example, script will not work with '?' positional but works which ':0' named-positional.
All commands support el variables.
For example:
@Query("select from ${type}")
List selectAll(@ElVar("type") String type);
Such variables are inserted into query string before actual execution. In theory may be used even to provide sql parts, but be careful with it.
By default you can use generics as el variables:
select from ${T}
where T is generic of query method declaring class (read more about hierarchies below).
Another example is oauth providers connection: suppose you have multiple auth providers and user object has property for each provider id. To avoid writing multiple queries for searching user by provider id, we can do like this:
@Query("select from User where ${provider} = ?")
Optional<User> findByProvider(@ElVar("provider") AuthProvider provider, String providerId);
Where AuthProvider is enum:
public enum AuthProvider {
google, facebook, twitter
}
Query methods used for queries (select/update/insert).
Select query:
@Query("select from Model where name=?")
List<Model> select(String name)
Update query:
@Query("update Model set name = ? where name = ?")
int update(String to, String from)
Update query return type could be void
, int
, long
, Integer
and Long
.
Insert query:
@Query("insert into Model (name) values (?)")
Model insert(String name)
Internally OSQLSynchQuery used for selects and OCommandSQL for updates and inserts.
Documentation links:
Function methods execute function.
For example, suppose we create a function like this:
CREATE FUNCTION function1 "select from Model" LANGUAGE SQL
Now we can call it like this:
@Function("function1")
List<Model> select(String name)
Internally OCommandFunction used.
Allows you to write small scripts in sql, javascript or any other scripting language.
For example:
@Script("begin" +
"let account = create vertex Account set name = :name" +
"let city = select from City where name = :city" +
"let edge = create edge Lives from $account to $city" +
"commit retry 100" +
"return $edge")
Edge linkCity(@Param("name") String name, @Param("city") String city)
By default SQL language used for commands.
Example of javascript command:
@Script(language = "javascript", value =
"for( i = 0; i < 1000; i++ ){" +
"db.command('insert into Model(name) values (\"test'+i+'\")');" +
"}")
void jsScript()
Note that in some cases script allows you to avoid OConcurrentModificationException:
@Script("begin" +
"update Model set name = :0" +
"commit")
void update(String name)
This may be not the best way in all cases, but it works (due to implementation specifics simple query will fail in concurrent cases). Also, note as positional parameter used as named. Script doesn't work with positional parameters, but it works like this.
Internally OCommandScript used.
Executes query asynchronously. By default, async query execution is blocking: method is blocking while listener is called (and listener is executed at the same thread). Such query is useful for dynamic filtering: results are analyzed one by one and you can manually stop further results processing.
Example:
@AsyncQuery("select from Model")
void select(@Listen OCommandResultListener listener)
Returned result will be passed to the provided listener (always as ODocument).
Since 3.3.0
Special listener type could be used to automatically convert the provided document (the same way as repository return result is converted):
@AsyncQuery("select from Model")
void select(@Listen AsyncQueryListener<Model> listener)
Projection will also work:
@AsyncQuery("select name from Model")
void selectProjection(@Listen AsyncQueryListener<String> listener)
And even conversion to graph api:
@AsyncQuery("select from VertexModel")
void selectVertex(@Listen AsyncQueryListener<Vertex> listener)
Non blocking async query is executed truly asynchronously: listener called in a separate thread. Non blocking query may return future.
@AsyncQuery(value = "select from Model", blocking = false)
Future<List<Model>> selectNonBlock(@Listen AsyncQueryListener<Model> listener)
Future may be used to wait for the result:
// most likely get will be called somewhere later in code and not directly after async method call
List<Model> result = selectNonBlock().get()
Listener execution is wrapped with external transacton, so guice can use the same connection instance as orient in current thread. But it is highly recommended to avoid database operations inside listener because listener must execute as fast as possible (orient recommendation).
Internally OSQLAsynchQuery or OSQLNonBlockingQuery used accordingly.
Since 3.3.0
Subscribe listener to orient live query.
Live query may use row orient listener interface:
@LiveQuery("select from Model")
int subscribe(@Listen OLiveResultListener listener)
Note that live query must start with "live", but this is optional as annotation already declares query as live. Anyway, you can write "live select from Model" if you want.
Subscription call will return subscription token, which may be used to unsubscribe query:
@Query("live unsubscribe \${token}")
void unsubscribe(@ElVar("token") int token)
Special live result listener may be used with automatica conversions support (much like repository method result conversions):
@LiveQuery("select from Model")
int subscribe(@Listen LiveQueryListener<Model> listener)
Graph api could also be used:
@LiveQuery("select from VertexModel")
int subscribeVertex(@Listen LiveQueryListener<Vertex> listener)
Of course, pure document is allowed too. But note, that projections will not work here as live query always return entity.
Listener execution is wrapped with external transacton, so guice can use the same connection instance as orient in current thread.
OLiveQuery used for query execution.
- @Param - named parameter
- @ElVar - query variable value (substituted in string before query execution)
- @RidElVar - extract rid from provided object, document, vertex, string orid and insert into query
- @Var - orient command variable ($var), may be used by query during execution
- @Skip and @Limit - orient pagination
- @FetchPlan - defines fetch plan for query
- @Listen - to provide query listener (required for async queries)
- @DynamicParams - map dynamic count of parameters from array/collection/map
Marks named parameter query parameter. Parameter may be of any type, supported by orient. For example, if ODocument passed, orient will use it as @rid.
@Query("select from Model where name = :name")
List<Model> findByName(@Param("name") String name);
You are not restricted to use only this annotation: you may write your own extension annotation (e.g. to add some additional validations).
Marks parameter as el variable (variables are substituted in query before execution).
@Query("select from Model where ${prop} = ?")
List<Model> findBy(@ElVar("prop") String prop, String value);
Any type could be used for variable. Value is converted to string using object.toString. Null value converted to empty string ("").
If Class used as variable then only class name will be used, for example:
@Query("select from ${model}")
List<Model> findAll(@ElVar("model") Class model);
It is safe to use Class, Enum, Number (int, long etc), Character types, because they not allow sql injection. But when string or raw object used as value, you can define a list of allowed values to avoid injection:
@Query("select from Model where ${prop} = ?")
List<Model> findAll(@ElVar(value = "prop", allowedValues = {"name", "nick"}) String prop, String value);
Now if provided value is not "name" or "nick" exception will be thrown.
If you use String variable without list of allowed values, warning will be shown in log (possible injection). If you 100% sure that it's safe, you can disable warning:
@Query("select from Model where ${cond}")
List<Model> findWhen(@ElVar(value = "cond", safe = true) String cond);
...
repository.findWhen("name='luke' and nick='light'")
Also, safe marker documents method safety (kind of "yes, I'm sure").
A special el variable to extract rid from provided object, document, vertex, ORID (object or string) and set as el variable.
It is implemented as el variable and not as parameter because current orient sql parser works not so well in some cases. For example, query like create edge from ? to ?
will not work with parameters (named too) and the only way to make it work is to embed rids directly into query create edge from #12:1 to #12:10
.
@Query('select from (traverse out from ${id})')
List<Model> string(@RidElVar("id") String id)
@Query('create edge MyEdge from ${from} to ${to}')
void createEdge(@RidElVar("from") Object from, @RidElVar("to") Object to)
NOTE: from the first example it looks like @RidElVar
could be replaced with simple @ElVar
, but it's not: @ElVar
will complain on string parameter because it's not safe for injection. @RidElVar always validate that provided string is valid rid (e.g. #12:11) and fail if string is not (guards from injection).
You may use any supported type as variable type: ODocument, object model type, ORID, String, Vertex, Edge. By using exact type you can restrict method contract. Or use Object
to accept all possible values (simplify usage when multiple apis used).
Also, may be used for collections, arrays (or varargs). Such types will be inserted as "[rid1, rid2, rid3]" into query.
@Query("select from (traverse out from ${ids})")
public List doSmth(@RidElVar("ids") List<ODocument> ids)
@Query("select from (traverse out from ${ids})")
public List doSmth(@RidElVar("ids") Vertex... ids)
Objects inside collection may be of any supported type (even different objects).
Marks parameter as command variable.
In contrast to el vars, these variables are used during query execution:
For example,
@Query('select name from Model where name in $tst')
String[] findByName(@Var("tst") List tst);
Not the best example (could be easily rewritten with sql parameter), but it just demonstrates usage.
Annotates pagination parameters (of course, may be used separately).
@Query("select from Model")
List<Model> getAll(@Skip int skip, @Limit int limit);
Parameter type may be any Number type (Integer, Long etc)
See bundled Pagination
mixin as usage example.
Annotates parameter as fetch plan value. This is useful for universal queries to use different fetch plans with different calls.
@Query("select from Model")
List<Model> selectAll(@FetchPlan String plan);
Only String parameter type may be used. Default fetch plan may be specified:
@Query("select from Model")
List<Model> selectAll(@FetchPlan("*:0") String plan);
If null value provided as fetch plan and no default set, then no fetch plan will be set.
Marks parameter as command listener. Must be used together with @AsyncQuery or @LiveQuery (exact annotation defines which listener interfaces may be used).
Listener will be wrapped with an external transaction, so listener code could access orient connection instance, used for listener from guice (normal connection access).
Marks parameter as dynamic command parameters provider.
For positional parameters, parameter type must be List, array or vararg.
@Query("select from Model where name=? and nick=?")
List<ODocument> positionalList(@DynamicParams List<String> params)
For named parameters use Map.
@Query("select from Model where name=:name and nick=:nick")
List<ODocument> namedMap(@DynamicParams Map<String, String> params);
Dynamic parameters may be used with static definitions
@Query("select from Model where name=? and nick=?")
List<ODocument> mixPositional(String name, @DynamicParams String... params);
Dynamic parameters may be used when it's more comfortable (for any reason) to provide prepared parameters object instead of static parameters binding in method. And, of course, when number of parameters is not strict.
@Query('select from Model where ${cond}')
List<ODocument> findWhere(@ElVar("cond") String cond, @DynamicParams Object... params);
-
@Timeout
- defines query timeout and timeout strategy
Amend annotations may be used directly on method, on class (to apply for all methods) or on root repository type (to apply to all inherited mixins). Command amend methods doesn't affect delegate methods (only if you define command amend annotation directly on delegate method it will cause error, because obviously it's impropriate usage)
Sets command execution timeout and timeout strategy (throw exception or return empty result).
@Query("select from Model")
@Timeout(200)
List<Model> all()
If query will not execute in 200 milliseconds, exception will be thrown (by default exception timeout startegy used).
@Query("select from Model")
@Timeout(value = 200, strategy = OCommandContext.TIMEOUT_STRATEGY.RETURN)
List<Model> all()
Will return empty (or incomplete) result if query executes longer than 200 milliseconds.
Internally timeout set using OCommandRequest.setTimeout() method.