Начну с того, что появилась задача генерировать cypher-запросы по дата-классам на основе аннотаций @NodeEntity
, @RelationshipEntity
и @Relationship
и дополнительных параметров. Был написан генератор запросов, однако строка запроса вычислялась в рантайме и мы не могли использовать ее в качестве параметра существующей аннотации @Query
, поэтому была добавлена новая аннотация @CustomQuery
, обработчик которой использовал написанный генератор запросов.
Версии использованного ПО:
spring-data-neo4j — 5.0.9.RELEASE
neo4j — 3.4.6
neo4j-ogm — 3.1.4
Добавление кастомного генератора запросов в Spring Data Neo4j
Для добавления своего механизма генерации cypher-запросов для методов neo4j-репозиториев, помеченных, например, аннотацией @CustomQuery
необходимо создать класс CustomNeo4jRepositoryFactory extends Neo4jRepositoryFactory
и переопределить в нем метод getQueryLookupStrategy
, он должен возвращать новую стратегию поиска обработчиков query-аннотаций CustomGraphQueryLookupStrategy extends GraphQueryLookupStrategy
:
@Override
protected Optional<QueryLookupStrategy> getQueryLookupStrategy(QueryLookupStrategy.Key key, EvaluationContextProvider evaluationContextProvider) {
return Optional.of(new CustomGraphQueryLookupStrategy(session));
}
Также необходимо расширить стандартный класс Neo4jRepositoryFactoryBean
новым классом CustomNeo4jRepositoryFactoryBean
и переопределить метод createRepositoryFactory
. Он должен возвращать экземпляр новой фабрики репозиториев:
@Override
protected RepositoryFactorySupport createRepositoryFactory(Session session) {
return new CustomNeo4jRepositoryFactory(session);
}
Чтобы Spring Data Neo4j понял, какой бин фабрики репозиториев использовать, его необходимо явно указать в аннотации @EnableNeo4jRepositories
в конфигурации:
@EnableNeo4jRepositories(..., repositoryFactoryBeanClass = CustomNeo4jRepositoryFactoryBean.class)
Была введена дополнительная query-аннотация CustomQuery
. Если метод репозитория помечен такой аннотацией, то CustomGraphQueryLookupStrategy
в переопределенном методе resolveQuery
будет возвращать объект нашего запроса CustomGraphRepositoryQuery extends GraphRepositoryQuery
:
public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, ProjectionFactory factory, NamedQueries namedQueries) {
if (method.isAnnotationPresent(CustomQuery.class)) {
GraphQueryMethod queryMethod = new GraphQueryMethod(method, metadata, factory);
return new CustomGraphRepositoryQuery(queryMethod, session, method.getAnnotation(CustomQuery.class));
} else {
return super.resolveQuery(method, metadata, factory, namedQueries);
}
}
CustomGraphRepositoryQuery
реализует метод getQuery
, возвращающий объект Query
, его конструктор принимает cypher-запрос, который был построен в CustomGraphRepositoryQuery
на основе данных аннотации CustomQuery
и объекта метода Method
, помеченного такой аннотацией:
@Override
protected Query getQuery(Object[] parameters) {
return new Query(query, resolveParams(parameters));
}
Метод resolveParametres(Object[])
и используемый им getParameterValue(Object)
являются приватными в GraphRepositoryQuery
, поэтому они были просто скопированы в CustomGraphRepositoryQuery
(можно использовать рефлексию, это не повлияет на производительность, т.к. генерация запросов происходит до рантайма).
Заключение
Таким образом можно объявить свой механизм генерации cypher-запросов, если в этом есть необходимость.
В следующих статьях будет описан сам генератор запросов, его параметры, механизм работы, возникшие проблемы и их решение.