夏邑好心情网站建设有限公司,梅州高铁,中石油七建设公司官网,网站里的聊天怎么做文章目录 前言集成使用定义实体配置定义Repository查询方法方式一#xff1a;Query方式二#xff1a;Cypher语法构建器方式三#xff1a;Example条件构建器方式四#xff1a;DSL语法 自定义方法自定义接口继承自定义接口实现自定义接口neo4jTemplateNeo4jClient 自定义抽象… 文章目录 前言集成使用定义实体配置定义Repository查询方法方式一Query方式二Cypher语法构建器方式三Example条件构建器方式四DSL语法 自定义方法自定义接口继承自定义接口实现自定义接口neo4jTemplateNeo4jClient 自定义抽象类执行与结果转换 前言
本篇主要是对neo4j的集成应用会给出普遍的用法但不是很详细如果需要详细的话每种方式都可以单独一篇说明但应用都是举一反三并没有必要都进行详解而且一些特殊的用法也举例了也给出了一个自定义方式的查询和结果转换虽然算不上完美但也是很简单的也希望大家有所收获。
集成
使用高版本的Spring data我boot版本2.7下面两个依赖随便引入一个就行我试过都是可以地 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-neo4j/artifactId/dependency
!--两者任选其一--dependencygroupIdorg.springframework.data/groupIdartifactIdspring-data-neo4j/artifactIdversion3.3.11/version/dependency使用
定义实体
我们先定义三个实体
job作业实体table表实体JobRelationship关系实体
Data
Node(Job)
public class Job {IdGeneratedValueprivate Long id;private String name;Propertyprivate String type;Relationship(dep)private ListTableRelationship tables;Relationship(dep)private ListJobRelationship jobs;
}
Data
Node
public class Table {IdGeneratedValueprivate Long id;private String name;private String type;Relationship(dep)private ListJob jobs;
}
Data
RelationshipProperties
public class JobRelationship {IdGeneratedValueprivate Long id;TargetNodeprivate Job job;
}
Data
RelationshipProperties
public class TableRelationship {IdGeneratedValueprivate Long id;TargetNodeprivate Table table;
}
上面三个实体的写法都一些不同这里需要注意下
Node标注的实体为数据库对象实体表示一个节点其value值就是标签如果不设置就是类名Id标注的属性是主键字段GeneratedValue主键字段必须设置它的一个生成方式如果是自己设置可以忽略Property标注的属性为数据库属性字段可以不写这个注解Node标注的实体中的属性默认为数据库属性字段同样其value值也是数据库属性字段也可以不写类似做了映射Relationship标注的属性为关系属性值为关系的标签值另一个值为关系的指向默认从当前节点向外指向类似关系型数据库的关系RelationshipProperties标注的类为关系实体是对Relationship类型的描述TargetNode标注的属性是关系中的目标节点
更加详细说明在spring Data Neo4j
配置
application.yml
spring:neo4j:uri: bolt://192.168.0.103:7687authentication:username: neo4jpassword: password定义Repository
这里使用的是spring jpa的开发方式它提供了增删改查的基础功能定义如下
Repository
public interface TableRepository extends Neo4jRepositoryTable, Long {
}Repository
public interface JobRepository extends Neo4jRepositoryJob, Long {/*** 根据名称查找*/Job findByName(String name);/*** 节点name是否包含指定的值*/boolean existsByNameContaining(String name);/*** 强制删除节点*/Long deleteByName(String name);
}
那这个JPA的方式的话可以通过关键字和属性名定义查询方法如上面的的findByName就可以实现通过名称查询不需要写实现这里只说这么多详细的可以看Spring Data JPA - Reference Documentation
查询方法
spring data提供的基础增删改查其实在在业务中是不够用的但是它额外的提供了一些条件构建器使得我们可以借助一些快捷的API进行查询条件构造以适应这些复杂查询也就是动态语法。
方式一Query
说起JPA自定义查询好像大多是用Query注解进行标注的比如 Query(match(a:Job{name:$name}) return a)Job findByName2(Param(name) String name);Query(match(a:Job{name: $0) return a)ListJob findByName3(Param(name) String name);这样的方式很简单是不是但是存在的问题是语句中的Job,name都是对应数据库里的标签和字段所以这就和mybatis xml里的方式一样如果数据库字段变更或标签变更需要全局替换。
方式二Cypher语法构建器
Spring data提供了针对Cypher语法的构建器可以让我们对复杂cypher的语法构建
示例一 match(a:Job) where a.name‘liry’ return a order by a.id limit 1 // match(a:Job) where a.nameliry return a order by a.id limit 1// 创建节点对象标签为 Job别名 a - (a:Job)
Node temp Cypher.node(Job).named(a);
// 构建查询声明对象
// 创建match查询- match(a:Job)
ResultStatement statement Cypher.match(temp)// 添加条件- a.name$name.where(temp.property(name).isEqualTo(Cypher.anonParameter(job.getName())))// 返回对象return 别名 - return a.returning(temp.getRequiredSymbolicName())// 排序以属性id正序.orderBy(temp.property(id))// 限制数量1.limit(1)// 构建为语法对象.build();示例二
// merge(a:Job{name:$name}) set a.type$type return a// 构建参数
MapString, Object pro new HashMap();
pro.put(name, job.getName());
// 创建节点对象标签Job别名a并且设置参数 - (a:Job{name:$name})
Node temp Cypher.node(Job).named(a).withProperties(pro);
// 创建merge查询 - merge(a:Job{name:$name})
ResultStatement statement Cypher.merge(temp)// 设置值- a.type$type.set(temp.property(type),Cypher.anonParameter(job.getType()))// 返回对象return别名 - return a.returning(temp.getRequiredSymbolicName())// 构建声明对象.build();示例三
// MATCH (a:Job {name: $pcdsl01})-[r*..2]-(b:Job) RETURN a,b,r// 创建两个节点给都死标签Job别名分别是a,ba节点关联属性name
Node node Cypher.node(Job).named(a).withProperties(name, Cypher.anonParameter(name));
Node node2 Cypher.node(Job).named(b);// 创建a-r-b的关系
Relationship r node.relationshipBetween(node2).named(r).max(length);
// a-r-b
// Relationship r node.relationshipTo(node2).named(r).max(length);
// a-r-b
// Relationship r node.relationshipFrom(node2).named(r).max(length);
// 创建match查询
ResultStatement statement Cypher.match(r)// 返回对象.returning(a,b,r)// 构建声明对象.build();这里的withProperties我又用了另一种方式.withProperties(name, Cypher.anonParameter(name));上面我传的是一个map它这里其实很巧妙的做了一个适配只要你以key value那么便是偶数个参数他会自动帮你绑定或者传map也行。
方式三Example条件构建器
Spring Data默认的Neo4jRepository是继承 了QueryByExampleExecutor如下 那么它所能使用的方法是这些 其应用示例一
// Neo4jRepository默认继承了example功能Job job new Job();job.setName(liry);ExampleMatcher exampleMatcher ExampleMatcher.matching().withMatcher(na, ExampleMatcher.GenericPropertyMatchers.endsWith());// 根据示例匹配器规则进行条件重组 查询ListJob all1 jobRepository.findAll(Example.of(job, exampleMatcher));// 精确查询ListJob all2 jobRepository.findAll(Example.of(job));ListJob all3 jobRepository.findAll(Example.of(job), Sort.by(Sort.Order.desc(id)));PageJob all4 jobRepository.findAll(Example.of(job), PageRequest.of(1, 10));示例二
流处理查询 Job job new Job();job.setJobName(name);// 查询一个一个值Job one jobRepository.findBy(Example.of(job), query - query.oneValue());// lambda简化
// Job one jobRepository.findBy(Example.of(job), FluentQuery.FetchableFluentQuery::oneValue);// 查询countLong count jobRepository.findBy(Example.of(job), query - query.count());// lambda简化
// Long count jobRepository.findBy(Example.of(job), FluentQuery.FetchableFluentQuery::count);// 是否存在Boolean exist jobRepository.findBy(Example.of(job), query - query.exists());// lambda简化
// Boolean exist jobRepository.findBy(Example.of(job), FluentQuery.FetchableFluentQuery::exists);// 查询全部ListJob list jobRepository.findBy(Example.of(job), query - query.all());// lambda简化
// ListJob list jobRepository.findBy(Example.of(job), FluentQuery.FetchableFluentQuery::all);// 查询2个ListJob list2 jobRepository.findBy(Example.of(job),query - query.stream().limit(2).collect(Collectors.toList()));// 查询并进行处理ListObject list3 jobRepository.findBy(Example.of(job),query - query.stream().peek(d - d.setName(d.getName() 1)).collect(Collectors.toList()));// 查询并排序ListJob list4 jobRepository.findBy(Example.of(job),query - query.sortBy(Sort.by(Sort.Order.asc(id)))).all();上面这个流处理查询都可以用lambda表达式来处理。
注意使用example查询时不要有关联关系不然他会报错即不要有Relationship这个注解
方式四DSL语法 这个是借助DSL框架实现的语法构建添加DSL依赖 dependencygroupIdcom.querydsl/groupIdartifactIdquerydsl-jpa/artifactId/dependencydependencygroupIdcom.querydsl/groupIdartifactIdquerydsl-apt/artifactId/dependency添加插件 plugingroupIdcom.mysema.maven/groupIdartifactIdapt-maven-plugin/artifactIdversion1.1.3/versionexecutionsexecutiongoalsgoalprocess/goal/goalsconfigurationoutputDirectorytarget/generated-sources/java/outputDirectoryprocessorcom.querydsl.apt.jpa.JPAAnnotationProcessor/processor/configuration/execution/executions
/plugin实体类上需要加上Entity
但是只引入neo4j的依赖是不行的还需要jpa的依赖因为JPA依赖数据库所以可以先引入生成之后再删除掉。
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-data-jpa/artifactId
/dependency
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-jdbc/artifactId
/dependency
dependencygroupIdmysql/groupIdartifactIdmysql-connector-java/artifactIdversion8.0.28/version
/dependency然后maven compile编译一下会在target/generated-sources/java目录下找到生成的Q类把它复制到我们实体目录下 继承接口QuerydslPredicateExecutor 它的使用方式和Example一样。 Repository
public interface JobRepository extends Neo4jRepositoryJob, Long , QuerydslPredicateExecutorJob{
}这个接口下的功能都可以使用
示例一 QJob job QJob.job;
// name$nameIterableJob all jobRepository.findAll(job.name.eq(name));// name like $name and type $typeBooleanExpression exp job.name.like(name).and(job.type.eq(test));boolean exists jobRepository.exists(exp);IterableJob all1 jobRepository.findAll(exp);
// 排序IterableJob id jobRepository.findAll(exp, Sort.by(Sort.Order.desc(id)));
// 分月PageJob all2 jobRepository.findAll(exp, PageRequest.of(1, 10));// in查询 exp job.name.in(liryc, xx);jobRepository.findAll(exp);示例二 // 查询一个一个值Job one jobRepository.findBy(exp, query - query.oneValue());// lambda简化// Job one jobRepository.findBy(exp, FluentQuery.FetchableFluentQuery::oneValue);// 查询countLong count jobRepository.findBy(exp, query - query.count());// lambda简化// Long count jobRepository.findBy(exp, FluentQuery.FetchableFluentQuery::count);// 是否存在Boolean exist jobRepository.findBy(exp, query - query.exists());// lambda简化// Boolean exist jobRepository.findBy(exp, FluentQuery.FetchableFluentQuery::exists);// 查询全部ListJob list jobRepository.findBy(exp, query - query.all());// lambda简化// ListJob list jobRepository.findBy(exp, FluentQuery.FetchableFluentQuery::all);// 查询2个ListJob list2 jobRepository.findBy(exp,query - query.stream().limit(2).collect(Collectors.toList()));// 查询并进行处理ListObject list3 jobRepository.findBy(exp,query - query.stream().peek(d - d.setName(d.getName() 1)).collect(Collectors.toList()));// 查询并排序ListJob list4 jobRepository.findBy(exp,query - query.sortBy(Sort.by(Sort.Order.asc(id)))).all();自定义方法
上面都是spring data自带的方法已经可以实现大部分的复杂查询了当时在业务层的时候我们一般不会把这些数据访问放在业务层中这些都应该是置于底层作为最基本的数据能力不该带入业务并且也不该把数据访问的逻辑赤裸的放到业务层中那么我们就会需要子党员方法。
自定义接口
public interface JobCustomRepository {/*** 查询所有*/Object selectAll();/*** 保持节点信息会先判断是否存在*/Job saveCondition(Job job);/*** 创建或合并节点*/Job saveMerge(Job job);
}继承自定义接口
Repository
public interface JobRepository extends Neo4jRepositoryJob, Long , JobCustomRepository {}实现自定义接口
Component(jobCustomRepository)
public class JobCustomRepositoryImpl implements JobCustomRepository {
}如此我们就可以在业务层中这样
// 这里引入的接口就具备了原本spring的能力和你自定义的能力
Autowiredprivate JobRepository jobRepository;
neo4jTemplate
在自定义中可以引入这个作为执行类这个类实现Neo4jOperationstemplate它是支持直接写cypher语句的比如下面这个 如果需要参数分离的话参数占位用$那么cypher语句应该是这样
match(a) where a.name$name return a示例 // 方式一template执行cql字符串String cql String.format(merge(a:Job{name:%s}) set a.time2 return a, job.getName());ListJob all neo4jTemplate.findAll(cql, Job.class);// 方式二cypher语句cql String.format(merge(a:Job{name:%s}) set a.time$time return a, job.getName());MapString, Object param new HashMap();param.put(time, 1);all neo4jTemplate.findAll(cql, param, Job.class);// 方式三Cypher构建声明对象MapString, Object pro new HashMap();pro.put(name, job.getName());Node temp Cypher.node(Job).named(a).withProperties(pro);ResultStatement statement Cypher.merge(temp).set(temp.property(type), Cypher.anonParameter(job.getType())).returning(temp.getRequiredSymbolicName()).build();ListJob all1 neo4jTemplate.findAll(statement, statement.getParameters(), Job.class);Neo4jClient
除了neo4jTemplate还可以用neo4jClient
示例 String cpl match(a) where a.name contains liry return a limit 1;// 执行cypherResult run client.getQueryRunner().run(cpl);// 结果获取的方式// **** 注意下面的几种方式中只能选用一种在结果读取后就不能在读取了 ****// 方式一迭代器while (run.hasNext()) {Record d run.next();// 这几个的结构差不多ListPairString, Value fields d.fields();ListValue values d.values();MapString, Object stringObjectMap d.asMap();System.out.println(d);}// 方式二lambda// 或者直接获取列表ListRecord list run.list();ListRecord dd list.stream().map(d - {ListPairString, Value fields d.fields();ListValue values d.values();MapString, Object stringObjectMap d.asMap();return d;}).collect(Collectors.toList());// 方式三函数式接口ListObject result run.list(map - {return map;});这里有需要注意的地方就是在run执行后只能获取一次结果如上代码所示不论用那种方式进行结果的获取都只能获取一次
自定义抽象类执行与结果转换
但是很多人就会觉得这个方式需要自己处理结果集确实它的这个结果集不是很友好所以我这里也提供一个结果处理案例
我这里就直接贴已经完成的代码了可以直接复制使用同时这个类也提供了执行cypher语句的方法和JPA一样。
package com.liry.neo.repository;import com.liry.neo.entity.RelationshipInfo;
import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Supplier;
import org.neo4j.driver.internal.types.InternalTypeSystem;
import org.neo4j.driver.internal.value.NodeValue;
import org.neo4j.driver.types.MapAccessor;
import org.neo4j.driver.types.Relationship;
import org.neo4j.driver.types.TypeSystem;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.data.neo4j.core.Neo4jOperations;
import org.springframework.data.neo4j.core.Neo4jTemplate;
import org.springframework.data.neo4j.core.PreparedQuery;
import org.springframework.data.neo4j.core.mapping.EntityInstanceWithSource;
import org.springframework.data.neo4j.core.mapping.Neo4jMappingContext;
import org.springframework.lang.Nullable;
import org.springframework.util.Assert;/*** author ALI* since 2023/10/25*/
public abstract class AbstractCustomRepository implements ApplicationContextAware {protected ApplicationContext applicationContext;private Neo4jMappingContext neo4jMappingContext;private Neo4jTemplate neo4jTemplate;private static T SupplierBiFunctionTypeSystem, MapAccessor, ? getAndDecorateMappingFunction(Neo4jMappingContext mappingContext, ClassT domainType, Nullable Class? resultType) {Assert.notNull(mappingContext.getPersistentEntity(domainType), Cannot get or create persistent entity.);return () - {BiFunctionTypeSystem, MapAccessor, ? mappingFunction mappingContext.getRequiredMappingFunctionFor(domainType);if (resultType ! null domainType ! resultType !resultType.isInterface()) {mappingFunction EntityInstanceWithSource.decorateMappingFunction(mappingFunction);}return mappingFunction;};}Overridepublic void setApplicationContext(ApplicationContext applicationContext) throws BeansException {this.applicationContext applicationContext;}private Neo4jMappingContext getNeo4jMappingContext() {if (neo4jMappingContext null) {neo4jMappingContext applicationContext.getBean(Neo4jMappingContext.class);}return neo4jMappingContext;}private Neo4jTemplate getNeo4jTemplate() {if (neo4jTemplate null) {neo4jTemplate applicationContext.getBean(Neo4jTemplate.class);}return neo4jTemplate;}/*** 执行cypher查询** param domainType 实体类型* param cypherStatement cypher语句* param parameters 参数*/protected T Neo4jOperations.ExecutableQueryT createExecutableQuery(ClassT domainType, String cypherStatement,MapString, Object parameters) {return createExecutableQuery(domainType, domainType, cypherStatement, parameters);}/*** 执行cypher查询** param domainType 实体类型* param resultType 返回的结果类型* param cypherStatement cypher语句* param parameters 参数*/protected T Neo4jOperations.ExecutableQueryT createExecutableQuery(Class? domainType, ClassT resultType, String cypherStatement,MapString, Object parameters) {PreparedQuery.OptionalBuildStepsT step PreparedQuery.queryFor(resultType).withCypherQuery(cypherStatement).withParameters(parameters);// 基本类型转换if (!Number.class.isAssignableFrom(resultType) !Boolean.class.isAssignableFrom(resultType) !String.class.isAssignableFrom(resultType)) {SupplierBiFunctionTypeSystem, MapAccessor, ? mappingFunction getAndDecorateMappingFunction(getNeo4jMappingContext(),domainType,resultType);step.usingMappingFunction(mappingFunction);}return getNeo4jTemplate().toExecutableQuery(step.build());}protected T T mapping(ClassT domainType, NodeValue node) {Object apply getMappingFunction(domainType).get().apply(InternalTypeSystem.TYPE_SYSTEM, node);return (T) ((EntityInstanceWithSource) apply).getEntityInstance();}protected RelationshipInfo mapping(Relationship node) {RelationshipInfo result new RelationshipInfo();result.setStart(node.startNodeId());result.setEnd(node.endNodeId());result.setType(node.type());result.setId(node.id());return result;}/*** 结果映射** param domainType 实体类型也是结果类型* return 映射方法*/protected T SupplierBiFunctionTypeSystem, MapAccessor, ? getMappingFunction(ClassT domainType) {return () - {BiFunctionTypeSystem, MapAccessor, T mappingFunction getNeo4jMappingContext().getRequiredMappingFunctionFor(domainType);return EntityInstanceWithSource.decorateMappingFunction(mappingFunction);};}
}
之后我们自定义的Repository就要继承这个抽象类以达到可以直接使用的功能如下
Component(jobCustomRepository)
public class JobCustomRepositoryImpl extends AbstractCustomRepository implements JobCustomRepository {Autowiredprivate Neo4jTemplate neo4jTemplate;
}再定义一个关系实体
Data
public class RelationshipDto {private Long id;private Long start;private Long end;private String type;
}
然后我们查询并转换如下
String cpl match(a:Job)-[r*]-(b:Job) return a,b,r;// 执行cypher
Result run client.getQueryRunner().run(cpl);// 结果接受容器
SetObject nodes new HashSet();
SetObject relationships new HashSet();
// 结果转换
ListRecord list run.list();
list.forEach(d - {// cypher中别名ab是表示节点那么这里就取 ab的值转换为实体nodes.add(mapping(Job.class, (NodeValue) d.get(a)));nodes.add(mapping(Job.class, (NodeValue)d.get(b)));// 然后获取关系Value r d.get(r);Value a d.get(a);ListObject reList r.asList().stream().map(rd - {System.out.println(rd);return mapping((org.neo4j.driver.types.Relationship) rd);}).collect(Collectors.toList());relationships.addAll(reList);
});