【DM系列】DM 集成 JDBC 开发指南

前言

数据库访问是数据库应用系统中非常重要的组成部分,DM 作为一个通用数据库管理系统,提供了多种数据库访问接口,包括 ODBC、JDBC、DPI 等方式。本开发指南详细介绍了 DM 的各种访问接口、相应开发环境的配置、以及一些开发用例。本指南的主要读者是从事过数据库应用系统开发,并具有 SQL 使用基础的程序员。开发一个应用系统,需要对其使用的数据库管理系统所具有的功能、性能、特性有较深入的了解。DM 作为一个通用的数据库管理系统,具有非常丰富的功能和特色。

测试工具 说明
开发工具 IDEA 2024
数据库版本 DM8
JDBC驱动包 DmJdbcDriver18
JDK环境 17

一、概述

1.1 JDBC介绍

在实际项目开发中,操作数据库是必不可少的,常用的操作数据库的框架,如:MyBatis、JdbcTemplate等有很多。但是,无论使用哪种框架操作数据库,最底层的api实现都是JDBC,就是说,在开发中,JDBC有着举足轻重的地位,是最基础也是最核心的。

JDBC 是 Java 应用程序与数据库的接口规范,旨在让各数据库开发商为 Java 程序员提供标准的数据库应用程序编程接口,其定义了一个跨数据库、跨平台的通用 SQL 数据库 API。DM JDBC 驱动程序是 DM 数据库的 JDBC 驱动程序,它是一个能够支持基本SQL功能的通用应用程序编程接口,支持一般的SQL数据库访问。通过 JDBC 驱动程序,用户可以在应用程序中实现对 DM 数据库的连接与访问,JDBC 驱动程序的主要功能包括:建立与DM数据库的连接、转接发送SQL语句到数据库以及处理并返回语句执行结果。

利用JDBC 驱动程序进行编程的一般步骤为:

序号 步骤 说明
1 获取 java.sql.Connection对象 利用 DriverManager 或者数据库源来建立同数据库的连接。
2 创建 java.sql.Statement 对象 这里也包含了 java.sql.PreparedStatement 和 java.sql.CallableStatement对象。 利用连接对象的创建语句对象的方法来创建,在创建过程中根据需要来设置结果集的属性。
3 操作数据库 操作数据库分为两种情况,更新操作和查询操作。 查询操作执行完成后会得到一个 java.sql.ResultSet 对象,可以操作该对象来获得指定列的信息。
4 释放资源 操作完成之后,用户需要释放系统资源,主要是关闭结果集、关闭语句对象,释放连接。

1.2 建立JDBC连接

获得 JDBC 的连接对象,是通过驱动管理器 DriverManager 的 getConnection 方法来建立。若是想要用 Java 连接达梦,需要使用DM JDBC 驱动,其位于 DM 安装目录 /dmdbms/drivers/jdbc 下,将其路径添加到项目工程下,也可以通过maven等方式将对应的依赖引入,相关配置如下:

xml 复制代码
<!-- https://mvnrepository.com/artifact/com.dameng/DmJdbcDriver18 -->
<dependency>
	<groupId>com.dameng</groupId>
	<artifactId>DmJdbcDriver18</artifactId>
    <version>${dm.version}</version>
</dependency>

注意:使用 JDBC 驱动时,需要使用和服务器端 DM 数据库相近的版本,相隔时间太久的版本,可能出现部分功能无法适配的情况。达梦的 JDBC 驱动目前有三个版本:DmJdbcDriver16.jar、DmJdbcDriver17.jar、DmJdbcDriver18.jar,分别对应 jdk 1.6、jdk 1.7、jdk 1.8 及以上版本,请根据开发环境选择合适的 DM JDBC 驱动包。

建立配置文件 jdbc.properties文件,用来存放数据库连接配置。properties 文件是Java 支持的一种配置文件类型,将此文件直接放在 /src/main/resources 目录下,不然后面执行可能找不到此配置文件!

properties 复制代码
# 定义 DM JDBC 驱动串
db.driverClassName=dm.jdbc.driver.DmDriver
# 定义 DM URL 连接串
db.url=jdbc:dm://localhost:5236
# 定义连接用户名
db.user=SYSDBA
# 定义连接用户口令
db.password=SYSDBA

