-
Notifications
You must be signed in to change notification settings - Fork 7
Repository mixins
DEPRECATED Documentation was moved into new docuemntation site
Mixins are reusable parts, which you can use in repositories.
Interfaces are perfect for writing mixins, because both interface and simple class can implement multiple interfaces and so can use any number of mixins.
Java generics plays major role in mixins: as you read before exact method return types are important for proper connection selection. Also, mixins plays parametrization role for commands or for delegates.
Generics resolution is implemented as separate lib generics-resolver.
Toc:
All commands support el variables and mixin class generic names may be used as variables:
public interface SelectMixin<T> {
@Query("select from ${T}")
List<T> selectAll();
}
Note that neither @Transactional nor @ProvidedBy annotations are not required on mixin, because mixin is not a repository itself, it simply interface.
Now when mixin will be used in repository, actual type could be recognized and used:
@Transactional
@ProvidedBy(DynamicSingletonProvider.class)
public interface ModelRepository extends SelectMixin<Model> {}
When method selectAll called from repository, generic resolved and used as variable "Model" in query and return type will be treated as List, allowing correctly select object connection (suppose its registered model type).
Depth of hierarchy doesn't matter for generics resolution. So if you want, you can compose few mixins into new one and use it in repositories, instead of implementing all mixins in each repository:
public interface RepositoryBaseMixin<T> extends Mixin1<T>, Mixin2<T> {}
Suppose you don't want to use object mapping and don't have mapped entities. You may still use generics for types resolution: create empty classes, named the same as your model scheme entities. Use such generics just for queries parametrization.
Imagine you have few different repositories for different entities.
Some of them have name
property.
Normally you will have to write select by name query in each repository (can't be moved to some base class, because not all entities contains name).
You can write generic mixin:
public interface NamedEntityMixin<T> {
@Query(query = "select from ${T} where name=?")
T findByName(String name);
}
Now some repositories could simply implement this interface. Such things could potentially greatly improve code reuse in repositories.
Delegates provide generalization mechanism for custom logic. Usually, such cases are handled by abstract classes (AbstrctDao or something like this).
Comparing to simple bean method call, delegate:
- Provides calling repository method context (selected connection, resolved generics etc)
- Adds support for annotation driven extensions (amend extensions)
- Applies result conversion
The simplest case is implementing base crud operations for repostiory as delegate mixin:
@Delegate(ObjectCrudDelegate.class)
public interface ObjectCrud<T> {
T get(String id);
T create();
}
@ProvidedBy(DynamicSingletonProvider.class)
public abstract class ObjectCrudDelegate<T> implements ObjectCrud<T> {
private final Provider<OObjectDatabaseTx> dbProvider;
@Override
public T get(final String id) {
return dbProvider.get().load(new ORecordId(id));
}
public T create(@Generic("T") final Class<T> type) {
return dbProvider.get().newInstance(type);
}
}
Now it could be used to easily apply common operation for repository:
@Transactional
@ProvidedBy(DynamicSingletonProvider.class)
public interface ModelRepository extends ObjectCrud<Model> {}
Predefined mixins:
- DocumentCrud - crud operations for document repositories
- ObjectCrud - crud operations for object repositories
- ObjectVertexCrud - object crud for using with vertex objects (annotated with @VertexType)
- EdgesSupport - general support for edge objects (annotated with @EdgeType)
- EdgeTypeSupport - support for exact edge object type
- Pagination - pagination support for object or document repositories
Provide basic crud operations for document connection. Usage:
@Transactional
@ProvidedBy(DynamicSingletonProvider.class)
public interface ModelRepository extends DocumentCrud<Model> {}
It relies on model class to resolve model type, but, as described above, even empty class may be used (just to define scheme class name).
Provide basic crud operations for object connection. Usage:
@Transactional
@ProvidedBy(DynamicSingletonProvider.class)
public interface ModelRepository extends ObjectCrud<Model> {}
You can use objectToDocument
and documentToObject
methods for conversion to/from documents.
Note: don't use it with vertex objects (annotated with @VertexType
). Use ObjectVertexCrud instead.
Provide basic crud operations for vertex objects: objects annotated with @VertexType
or simply extends V
in scheme. This mixin mixes Object and Graph apis to let you correctly work with graph vertexes through object api.
It has the same api as ObjectCrud
(internally they both extend the same BaseObjectCrud
mixin). It is important to use this specific version because it uses graph api for remove: if object api directly used, graph consistency is not checked and so edges leading to/from this vertex are not removed.
It also contains special methods for conversion between object and graph api: vertexToObject
and objectToVertex
.
@VertexType
public class Model {}
@Transactional
@ProvidedBy(DynamicSingletonProvider.class)
public interface ModelRepository extends ObjectVertexCrud<Model> {}
You can use it together with EdgesSupport or EdgeTypeSupport to add relevant edges operations into repository.
For example, suppose we want to always connect Model
objects with ModelConnection
edge type:
@EdgeType
public class ModelConnection {}
@Transactional
@ProvidedBy(DynamicSingletonProvider.class)
public interface ModelRepository extends ObjectVertexCrud<Model>,
EdgeTypeSupport<ModelConnection, Model, Model> {}
Now we can operate on objects and connect them with the same repository:
@Inject ModelRepository repository;
...
Model from = repository.save(new Model(..));
Model to = repository.save(new Model(..));
ModelConnection edge = repository.createEdge(from, to);
// we may update edge object properties (if it contains it)
edge.setComment("important connection");
repository.updateEdge(edge);
Provide basic operations for edge objects: objects annotated with @EdgeType
or simply extends E
in scheme. This mixin mixes Object and Graph apis to let you correctly work with graph edges through object api.
It is very convenient to use edge objects even if they have no properties: it's more type safe and you can use hierarchy of edge types for polymorphic queries.
For example, if you have BaseConnection
and SpecificConnection extends BaseConnection
then querying for BaseConnection
type edges will return everything and when required we can query only for SpecificConnection
to get more specific results (you can see a lot of examples in orient docs with querying edges by base class E
). This works the same way as with normal objects (vertexes) providing very powerful ability for design.
Mixin intended to be used as supplement to ObjectVertexCrud
(to add edge functions for vertex repository).
NOTE: EdgesSupport may be used directly (as bean). This is because its very generic itself.
To create new edge we always need two objects (from and to vertexes). You can use objects, vertexes, documents or simply string id's.
@Inject EdgesSupport edges;
...
MyEdgeType edge = edges.createEdge(MyEdgeType.class, from, to)
Here new edge of class MyEdgeType
is created between from and to vertexes. Api returns object instance of edge, which may be used for property updates or some other need.
If edge contains properties, you can create edge from object or document instance:
MyEdgeType edge = edges.createEdge(from, to , new MyEdgeType(...));
Orient graph api is hiddent, but you can always convert edge object to orient edge and back: use edgeToObject
and objectToEdge
methods.
In orient, edges are stored as normal objects (they just have special meaning). So you can operate on edge using any api (document, object and graph). But always remove edges with graph api to grant consistency.
Edge creation methods return created edge as object (it's an edge record loaded with object api). This is useful for edge properties updates:
MyEdgeType edge = edges.createEdge(MyEdgeType.class, form, to);
edge.setName("some name");
edges.updateEdge(edge);
Method updateEdge is generic and accepts not just object pojo but also OrientEdge instance.
You can find edge of type by nodes:
MyEdgeType edge = edges.findEdge(MyEdgeType.class, from, to)
Or ignoring direction:
MyEdgeType edge = edges.findEdgeBetween(MyEdgeType.class, node1, node2)
As with creation, object, document, vertex or rid may be used as node parameters.
NOTE: api returns only first edge! There may be other edges. For example, create method is not checking existing edge between nodes, so you can create many edges (even of the same class) between same nodes.
You can always make your own methods with sql (or maybe custom delegate logic) to implement more specific search cases.
Edge may be removed directly
edges.deleteEdge(edge);
Using object, document, orient edge or rid.
Or you can remove all edges of type (possibly more then one!) between nodes (but only in one direction!):
int cnt = edges.deleteEdge(MyEdgeType.class, from, to);
Very similar to EdgesSupprt
mixin, but for one concrete edge type. Also, you can restrict from/to types using generics.
It also supposed to be used to supplement ObjectVertexCrud
.
@VertexType
public class Model {}
@EdgeType
public class ModelConnection {}
@Transactional
@ProvidedBy(DynamicSingletonProvider.class)
public interface ModelRepository extends ObjectVertexCrud<Model>,
EdgeTypeSupport<ModelConnection, Model, Model> {}
It uses EdgesSupport
as bean internally and may serve as an example of how to implement more specific edge mixins.
Pagination
mixin provides simple pagination for your object entity or document (but document should have reference type, at least to specify schema type name (may be empty class)).
@Transactional
@ProvidedBy(DynamicSingletonProvider.class)
public interface MyEntityRepository extends ObjectCrud<MyEntity>, Pagination<MyEntity, MyEntity> {}
...
// return page
Page page = repository.getPage(1, 20);
Pagination provides 3 methods:
-
getPage
- receive composed page object -
getAll
- paginated data selection -
getCount
- count of all entities
Pagination implementation is a very good example mixin:
Base class PageSupport
is delegate (to PageSupportDelegate
), which incapsulates Page object logic.
Pagination
interface extends it (Pagination<M, R> extends PageSupport<R>
) and defines query methods (for actual data and entore count).
PageSupportDelegate use reference to repository to call these methods:
public Page getPage(@Repository final Pagination repository, final int page, final int pageSize)
Assuming Pagination interface will be always used and so delegate could be sure about repository type (implementing just PageSupport in repository would make no sense).