2-最简Springboot+Neo4j开发框架搭建

91 阅读10分钟

1. 前言

使用最新springboot 和图数据库Neo4j搭建最简AI开发框架(图数据库Neo4j的优点大家可以百度),实现后台代码快速开发。 整个后台框架争取做到最简,只引入必须的依赖;开发的模块代码也争取做到最少。

  • 数据库用neo4j ;

  • 缓存用redis;

  • 大模型用的是阿里云百炼平台;

  • 对象存储服务用阿里云/华为云的云存储;

  • 开发工具使用:IntelliJ IDEA 社区版,下载地址:www.jetbrains.com/zh-cn/idea/…

2. 使用IDEA社区版创建maven项目

2.1 创建空项目

注意archetype选择如上图(...archetype-quickstart模式)。

Advanced Settings里面可以填写GroupId,对应的是pom.xml中的groupId标签。

创建完后,会生成如下极其简单的目录:

2.2 调整src目录

生成的src目录不完整,可以手动创建必要的目录,如下图:

  • common

    存放公共的类,如常量、公共异常、工具类等。

  • config

    存放系统的配置

  • controller

    controller接口类和页面请求实体类。

  • domain

    与数据库对应的实体类

  • repository

    数据库操作类

  • service

    service业务类。

2.3 编写启动类

启动类修改为:GutApplication.java ,放在com.yunei.gut目录下

package com.yunei.gut;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.validation.annotation.Validated;

@SpringBootApplication
@EnableAsync
public class GutApplication {
    public static void main(String[] args) {
        SpringApplication.run(GutApplication.class, args);
    }
}

2.4 调整resources目录

按照环境的不同增加配置:

application.yml

定义公共的配置:启动的端口及运行环境定义。

server:
  port: 8080
spring:
  profiles:
    active: dev

application-dev.yml

定义具体的配置,如neo4j的连接参数、redis的连接、华为obs的连接参数,这些参数开发环境和生产环境的配置可能不一样。

spring:
  neo4j:
    uri: bolt://neo4jip:7687
    authentication:
      username: neo4j
      password: *****
    connection:
      # 连接池配置
      max-connection-pool-size: 10    # 默认20
      max-connection-lifetime: 3000m    # 连接最大生存时间
      pool:
        idle-before-connection-test: 6s  # 空闲连接检测间隔
        validate-connection: false         # 使用前验证连接有效性
    # 驱动配置
    driver:
      config:
        connection-timeout: 3000s        # 连接超时时间
        max-transaction-retry-time: 10s # 事务重试时间
        connection-acquisition-timeout: 60s # 获取连接超时时间

  data:
    redis:
      host: redisip
      #      host: 127.0.0.1
      port: 7479   #端口
      database: 0  # 使用的数据库编号
      password: ***** #Redis密码
      lettuce: #Lettuce客户端配置
        pool: # 连接池配置
          max-active: 40  # 最大活跃连接数
          max-wait: -1  # 最大等待时间(-1表示无限等待)
          max-idle: 40  # 最大空闲连接数
          min-idle: 5  # 最小空闲连接数

huawei:
  obs:
    accessKey: HP**************QAA
    secretAccessKey: LLZ****************REpckdoy
    endpoint: obs.cn-south-1.myhuaweicloud.com
    bucketName: gut

application-prod.yml配置文件和dev配置文件一样,修改对应的参数值即可。

日志文件logback

Logback 是 Java 生态中主流的日志框架,具有高性能、灵活配置等特点。Spring Boot 3 默认集成 Logback 作为日志系统,配置文件如下:

<configuration>
    <property name="LOG_PATH" value="logs"/>
    <property name="APP_NAME" value="gut"/>


    <springProfile name="dev">
        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n</pattern>
            </encoder>
        </appender>

        <root level="debug">
            <appender-ref ref="STDOUT" />
        </root>
    </springProfile>

    <springProfile name="prod">
        <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
            <file>${LOG_PATH}/prod-${APP_NAME}.log</file>
            <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
                <fileNamePattern>${LOG_PATH}/prod-${APP_NAME}-%d{yyyy-MM-dd}.log</fileNamePattern>
                <maxHistory>30</maxHistory>
            </rollingPolicy>
            <encoder>
                <pattern>%d{yyyy-MM-dd HH:mm:ss} - %msg%n</pattern>
            </encoder>
        </appender>
        <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <pattern>%d{yyyy-MM-dd HH:mm:ss} %-5level %logger{36} - %msg%n</pattern>
            </encoder>
        </appender>

        <root level="info">
            <appender-ref ref="FILE" />
            <appender-ref ref="STDOUT" />
        </root>
    </springProfile>