二、基础操作示例

在DM数据库中,表是用于存储结构化数据的核心组件,其基本结构是由行和列组成,每个表具有唯一名称,用于在数据库中标识它。表中的每个列表示数据的一个属性,每一行表示一个完整的数据记录。表是DM数据库中最基本的存储单元,所有的数据操作都围绕表展开。数据操作主要分为两个方面,一个是更新操作,例如更新数据库、删除一行、创建一个新表等;另一个就是查询操作,执行完查询之后,会得到一个 java.sql.ResultSet 对象,可以操作该对象来获得指定列的信息、读取指定行的某一列的值。

2.1 DM 连接示例

连接数据库是一项非常重要的功能,这里讲介绍如何连接 DM 数据库。创建工具类 DmJdbcHelperUtil.java,之后的增删改查操作可直接导入这个工具类完成获取连接,释放资源的操作,很方便!废话就不多说,直接上代码,如下所示:

java 复制代码
import cn.hutool.setting.dialect.Props;
import cn.hutool.setting.dialect.PropsUtil;
import java.sql.*;

public final class DmJdbcHelperUtil {
    /**
     * 获取连接的方法. 通过读取配置文件从数据库服务器获取一个连接.
     */
    public static Connection getDmJdbcConnection() {
        Props props = PropsUtil.get("jdbc.properties");
        // 定义DM JDBC驱动串
        String jdbcString = props.getStr("db.driverClassName");
        // 定义DM URL连接串
        String urlString = props.getStr("db.url");
        // 定义连接用户名
        String userName = props.getStr("db.user");
        // 定义连接用户口令
        String password = props.getStr("db.password");

        // 定义连接对象
        Connection connection = null;
        try {
            // 1、加载 JDBC 驱动程序(对应的 Driver 实现类中有注册驱动的静态代码块)
            System.out.println("加载 JDBC 驱动...");
            Class.forName(jdbcString);

            // 2、通过 DriverManager 的 getConnection() 方法获取数据库连接
            System.out.println("连接 DM 数据库...");
            connection = DriverManager.getConnection(urlString, userName, password);
            connection.setAutoCommit(true);
            System.out.println("[SUCCESS]conn database");

        } catch (Exception exp) {
            System.out.println("[FAIL]conn database:" + exp.getMessage());
        }
        return connection;
    }

    /**
     * 释放资源,关闭 ResultSet、Statement、Connection
     */
    public static void release(ResultSet resultSet, Statement statement, Connection conn) throws SQLException {
        if (resultSet != null) {
            resultSet.close();
        }

        if (statement != null) {
            statement.close();
        }

        if (conn != null) {
            conn.close();
        }
    }
}

运行后控制台输出如下图所示:

在操作完成之后,用户需要释放系统资源,主要是关闭结果集、关闭语句对象,释放连接。当然,这些动作也可以由 JDBC 驱动程序自动执行,但由于 Java 语言的特点,这个过程会比较慢(需要等到 Java 进行垃圾回收时进行),容易出现意想不到的问题。

2.2 表的创建与管理

DM数据库使用SQL语句来创建和管理表。创建表的基本语法是 CREATE TABLE 语句,后跟表名和列定义。列定义包括列名、数据类型和约束条件。例如:

java 复制代码
@Test
public void createTable() throws SQLException {
    Connection conn = DmJdbcHelperUtil.getDmJdbcConnection();
    // 创建表语句
    String createSql = "CREATE TABLE city ( id CHAR(3) NOT NULL, name VARCHAR(40) NULL, region INT NULL );";
    // 创建语句对象
    PreparedStatement preparedStatement = conn.prepareStatement(createSql);
    // 执行语句
    preparedStatement.executeUpdate();
    // 关闭语句
    DmJdbcHelperUtil.release(null, preparedStatement, conn);
}

创建表后,可以使用 ALTER TABLE 语句对表进行修改。例如,添加新列、修改列的数据类型、更改约束条件等。删除表可以使用 DROP TABLE 语句,但需要谨慎操作,因为删除表将永久删除表中的所有数据。

2.3 数据的基本操作

插入数据

