Начну с того, что появилась задача генерировать 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-запросов, если в этом есть необходимость.
В следующих статьях будет описан сам генератор запросов, его параметры, механизм работы, возникшие проблемы и их решение.
