:::tip
SpringBoot整合Neo4j,通过自定义注解@SyncNeo4j和AOP切面增强,以及对应的SyncStrategy实现接口增删改导入的时候自动落库关系型数据库和Neo4j库
:::
1. 创建项目安装依赖
- pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.17</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-neo4j</artifactId>
</dependency>
</dependencies>
- application.yml
server:
port: 8888
spring:
neo4j:
uri: bolt://localhost:7687
authentication:
username: neo4j
password: neo4j123
project:
neo4j:
sync:
enabled: true
sync-on-startup: true
2. 相关配置
1. 创建节点 @Node
@Data
@Node(labels = "Person")
public class Person implements Serializable,Neo4jBaseNode {
// Neo4j节点的主键
@Id
@GeneratedValue
private Long id;
// Neo4j节点的属性
@Property
private String name;
}
2.创建Repository
import org.springframework.data.neo4j.repository.query.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
@Repository
public interface PersonRepository extends Neo4jRepository<Person, String> {
@Query("MATCH (p:Person) DETACH DELETE p")
void detachDeleteAll();
@Query("MATCH (p: Person{id: $id}-[r]-() DELETE r")
void clearRelations(@Param("id") String id);
@Query("MATCH (c: Company{id: $companyId}),(p:Person:{id: $personId}) MERGE(c)-[:WORKS_AT]->(p)")
void addCmpPersonRelation(@Param("companyId") String companyId, @Param("personId") String personId);
}
3. 自定义注解实现同步
1. 自定义注解@SyncNeo4j
import net.lesscoding.enums.Neo4jOperateType;
import net.lesscoding.strategy.neo4j.Neo4jSyncStrategy;
import java.lang.annotation.*;
/**
*
* @author eleven
* @since 2026/7/8 15:19
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SyncNeo4j {
/**
* 操作类型
* @return {@link Neo4jOperateType} 操作类型
*/
Neo4jOperateType type();
/**
* 指定当前实体类对应的同步策略类
* @return {@link Class}< ? extends {@link Neo4jSyncStrategy}>}
*/
Class<? extends Neo4jSyncStrategy> strategy();
/**
* 参数索引
* @return
*/
int paramIndex() default 0;
}
2. Neo4j操作类型
/**
*
* @author eleven
* @since 2026/7/8 15:21
*/
public enum Neo4jOperateType {
CREATE,
UPDATE,
DELETE,
DELETE_BATCH,
DETACH_DELETE,
CREATE_BATCH,
UPDATE_BATCH
;
}
3. AOP增强类
这个
@SyncNeo4j可以添加到方法上,然后通过这个AOP切面增强自动同步到Neo4j数据库, 使用详情可以看后续的示例
import net.lesscoding.annotation.neo4j.SyncNeo4j;
import net.lesscoding.config.ProjectConfig;
import net.lesscoding.strategy.neo4j.Neo4jStrategyContext;
import net.lesscoding.strategy.neo4j.Neo4jSyncStrategy;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.List;
/**
*
* @author eleven
* @since 2026/7/8 15:35
*/
@Order(16)
@Slf4j
@Aspect
@Component
@RequiredArgsConstructor
public class SyncNeo4jAspect {
private final Neo4jStrategyContext strategyContext;
private final ProjectConfig projectConfig;
@SuppressWarnings("unchecked")
@Around("@annotation(syncNeo4j)")
public Object around(ProceedingJoinPoint point, SyncNeo4j syncNeo4j) throws Throwable {
Object proceed = point.proceed();
if (projectConfig.getNeo4j().isEnable()) {
Object[] args = point.getArgs();
Class<? extends Neo4jSyncStrategy> strategyCls = syncNeo4j.strategy();
Neo4jSyncStrategy strategy = strategyContext.getStrategy(strategyCls);
log.info("执行Neo4j同步操作 类型: {} 参数:{}",syncNeo4j.type(), args[syncNeo4j.paramIndex()]);
switch (syncNeo4j.type()) {
case CREATE:
strategy.save(strategy.extractEntity(args[syncNeo4j.paramIndex()]));
break;
case UPDATE:
strategy.update(strategy.extractEntity(args[syncNeo4j.paramIndex()]));
break;
case DELETE:
strategy.delete(String.valueOf(args[syncNeo4j.paramIndex()]));
break;
case DETACH_DELETE:
strategy.detachDelete(String.valueOf(args[syncNeo4j.paramIndex()]));
break;
case CREATE_BATCH:
strategy.createBatch((List<?>) args[syncNeo4j.paramIndex()]);
break;
case UPDATE_BATCH:
strategy.updateBatch((List<?>) args[syncNeo4j.paramIndex()]);
break;
case DELETE_BATCH:
strategy.deleteBatch((List<?>) args[syncNeo4j.paramIndex()]);
break;
default:
throw new IllegalArgumentException("Unsupported SyncNeo4j type: " + syncNeo4j.type());
}
}
return proceed;
}
}
4. 同步策略类
1. 同步策略接口
import java.util.List;
/**
* Neo4j 节点同步策略接口
* <p>
* 统一管理节点的增删改与关系重建。
* 全量同步(Neo4jInitRunner)和增量同步(切面触发 update)均复用
* {@link #clearRelations} / {@link #rebuildRelations},避免两处维护。
* </p>
*
* @param <T> DB 实体类型
* @param <N> Neo4j Node 类型
* @author eleven
* @since 2026/7/8 15:23
*/
public interface Neo4jSyncStrategy<T, N> {
/**
* 返回当前策略处理的 DB 实体类型,用于 {@link Neo4jStrategyContext} 索引。
*/
Class<T> getEntityClass();
N save(T dbEntity);
/**
* 增量更新:先保存节点属性,再清除旧关系,最后从 DB 查最新数据重建关系
*/
N update(T dbEntity);
Boolean delete(String dbId);
Boolean detachDelete(String dbId);
/**
* 清空该类型的所有 Neo4j 节点及关系(全量同步专用)。
*/
void detachDeleteAll();
List<N> createBatch(List<T> dbEntities);
List<N> updateBatch(List<T> dbEntities);
/**
* 批量删除 Neo4j 节点
* <p>默认实现逐条调用 {@link #delete},对于能批量操作的 Repository 应重写此方法。</p>
*
* @param dbIds DB 实体 ID 列表
* @return 删除是否成功
*/
default Boolean deleteBatch(List<String> dbIds) {
if (dbIds != null) {
dbIds.forEach(this::delete);
}
return true;
}
N convertDbEntityToNeo4jNode(T dbEntity);
/**
* 清除该节点的所有关系(入边 + 出边)
* <p>全量同步和增量更新均复用此方法</p>
*
* @param id Neo4j 节点 ID
*/
void clearRelations(String id);
/**
* 从 DB 查最新关联数据,重建该节点的所有关系。
* <p>
* 全量同步和增量更新均复用此方法。
* 各策略实现时应完全对齐 Neo4jInitRunner 中的对应关系逻辑。
* </p>
*
* @param dbEntity DB 实体(含最新字段值)
*/
void rebuildRelations(T dbEntity);
/**
* 从 DB 加载全量实体列表(全量同步专用)。
*/
List<T> loadEntities();
/**
* 批量重建节点(全量同步专用)。
* <p>将实体列表转换为 Neo4j Node 并批量保存。</p>
*
* @param dbEntities DB 实体列表
*/
void rebuildNodes(List<T> dbEntities);
/**
* 批量重建关系(全量同步专用)。
* <p>
* 默认实现逐条调用 {@link #rebuildRelations}。
* 对于需要查关联表的策略,应重写此方法:先全量加载关联数据,
* 再批量 MERGE,避免 N+1 查询。
* </p>
*
* @param dbEntities DB 实体列表
*/
default void rebuildRelationsBatch(List<T> dbEntities) {
if (dbEntities != null) {
dbEntities.forEach(this::rebuildRelations);
}
}
@SuppressWarnings("unchecked")
default T extractEntity(Object args) {
return (T) args;
}
}
2. 策略注入类
这个类可以将
Neo4jSyncStrategy所有的子类都注入进入,我们使用的时候只需要注入当前这个类,通过getStrategy(Class)方法就能够获取到对应的策略类了
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
/**
* 策略上下文,按实体类型索引所有 {@link Neo4jSyncStrategy} 实现。
* <p>
* Spring 自动注入全部策略实例,Runner 和切面通过本类获取策略进行批量操作。
* </p>
*
* @author eleven
* @since 2026/7/18
*/
@Component
public class Neo4jStrategyContext {
private final Map<Class<?>, Neo4jSyncStrategy<?, ?>> strategyCache;
private final List<Neo4jSyncStrategy<?, ?>> allStrategies;
@Autowired
public Neo4jStrategyContext(List<Neo4jSyncStrategy<?, ?>> strategies) {
this.allStrategies = Collections.unmodifiableList(strategies);
this.strategyCache = strategies.stream()
.collect(Collectors.toMap(Neo4jSyncStrategy::getClass, s -> s));
}
/**
* 按 DB 实体类型获取对应策略。
*/
@SuppressWarnings("unchecked")
public <T> Neo4jSyncStrategy<T, ?> getStrategy(Class<T> entityClass) {
return (Neo4jSyncStrategy<T, ?>) strategyCache.get(entityClass);
}
/**
* 返回所有策略(按 Spring 注入顺序),用于全量同步的批量遍历。
*/
public List<Neo4jSyncStrategy<?, ?>> getAllStrategies() {
return allStrategies;
}
}
3. 具体的策略实现类
import net.lesscoding.domain.entity.DbPerson;
import net.lesscoding.mapper.DbPersonMapper;
import net.lesscoding.neo4j.entity.Neo4jPerson;
import net.lesscoding.neo4j.repository.Neo4jPersonRepository;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.stream.Collectors;
/**
* 合同节点同步策略
* @author eleven
* @since 2026/7/17
*/
@Slf4j
@Component
@RequiredArgsConstructor
public class Neo4jPersonSyncStrategy implements Neo4jSyncStrategy<DbPerson, Neo4jPerson> {
private final Neo4jPersonRepository Neo4jPersonRepository;
private final DbPersonMapper personMapper;
@Override
public Neo4jPerson save(DbPerson dbPerson) {
return Neo4jPersonRepository.save(convertDbEntityToNeo4jNode(dbPerson));
}
@Override
public Neo4jPerson update(DbPerson dbPerson) {
Neo4jPerson node = save(dbPerson);
clearRelations(dbPerson.getId());
rebuildRelations(dbPerson);
return node;
}
@Override
public void clearRelations(String id) {
Neo4jPersonRepository.clearRelations(id);
}
@Override
public void rebuildRelations(DbPerson entity) {
// todo 换成自己的节点对应的关系
if (StrUtil.isNotBlank(entity.getCompanyId())) {
Neo4jPersonRepository.addCompanyRelation(entity.getCompanyId(), entity.getId());
}
}
@Override
public Boolean delete(String dbId) {
Neo4jPersonRepository.deleteById(dbId);
return true;
}
@Override
public Boolean detachDelete(String dbId) {
Neo4jPersonRepository.detachDeleteAll();
return true;
}
@Override
public void detachDeleteAll() {
Neo4jPersonRepository.detachDeleteAll();
}
@Override
public List<Neo4jPerson> createBatch(List<DbPerson> dbEntities) {
return Neo4jPersonRepository.saveAll(dbEntities.stream()
.map(this::convertDbEntityToNeo4jNode)
.collect(Collectors.toList()));
}
@Override
public List<Neo4jPerson> updateBatch(List<DbPerson> dbEntities) {
return createBatch(dbEntities);
}
@Override
public Class<DbPerson> getEntityClass() {
return DbPerson.class;
}
@Override
public List<DbPerson> loadEntities() {
return personMapper.selectList(
new LambdaQueryWrapper<DbPerson>().eq(DbPerson::getDeleted, "0"));
}
@Override
public void rebuildNodes(List<DbPerson> dbEntities) {
Neo4jPersonRepository.saveAll(dbEntities.stream()
.map(this::convertDbEntityToNeo4jNode)
.collect(Collectors.toList()));
}
@Override
public Neo4jPerson convertDbEntityToNeo4jNode(DbPerson dbPerson) {
return BeanUtil.copyProperties(dbPerson, Neo4jPerson.class);
}
}
5. Neo4j基础节点
所有的Neo4j节点都需要实现这个基础Node,这样从Neo4j查出来结果的时候,可以通过
Neo4jBaseNode.form(Class a)转换成对应的实体Neo4jNode
import org.neo4j.driver.types.Node;
import org.springframework.beans.BeanWrapperImpl;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AssignableTypeFilter;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Neo4j 节点实体基类接口
* 提供 label → 实体类 的自动注册与 Neo4j 原生 Node 到实体类的转换能力
*
* @author eleven
*/
public interface Neo4jBaseNode {
/** 实体类扫描包路径 */
String SCAN_PACKAGE = "net.lesscoding.neo4j.entity";
/** label → 实体类 注册表(DCL 懒加载,首次 from() 调用时自动填充) */
Map<String, Class<? extends Neo4jBaseNode>> REGISTRY = new ConcurrentHashMap<>();
/**
* 根据 Neo4j 原生 Node 的第一个 label 查找对应实体类并实例化。
* 首次调用时自动扫描 SCAN_PACKAGE 下所有 Neo4jBaseNode 实现类并注册。
*
* @param driverNode Neo4j Driver 原生 Node
* @param <T> 实体类型
* @return 转换后的实体对象,label 未注册时返回 null
*/
@SuppressWarnings("unchecked")
static <T extends Neo4jBaseNode> T from(Node driverNode) {
if (REGISTRY.isEmpty()) {
synchronized (REGISTRY) {
if (REGISTRY.isEmpty()) {
scan(SCAN_PACKAGE);
}
}
}
// 遍历所有 label,优先匹配非 OsRelation 的具体子类(Database / Middleware 等)
Class<? extends Neo4jBaseNode> clazz = null;
for (String label : driverNode.labels()) {
Class<? extends Neo4jBaseNode> candidate = REGISTRY.get(label);
if (candidate != null) {
clazz = candidate;
if (candidate != Neo4jOsRelation.class) {
break;
}
}
}
if (clazz == null) {
return null;
}
return (T) mapToEntity(driverNode, clazz);
}
/**
* 手动扫描指定包路径,将 Neo4jBaseNode 实现类注册到 REGISTRY。
* 一般在 from() 中自动调用,也可在应用启动时显式调用提前预热。
*/
@SuppressWarnings("unchecked")
static void scan(String basePackage) {
ClassPathScanningCandidateComponentProvider scanner =
new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AssignableTypeFilter(Neo4jBaseNode.class));
for (BeanDefinition bd : scanner.findCandidateComponents(basePackage)) {
try {
Class<?> clazz = Class.forName(bd.getBeanClassName());
if (!Neo4jBaseNode.class.isAssignableFrom(clazz) || clazz.isInterface()) {
continue;
}
org.springframework.data.neo4j.core.schema.Node nodeAnno =
clazz.getAnnotation(org.springframework.data.neo4j.core.schema.Node.class);
if (nodeAnno != null && nodeAnno.labels().length > 0) {
REGISTRY.put(nodeAnno.labels()[0], (Class<? extends Neo4jBaseNode>) clazz);
}
} catch (ClassNotFoundException ignored) {
}
}
}
/**
* 将 Neo4j 原生 Node 的属性映射到实体类字段。
* 使用 BeanWrapperImpl 自动处理类型转换(String / BigDecimal / LocalDate 等)。
*/
static <T extends Neo4jBaseNode> T mapToEntity(Node driverNode, Class<T> clazz) {
try {
T instance = clazz.getDeclaredConstructor().newInstance();
BeanWrapperImpl wrapper = new BeanWrapperImpl(instance);
Map<String, Object> props = driverNode.asMap();
for (Map.Entry<String, Object> entry : props.entrySet()) {
if (wrapper.isWritableProperty(entry.getKey())) {
wrapper.setPropertyValue(entry.getKey(), entry.getValue());
}
}
return instance;
} catch (Exception e) {
throw new RuntimeException("Failed to map Node to " + clazz.getSimpleName(), e);
}
}
}
6. 启动重建所有节点和关系
通过实现
CommandLineRunner或者ApplicationRunner接口,使用ExecutorService在项目启动后异步执行重建所有节点和关系的逻辑
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 启动任务专用线程池,避免 Runner 阻塞主线程启动
*
* @author eleven
*/
@Configuration
public class TaskThreadPool {
@Bean(name = "taskRunnerExecutor", destroyMethod = "shutdown")
public ExecutorService taskRunnerExecutor() {
return new ThreadPoolExecutor(
2, 4,
60L, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(),
r -> new Thread(r, "task-runner-" + r.hashCode())
);
}
}
import net.lesscoding.neo4j.entity.Neo4jBaseNode;
import net.lesscoding.neo4j.entity.Neo4jNetworkRelation;
import net.lesscoding.neo4j.entity.Neo4jOsRelation;
import net.lesscoding.neo4j.entity.Neo4jSystemRelation;
import net.lesscoding.neo4j.repository.Neo4jOsRelationRepository;
import net.lesscoding.strategy.neo4j.Neo4jStrategyContext;
import net.lesscoding.strategy.neo4j.Neo4jSyncStrategy;
import cn.hutool.core.util.StrUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.data.neo4j.core.Neo4jClient;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutorService;
/**
* Neo4j 全量同步 Runner
* <p>
* 启动时 DETACH DELETE 全量重建节点与关系。
* 通过 {@link Neo4jStrategyContext} 遍历所有策略,统一调用
* {@link Neo4jSyncStrategy#detachDeleteAll()} /
* {@link Neo4jSyncStrategy#loadEntities()} /
* {@link Neo4jSyncStrategy#rebuildNodes} /
* {@link Neo4jSyncStrategy#rebuildRelationsBatch}。
* </p>
* <p>
* Middleware / Database 的关系因 Node ID 不一致
* (全量同步用 os_relation.id,增量同步用 entity.id)仍在 Runner 中特殊处理。
* </p>
*
* @author eleven
*/
@Slf4j
@Order(1)
@Component
@RequiredArgsConstructor
public class Neo4jInitRunner implements CommandLineRunner {
private final Neo4jStrategyContext context;
private final Neo4jClient neo4jClient;
private final Environment env;
private final ExecutorService taskRunnerExecutor;
@Value("${project.neo4j.sync-on-startup:true}")
private Boolean syncOnStartup;
private static final List<Class<? extends Neo4jBaseNode>> IGNORE_CLASS = Arrays.asList(Neo4jUser.class);
@Override
public void run(String... args) {
if (Arrays.asList(env.getActiveProfiles()).contains("test")) {
log.info("当前为测试环境,跳过Neo4j初始化");
return;
}
if (Boolean.FALSE.equals(syncOnStartup)) {
log.info("[Neo4jInitRunner] sync-on-startup=false, 跳过");
return;
}
taskRunnerExecutor.execute(this::syncAll);
}
public void syncAll() {
long start = System.currentTimeMillis();
log.info("[Neo4jInitRunner] 开始全量同步 Neo4j ...");
try {
clearAll();
syncNodes();
syncRelations();
log.info("[Neo4jInitRunner] 同步完成, 耗时 {} ms", System.currentTimeMillis() - start);
} catch (Exception e) {
log.error("[Neo4jInitRunner] 同步失败", e);
}
}
// ==================== 清空 ====================
private void clearAll() {
log.info("[Neo4jInitRunner] DETACH DELETE 全部节点 ...");
for (Neo4jSyncStrategy<?, ?> s : context.getAllStrategies()) {
if (IGNORE_CLASS.contains(s.getClass())) {
continue;
}
s.detachDeleteAll();
}
}
// ==================== 节点同步 ====================
@SuppressWarnings({"rawtypes", "unchecked"})
private void syncNodes() {
log.info("[Neo4jInitRunner] 批量重建节点 ...");
for (Neo4jSyncStrategy s : context.getAllStrategies()) {
if (IGNORE_CLASS.contains(s.getClass())) {
continue;
}
s.rebuildNodes(s.loadEntities());
}
}
// ==================== 关系同步 ====================
@SuppressWarnings({"rawtypes", "unchecked"})
private void syncRelations() {
log.info("[Neo4jInitRunner] 批量重建关系 ...");
for (Neo4jSyncStrategy s : context.getAllStrategies()) {
if (IGNORE_CLASS.contains(s.getClass())) {
continue;
}
s.rebuildRelationsBatch(s.loadEntities());
}
}
// ==================== 工具方法 ====================
/**
* 执行 Cypher 语句,绑定两个参数 osRelationId 和 osId
*
* @param cypher Cypher 语句,包含两个参数 $a 和 $b
* @param valA osRelationId 参数值
* @param valB osId 参数值
* @deprecated 请使用 {@link Neo4jOsRelationRepository#saveOsRelation(String, String)}
*/
@Deprecated
private void runCypher(String cypher, String valA, String valB) {
if (StrUtil.isBlank(valA) || StrUtil.isBlank(valB)) return;
try {
neo4jClient.query(cypher).bind(valA).to("a").bind(valB).to("b").run();
} catch (Exception e) {
log.warn("[Neo4jInitRunner] Cypher failed: {} a={} b={}", cypher, valA, valB);
}
}
}
7. 具体使用
在
ServiceImpl的方法上添加@SyncNeo4j注解,指定操作类型和对应的同步策略类,即可在 DB 操作完成后自动同步到 Neo4j。这里有一个问题,这个注解不能加载MybatisPlus的Mapper上,如果写了insertBatch或者updateBatch之类的需要建一个统一的service来实现,然后在这个service的方法上添加上注解,这样AOP就能够获取到里边的参数。
以
PersonServiceImpl为例:
package net.lesscoding.service.impl;
import net.lesscoding.annotation.neo4j.SyncNeo4j;
import net.lesscoding.domain.entity.Person;
import net.lesscoding.enums.Neo4jOperateType;
import net.lesscoding.mapper.PersonMapper;
import net.lesscoding.service.PersonService;
import net.lesscoding.strategy.neo4j.Neo4jPersonSyncStrategy;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
* Person Service 实现类
*
* @author eleven
*/
@Service
@Slf4j
@RequiredArgsConstructor
public class PersonServiceImpl extends ServiceImpl<PersonMapper, Person> implements PersonService {
private final PersonMapper personMapper;
@Override
public Page<Person> page(PersonQueryDto queryDto) {
Page<Person> page = queryDto.getPage();
List<Person> list = personMapper.getPersonPage(page, queryDto);
page.setRecords(list);
return page;
}
@Override
@Transactional
@SyncNeo4j(type = Neo4jOperateType.CREATE, strategy = Neo4jPersonSyncStrategy.class)
public Integer addPerson(Person entity) {
entity.setId(IdUtil.simpleUUID());
return personMapper.insert(entity);
}
@Override
@SyncNeo4j(type = Neo4jOperateType.UPDATE, strategy = Neo4jPersonSyncStrategy.class)
public Integer editPerson(Person entity) {
return personMapper.updateById(entity);
}
@Override
@SyncNeo4j(type = Neo4jOperateType.DELETE, strategy = Neo4jPersonSyncStrategy.class)
public Boolean delById(String id) {
return removeById(id);
}
@Override
public List<Person> allPerson() {
return personMapper.selectList(
new LambdaQueryWrapper<Person>().eq(Person::getDeleted, "0"));
}
@SyncNeo4j(type = Neo4jOperateType.CREATE_BATCH, strategy = Neo4jPersonSyncStrategy.class)
public int syncCreateBatch(List<Person> entities) {
return personMapper.insertBatch(entities);
}
}
关键说明:
| 注解参数 | 含义 |
|---|---|
type | CREATE/UPDATE/DELETE 分别对应新增、修改、删除的单条同步;*_BATCH 对应批量操作 |
strategy | 指向具体的同步策略实现类(如 Neo4jPersonSyncStrategy),AOP 切面会根据此参数从 Neo4jStrategyContext 中获取对应策略执行同步 |
AOP 切面
SyncNeo4jAspect会在目标方法执行成功后(point.proceed()返回),根据注解参数调用策略的save/update/delete/createBatch等方法,完成 Neo4j 增量同步。@Transactional与@SyncNeo4j可叠加使用,DB 事务内操作完成后才触发 Neo4j 同步。