【SpringBoot+Neo4j+MySQL+Mybatis-Plus集成和 *** 作实现】

【SpringBoot+Neo4j+MySQL+Mybatis-Plus集成和 *** 作实现】,第1张

【SpringBoot+Neo4j+MySQL+Mybatis-Plus集成和 *** 作实现】 SpringBoot+Neo4j+MySQL集成和 *** 作实现

吐槽:公司领导突然说了一个之前一直没有听说过的数据库->neo4j图形数据库,当时一阵懵逼,一步步填坑过来,留下项目笔记,等一个有缘人。本文讲解了数据库安装、SpringBoot整合、常规CRUD、自定义分页查询、集成Mybatis-Plus、事务处理。


目录:

本文主要分为如下几个章节进行集成 *** 作示例和填坑说明。

1、 neo4j数据库的下载安装 2、 SpringBoot集成neo4j数据库依赖引入和配置 3、 数据库节点CRUD *** 作 4、 自定义分页实现 5、 集成MySQL+Mybatis-Plus 6、多数据源事务处理
1、neo4j数据库的下载安装

数据库可以直接在官网下载,根据自己的系统选择,neo4j数据库的使用需要注意JDK的版本,3.*版本jdk要求为8,4.*版本jdk要求为11,本文以jdk8为例。
官网地址:neo4j官网地址社区版下载地址
下载好后windows直接解压(感兴趣的也可下载对应的docker镜像使用)
在对应的bin目录下运行cmd启动数据库输入(相关 *** 作指令可自行了解)

neo4j console

指令运行成功后复制cmd中的地址打开即可访问数据库
如图:

