【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等方式将对应的依赖引入,相关配置如下:
<!-- 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
目录下,不然后面执行可能找不到此配置文件!
# 定义 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,之后的增删改查操作可直接导入这个工具类完成获取连接,释放资源的操作,很方便!废话就不多说,直接上代码,如下所示:
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
语句,后跟表名和列定义。列定义包括列名、数据类型和约束条件。例如:
@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
语句,指定表名和要插入的数据。例如:
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);
@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。
@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("插入成功");
}
删除数据
@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("删除成功");
}
修改数据
@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语句,可以指定要查询的列和查询条件。处理查询结果集,获取当前光标指向行中的数据,有三种方式,如下所示:
@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() 执行更新操作,包括增、删、修改等。
@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)的一部分,为开发人员提供了访问数据库元数据的便捷途径。
获取数据库的元信息
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());
}
获取数据库的所有表
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"));
}
}
列出表的所有字段
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
语法绘制以下流程图:
通过上述步骤,可以成功配置 Java 程序以连接DM数据库。理解每一步的意义,并运行示例代码,将掌握数据库操作的基本要领。希望在后续的学习与实践中,能进一步深入理解数据库的使用,提升自己的开发技能,加油!