</configuration>

2.5 构建pom.xml

  1. SpringBoot 3.5.5

    截止目前SpringBoot最新版本是3.5.5,这应该是3.5版本的最高版本了,后面估计就直接使用SpringBoot 4.0了。SpringBoot在3.5 这个版本上做了一些升级,有兴趣的可以百度了解一下。

    pom.xml中增加:

        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>3.5.5</version>
            <relativePath/>
        </parent> 
    
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
    

jdk17配置

<java.version>17</java.version>
  1. hutool 工具

    Hutool是一个Java工具包类库,对文件、流、加密解密、转码、正则、线程、XML等JDK方法进行封装,组成各种Util工具类。官网地址:www.hutool.cn/

            <dependency>
                <groupId>cn.hutool</groupId>
                <artifactId>hutool-all</artifactId>
                <version>5.8.39</version>
            </dependency>javax.validation
    
  2. validation校验

    validation的一系列注解可以帮我们完成参数校验,免去繁琐的串行校验

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-validation</artifactId>
            </dependency>
    
  3. jedis

    jedis主要用于redis的连接

            <dependency>
                <groupId>redis.clients</groupId>
                <artifactId>jedis</artifactId>
            </dependency>
    
  4. neo4j集成

    SpringBoot集成Neo4j非常方便,pom.xml中加入如下配置即可

            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-data-neo4j</artifactId>
            </dependency>
    
  5. fastjson

    Fastjson 是一个 Java 库,可以将 Java 对象转换为 JSON 格式,当然它也可以将 JSON 字符串转换为 Java 对象。Fastjson 可以操作任何 Java 对象,即使是一些预先存在的没有源码的对象。

        <!-- JSON 解 析 工 具 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83</version>
        </dependency>
  1. pom.xml的modelVersion

modelVersion 标签用于指定当前 pom.xml 文件所遵循的项目对象模型(Project Object Model,POM)的版本。这个版本号确保了 Maven 能够正确地解析和处理 pom.xml 文件中的其他元素和配置。

在大多数 Maven 项目中,modelVersion 的标准值通常为 4.0.0,这是自 Maven 2.0 以来一直使用的版本。尽管 Maven 可能会引入新的 POM 模型版本,但在实践中,4.0.0 仍然是广泛使用和推荐的版本。

<modelVersion>4.0.0</modelVersion>

最终的pom.xml文件见源代码。

写好pom.xml文件后,通过IDEA工具编译打包,maven会自动下载依赖的包。

3. IDEA安装百度的Comate

百度的文心快码可以辅助我们快速开发,推荐安装一下,在设置的插件里面,搜索baidu即可看到Baidu Comate ,点击 install 安装,安装后重启IDEA。

从IDEA市场下载速度比较慢,可以去百度官网下载插件后,从硬盘安装。

  1. 下载插件

    地址:下载 Baidu Comate

        

  1. 从磁盘安装

    在设置的插件里面,有一个从磁盘安装插件的选项,点击后选择下载的插件即可。

安装后,可以使用试试,如IDEA使用时控制台出现乱码,我们可以直接问:idea控制台输出乱码怎么处理 。回复如下:

根据第二个回复即可修复问题 ,我们还可通过文心快码生成代码,后续专门说明如何使用。

4. 安装lombok插件

lombok插件是必须安装的插件了,同理在插件市场搜索后安装即可。

5. 启动

编译通过后,打开启动类GutApplication,右键运行,启动不报错,则运行成功。

6. 第一个实体类User的增删改查

6.1 实体类User

首先在domain目录下创建一个User类,代码如下:

package com.yunei.gut.domain;

import com.yunei.gut.domain.base.BaseDomain;
import lombok.Data;
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;
import java.time.LocalDate;
import java.time.LocalDateTime;

@Node("User")
@Data
public class User extends BaseDomain implements Serializable {
    @Id
    private String userId;
    private String account;
    private String phone;
    private String openId;
    private String unionId; // 微信唯一标识[3]()
    private String nickname;
    private String avatar;
    private String bio;//个人签名、简介
    private String status;//状态:Y-有效;N-停用;U-未激活
    private String realName;
    private String sex;
    private LocalDate birthday;
    private int grade;//年级 1-一年级,2-二年级...7-七年级;8-八年级...12-十二年级;-2-幼儿园小班;-1-幼儿园中班;0-幼儿园大班
    private String password;
    private LocalDateTime lastLoginTime;
    private String email;
    private String userType;//C-孩子;P-家长
    private String userRight="N";//用户权限;N-普通用户;V-付费VIP用户;A-管理员;T-老师
    private LocalDateTime vipBeginDate;//VIP开始时间
    private LocalDateTime vipEndDate;//VIP结束时间
}