浏览器访问该地址(会自动跳转到http://localhost:7474/browser/)默认会连接上本地运行的neo4j数据库,若未连接可手动连接如图:

初始账号和密码都是neo4j,第一次登录会提示修改密码,自行修改后记住密码即可。连接成功如图:

这样就可以直接在上面的命令行中输入对应的CQL语句进行 *** 作了。

2、SpringBoot集成neo4j数据库依赖引入和配置

SpringBoot集成的neo4j数据库 *** 作有很多版本,且每个版本对应的neo4j *** 作源码存在很大的差异,这点需要特别注意,版本不对应是无法集成成功的。本文采用springboot.version=2.5.3,对应的neo4j的驱动为6.1.3[此版本高出目前网络上大多资料版本,导致一直踩坑]。

pom关键配置如下:

 
     org.springframework.boot
     spring-boot-starter-parent
     2.5.3
     
 
 
 
	 
	 
	     org.springframework.boot
	     spring-boot-starter-data-neo4j
	 
	 
	 
	    org.projectlombok
	    lombok
	 
 

对应的application.yml数据库连接信息如图:

注意此处的uri地址,有bolt/http/https三种方式,这个在大家安装该数据库时会有了解。

3、数据库节点CRUD *** 作 实体类创建

其实和常见实体无太大差别,只是需要指定对应的数据库节点和标明字段为属性等。

import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.data.neo4j.core.schema.GeneratedValue;
import org.springframework.data.neo4j.core.schema.Id;
import org.springframework.data.neo4j.core.schema.Node;
import org.springframework.data.neo4j.core.schema.Property;
import java.io.Serializable;


@Data
@Node("dongMan")
public class DongMan implements Serializable {
    @Id
    @GeneratedValue
    @ApiModelProperty(value = "主键,neo4j数据库自生成值")
    private Long id;
    @Property
    @ApiModelProperty(value = "姓名")
    private String name;
    @Property
    @ApiModelProperty(value = "年龄")
    private String age;
    @Property
    @ApiModelProperty(value = "性别")
    private String sex;
}
创建Service

springboot-data-neo4j提供了和mybatis-plus类似的crud *** 作仓库,我们可以直接继承使用,也可自行编写实现。
service代码:

import org.springframework.data.domain.Page;
import work.order.system.entity.dongman.DongMan;
import work.order.system.entity.dongman.DongManRelation;

import java.util.List;


public interface DongManService {

   
   DongMan addDongMan(DongMan dongMan);

   
   DongMan getInfoById(long id);

   
   void delById(long id);

   
   void createRelation(String from,String relation, String to);

   
   void createRelationByName(String fromName);

   
   Page getListByPage(int current, int pageSize, String Name);

   
   List getAllRealationTypes();

   
   Boolean existById(long id);

   
   DongMan updateById(DongMan dongMan);

   //查询指定用户的所有关系
   List getRelationsByName(String name,String relation);

}

创建Dao

dao *** 作仓库代码其中有自定义 *** 作集合Mybatis自定义SQL执行类似:

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.neo4j.repository.Neo4jRepository;
import org.springframework.data.neo4j.repository.query.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import work.order.system.entity.dongman.DongMan;
import work.order.system.entity.dongman.DongManRelation;

import java.util.List;


@Repository
public interface DongManRepository extends Neo4jRepository {

    //一对一手动指定关系
    @Query("match (n:dongMan {name:{0}}),(m:dongMan {name:{2}})"+
    "create (n)-[:动漫人物关系{relation:{1}}]->(m)")
    void createRelation(String from,String relation, String to);

    //根据关系数据进行当前用户的所有关系生成
    @Query("match (n:dongMan {name:{0}}),(m:dmRelation),(s:dongMan) where m.from={0} and s.name=m.to create(n)-[:动漫人物关系 {relation:m.relation}]->(s)")
    void createRelationByName(String fromName);

    //根据关系数据进行当前用户的所有关系生成
    @Query("CALL db.relationshipTypes()")
    List getAllRealationTypes();

    //修改
    @Query("MATCH (n) WHERe id(n) = :#{#dongMan.id} SET n.name = :#{#dongMan.name},n.age = :#{#dongMan.age},n.sex = :#{#dongMan.sex} RETURN n")
    DongMan updateById(@Param("dongMan") DongMan dongMan);

    @Query("match (n:dongMan {name:{name}})-[r:`动漫人物关系`]->(m:dongMan) where r.relation={relation} return m")
    List getRelationsByName(@Param("name")String name,@Param("relation")String relation);

    @Query("MATCH (n:dongMan {name:'冯宝宝'}) RETURN n")
    DongMan getTest();
}

创建ServiceImpl

需要注意的是,目前springboot-data-neo4j中的分页首页是从0开始的,不是从1开始的,很多人估计在这个地方郁闷了很久,查询第一页数据时输入1的页码,结果看不到数据。就是因为这个差别导致的!!!
接口实现层代码如下:

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import work.order.system.dao.doc.DocSqlMapper;
import work.order.system.dao.dongman.DongManRepository;
import work.order.system.entity.doc.DocSql;
import work.order.system.entity.dongman.DongMan;
import work.order.system.entity.dongman.DongManRelation;
import work.order.system.service.DongManService;
import javax.annotation.Resource;
import java.util.List;


@Service
@Transactional(value="transactionManager")
public class DongManServiceImpl implements DongManService {

    @Resource
    DongManRepository dongManRepository;

    @Override
    public DongMan addDongMan(DongMan dongMan) {
        return dongManRepository.save(dongMan);
    }

    @Override
    public DongMan getInfoById(long id) {
        return dongManRepository.findById(id).get();
    }

    @Override
    public void delById(long id) {
        dongManRepository.deleteById(id);
    }

    @Override
    public void createRelation(String from, String relation, String to) {
        dongManRepository.createRelation(from,relation,to);
    }

    @Override
    public void createRelationByName(String fromName) {
        dongManRepository.createRelationByName(fromName);
    }

    @Override
    public Page getListByPage(int current, int pageSize, String Name) {
        Pageable pageable= PageRequest.of(current,pageSize);
        return dongManRepository.findAll(pageable);
    }

    @Override
    public List getAllRealationTypes() {
        return dongManRepository.getAllRealationTypes();
    }

    @Override
    public Boolean existById(long id) {
        return dongManRepository.existsById(id);
    }

    @Override
    public DongMan updateById(DongMan dongMan) {
        return dongManRepository.updateById(dongMan);
    }

    @Override
    public List getRelationsByName(String name,String relation) {
        return dongManRepository.getRelationsByName(name,relation);
    }
}
创建Controller

提供服务访问入口进行服务消费
控制层代码如下:

import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import work.order.system.entity.dongman.DongMan;
import work.order.system.entity.Result;
import work.order.system.service.DongManService;
import javax.annotation.Resource;


@RestController
@RequestMapping("/dongMan")
@Api(tags="动漫人物 *** 作测试")
public class DongManController {

    @Resource
    DongManService dongManService;

    @PostMapping("/addDongMan")
    @ApiOperation(value="添加对象节点")
    public Result addDongMan(DongMan dongMan) {
        return Result.success(" *** 作成功!", dongManService.addDongMan(dongMan));
    }

    @PostMapping("/delById")
    @ApiOperation(value="根据主键删除")
    @ApiImplicitParam(name="id",value="主键",paramType="form")
    public Result delById(Long id) {
        dongManService.delById(id);
        return Result.success(" *** 作成功!");
    }

    @PostMapping("/updateDongMane")
    @ApiOperation(value="修改节点信息")
    public Result updateDongMane(DongMan dongMan) {
        return Result.success(" *** 作成功!", dongManService.updateById(dongMan));
    }

    @GetMapping("/getInfoById")
    @ApiOperation(value="根据主键查询")
    @ApiImplicitParam(name="id",value="主键",paramType="form")
    public Result getInfoById(Long id) {
        return Result.success(" *** 作成功!",dongManService.getInfoById(id));
    }
    
    @GetMapping("/getAllRelationTypes")
    @ApiOperation(value="获取所有的关系类型")
    public Result getAllRelationTypes() {
        return Result.success(" *** 作成功!", dongManService.getAllRealationTypes());
    }

    @PostMapping("/addDMRelationShip")
    @ApiOperation(value="指定两个节点的关系(两个节点须存在)")
    @ApiImplicitParams({
            @ApiImplicitParam(name="name",value="对象名:唐三-",paramType="form"),
            @ApiImplicitParam(name="relation",value="关系[父亲]->",paramType="form"),
            @ApiImplicitParam(name="to",value="对象名:唐昊",paramType = "form")
    })
    public Result addDMRelationShip(String name,String relation,String to) {
        //直接指定关系
        dongManService.createRelation(name, relation, to);
        return Result.success(" *** 作成功!" );
    }

    @GetMapping("/getRelationsByName")
    @ApiOperation(value="获取指定节点指定关系信息")
    @ApiImplicitParams({
            @ApiImplicitParam(name="name",value="对象名:唐三",paramType="form"),
            @ApiImplicitParam(name="relation",value="具体关系",paramType = "form")
    })
    public Result getRelationsByName(String name,String relation) {
        return Result.success(" *** 作成功!" , dongManService.getRelationsByName(name,relation));
    }
}

效果演示

运行项目进行接口 *** 作验证,我这边使用swagger进行测试,(因为我数据库中已经有测试数据了,我们先看下数据库中的数据)。
源数据库中的数据如图:

共有18条记录,我们验证添加节点 *** 作,新增武庚和逆天而行。如图:

创建节点时会根据对应实体上的注解指定的节点名进行创建(也可以先手动在数据库中创建好节点)。
创建节点指令:

create (n:dongMan {name:'李四',sex:'男',age:'22'})	

数据库中如图:

其他的修改和删除 *** 作就不一一演示了,下面演示下给这两个节点添加关系。


基础的 *** 作就实现了。

4、 自定义分页实现

在controller中添加如下代码:

    @GetMapping("/getListByPage")
    @ApiOperation(value="分页查询")
    @ApiImplicitParams({
            @ApiImplicitParam(name="current",value="起始页0为第一页",paramType="form"),
            @ApiImplicitParam(name="pageSize",value="页数量",paramType = "form")
    })
    public Result getListByPage(int current,int pageSize) {
        return Result.success(" *** 作成功!",dongManService.getListByPage(current,pageSize,""));
    }

在swagger中查看效果

该分页实现为源码中的PagingAndSortingRepository仓库实现的,但是源码中只有一个排序 *** 作没有关于参数指定的分页查询实现,即需要我们自己手动实现自定义分页 *** 作。
自定义分页实现service加入如下代码:

   
   Page getRelationsByName(int current, int pageSize,String name);

dao加入如下代码:

    @Query(value="match (n:dongMan {name:{name}})-[r:`动漫人物关系`]->(m:dongMan) return id(n) as pid, n.name as name,r.relation as relation" +
            ",m as dongMan skip {skip} limit {pageSize}"
        ,countQuery = "match (n:dongMan {name:{name}})-[r:`动漫人物关系`]->(m:dongMan) return count(r)")
    Page getRelationsByName(@Param("name")String name,@Param("skip")int ship,@Param("pageSize")int pageSize,Pageable pageable);
    //不传递pageable分页无效

对应的CQL需要指定skip和limit的值一级获取总条数的SQL,且必须接收Pageable。
serviceImpl加入如下代码:

    @Override
    public Page getRelationsByName(int current, int pageSize,String name) {
        Pageable pageable= PageRequest.of(current,pageSize);
        return dongManRepository.getRelationsByName(name,current*pageSize,pageSize,pageable);
    }

controller加入如下代码:

    @GetMapping("/getRelations")
    @ApiOperation(value="获取指节点关系信息")
    @ApiImplicitParams({
            @ApiImplicitParam(name="name",value="对象名:唐三",paramType="form"),
            @ApiImplicitParam(name="current",value="起始页0为第一页",paramType="form"),
            @ApiImplicitParam(name="pageSize",value="页数量",paramType = "form")
    })
    public Result getRelations(int current,int pageSize,String name) {
        return Result.success(" *** 作成功!" , dongManService.getRelationsByName(current,pageSize,name));
    }

效果如图:

这样就满足了自定义条件分页查询的实现了。

5、 集成MySQL+Mybatis-Plus

pom关键添加如下信息:


    mysql
    mysql-connector-java
    runtime


    com.alibaba
    druid-spring-boot-starter
    1.2.6


    com.baomidou
    mybatis-plus-boot-starter
    3.4.2

application.yml添加如下信息:

spring:  
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    driverClassName: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://xxxxxxxx:3306/xxx?useUnicode=true&characterEncoding=utf-8
    username: root
    password: xxxx

#mybatis-plus设置
mybatis-plus:
  mapper-locations: classpath:mapper*.xml # Mapper文件的位置
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 日志的实现类(打印SQL)
    map-underscore-to-camel-case: true # 下划线转驼峰

添加好如上信息后直接按照SpringBoot集成Mybatis-plus的crud *** 作实现即可进行mysql数据库的 *** 作,无需进行数据源切换(目前未明确为何可自行寻找到数据源进行数据库 *** 作,欢迎大家留言讨论)。这里就直接演示MySQL的 *** 作了,如图:

注意哈:mybatis的分页首页为1,neo4j的首页为0。

6、多数据源事务处理

在实际的 *** 作中很有可能用到事务的回滚功能,集成了MySQL和neo4j数据库后,只需对事务进行简单配置即可实现。
首先启用事务,在启动类上添加@EnableTransactionManagement注解。因为集成了不同的数据库,直接启用事务,系统无法辨别具体的回滚 *** 作在那个数据库执行,我的实现如下,添加事务配置类:

import org.neo4j.driver.Driver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.neo4j.core.transaction.Neo4jTransactionManager;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import javax.sql.DataSource;


@Configuration
public class TransactionConfig {

    
    @Bean("mysqlTransaction")
    public DataSourceTransactionManager jpaTransactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }

    
    @Bean("transactionManager")
    public Neo4jTransactionManager neo4jTransactionManager(Driver driver) {
        return new Neo4jTransactionManager(driver);
    }
}