在表中插入数据使用 INSERT INTO 语句,指定表名和要插入的数据。例如:

sql 复制代码
INSERT INTO city(id, name, region) VALUES('BJ', '北京', 1); 
INSERT INTO city(id, name, region) VALUES('SJZ', '石家庄', 1); 
INSERT INTO city(id, name, region) VALUES('SH', '上海', 2); 
INSERT INTO city(id, name, region) VALUES('NJ', '南京', 2); 
INSERT INTO city(id, name, region) VALUES('GZ', '广州', 3); 
INSERT INTO city(id, name, region) VALUES('HK', '海口', 3); 
INSERT INTO city(id, name, region) VALUES('WH', '武汉', 4); 
INSERT INTO city(id, name, region) VALUES('CS', '长沙', 4); 
INSERT INTO city(id, name, region) VALUES('SY', '沈阳', 5); 
INSERT INTO city(id, name, region) VALUES('XA', '西安', 6); 
INSERT INTO city(id, name, region) VALUES('CD', '成都', 7);
java 复制代码
@Test
public void insertTable() throws SQLException {
    Connection conn = DmJdbcHelperUtil.getDmJdbcConnection();
    // 创建表语句
    String insertSql = "INSERT INTO city(id, name, region) VALUES('BJ', '北京', 1)";
    // 创建语句对象
    Statement statement = conn.createStatement();
    // 执行语句
    statement.execute(insertSql);
    // 关闭语句
    DmJdbcHelperUtil.release(null, statement, conn);
    System.out.println("插入成功");
}

在连接建立后,需要对数据库进行访问,执行命令或是SQL语句,可以通过 Statement 对象执行静态SQL语句并返回其生成结果。但 Statement 对象执行SQL语句存在SQL注入风险,为防范 SQL注入,需要用 PreparedStatement 取代 Statement。

java 复制代码
@Test
public void insertTable() throws SQLException {
    Connection conn = DmJdbcHelperUtil.getDmJdbcConnection();
    // 创建表语句
    String insertSql = "INSERT INTO city(id, name, region) VALUES('BJ', '北京', 1)";
    // 创建语句对象
    PreparedStatement preparedStatement = conn.prepareStatement(insertSql);
    // 执行语句
    preparedStatement.executeUpdate();
    // 关闭语句
    DmJdbcHelperUtil.release(null, preparedStatement, conn);
    System.out.println("插入成功");
}

删除数据

java 复制代码
@Test
public void deleteTable() throws SQLException {
    Connection conn = DmJdbcHelperUtil.getDmJdbcConnection();
    // 删除数据语句
    String deleteSql = "delete from city where id = 'BJ'";
    // 创建语句对象
    PreparedStatement preparedStatement = conn.prepareStatement(deleteSql);
    // 执行语句
    preparedStatement.executeUpdate();
    DmJdbcHelperUtil.release(null, preparedStatement, conn);
    System.out.println("删除成功");
}

修改数据

java 复制代码
@Test
public void updateTable() throws SQLException {
    Connection conn = DmJdbcHelperUtil.getDmJdbcConnection();
    // 更新数据语句
    String updateSql = "update city set name = '北京_BJ' where id = 'BJ'";
    // 创建语句对象
    PreparedStatement preparedStatement = conn.prepareStatement(updateSql);
    // 执行语句
    preparedStatement.executeUpdate();
    // 关闭语句
    DmJdbcHelperUtil.release(null, preparedStatement, conn);
    System.out.println("更新成功");
}

查询数据

查询表中的数据使用SELECT语句,可以指定要查询的列和查询条件。处理查询结果集,获取当前光标指向行中的数据,有三种方式,如下所示:

java 复制代码
@Test
public void queryTable() throws SQLException {
    Connection conn = DmJdbcHelperUtil.getDmJdbcConnection();
    // 查询语句
    String querySql = "SELECT * FROM city";
    // 创建语句对象
    PreparedStatement preparedStatement = conn.prepareStatement(querySql);
    // 执行查询
    ResultSet resultSet = preparedStatement.executeQuery();
    // 显示结果集
    displayResultSet(resultSet);
    // 关闭
    DmJdbcHelperUtil.release(resultSet, preparedStatement, conn);
}

