Spring Boot整合Doris

简介

Doris 是一款数据库,本文介绍 Spring Boot 如何整合 Doris,关于 Doris 部署,参考这篇这篇博客

整合

以一个简单的 RBAC 项目为例,用 Spring Boot 实现,数据库初始化脚本如下:

sql 复制代码
-- =============================================
-- Doris RBAC Demo 数据库初始化脚本
-- =============================================

CREATE DATABASE IF NOT EXISTS rbac_demo;

USE rbac_demo;

-- 用户表
DROP TABLE IF EXISTS sys_user;
CREATE TABLE IF NOT EXISTS sys_user (
    id          BIGINT       NOT NULL   COMMENT '主键ID',
    username    VARCHAR(50)  NOT NULL   COMMENT '用户名',
    password    VARCHAR(200) NOT NULL   COMMENT '密码',
    email       VARCHAR(100) DEFAULT '' COMMENT '邮箱',
    status      TINYINT      DEFAULT 1  COMMENT '状态:0-禁用,1-启用',
    create_time DATETIME                COMMENT '创建时间',
    update_time DATETIME                COMMENT '更新时间'
)
DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 10
PROPERTIES (
    "replication_num" = "1"
);

-- 角色表
DROP TABLE IF EXISTS sys_role;
CREATE TABLE IF NOT EXISTS sys_role (
    id          BIGINT       NOT NULL   COMMENT '主键ID',
    role_name   VARCHAR(50)  NOT NULL   COMMENT '角色名称',
    role_code   VARCHAR(50)  NOT NULL   COMMENT '角色编码',
    description VARCHAR(200) DEFAULT '' COMMENT '角色描述',
    status      TINYINT      DEFAULT 1  COMMENT '状态:0-禁用,1-启用',
    create_time DATETIME                COMMENT '创建时间',
    update_time DATETIME                COMMENT '更新时间'
)
DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 10
PROPERTIES (
    "replication_num" = "1"
);

-- 权限表
DROP TABLE IF EXISTS sys_permission;
CREATE TABLE IF NOT EXISTS sys_permission (
    id              BIGINT       NOT NULL   COMMENT '主键ID',
    permission_name VARCHAR(50)  NOT NULL   COMMENT '权限名称',
    permission_code VARCHAR(100) NOT NULL   COMMENT '权限编码,如 user:add、role:delete',
    description     VARCHAR(200) DEFAULT '' COMMENT '权限描述',
    status          TINYINT      DEFAULT 1  COMMENT '状态:0-禁用,1-启用',
    create_time     DATETIME                COMMENT '创建时间',
    update_time     DATETIME                COMMENT '更新时间'
)
DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 10
PROPERTIES (
    "replication_num" = "1"
);

-- 用户角色关联表
DROP TABLE IF EXISTS sys_user_role;
CREATE TABLE IF NOT EXISTS sys_user_role (
    id      BIGINT NOT NULL   COMMENT '主键ID',
    user_id BIGINT NOT NULL   COMMENT '用户ID',
    role_id BIGINT NOT NULL   COMMENT '角色ID'
)
DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 10
PROPERTIES (
    "replication_num" = "1"
);

-- 角色权限关联表
DROP TABLE IF EXISTS sys_role_permission;
CREATE TABLE IF NOT EXISTS sys_role_permission (
    id            BIGINT NOT NULL   COMMENT '主键ID',
    role_id       BIGINT NOT NULL   COMMENT '角色ID',
    permission_id BIGINT NOT NULL   COMMENT '权限ID'
)
DUPLICATE KEY(id)
DISTRIBUTED BY HASH(id) BUCKETS 10
PROPERTIES (
    "replication_num" = "1"
);

-- =============================================
-- 初始化测试数据
-- =============================================