此处一定要注意neo4j的事务必须命名为transactionManager,否则会出错。这个地方坑了很长时间。
在对应的serviceImpl类或者具体的实现方法上加入事务注解,指定使用那个事务进行实现。依据数据库进行区分,
mysql数据库 *** 作的添加@Transactional(value=“mysqlTransaction”)
neo4j数据库 *** 作的添加@Transactional(value=“transactionManager”)
这样就可以实现事务的回滚 *** 作了。我们在DongManServiceImpl类中添加异常 *** 作,验证事务是否生效。
修改原有addDongMan方法为:

    @Override
    public DongMan addDongMan(DongMan dongMan) {
        //正常 *** 作
        dongManRepository.save(dongMan);
         //异常 *** 作
       dongManRepository.getTest();
        return dongManRepository.save(dongMan);
    }

修改DongManRepository中的getTest接口的CQL为错误的CQL:

    @Query("MATCH (n:dongMan {name:冯宝宝}) RETURN n")
    DongMan getTest();

进行 *** 作验证,添加一个CSDN的节点,添加成功则事务无效,因为添加成功 *** 作后面有一个异常 *** 作,事务应该回滚才正确。

后台异常如图:

数据库中查看是否添加CSDN成功:

说明neo4j的事务生效了,mysql的事务验证就不演示了,这里再演示下neo4j *** 作成功,mysql *** 作失败的数据库混合 *** 作,看事务是否生效:
修改DongManServiceImpl中的addDongMan,引入mysql的数据库 *** 作。代码如下:

    //模拟混合数据源事务管理
    @Resource
    DocSqlMapper sqlMapper;

    @Override
    public DongMan addDongMan(DongMan dongMan) {
        //正常 *** 作
        dongManRepository.save(dongMan);
         //异常 *** 作
        DocSql docSql2 = sqlMapper.selectById("3");
        docSql2.setDocId(null);
        docSql2.setCreateTime("111");
        sqlMapper.updateById(docSql2);
        return dongMan;
    }

运行查看事务是否生效:



MySQL *** 作失败,neo4j *** 作成功,但是2个 *** 作在一个方法中,事务回滚,判定为 *** 作失败,数据库不进行 *** 作成功的数据写入。混合式事务验证成功。

大家不喜勿喷!

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5687103.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-17
下一篇 2022-12-17

发表评论

登录后才能评论

评论列表(0条)

保存