一、背景
近期,用户使用 Kotlin语言的 ORM 框架JetBrains/Exposed框架进行开发需要适配达梦数据库,查看Exposed官网发现原生支持的数据库不包含达梦数据库,随后进行适配操作并进行记录。 
二、软件说明
DM数据库
官网:https://eco.dameng.com/
- 达梦数据库作为已商业化的国产数据库代表,在政府及事业单位应用还是比较广泛。
- 达梦数据库管理系统是达梦公司推出的具有完全自主知识产权的高性能数据库管理系统,简称DM。达梦数据库管理系统的最新版本是8.0版本,简称DM8。
- DM8采用全新的体系架构,在保证大型通用的基础上,针对可靠性、高性能、海量数据处理和安全性做了大量的研发和改进工作,极大提升了达梦数据库产品的性能、可靠性、可扩展性,能同时兼顾OLTP和OLAP请求,从根本上提升了DM8产品的品质。
Kotlin
官网:https://kotlinlang.org/
- Kotlin是由JetBrains开发的针对JVM、Android和浏览器的静态编程语言,目前,在Apache组织的许可下已经开源。
- 使用Kotlin,开发者可以很方便地开发移动Android应用、服务器程序和JavaScript程序。
- Kotlin可以将代码编译成Java字节码,也可以编译成JavaScript,方便在没有JVM的设备上运行。
JetBrains/Exposed
官网:https://github.com/JetBrains/Exposed 文档地址:https://github.com/JetBrains/Exposed/wiki
- Kotlin语言的 ORM 框架Exposed。
- Exposed 是一个轻量级 SQL 库,位于 Kotlin 语言的 JDBC 驱动程序之上。Exposed 有两种类型的数据库访问:类型安全的 SQL 包装 DSL 和轻量级数据访问对象 (DAO),可以拥有两个级别的数据库访问权限。
- Exposed 可用于模拟各种数据库引擎,帮助您构建不依赖于任何特定数据库引擎的应用程序,并在它们之间进行切换,只需很少或无需更改。
Gradle
官网地址: https://gradle.org/ Gradle 是一款 Google 推出的基于 JVM、通用灵活的项目构建工具,支持 Maven,JCenter 多种第三方仓库;支持传递性 依赖管理、废弃了繁杂的 xml 文件,转而使用简洁的、支持多种语言(例如:java、groovy 等)的 build 脚本文件。
三、项目搭建
1. 环境版本
软件名称 | 版本号 |
---|
Gradle | 7.4.2 | kotlin | 1.7.20 | JDK | 1.8 | JetBrains/Exposed | 0.40.1 | DM8 | 1-2-114-22.03.04-158322-10045-ENT | jdbc驱动 | V8.1.2.138-Build(2022.09.27-13061) |
2. 搭建项目
-
IDEA新建Kotlin项目  -
引入达梦的jar包或新建lib目录

- 标准的jdbc操作
import java.sql.Connection
import java.sql.DriverManager
import java.sql.SQLException
fun main(args: Array<String>) {
val connection = getConnection()
val sql = connection!!.prepareStatement("select * from TEST")
val res = sql.executeQuery()
while (res.next()) {
println("ID -> ${res.getString(1)}")
println("姓名 -> ${res.getString(2)}")
println("性别 -> ${res.getString(3)}")
}
}
fun getConnection(): Connection? {
val driver = "dm.jdbc.driver.DmDriver"
val url = "jdbc:dm://127.0.0.1:5236"
val username = "SYSDBA"
val password = "SYSDBA"
try {
Class.forName(driver)
} catch (e: ClassNotFoundException) {
e.printStackTrace()
}
var conn: Connection? = null
try {
conn = DriverManager.getConnection(url, username, password)
} catch (e: SQLException) {
e.printStackTrace()
}
return conn
}
执行截图 
3. 整合Exposed框架
新增Exposed的相关依赖(build.gradle.kts)
dependencies {
testImplementation(kotlin("test"))
implementation("org.jetbrains.exposed", "exposed-core", "0.40.1")
implementation("org.jetbrains.exposed", "exposed-dao", "0.40.1")
implementation("org.jetbrains.exposed", "exposed-jdbc", "0.40.1")
}
新建dameng_conn.kt
import org.jetbrains.exposed.dao.*
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.dao.id.IntIdTable
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
import org.jetbrains.exposed.sql.vendors.DatabaseDialect
import org.jetbrains.exposed.sql.vendors.OracleDialect
object Users : IntIdTable() {
val name = varchar("name", 50).index()
val city = reference("city", Cities)
val age = integer("age")
}
object Cities: IntIdTable() {
val name = varchar("name", 50)
}
class User(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<User>(Users)
var name by Users.name
var city by City referencedOn Users.city
var age by Users.age
}
class City(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<City>(Cities)
var name by Cities.name
val users by User referrersOn Users.city
}
fun main() {
Database.connect(
"jdbc:dm://127.0.0.1:5236?compatibleMode=oracle",
driver = "dm.jdbc.driver.DmDriver",
user = "SYSDBA",
password = "SYSDBA"
)
transaction {
addLogger(StdOutSqlLogger)
SchemaUtils.create (Cities, Users)
val stPete = City.new {
name = "St. Petersburg"
}
val munich = City.new {
name = "Munich"
}
User.new {
name = "a"
city = stPete
age = 5
}
User.new {
name = "b"
city = stPete
age = 27
}
User.new {
name = "c"
city = munich
age = 42
}
println("Cities: ${City.all().joinToString {it.name}}")
println("Users in ${stPete.name}: ${stPete.users.joinToString {it.name}}")
println("Adults: ${User.find { Users.age greaterEq 18 }.joinToString {it.name}}")
}
}
执行报错Can’t resolve dialect for connection: jdbc:dm://127.0.0.1:5236