-- 插入演示权限
INSERT INTO sys_permission(id, permission_name, permission_code, description, status, create_time, update_time)
VALUES
(1, '用户查询', 'user:list',   '查看用户列表',   1, NOW(), NOW()),
(2, '用户新增', 'user:add',    '新增用户',       1, NOW(), NOW()),
(3, '用户编辑', 'user:edit',   '编辑用户信息',   1, NOW(), NOW()),
(4, '用户删除', 'user:delete', '删除用户',       1, NOW(), NOW()),
(5, '角色查询', 'role:list',   '查看角色列表',   1, NOW(), NOW()),
(6, '角色新增', 'role:add',    '新增角色',       1, NOW(), NOW()),
(7, '角色编辑', 'role:edit',   '编辑角色信息',   1, NOW(), NOW()),
(8, '角色删除', 'role:delete', '删除角色',       1, NOW(), NOW());

-- 插入演示角色
INSERT INTO sys_role(id, role_name, role_code, description, status, create_time, update_time)
VALUES
(1, '超级管理员', 'ADMIN',   '拥有所有权限',  1, NOW(), NOW()),
(2, '普通用户',   'USER',    '普通用户权限',  1, NOW(), NOW()),
(3, '访客',       'GUEST',   '仅有查看权限',  1, NOW(), NOW());

-- 给角色分配权限
-- 超级管理员拥有全部权限
INSERT INTO sys_role_permission(id, role_id, permission_id)
VALUES
(1, 1, 1), (2, 1, 2), (3, 1, 3), (4, 1, 4),
(5, 1, 5), (6, 1, 6), (7, 1, 7), (8, 1, 8);

-- 普通用户有用户管理和角色查看权限
INSERT INTO sys_role_permission(id, role_id, permission_id)
VALUES
(9, 2, 1), (10, 2, 2), (11, 2, 3), (12, 2, 5);

-- 访客只有查看权限
INSERT INTO sys_role_permission(id, role_id, permission_id)
VALUES
(13, 3, 1), (14, 3, 5);

-- 插入演示用户
INSERT INTO sys_user(id, username, password, email, status, create_time, update_time)
VALUES
(1, 'admin', '123456', 'admin@example.com', 1, NOW(), NOW()),
(2, 'zhangsan', '123456', 'zhangsan@example.com', 1, NOW(), NOW()),
(3, 'lisi', '123456', 'lisi@example.com', 1, NOW(), NOW());

-- 给用户分配角色
INSERT INTO sys_user_role(id, user_id, role_id)
VALUES
(1, 1, 1),
(2, 2, 2),
(3, 3, 3);

pom.xml 文件如下,其实整合 Doris,直接用 MySQL 的驱动和依赖就行,DAO 框架就用 MyBatis-Plus

xml 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!-- Spring Boot 父工程,统一管理依赖版本和插件配置 -->
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.7.12</version>
        <relativePath/>
    </parent>

    <!-- 项目坐标 -->
    <groupId>com.hezy</groupId>
    <artifactId>doris_rbac_demo</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!-- 全局属性配置 -->
    <properties>
        <maven.compiler.source>17</maven.compiler.source>
        <maven.compiler.target>17</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- Spring Boot Web 启动器:内嵌 Tomcat,支持 Spring MVC RESTful 接口 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- MySQL JDBC 驱动:连接后端数据库(运行时必需) -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!-- Druid 连接池:数据库连接池管理,支持监控统计 -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid-spring-boot-starter</artifactId>
            <version>1.2.8</version>
        </dependency>

        <!-- Lombok:简化 Java POJO 代码(@Data/@Slf4j 等注解) -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!-- MyBatis-Plus:增强版 ORM 框架,提供通用 CRUD、分页等功能 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.16</version>
        </dependency>

        <!-- Spring Boot 测试框架:单元测试和集成测试支持 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <!-- Hutool 工具包:提供丰富的 Java 工具类(字符串、日期、加密等) -->
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.8.6</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- Spring Boot Maven 插件:打包可执行 jar,支持 spring-boot:run -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

配置文件如下,与 MySQL 不同的是 Doris 的端口,默认为 9030

yml 复制代码
# ==================== 服务端口配置 ====================
server:
  port: 8088

