安装部署、SQL实战、代码连接、架构原理——一篇讲透国产数据库怎么用

如果你用过 Oracle 或 MySQL,那么上手达梦只需要搞清楚"它和它们像在哪里、又不一样在哪里"。这篇文章就是你的对照地图。


一、达梦数据库到底是什么?(5分钟搞懂)

1.1 一句话定义

达梦数据库(DM Database)是武汉达梦公司开发的关系型数据库管理系统(RDBMS),核心代码100%自研,兼容Oracle语法,支持Windows/Linux/麒麟/UOS等主流操作系统。

你可以把它理解成:一个中国版的Oracle——功能定位相似、SQL语法高度兼容、但代码完全自主可控。

1.2 核心产品线一览

产品 定位 一句话说明
DM8 新一代集中式关系型数据库 主力产品,支撑千亿级数据实时处理
DM7 企业级数据库平台 前代产品,仍在大量生产环境运行
DMDSC 共享存储集群 多节点高可用,对标Oracle RAC
DM MPP 分布式分析型数据库 海量数据分析,2025版效率提升60%
DMDPC 数据库一体机 软硬件一体交付的开箱即用方案

本文所有代码示例基于 DM8(当前最广泛使用的版本)。

1.3 架构:单进程多线程 vs Oracle的多进程

这是达梦和Oracle在底层架构上的核心差异:

┌─────────────────────────────────────────────┐
│              达梦 DM8 实例                    │
│                                             │
│  ┌─────────┐ ┌─────────┐ ┌──────────────┐  │
│  │监听线程  │ │工作线程  │ │  IO线程      │  │
│  │(接收连接)│ │(处理SQL)│ │(磁盘读写)    │  │
│  └─────────┘ └─────────┘ └──────────────┘  │
│                                             │
│         ┌──────────────────────┐           │
│         │   共享内存池          │           │
│         │  (数据缓冲+日志缓冲)  │           │
│         └──────────────────────┘           │
│                                             │
│  ┌──────────┐ ┌──────────┐ ┌───────────┐  │
│  │数据文件   │ │日志文件   │ │控制文件    │  │
│  │(.DBF)   │ │(.LOG)   │ │ (.CTL)    │  │
│  └──────────┘ └──────────┘ └───────────┘  │
└─────────────────────────────────────────────┘
​
对比 Oracle:
┌─────────────────────────────────────────────┐
│              Oracle 实例                      │
│  ┌───────┐ ┌───────┐ ┌───────┐ ┌────────┐ │
│  │PMON   │ │SMON   │ │DBWn   │ │LGWR   │ │ ← 独立进程
│  │(进程) │ │(进程) │ │(进程) │ │(进程) │ │
│  └───────┘ └───────┘ └───────┘ └────────┘ │
└─────────────────────────────────────────────┘

关键区别

  • 达梦:单进程 + 多线程模型,资源开销更小,启动更快

  • Oracle:多进程模型,每个功能模块是独立进程,隔离性更好但资源消耗更大

1.4 为什么会出现达梦?

简单说三个原因:

  1. 安全合规:金融、党政、军工等领域要求数据库必须自主可控,不能依赖国外产品

  2. 成本:Oracle的授权费用极高(企业版按CPU核心收费,一套下来几百万很正常),达梦的授权成本通常只有Oracle的1/3~1/5

  3. 本地化服务:原厂中文技术支持,响应速度远快于国外厂商


二、安装部署:手把手教你搭一个DM8环境

2.1 环境要求

项目 最低要求 推荐配置(生产环境)
CPU x86_64,2核以上 4核以上
内存 2GB 16GB+
磁盘 10GB(安装+基础数据) SSD,500GB+
操作系统 CentOS 7+/RedHat 7+/Ubuntu 16.04+ / Windows Server 2012+ / 麒麟V10 / 统信UOS 同左

2.2 Linux 安装步骤(CentOS 为例)

# ========== 第一步:创建专用用户 ==========
groupadd dinstall
useradd -g dinstall -m -d /home/dmdba -s /bin/bash dmdba
echo "dmdba:Dameng@123" | chpasswd
​
mkdir -p /dm8
chown -R dmdba:dinstall /dm8
chmod -R 755 /dm8
​
# ========== 第二步:上传并挂载安装包 ==========
mount -o loop /path/to/dm8_xxx.iso /mnt
​
# ========== 第三步:执行安装 ==========
su - dmdba
cd /mnt
./DMInstall.bin -i        # 交互式命令行安装
# 或图形化方式:./DMInstall.bin (需要 X Window/VNC)
​
# 安装过程中关键选择:
# 1. 语言:中文
# 2. 类型:典型安装(Typical)
# 3. 路径:/dm8/DAMENG
# ========== 第四步:初始化数据库实例 ==========
cd /dm8/DAMENG/bin
./dminit \
  PATH=/dm8/DAMENG/data \
  DB_NAME=DAMENG \
  INSTANCE_NAME=DMSERVER \
  PORT_NUM=5236 \
  PAGE_SIZE=16 \            # 页大小:8/16/32
  CHARSET=1 \               # 0=GB18030, 1=UTF-8, 2=EUC-KR
  CASE_SENSITIVE=Y          # 大小写敏感