/**
 * 处理查询结果集。
 * 
 * 获取当前光标指向行中的数据,有三种方式:
 * 
 * 第一种方式:根据字段下标获取。不管数据库表中的字段是什么类型,都以字符串方式取出。
 * 第二种方式:通过结果集中字段名称获取数据,该方式的程序更加健壮,建议使用。
 * 第三种方式:通过特定类型获取数据。该方式可以节省类型转换花费的性能,程序更加健壮,性能更高,推荐使用
 *
 */
private void displayResultSet(ResultSet resultSet) throws SQLException {
    // 将光标向前移动一行,如果指向当前行有记录,则返回true,若指向当前行无记录,返回false
    while (resultSet.next()) {
        /*
         * 第一种方式
         */
        String id1 = resultSet.getString(1);
        String name1 = resultSet.getString(2);
        System.out.println("id1:" + id1 + ",名称1:" + name1);

        /*
         * 第二种方式
         */
        String id2 = resultSet.getString("id");
        String name2 = resultSet.getString("name");
        System.out.println("id2:" + id2 + ",名称2:" + name2);

        /*
         * 第三种方式
         */
        String id3 = resultSet.getString("id");
        String name3 = resultSet.getString("name");
        System.out.println("id3:" + id3 + ",名称3:" + name3);
    }
}

绑定变量示例

上面提到为防范 SQL 注入,需要用 PreparedStatement 对象执行 SQL 语句。PreparedStatement 执行 SQL 语句中的参数用(?)表示,调用PreparedStatement 对象的 setXXX() 方法来设置这些参数,setXXX() 方法有两个参数,第一个参数是要设置的SQL语句中的索引(从1开始),第二个是设置的SQL语句中参数的值。调用 executeQuery() 返回ResultSet对象,调用 executeUpdate() 执行更新操作,包括增、删、修改等。

java 复制代码
@Test
public void preparedStatementTest() throws SQLException {
    Connection conn = DmJdbcHelperUtil.getDmJdbcConnection();
    // 更新数据语句
    String updateSql = "update city set name = ? where id = ?";
    // 创建语句对象
    PreparedStatement preparedStatement = conn.prepareStatement(updateSql);
    // 通过连接对象和修改语句的模板,创建 java.sql.PreparedStatement 对象
    preparedStatement.setString(1, "北京_BJ");
    preparedStatement.setString(2, "BJ");
    // 执行语句
    preparedStatement.executeUpdate();
    // 关闭语句
    DmJdbcHelperUtil.release(null, preparedStatement, conn);
    System.out.println("更新成功");
}

2.4 元数据MetaData

DatabaseMetaData 用于获取数据库的元数据信息,包括表名、表的索引、数据库产品的名称和版本、数据库支持的操作等等。它是Java Database Connectivity(JDBC)的一部分,为开发人员提供了访问数据库元数据的便捷途径。

获取数据库的元信息

java 复制代码
public static void getDatabaseMetaData() throws SQLException {
    Connection connection = DmJdbcHelperUtil.getDmJdbcConnection();
    // 获取DatabaseMetaData对象
    DatabaseMetaData metaData = connection.getMetaData();

    // 打印数据库的一些基本信息
    System.out.println("数据库名称: " + metaData.getDatabaseProductName());
    System.out.println("数据库版本: " + metaData.getDatabaseProductVersion());
    System.out.println("数据库的主版本:" + metaData.getDatabaseMajorVersion());
    System.out.println("数据库的小版本:" + metaData.getDatabaseMinorVersion());
    System.out.println("数据库支持的SQL语法: " + metaData.getSQLKeywords());
    System.out.println("JDBC驱动名称: " + metaData.getDriverName());
    System.out.println("JDBC驱动版本: " + metaData.getDriverVersion());
    System.out.println("数据库已知的用户: " + metaData.getUserName());
    System.out.println("数据库的系统函数的逗号分隔列表: " + metaData.getSystemFunctions());
    System.out.println("数据库的时间和日期函数的逗号分隔列表: " + metaData.getTimeDateFunctions());
    System.out.println("数据库的字符串函数的逗号分隔列表: " + metaData.getStringFunctions());
    System.out.println("数据库供应商用于 'schema' 的首选术语: " + metaData.getSchemaTerm());
    System.out.println("数据库URL: " + metaData.getURL());
    System.out.println("是否允许只读:" + metaData.isReadOnly());
    System.out.println("驱动程序的名称:" + metaData.getDriverName());
    System.out.println("驱动程序的版本:" + metaData.getDriverVersion());
}