公共的属性放到了BaseDomain

package com.yunei.gut.domain.base;

import lombok.Data;
import org.springframework.data.neo4j.core.schema.Property;

import java.io.Serializable;
import java.time.LocalDateTime;

@Data
public class BaseDomain implements Serializable {

    private LocalDateTime createdAt;//创建时间
    private LocalDateTime updateAt;//更新时间
    private String createBy;//创建人
    private String updateBy;//更新人
    private String delFlag="N";//Y-已经删除;N-正常;未删除
    private String familyId;//家庭ID,做数据隔离
}

因数据库是使用Neo4j数据库,这里的注解就是和Neo4j相关的。

@Node注解

该注解用于标记一个Java类作为Neo4j图数据库中的一个节点。注解中的字符串参数(@Node("User")中的"User")通常用于指定节点的标签(Label),这有助于在Neo4j中组织和查询数据。

@Id注解

该注解用于标识该节点的主键。

@Data 注解

是lombok的注解,用于减少代码量。

实体类只使用如上3个注解即可。

6.2 UserRepository接口

UserRepository是一个接口,它扩展了Neo4jRepository接口。在Spring Data Neo4j中,Neo4jRepository`是一个泛型接口,提供了对Neo4j图数据库进行CRUD(创建、读取、更新、删除)操作的能力。UserRepository需增加注解:@Repository

具体代码如下:

package com.yunei.gut.repository;

import com.yunei.gut.domain.User;
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 java.util.List;
import java.util.Optional;

@Repository
public interface UserRepository extends Neo4jRepository<User, String> {


}

单表的查询、新增、修改可不用在UserRepository中写代码。

6.3 UserService类

该类没有接口,减少代码量。

  • @Service注解表明这是一个service 。

  • @RequiredArgsConstructor 注解

    • RequiredArgsConstructor 注解是一个提高开发效率的工具,它自动为类生成包含必需参数的构造函数。必需参数指的是 @NonNull 注解标记的字段,或者是 final 字段。如下段代码中对应的就是userRepository 参数,系统会自动给改参数生成构造函数。
    • 使用这个注解可以减少样板代码,并且确保对象在创建时就初始化了所有必需的状态。
@Slf4j
@Service
@RequiredArgsConstructor
public class UserService {


    private final UserRepository userRepository;

    /**
     * 根据userId查询用户
     * @param userId
     * @return
     */
    public User getUserById(String userId) {
        User u = new User();
        u.setUserId(userId);
        Example<User> example = Example.of(u);
        return userRepository.findOne(example).orElseThrow(() -> new ServiceException(BusErrorCodeConstants.USER_NOT_EXIST)
);

    }

public User register(RegisterReq registerReq) {

        User user = new User();
        BeanUtil.copyProperties(registerReq,user);
        user.setUserId("UR"+IdUtil.fastSimpleUUID());
        user.setCreatedAt(LocalDateTime.now());

        user.setPassword(PasswordUtil.encryptPassword(registerReq.getPassword()));
        user.setStatus(Constant.STATUS_Y);
        user.setUserType(UserTypeEnum.PARENT.getCode());
        user.setAvatar(Constant.DEFAULT_AVATAR);
        //创建家庭
        CreateFamilyReq req = new CreateFamilyReq();
        req.setFamilyName(registerReq.getNickname()+"的家庭");
        Family family = familyService.createFamily(req,user.getUserId());
        //更新用户的familyId
        user.setFamilyId(family.getFamilyId());
        user.setVipBeginDate(LocalDateTime.now());
        user.setVipEndDate(LocalDateTime.now().plusMonths(2));//注册送2个月的VIP
        userRepository.save(user);
        return user;
    }
    public Page<User> getAllUser(int page, int size) {
        Pageable pageable = PageRequest.of(page, size);
        User user = new User();
        user.setStatus(Constant.STATUS_Y);
        user.setDelFlag(Constant.STATUS_N);
        Example<User> example = Example.of(user);
        return  userRepository.findAll(example,pageable);
    }

}

单表查询

  1. 单表查询可使用Example类构建查询条件(不建议)
  • Example 类是 Spring Data JPA 提供的一个工具类,用于构建基于属性的查询条件。

  • Example<User> example = Example.of(u); 这行代码创建了一个 Example 对象,该对象封装了一个 User 类型的实体对象 u,用于后续构建查询条件。

  1. 使用CQL(建议)

    在UserRepository中增加如下代码,使用@Query注解撰写CQL方法

    @Query("MATCH (u:User) where u.account = $account return u")
    List<User> findByAccount(@Param("account") String account);
    

新增、修改、删除

 UserRepository 继承 Neo4jRepository , Neo4jRepository 继承 CrudRepository ,基础的新增、修改、删除都已经实现。

*** 注:新增和修改都是使用save方法。**

分页

分页示例见上面的getAllUser方法,使用的是Pageable对象。

Pageable pageable = PageRequest.of(page, size); 首先根据传入的分页参数组织pageable对象,page代表当前页码,size代表当前页有多少条数据。

userRepository.findAll(example,pageable); findAll方法支持传入分页对象pageable。

6.4 UserController类

注解

Controller层相对就比较简单,如下段代码,核心是需加上4个注解:

  • @Slf4j

    记录日志用

  • @RestController

    该注解简化了RESTful Web服务的开发,它结合了控制器声明和响应体自动转换的功能。使用这个注解可以方便地将控制器方法的返回值转换成JSON或XML格式的数据,并返回给客户端。

  • @RequiredArgsConstructor

  • @RequestMapping("/api/users")

        该注解是Spring MVC中用于将HTTP请求映射到处理器方法上的重要注解。它可以定义在类级别或方法级别,用于指定请求的路径、请求方法、请求参数等条件。通过使用 @RequestMapping 注解,您可以灵活地定义控制器中各个处理器方法的请求映射规则。

@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/api/users")
public class UserController {
    private final UserService userService;

    @GetMapping("/getUserById/{userId}")
    public PageResult<User> getUserById(@PathVariable @Validated String userId) {
        User user = userService.getUserById(userId);
        return PageResult.success(user);
    }
}

页面返回封装

Controller层最核心的是要封装返回给前端页面的对象,因此要封装一个PageResult对象,代码如下:

package com.yunei.gut.common;

import cn.hutool.core.lang.Assert;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.yunei.gut.common.exception.ErrorCode;
import com.yunei.gut.common.exception.GlobalErrorCodeConstants;
import lombok.Data;

import java.io.Serializable;
import java.util.Objects;

/**
 * 通用返回
 *
 * @param <T> 数据泛型
 */
@Data
public class PageResult<T> implements Serializable {

    /**
     * 错误码
     *
     */
    private Integer code;
    /**
     * 返回数据
     */
    private T data;
    /**
     * 错误提示,用户可阅读
     *
     */
    private String msg;

    /**
     * 将传入的 result 对象,转换成另外一个泛型结果的对象
     *
     * 因为 A 方法返回的 CommonResult 对象,不满足调用其的 B 方法的返回,所以需要进行转换。
     *
     * @param result 传入的 result 对象
     * @param <T> 返回的泛型
     * @return 新的 CommonResult 对象
     */
    public static <T> PageResult<T> error(PageResult<?> result) {
        return error(result.getCode(), result.getMsg());
    }

    public static <T> PageResult<T> error(Integer code, String message) {
        Assert.notEquals(GlobalErrorCodeConstants.SUCCESS.code(), code, "code 必须是错误的!");
        PageResult<T> result = new PageResult<>();
        result.code = code;
        result.msg = message;
        return result;
    }

    public static <T> PageResult<T> error(ErrorCode errorCode) {
        return error(errorCode.code(), errorCode.msg());
    }

    public static <T> PageResult<T> success(T data) {
        PageResult<T> result = new PageResult<>();
        result.code = GlobalErrorCodeConstants.SUCCESS.code();
        result.data = data;
        result.msg = "";
        return result;
    }

    public static boolean isSuccess(Integer code) {
        return Objects.equals(code, GlobalErrorCodeConstants.SUCCESS.code());
    }

    @JsonIgnore // 避免 jackson 序列化
    public boolean isSuccess() {
        return isSuccess(code);
    }

    @JsonIgnore // 避免 jackson 序列化
    public boolean isError() {
        return !isSuccess();
    }


}

系统还在开发中,后续会分享如何打通阿里云的百炼大模型,有兴趣可以加入交流群:

学习群