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