获取数据库的所有表

java 复制代码
public static void getTableList() throws SQLException {
    Connection conn = DmJdbcHelperUtil.getDmJdbcConnection();
    // 获取Meta信息对象
    DatabaseMetaData meta = conn.getMetaData();
    // 数据库所有者的用户名,一般将它设置为 null。
    String schemaPattern = meta.getUserName();
    // 检索的表的名称,这里设置null,则代表检索全部的表
    String tableNamePattern = null;
    // 想要检索的表的类型
    String[] types = {"TABLE"};
    ResultSet resultSet = meta.getTables(null, schemaPattern, tableNamePattern, types);
    while (resultSet.next()) {
        System.out.println("表名: " + resultSet.getString("TABLE_NAME"));
        System.out.println("注释:" + resultSet.getString("REMARKS"));
    }
}

列出表的所有字段

java 复制代码
public static void getColumnList(String tableName) throws SQLException {
    Connection conn = DmJdbcHelperUtil.getDmJdbcConnection();
    // 获取Meta信息对象
    DatabaseMetaData meta = conn.getMetaData();
    // 数据库的用户
    String schemaPattern = meta.getUserName();
    // 表名
    String tableNamePattern = tableName;//
    // 转换为大写
    if (null != tableNamePattern) {
        tableNamePattern = tableNamePattern.toUpperCase();
    }
    String columnNamePattern = null;
    ResultSet resultSet = meta.getColumns(null, schemaPattern, tableNamePattern, null);
    while (resultSet.next()) {
        ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
        int colNum = resultSetMetaData.getColumnCount();
        for (int i = 1; i <= colNum; i++) {
            // 列名
            // String name = resultSetMetaData.getColumnLabel(i);
            System.out.println("表名:" + resultSet.getString("TABLE_NAME"));
            System.out.println("列名:" + resultSet.getString("COLUMN_NAME"));
            System.out.println("字段类型:" + resultSet.getString("TYPE_NAME"));
            System.out.println("列大小:" + resultSet.getLong("COLUMN_SIZE"));
            System.out.println("是否为空:" + resultSet.getInt("NULLABLE"));
            System.out.println("注释:" + resultSet.getString("REMARKS"));
            System.out.println("默认值:" + resultSet.getString("COLUMN_DEF"));
            System.out.println("是否自递:" + "YES".equalsIgnoreCase(resultSet.getString("IS_AUTOINCREMENT")));
        }
    }
}

三、附录

3.1 数据类型映射

DM 数据类型 JAVA数据类型
bit、boolean boolean
tinyint short
smallint int
int long
bigint float
float、double douoble
decimal Bigdecimal
char、varchar、VARCHAR2 String
binary、varbinary Byte[]
date Date
time Time

3.2 JDBC-API详解

JDBC API是Java语言访问数据库的标准API,它定义了一组类和接口,用于封装数据库访问的细节,由于 DM JDBC 驱动遵照 JDBC 标准规范设计与开发,JDBC 接口函数较多,这里仅列出了 DM JDBC 主要接口和函数。

java.sql.DriverManager

用于管理驱动程序、并可与数据库建立连接,如下表所示。

主要函数 说明
getConnection 获取数据库连接对象
setLoginTimerout 设置登录超时时间
registerDriver 注册驱动
deregisterDriver 卸载驱动

java.sql.Connection

数据库连接类,作用是管理执行数据库的连接。可用于提交和回滚事务、创建Statement对象等操作,如下表所示。

主要函数 说明
createStatement 创建一个sql执行对象:Statement
setAutoCommit 设置自动提交
close 关闭数据库连接
commit 执行事务的提交操作
rollback 执行事务的回滚操作

java.sql.Statement

用于向数据库发送要执行的sql语句(增删改查)并可访问结果,如下表所示。