4. 报错分析
- org.jetbrains.exposed.sql.Database.Companion#connect#getDeclaredConstructor 根据url地址去getDeclaredConstructor 取值后为null,顾直接抛异常

- getDialectName函数在dialectMapping中取值并判断前缀

- dialectMapping是Map集合,里面封装的支持的数据库以及前缀。因达梦的url无法识别,无法指定数据库具体的dialectName,顾报错。

5. 解决方案
1. 推荐#方案一(Dm注册到Database)
根据官方的F&A的其他的数据库可实现DatabaseDialect接口并Database.registerDialect()注册进去。 https://github.com/JetBrains/Exposed/wiki/FAQ 
Database.registerJdbcDriver("jdbc:dm","dm.jdbc.driver.DmDriver", OracleDialect.dialectName);
Database.connect(
"jdbc:dm://127.0.0.1:5236?compatibleMode=oracle",
driver = "dm.jdbc.driver.DmDriver",
user = "SYSDBA",
password = "SYSDBA"
)

四、其他
Mysql数据库写法
- build.gradle.kts中添加mysql以及相关依赖
dependencies {
testImplementation(kotlin("test"))
implementation("org.jetbrains.exposed", "exposed-core", "0.40.1")
implementation("org.jetbrains.exposed", "exposed-dao", "0.40.1")
implementation("org.jetbrains.exposed", "exposed-jdbc", "0.40.1")
implementation("mysql", "mysql-connector-java", "8.0.11")
}
- mysql_conn.kt
import org.jetbrains.exposed.dao.*
import org.jetbrains.exposed.dao.id.EntityID
import org.jetbrains.exposed.dao.id.IntIdTable
import org.jetbrains.exposed.sql.*
import org.jetbrains.exposed.sql.transactions.transaction
object Users : IntIdTable() {
val name = varchar("name", 50).index()
val city = reference("city", Cities)
val age = integer("age")
}
object Cities: IntIdTable() {
val name = varchar("name", 50)
}
class User(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<User>(Users)
var name by Users.name
var city by City referencedOn Users.city
var age by Users.age
}
class City(id: EntityID<Int>) : IntEntity(id) {
companion object : IntEntityClass<City>(Cities)
var name by Cities.name
val users by User referrersOn Users.city
}
fun main() {
val connect = Database.connect(
"jdbc:mysql://127.0.0.1:3306/dmtest?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&useSSL=false",
driver = "com.mysql.cj.jdbc.Driver",
user = "root",
password = "Gas123."
)
transaction {
addLogger(StdOutSqlLogger)
SchemaUtils.create (Cities, Users)
val stPete = City.new {
name = "St. Petersburg"
}
val munich = City.new {
name = "Munich"
}
User.new {
name = "a"
city = stPete
age = 5
}
User.new {
name = "b"
city = stPete
age = 27
}
User.new {
name = "c"
city = munich
age = 42
}
println("Cities: ${City.all().joinToString {it.name}}")
println("Users in ${stPete.name}: ${stPete.users.joinToString {it.name}}")
println("Adults: ${User.find { Users.age greaterEq 18 }.joinToString {it.name}}")
}
}
五、代码
dm_exposed.zip 链接:https://pan.baidu.com/s/1j9fFXnFnH6_R-C79h6-zSw?pwd=cw01 提取码:cw01
|