# ==================== 数据源 & 连接池配置 ====================
spring:
  datasource:
    # MySQL JDBC 驱动(使用 com.mysql.cj 新版本驱动类)
    driver-class-name: com.mysql.cj.jdbc.Driver
    # 数据库连接 URL(useUnicode/utf-8:支持中文,useSSL:关闭 SSL,serverTimezone:时区设为东八区)
    url: jdbc:mysql://192.168.101.11:9030/rbac_demo?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8
    username: root
    password:
    # 使用 Druid 连接池
    type: com.alibaba.druid.pool.DruidDataSource
    druid:
      # 连接池初始化大小
      initial-size: 2
      # 最小空闲连接数
      min-idle: 2
      # 最大活跃连接数
      max-active: 10
      # 连接有效性检测 SQL
      validation-query: SELECT 1

# ==================== MyBatis-Plus 配置 ====================
mybatis-plus:
  configuration:
    # SQL 日志输出到控制台(开发调试用,生产环境建议关闭)
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
    # 自动驼峰命名转换(数据库下划线字段 → Java 驼峰属性)
    map-underscore-to-camel-case: true
  # Mapper XML 文件路径
  mapper-locations: classpath:mapper/*.xml
  global-config:
    db-config:
      # 主键生成策略:使用雪花算法生成分布式 ID
      id-type: assign_id

到这就整合好了,其实就将 Doris 当作 MySQL 就行,后面就是实现 RBAC,很简单,以 User 为例,其他代码不再赘述

User 实体类

java 复制代码
package com.hezy.entity;

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

import java.time.LocalDateTime;

/**
 * 用户实体,映射 sys_user 表
 *
 * @author hezy
 */
@Data
@TableName("sys_user")
public class User {

    /** 主键ID,使用 MyBatis-Plus 雪花算法自动生成 */
    @TableId(type = IdType.ASSIGN_ID)
    private Long id;

    /** 用户名 */
    private String username;

    /** 密码 */
    private String password;

    /** 邮箱 */
    private String email;

    /** 状态:0-禁用,1-启用 */
    private Integer status;

    /** 创建时间 */
    private LocalDateTime createTime;

    /** 更新时间 */
    private LocalDateTime updateTime;
}

UserMapper,继承 BaseMapper

java 复制代码
package com.hezy.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hezy.entity.User;
import org.apache.ibatis.annotations.Mapper;

/**
 * 用户表 Mapper
 *
 * @author hezy
 */
@Mapper
public interface UserMapper extends BaseMapper<User> {
}

UserService,继承 IService

java 复制代码
package com.hezy.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.hezy.entity.User;

/**
 * 用户 Service 接口
 *
 * @author hezy
 */
public interface UserService extends IService<User> {
}

UserServiceImpl,实现接口,Service 增强,MyBatis-Plus 的特性

java 复制代码
package com.hezy.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hezy.entity.User;
import com.hezy.mapper.UserMapper;
import com.hezy.service.UserService;
import org.springframework.stereotype.Service;

/**
 * 用户 Service 实现
 *
 * @author hezy
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}

UserController,用户接口

java 复制代码
package com.hezy.controller;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hezy.entity.Role;
import com.hezy.entity.User;
import com.hezy.entity.UserRole;
import com.hezy.mapper.RoleMapper;
import com.hezy.mapper.UserRoleMapper;
import com.hezy.service.UserService;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 用户管理 Controller
 * <p>
 * 提供用户 CRUD 以及为用户分配/查询角色的接口。
 * 角色分配采用"先删后插"策略,即每次分配都会清除旧的关联关系再写入新的。
 * </p>
 *
 * @author hezy
 */
@RestController
@RequestMapping("/users")
public class UserController {

    @Resource
    private UserService userService;

    @Resource
    private UserRoleMapper userRoleMapper;

    @Resource
    private RoleMapper roleMapper;

    /**
     * 分页查询用户,支持按用户名/邮箱模糊搜索
     *
     * @param page    页码,默认 1
     * @param size    每页条数,默认 10
     * @param keyword 搜索关键字(可选),匹配用户名或邮箱
     * @return total + records 分页结果
     */
    @GetMapping
    public Map<String, Object> page(@RequestParam(defaultValue = "1") Integer page,
                                     @RequestParam(defaultValue = "10") Integer size,
                                     @RequestParam(required = false) String keyword) {
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        if (StrUtil.isNotBlank(keyword)) {
            wrapper.like(User::getUsername, keyword).or().like(User::getEmail, keyword);
        }
        wrapper.orderByDesc(User::getCreateTime);
        Page<User> result = userService.page(new Page<>(page, size), wrapper);
        return Map.of("total", result.getTotal(), "records", result.getRecords());
    }