主要函数 说明
execute 运行 SQL 语句
executeQuery 执行一条返回 ResultSet 的 SQL 语句
executeUpdate 执行 INSERT、UPDATE、DELETE 或一条没有返回数据集的 SQL 语句
getResultSet 用于得到当前 ResultSet 的结果

java.sql.ResultSet

是 JDBC 中最重要的结果集对象,主要用于查询关于某个表的信息或一个查询的结果,如下表所示。

主要函数 说明
getXxx 获取指向行、特定列的内容
absolute 将结果集的记录指针移动到指定行
next 将结果集的记录指针定位到下一行。如果没有剩余行,则返回 false。
last 将结果集的记录指针定位到最后一行
close 释放 ResultSet 对象
getMetaData 返回 ResultSetMetaData 对象。

java.sql.DatabaseMetaData

有关整个数据库的信息,如表名、表的索引、数据库产品的名称和版本等。用于获取数据库元数据信息的类,如模式、表、权限、列、存储过程等信息,如下表所示。

主要函数 说明
getCatalogs 获取该数据库中的信息目录列表。
getTables 获取指定参数的表信息
getColumns 获取指定表的列信息
getPrimaryKeys 获取指定表的主键信息
getImportedKeys 获取指定表的外键列信息
getSQLKeywords 获取数据库支持的SQL语法
getDriverName 获得所连接的数据库驱动程序的名称。
getTypeInfo 获取当前数据库的数据类型信息
getExportedKeys 获取制定表的外键信息

java.sql.ResultSetMetaData

用于获取结果集元数据信息的类,如列数、列名、列的数据类型、列大小等信息,如下表所示。

主要函数 说明
getColumnCount 获取数据集中的列数
getColumnName 获取数据集中指定的列名
getColumnLabel 获取数据集中指定的标签
getColumnType 获取数据集中指定的数据类型
isCurrency 如果此列包含带有货币单位的一个数字,则返回 true。
isReadOnly 如果此列为只读,则返回 true。
isAutoIncrement 如果此列自动递增,则返回 true。这类列通常为主键,而且始终是只读的。

四、总结

以上就是使用Java连接DM数据库的完整流程和操作步骤,为了更直观地展示利用JDBC驱动程序进行编程的过程,这里使用 mermaid 语法绘制以下流程图:

graph LR A1 --> A2 --> A3 --> A4 --> A5 --> A6 A1("1、加载驱动") A2("2、创建连接(Connection)") A3("3、创建句柄(Statement)") A4("4、执行操作") A5("5、结果处理(ResultSet)") A6("6、关闭连接")

通过上述步骤,可以成功配置 Java 程序以连接DM数据库。理解每一步的意义,并运行示例代码,将掌握数据库操作的基本要领。希望在后续的学习与实践中,能进一步深入理解数据库的使用,提升自己的开发技能,加油!

相关推荐
纪伊路上盛名在21 分钟前
爬虫1:uniprot蛋白质序列数据+canvas图片
数据库·学习·知识图谱·学习方法
繁川22 分钟前
深入理解Spring AOP
java·后端·spring
程序员黄同学2 小时前
如何使用 Python 连接 MySQL 数据库?
数据库·python·mysql
运维&陈同学3 小时前
【Elasticsearch05】企业级日志分析系统ELK之集群工作原理
运维·开发语言·后端·python·elasticsearch·自动化·jenkins·哈希算法
新手小袁_J3 小时前
实现Python将csv数据导入到Neo4j
数据库·python·neo4j·《我是刑警》·python连接neo4j·python导入csv·csv数据集导入neo4j
ZHOUPUYU3 小时前
最新 neo4j 5.26版本下载安装配置步骤【附安装包】
java·后端·jdk·nosql·数据库开发·neo4j·图形数据库
シ風箏4 小时前
Neo4j【环境部署 02】图形数据库Neo4j在Linux系统ARM架构下的安装使用
linux·数据库·arm·neo4j
张声录14 小时前
【ETCD】【实操篇(四)】etcd常见问题快问快答FAQ
数据库·etcd
Q_19284999065 小时前
基于Spring Boot的找律师系统
java·spring boot·后端
ZVAyIVqt0UFji5 小时前
go-zero负载均衡实现原理
运维·开发语言·后端·golang·负载均衡