# ========== 第五步:注册服务并启动 ==========
# 切换 root 执行
/dm8/DAMENG/script/root/dm_service_installer.sh \
  -t dmserver \
  -i /dm8/DAMENG/data/DAMENG/dm.ini \
  -p DMSERVER
​
systemctl start DmServiceDMSERVER
systemctl status DmServiceDMSERVER
# ========== 第六步:连接验证 ==========
/dm8/DAMENG/bin/disql SYSDBA/SYSDBA
# 看到 SQL> 提示符就说明成功了!

2.3 Windows 安装步骤

Windows 全程图形化向导:

  1. 解压 dm8_202xxxx_win64.zip → 双击 setup.exe

  2. 选中文 → 接受协议 → 典型安装

  3. 指定路径(如 E:\dmdbms

  4. 安装完成后弹出"数据库配置助手",创建实例:

    • 数据库名:DAMENG,端口:5236,默认用户:SYSDBA/SYSDBA

  5. 开始菜单 → 打开 DM管理工具disql 即可连接

2.4 安装后的目录结构速览

/dm8/DAMENG/
├── bin/
│   ├── disql                # 命令行工具(类似 sqlplus)
│   ├── dmservice.sh         # 服务管理
│   ├── dbca.sh              # 数据库配置助手
│   └── dmrman               # 备份恢复工具
├── data/DAMENG/
│   ├── dm.ini               # ★ 主配置文件(非常重要!)
│   ├── SYSTEM.DBF           # 系统表空间
│   ├── MAIN.DBF             # 用户表空间
│   └── TEMP.DBF             # 临时表空间
├── drivers/jdbc/
│   └── DmJdbcDriver18.jar   # JDBC驱动包(Java连接用!)
└── tools/manager/           # DM管理器(类似 SQL Developer)

三、SQL 实战:从建库到 CRUD 全流程

3.1 DM8 数据类型速查表

分类 DM8 类型 对应 Oracle 对应 MySQL 说明
整数 INT NUMBER(10) INT 4字节,±21亿
整数 BIGINT NUMBER(19) BIGINT 8字节
高精度数值 DECIMAL(M,D) NUMBER(M,D) DECIMAL(M,D) 如 DECIMAL(10,2)
浮点数 DOUBLE BINARY_DOUBLE DOUBLE 双精度浮点
字符串定长 CHAR(N) CHAR(N) CHAR(N) 最大8188字节
字符串变长 VARCHAR(N) VARCHAR2(N) VARCHAR(N) 最大32767字节
大文本 TEXT/CLOB CLOB LONGTEXT 最大2GB
二进制大对象 BLOB BLOB LONGBLOB 最大2GB
日期时间 DATE DATE DATETIME 包含年月日时分秒
时间戳 TIMESTAMP TIMESTAMP TIMESTAMP 更高精度
布尔 BIT 无(用NUMBER(1)) BOOLEAN 0或1

⚠️ 注意:DM8 的 DATE 类型包含时分秒(不同于 Oracle 的 DATE 只有年月日),这点更像 MySQL 的 DATETIME。

3.2 创建用户和表空间

-- =============================================
-- 以 SYSDBA 身份登录后执行
-- =============================================
​
-- 查看数据库状态和版本
SELECT status$ AS 状态 FROM v$instance;
SELECT banner AS 版本 FROM v$version;
​
-- ---------- 创建表空间 ----------
CREATE TABLESPACE app_tbs 
DATAFILE '/dm8/DAMENG/data/DAMENG/app_tbs.dbf' 
SIZE 128 AUTOEXTEND ON NEXT 32 MAXSIZE 10240;
​
-- ---------- 创建用户 ----------
CREATE USER app_user IDENTIFIED BY "App@2026"
DEFAULT TABLESPACE app_tbs;
​
-- 授予权限
GRANT RESOURCE TO app_user;
GRANT CREATE VIEW TO app_user;
GRANT CREATE PROCEDURE TO app_user;

3.3 建表实战:电商订单系统

下面用一个完整的电商场景演示 DM8 的完整操作:

-- =============================================
-- 以下 SQL 以 app_user 身份执行
-- =============================================
​
-- ---------- 1. 商品分类表 ----------
CREATE TABLE product_category (
    category_id     INT IDENTITY(1,1) PRIMARY KEY,
    category_name   VARCHAR(50) NOT NULL,
    parent_id       INT DEFAULT 0,
    sort_order      INT DEFAULT 0,
    status          TINYINT DEFAULT 1,
    created_at      TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
​
-- ---------- 2. 商品表 ----------
CREATE TABLE product (
    product_id      INT IDENTITY(1,1) PRIMARY KEY,
    category_id     INT NOT NULL,
    product_name    VARCHAR(200) NOT NULL,
    product_desc    TEXT,
    price           DECIMAL(10,2) NOT NULL,
    stock_qty       INT DEFAULT 0,
    sales_count     INT DEFAULT 0,
    status          TINYINT DEFAULT 1,
    created_at      TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    updated_at      TIMESTAMP,
    CONSTRAINT fk_product_category FOREIGN KEY (category_id) 
        REFERENCES product_category(category_id)
);
​
-- 创建索引
CREATE INDEX idx_product_category ON product(category_id);
CREATE INDEX idx_product_price ON product(price);
​
-- ---------- 3. 用户表 ----------
CREATE TABLE users (
    user_id         INT IDENTITY(1,1) PRIMARY KEY,
    username        VARCHAR(50) NOT NULL UNIQUE,
    email           VARCHAR(100),
    phone           VARCHAR(20),
    password_hash   VARCHAR(256) NOT NULL,
    register_time   TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    last_login      TIMESTAMP,
    status          TINYINT DEFAULT 1
);
​
CREATE UNIQUE INDEX idx_users_phone ON users(phone);
​
-- ---------- 4. 订单表 ----------
CREATE TABLE orders (
    order_id        BIGINT IDENTITY(1,1) PRIMARY KEY,
    order_no        VARCHAR(32) NOT NULL UNIQUE,
    user_id         INT NOT NULL,
    total_amount    DECIMAL(12,2) NOT NULL,
    pay_amount      DECIMAL(12,2),
    order_status    INT DEFAULT 0,
    pay_time        TIMESTAMP,
    created_at      TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    CONSTRAINT fk_orders_user FOREIGN KEY (user_id) 
        REFERENCES users(user_id)
);
​
-- ---------- 5. 订单明细表 ----------
CREATE TABLE order_item (
    item_id         BIGINT IDENTITY(1,1) PRIMARY KEY,
    order_id        BIGINT NOT NULL,
    product_id      INT NOT NULL,
    product_name    VARCHAR(200) NOT NULL,
    product_price   DECIMAL(10,2) NOT NULL,
    quantity        INT NOT NULL CHECK(quantity > 0),
    subtotal        DECIMAL(12,2) NOT NULL,
    CONSTRAINT fk_item_order FOREIGN KEY (order_id) 
        REFERENCES orders(order_id)
);
​
CREATE INDEX idx_orderitem_order ON order_item(order_id);

3.4 插入数据

-- 插入分类
INSERT INTO product_category (category_name, parent_id, sort_order) VALUES
('电子产品', 0, 1), ('手机', 1, 1), ('电脑', 1, 2),
('服装', 0, 2), ('男装', 4, 1), ('女装', 4, 2);
​
-- 插入商品
INSERT INTO product (category_id, product_name, price, stock_qty) VALUES
(2, '华为Mate60 Pro', 6999.00, 500),
(2, 'iPhone 15 Pro Max', 9999.00, 300),
(3, 'MacBook Pro 14 M3', 16999.00, 80),
(5, '优衣库纯棉T恤', 79.00, 2000);
​
-- 插入用户
INSERT INTO users (username, phone, password_hash) VALUES
('zhangsan', '13800138001', 'sha256_abc123'),
('lisi', '13800138002', 'sha256_def456');
​
-- 插入订单
INSERT INTO orders (order_no, user_id, total_amount, pay_amount, order_status) VALUES
('ORD20260627001', 1, 13998.00, 13998.00, 3),
('ORD20260627002', 2, 16999.00, 15499.00, 2);

3.5 查询数据(SELECT 实战)

-- ---------- 基础查询与分页 ----------
SELECT TOP 10 * FROM product WHERE status = 1 ORDER BY sales_count DESC;
-- 或者 MySQL 风格的分页:
SELECT * FROM product ORDER BY product_id LIMIT 0, 10;
​
-- 模糊搜索
SELECT * FROM product WHERE product_name LIKE '%iPhone%';
​
-- ---------- 聚合统计 ----------
SELECT 
    c.category_name,
    COUNT(p.product_id) AS 商品数量,
    AVG(p.price) AS 平均价格,
    SUM(p.stock_qty) AS 总库存
FROM product_category c
LEFT JOIN product p ON c.category_id = p.category_id
GROUP BY c.category_id, c.category_name
HAVING COUNT(p.product_id) > 0;
​
-- ---------- 多表关联查询:订单详情 ----------
SELECT 
    o.order_no AS 订单号,
    u.username AS 用户名,
    o.total_amount AS 订单总额,
    o.pay_amount AS 实付金额,
    o.order_status AS 状态,
    oi.product_name AS 商品名,
    oi.product_price AS 单价,
    oi.quantity AS 数量,
    oi.subtotal AS 小计
FROM orders o
INNER JOIN users u ON o.user_id = u.user_id
INNER JOIN order_item oi ON o.order_id = oi.order_id
WHERE o.order_status >= 1
ORDER BY o.created_at DESC;
​
-- ---------- 窗口函数(DM8 支持) ----------
-- 每个分类下价格排名前2的商品
SELECT * FROM (
    SELECT 
        product_name, price, category_name,
        ROW_NUMBER() OVER(PARTITION BY c.category_id ORDER BY p.price DESC) AS rn
    FROM product p
    JOIN product_category c ON p.category_id = c.category_id
) t WHERE rn <= 2;
​
-- ---------- 子查询 + EXISTS ----------
-- 查询有订单的用户
SELECT username, email, register_time 
FROM users u
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.user_id = u.user_id AND o.order_status = 3);

3.6 更新和删除

-- ---------- UPDATE ----------
-- 更新商品价格和库存
UPDATE product SET 
    price = 6499.00,
    updated_at = CURRENT_TIMESTAMP
WHERE product_id = 1;
​
-- 批量更新:某分类商品全部涨价5%
UPDATE product SET 
    price = price * 1.05,
    updated_at = CURRENT_TIMESTAMP
WHERE category_id = 2;
​
-- ---------- DELETE ----------
-- 删除已取消超过30天的订单
DELETE FROM orders 
WHERE order_status = 4 
  AND created_at < DATEADD(DAY, -30, CURRENT_TIMESTAMP);
​
-- 清空表(保留结构,更快)
TRUNCATE TABLE order_item;

3.7 存储过程实战

-- ---------- 存储过程1:批量插入测试数据 ----------
CREATE OR REPLACE PROCEDURE sp_gen_test_data(
    p_count IN INT          -- 生成条数
) AS
    v_i INT;
    v_product_name VARCHAR(200);
BEGIN
    FOR v_i IN 1..p_count LOOP
        v_product_name := '测试商品_' || v_i;
        INSERT INTO product (category_id, product_name, price, stock_qty)
        VALUES (2, v_product_name, ROUND(DBMS_RANDOM.VALUE(100, 10000), 2), FLOOR(DBMS_RANDOM.VALUE(1, 500)));
        
        -- 每1000条提交一次,避免日志过大
        IF MOD(v_i, 1000) = 0 THEN
            COMMIT;
            PRINT('已插入 ' || v_i || ' 条');
        END IF;
    END LOOP;
    COMMIT;
    PRINT('完成!共插入 ' || p_count || ' 条数据');
END;
​
-- 调用
CALL sp_gen_test_data(5000);
​
​
-- ---------- 存储过程2:根据员工ID查询部门(带输出参数) ----------
CREATE OR REPLACE PROCEDURE sp_get_user_order_stats(
    p_user_id IN INT,
    p_total OUT DECIMAL,        -- 输出:总消费金额
    p_order_cnt OUT INT          -- 输出:订单数量
) AS
BEGIN
    SELECT SUM(total_amount), COUNT(*) 
    INTO p_total, p_order_cnt
    FROM orders 
    WHERE user_id = p_user_id AND order_status >= 1;
END;
​
-- 调用(需要用匿名块接收输出参数)
DECLARE
    v_total DECIMAL(12,2);
    v_cnt INT;
BEGIN
    sp_get_user_order_stats(1, v_total, v_cnt);
    PRINT('总消费: ' || v_total || ', 订单数: ' || v_cnt);
END;
​
​
-- ---------- 存储过程3:带异常处理 ----------
CREATE OR REPLACE PROCEDURE sp_safe_update_price(
    p_product_id IN INT,
    p_new_price IN DECIMAL
) AS
    v_old_price DECIMAL(10,2);
BEGIN
    -- 先查旧价
    SELECT price INTO v_old_price FROM product WHERE product_id = p_product_id;
    
    IF v_old_price IS NULL THEN
        RAISE EXCEPTION '-20001', '商品不存在';
    ELSIF p_new_price <= 0 THEN
        RAISE EXCEPTION '-20002', '价格必须大于0';
    ELSE
        UPDATE product SET price = p_new_price, updated_at = CURRENT_TIMESTAMP
        WHERE product_id = p_product_id;
        PRINT('更新成功: ' || v_old_price || ' -> ' || p_new_price);
    END IF;
    
EXCEPTION
    WHEN OTHERS THEN
        PRINT('错误: ' || SQLCODE || ' - ' || SQLERRM);
END;

3.8 触发器实战

-- ---------- 场景:订单状态变更时自动记录审计日志 ----------
​
-- 先建审计日志表
CREATE TABLE order_audit_log (
    log_id          BIGINT IDENTITY(1,1) PRIMARY KEY,
    order_id        BIGINT NOT NULL,
    old_status      INT,
    new_status      INT,
    changed_by      VARCHAR(50) DEFAULT 'SYSTEM',
    change_time     TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    remark          VARCHAR(500)
);
​
-- 创建触发器:订单状态变更时自动记录
CREATE OR REPLACE TRIGGER trg_order_status_audit
AFTER UPDATE OF order_status ON orders
FOR EACH ROW
DECLARE
    v_status_map VARCHAR(100);
BEGIN
    -- 记录变更前后状态
    INSERT INTO order_audit_log (order_id, old_status, new_status, remark)
    VALUES (
        :OLD.order_id, 
        :OLD.order_status, 
        :NEW.order_status,
        '订单 ' || :OLD.order_no || ' 状态变更'
    );
    
    -- 如果变为"已完成",自动更新商品的销量
    IF :NEW.order_status = 3 AND :OLD.order_status != 3 THEN
        UPDATE product p
        SET sales_count = sales_count + (
            SELECT COALESCE(SUM(oi.quantity), 0) 
            FROM order_item oi WHERE oi.order_id = :NEW.order_id 
            AND oi.product_id = p.product_id
        )
        WHERE p.product_id IN (SELECT product_id FROM order_item WHERE order_id = :NEW.order_id);
    END IF;
END;
​
-- 测试触发器
UPDATE orders SET order_status = 3 WHERE order_id = 1;
-- 查看 audit log 是否自动插入了记录
SELECT * FROM order_audit_log ORDER BY log_id DESC;

四、Java/JDBC 连接达梦数据库

4.1 准备工作

  1. 从达梦安装目录获取驱动包:drivers/jdbc/DmJdbcDriver18.jar

  2. 在项目中引入该 jar 包(Maven 项目可手动 install 到本地仓库)

<!-- pom.xml 中添加(需先将jar安装到本地Maven仓库) -->
<dependency>
    <groupId>com.dameng</groupId>
    <artifactId>DmJdbcDriver18</artifactId>
    <version>8.1.2.138</version>
</dependency>

4.2 基础 JDBC 连接代码

package com.example.dm;
​
import java.sql.*;
​
/**
 * 达梦数据库 JDBC 连接示例
 * 驱动类:dm.jdbc.driver.DmDriver
 * 连接串格式:jdbc:dm://主机:端口
 */
public class DmJdbcDemo {
​
    // 数据库连接信息
    private static final String DRIVER = "dm.jdbc.driver.DmDriver";
    private static final String URL = "jdbc:dm://192.168.1.100:5236";
    private static final String USER = "app_user";
    private static final String PASSWORD = "App@2026";
​
    public static void main(String[] args) {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
​
        try {
            // 1. 加载驱动
            Class.forName(DRIVER);
​
            // 2. 建立连接
            conn = DriverManager.getConnection(URL, USER, PASSWORD);
            System.out.println("[OK] 连接成功!");
​
            // 3. 执行查询
            stmt = conn.createStatement();
            rs = stmt.executeQuery("SELECT product_id, product_name, price FROM product");
​
            // 4. 处理结果集
            while (rs.next()) {
                int id = rs.getInt("product_id");
                String name = rs.getString("product_name");
                double price = rs.getDouble("price");
                System.out.printf("%d | %s | %.2f%n", id, name, price);
            }
​
            // 5. 执行插入(使用 PreparedStatement 防注入)
            String insertSql = "INSERT INTO product (category_id, product_name, price, stock_qty) "
                             + "VALUES (?, ?, ?, ?)";
            try (PreparedStatement ps = conn.prepareStatement(insertSql)) {
                ps.setInt(1, 2);                          // category_id
                ps.setString(2, "小米15 Ultra");            // product_name
                ps.setDouble(3, 5999.00);                  // price
                ps.setInt(4, 200);                         // stock_qty
                
                int rows = ps.executeUpdate();
                System.out.println("[OK] 插入 " + rows + " 行");
            }
​
            // 6. 事务控制示例
            try {
                conn.setAutoCommit(false);  // 开启手动事务
​
                // 下单操作:扣库存 + 创建订单
                Statement txStmt = conn.createStatement();
                txStmt.executeUpdate("UPDATE product SET stock_qty = stock_qty - 1 WHERE product_id = 1");
                txStmt.executeUpdate("INSERT INTO orders (order_no, user_id, total_amount, order_status) "
                                   + "VALUES ('ORD_JDBC_001', 1, 6499.00, 1)");
​
                conn.commit();   // 提交事务
                System.out.println("[OK] 事务提交成功");
            } catch (Exception e) {
                conn.rollback();  // 出错回滚
                System.err.println("[ERR] 事务回滚: " + e.getMessage());
            }
​
        } catch (ClassNotFoundException e) {
            System.err.println("[ERR] 驱动未找到: " + e.getMessage());
        } catch (SQLException e) {
            System.err.println("[ERR] SQL异常: " + e.getMessage());
        } finally {
            // 7. 关闭资源(按逆序关闭)
            if (rs != null) try { rs.close(); } catch (SQLException ignored) {}
            if (stmt != null) try { stmt.close(); } catch (SQLException ignored) {}
            if (conn != null) try { conn.close(); } catch (SQLException ignored) {}
        }
    }
}

4.3 HikariCP 连接池配置

生产环境必须用连接池。以下是 Spring Boot + HikariCP 配置:

# application.yml
spring:
  datasource:
    driver-class-name: dm.jdbc.driver.DmDriver
    url: jdbc:dm://192.168.1.100:5236?SCHEMA=APP_USER&loginMode=1
    username: app_user
    password: App@2026
    hikari:
      maximum-pool-size: 20
      minimum-idle: 5
      connection-timeout: 30000
      idle-timeout: 600000
      max-lifetime: 1800000

4.4 MyBatis 映射示例

<!-- ProductMapper.xml -->
<mapper namespace="com.example.mapper.ProductMapper">
​
    <!-- 结果映射 -->
    <resultMap id="BaseResultMap" type="com.example.entity.Product">
        <id column="product_id" property="productId"/>
        <result column="product_name" property="productName"/>
        <result column="price" property="price"/>
        <result column="stock_qty" property="stockQty"/>
        <result column="sales_count" property="salesCount"/>
        <result column="status" property="status"/>
        <result column="created_at" property="createdAt"/>
    </resultMap>
​
    <!-- 分页查询 -->
    <select id="selectByPage" resultMap="BaseResultMap">
        SELECT * FROM product
        WHERE status = 1
        <if test="keyword != null and keyword != ''">
            AND product_name LIKE CONCAT('%', #{keyword}, '%')
        </if>
        <if test="categoryId != null">
            AND category_id = #{categoryId}
        </if>
        ORDER BY product_id
    </select>
​
    <!-- 批量插入 -->
    <insert id="batchInsert">
        INSERT INTO product (category_id, product_name, price, stock_qty)
        VALUES
        <foreach collection="list" item="item" separator=",">
            (#{item.categoryId}, #{item.productName}, #{item.price}, #{item.stockQty})
        </foreach>
    </insert>
​
</mapper>

五、Python 连接达梦数据库

5.1 方式一:通过 JayDeBeApi(JDBC桥接)

"""
Python 通过 JDBC 连接达梦数据库
依赖:pip install JayDeBeApi Jpype1
"""
import jaydebeapi
import pandas as pd
​
# ======== 配置 ========
DM_HOST = "192.168.1.100"
DM_PORT = "5236"
DM_USER = "app_user"
DM_PASS = "App@2026"
JDBC_JAR = "/path/to/DmJdbcDriver18.jar"  # 改为你的实际路径
​
​
def get_connection():
    """建立数据库连接"""
    url = f"jdbc:dm://{DM_HOST}:{DM_PORT}"
    driver = "dm.jdbc.driver.DmDriver"
    conn = jaydebeapi.connect(
        driver,
        url,
        [DM_USER, DM_PASS],
        JDBC_JAR
    )
    return conn
​
​
def query_to_dataframe(sql: str) -> pd.DataFrame:
    """执行查询并返回 DataFrame"""
    conn = get_connection()
    try:
        df = pd.read_sql(sql, conn)
        return df
    finally:
        conn.close()
​
​
def execute_sql(sql: str):
    """执行增删改操作"""
    conn = get_connection()
    cursor = conn.cursor()
    try:
        rows = cursor.execute(sql)
        conn.commit()
        print(f"[OK] 影响 {rows} 行")
    except Exception as e:
        conn.rollback()
        print(f"[ERR] {e}")
        raise
    finally:
        cursor.close()
        conn.close()
​
​
# ======== 使用示例 ========
if __name__ == "__main__":
    # 查询所有商品
    df = query_to_dataframe("""
        SELECT product_id, product_name, price, stock_qty 
        FROM product WHERE status = 1
    """)
    print(df.head())
    print(f"共 {len(df)} 条记录")
​
    # 统计各分类商品数
    stats = query_to_dataframe("""
        SELECT c.category_name, COUNT(p.product_id) AS cnt
        FROM product_category c
        LEFT JOIN product p ON c.category_id = p.category_id
        GROUP BY c.category_name
    """)
    print("\n分类统计:")
    print(stats)

5.2 方式二:通过 SQLAlchemy(如果可用)

"""
注意:达梦没有官方的 Python DB-API 2.0 驱动,
目前最稳定的方式是通过 JayDeBeApi 做 JDBC 桥接。
如果项目使用了 Django/SQLAlchemy,可以封装一个自定义方言。
"""
from sqlalchemy import create_engine, text
​
# 使用 JayDeBeApi 的 SQLAlchemy URI 格式
engine = create_engine(
    "dm+jaydebeapi://app_user:App@2026@192.168.1.100:5236/DAMENG",
    connect_args={
        "jar_path": "/path/to/DmJdbcDriver18.jar"
    }
)
​
with engine.connect() as conn:
    result = conn.execute(text("SELECT COUNT(*) FROM product"))
    count = result.scalar()
    print(f"商品总数: {count}")

六、达梦 vs Oracle vs MySQL 核心语法差异对照

这是实际迁移中最常碰到的问题,建议收藏:

操作 Oracle MySQL 达梦 DM8 备注
自增主键 SEQUENCE + TRIGGER AUTO_INCREMENT IDENTITY(1,1) DM8 用 IDENTITY 最方便
分页 ROWNUM / FETCH FIRST LIMIT offset,count TOP NLIMIT 三种都支持 LIMIT
字符串拼接 \|\|CONCAT() CONCAT() \|\|CONCAT() 都行 兼容 Oracle
当前时间 SYSDATE NOW() SYSDATECURRENT_TIMESTAMP 兼容 Oracle
序列 CREATE SEQUENCE ... 无(用AUTO_INCREMENT) CREATE SEQUENCE ... 完整兼容 Oracle 序列
匿名块 BEGIN...END; 不支持 BEGIN...END; 支持 PL/SQL 风格
存储过程 CREATE PROCEDURE CREATE PROCEDURE CREATE OR REPLACE PROCEDURE 语法几乎一致
触发器 CREATE TRIGGER CREATE TRIGGER CREATE TRIGGER 支持 BEFORE/AFTER/FOR EACH ROW
窗口函数 完整支持 8.0+支持 完整支持 ✅ ROW_NUMBER/RANK/DENSE_RANK 都有
递归CTE WITH RECURSIVE 8.0+ WITH WITH ... AS 递归 ✅ 支持
JSON 自带JSON函数 JSON函数丰富 需装扩展包或用 dm_json_* 函数 相对较弱
日期加减 date + num / ADD_MONTHS DATE_ADD() DATEADD() / 直接加减数字 注意函数名不同
NVL空值处理 NVL(), NVL2() IFNULL(), COALESCE() NVL(), COALESCE(), NULLIF() 兼容 Oracle
DECODE函数 DECODE() CASE WHEN DECODE() DM8 特有兼容
序列取值 NEXTVAL, CURRVAL NEXTVAL NEXTVAL, CURRVAL 完全一致
双引号标识符 "COLUMN" `COLUMN` "COLUMN" 兼容 Oracle 风格
注释 COMMENT ON ALTER TABLE ... COMMENT COMMENT ON 兼容 Oracle

迁移建议:如果你的系统原来跑在 Oracle 上,迁移到达梦的成本主要在自定义存储过程复杂SQL语句的适配上。90%以上的标准SQL可以直接跑通。


七、达梦能干什么?典型应用场景

7.1 金融行业(核心交易系统)

银行核心账务系统
├── 账户管理(储蓄、贷款、信用卡)
├── 交易流水(每秒数千笔 TPS)
├── 对账清算(日终批处理)
└── 高可用要求:同城双活 + 异地灾备
​
→ 技术选型:DM8 + DMDSC 共享存储集群
→ 关键指标:故障切换 ≤ 14秒(同城)/ ≤ 32秒(异地)
→ 兼容性:原有 Oracle 存储过程大部分可直接迁移

7.2 电力/能源行业(SCADA数据采集)

智能电网调度系统
├── 实时数据采集(每秒百万级测点)
├── 历史数据存储(PB级时序数据)
├── 报警与事件管理
└── 数据分析与报表
​
→ 技术选型:DM8(OLTP)+ DM MPP(OLAP)混合架构
→ 优势:列式存储加速分析查询,分布式并行处理

7.3 党政机关(OA/政务系统)

政务服务平台
├── 用户认证与权限管理
├── 业务办理(审批、办件)
├── 数据共享交换
└── 安全合规(等保四级认证)
​
→ 技术选型:标准 DM8 单实例或主备集群
→ 优势:自主可控、安全认证齐全、本地化服务

7.4 电商/互联网业务

电商交易平台
├── 商品管理与搜索
├── 订单与支付
├── 库存管理
├── 用户中心
└── 数据分析(BI报表)
​
→ 技术选型:DM8 + 应用层缓存(Redis)
→ 适用规模:中小型电商平台(日订单万级~十万级)
→ 注意:超大规模互联网应用建议评估云原生方案

八、运维常用命令速查

-- =============================================
-- 以下为 DBA 日常运维常用 SQL
-- =============================================
​
-- ---------- 查看实例信息 ----------
SELECT * FROM v$instance;                    -- 实例基本信息
SELECT * FROM v$version;                     -- 版本信息
SELECT name, status$, total_size, free_size FROM v$tablespace;  -- 表空间使用情况
​
-- ---------- 查看会话和锁 ----------
SELECT * FROM v$sessions WHERE state='ACTIVE';   -- 当前活跃会话
SELECT * FROM v$trxwait;                            -- 等待事务
SELECT * FROM v$locks;                              -- 锁信息
​
-- ---------- 手动杀掉死锁会话 ----------
-- 先找到阻塞会话的 sess_id
SELECT s.sess_id, s.sql_text, s.state, s.clnt_ip
FROM v$sessions s
JOIN v$trxwait w ON s.trxid = w.wait_trxid;
​
-- 杀掉会话(替换实际的sess_id)
SP_CLOSE_SESSION(123456789);
​
-- ---------- 表空间扩容 ----------
-- 方式1:修改数据文件大小
ALTER TABLESPACE MAIN ADD DATAFILE '/dm8/DAMENG/data/DAMENG/MAIN_01.DBF' SIZE 1024 AUTOEXTEND ON NEXT 128 MAXSIZE 10240;
​
-- 方式2:resize已有数据文件
ALTER DATABASE RESIZE DATAFILE '/dm8/DAMENG/data/DAMENG/MAIN.DBF' TO 2048;
​
-- ---------- 用户管理 ----------
ALTER USER app_user IDENTIFIED BY "NewPassword@2026";  -- 修改密码
LOCK USER app_user;                                      -- 锁定用户
UNLOCK USER app_user;                                    -- 解锁用户
​
-- ---------- 备份与恢复 ----------
-- 全量备份(物理备份,需要在 disql 或 dmrman 中执行)
BACKUP DATABASE FULL TO "full_bak_20260627" BACKUPSET '/dm8/backup/full_bak_20260627';
​
-- 增量备份
BACKUP DATABASE INCREMENT TO "inc_bak_20260627" BACKUPSET '/dm8/backup/inc_bak_20260627';
​
-- 恿复(在 dmrman 工具中执行)
RESTORE DATABASE FROM BACKUPSET '/dm8/backup/full_bak_20260627';
​
-- ---------- 性能查看 ----------
-- 查看Top 10 慢SQL
SELECT *
FROM (
    SELECT sql_text, executions, elapsed_time / executions AS avg_time, rows_processed
    FROM v$sqlstat
    WHERE executions > 0
    ORDER BY elapsed_time DESC
) WHERE ROWNUM <= 10;
​
-- 查看表的大小
SELECT table_name, 
       (SELECT COUNT(*) FROM user_indexes i WHERE i.table_name = t.table_name) AS index_count
FROM user_tables t
ORDER BY table_name;

九、从零上手的学习路线图

如果你是第一次接触达梦数据库,推荐按以下顺序学习:

第1周:基础入门
  ├─ Day 1-2:安装 DM8(Linux 或 Windows),熟悉 disql 命令行工具
  ├─ Day 3-4:学习本文章第三节的 SQL CRUD 操作
  └─ Day 5-7:完成一个小练习(如建一个学生成绩管理系统)
​
第2周:进阶操作
  ├─ 学习存储过程和触发器(第三节 3.7-3.8)
  ├─ 学习索引优化和执行计划分析
  └─ 尝试用 Java/Python 连接数据库(第四、五节)
​
第3周:运维与管理
  ├─ 学习备份恢复策略
  ├─ 了解 DMDSC 集群原理
  └─ 学习性能调优方法
​
第4周:实战迁移
  ├─ 把一个已有的 MySQL 小项目迁移到 DM8
  ├─ 解决迁移过程中的语法差异问题
  └─ 编写自动化迁移脚本

达梦数据库不是什么神秘的东西——它就是一个功能完整的、兼容Oracle语法的、中国人自己写的关系型数据库。

如果你已经会用 MySQL 或 Oracle,那么上手 DM8 的学习曲线大概就是 2-3天能跑起来,1-2周能熟练使用,1个月能独立完成中小项目的开发和运维

真正有价值的不是记住每一个SQL语法,而是理解什么时候该用达梦、怎么用好达梦

  • 需要自主可控 → 达梦是首选之一

  • 从Oracle迁移 → 迁移成本最低的国产数据库

  • 金融/电力/党政 → 达梦在这些领域有大量成熟案例

  • 全新互联网项目 → 可以考虑,但也要评估社区生态和云原生能力

希望这篇技术指南能帮你快速迈出第一步。最好的学习方法永远是动手——打开终端,敲下第一条 SQL,让数据在你的屏幕上流动起来。


本文所有代码基于 DM8 V8.1.x 版本实测编写,不同版本可能存在细微差异。如有疑问请参考达梦官方文档《DM8_SQL语言手册》。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