    /**
     * 新增用户
     *
     * @param user 用户信息,id 由雪花算法自动生成
     */
    @PostMapping
    public String create(@RequestBody User user) {
        user.setCreateTime(LocalDateTime.now());
        user.setUpdateTime(LocalDateTime.now());
        userService.save(user);
        return "ok";
    }

    /**
     * 根据 ID 更新用户信息
     *
     * @param id   用户ID
     * @param user 要更新的字段
     */
    @PutMapping("/{id}")
    public String update(@PathVariable Long id, @RequestBody User user) {
        user.setId(id);
        user.setUpdateTime(LocalDateTime.now());
        userService.updateById(user);
        return "ok";
    }

    /**
     * 根据 ID 删除用户
     *
     * @param id 用户ID
     */
    @DeleteMapping("/{id}")
    public String delete(@PathVariable Long id) {
        userService.removeById(id);
        return "ok";
    }

    /**
     * 为用户分配角色(先清除旧关联,再写入新关联)
     *
     * @param userId  用户ID
     * @param roleIds 要分配的角色ID列表
     */
    @PostMapping("/{userId}/roles")
    public String assignRoles(@PathVariable Long userId, @RequestBody List<Long> roleIds) {
        // 先删除用户原有的所有角色关联
        userRoleMapper.delete(new QueryWrapper<UserRole>().eq("user_id", userId));
        // 逐条插入新的角色关联
        for (Long roleId : roleIds) {
            UserRole ur = new UserRole();
            ur.setUserId(userId);
            ur.setRoleId(roleId);
            userRoleMapper.insert(ur);
        }
        return "ok";
    }

    /**
     * 查询用户当前拥有的角色列表
     *
     * @param userId 用户ID
     * @return 角色列表
     */
    @GetMapping("/{userId}/roles")
    public List<Role> getUserRoles(@PathVariable Long userId) {
        List<UserRole> userRoles = userRoleMapper.selectList(
                new QueryWrapper<UserRole>().eq("user_id", userId));
        if (userRoles.isEmpty()) {
            return List.of();
        }
        List<Long> roleIds = userRoles.stream()
                .map(UserRole::getRoleId)
                .collect(Collectors.toList());
        return roleMapper.selectBatchIds(roleIds);
    }
}

测试

启动前,先在 Doris 数据库里,将初始化脚本执行了

启动测试,没有报错,如果 Doris 配置写歪了,或者初始化脚本没执行,没对应数据库,都会报错

调用接口,查询成功,没得问题

相关推荐
鸽芷咕1 小时前
MySQL/PostgreSQL 迁移金仓 KES:LEFT JOIN 丢数据排查与避坑指南
数据库·mysql·postgresql
架构源启8 小时前
文档接入与智能解析:基于 Spring AI 1.1.x 的多格式解析、版面理解与结构化抽取
java·人工智能·spring
2401_8414956411 小时前
【数据结构】B+树
数据结构·数据库·c++·b+树·概念·结构·操作原理
大模型码小白12 小时前
JAVA 集合框架进阶:List 与 Set 的深度解析与实战
java·开发语言·人工智能·windows·语言模型·list·ai编程
Hardworking66613 小时前
第7章 软硬件系统集成
数据库·软硬件系统集成
名字还没想好☜13 小时前
Go 的 time.Ticker 陷阱:定时任务里被忽略的内存泄漏与正确关闭
java·数据库·golang·go·定时器
熊猫钓鱼>_>13 小时前
Redis 突发缓存穿透:一次完整的定位复盘
数据库·人工智能·redis·缓存·ai·agent·智能
音符犹如代码14 小时前
后端视角看 EventBus:发布订阅总线的原理、场景与用法
java·spring boot·guava
前端炒粉14 小时前
手撕小汇总
java·前端·javascript
wenb1n14 小时前
MySQL诊断系列(3/6):索引分析——5个SQL揪出“僵尸索引”
数据库·人工智能·编程语言