「JENKINS-PIPELINE」- 连接 MySQL 数据库

在 Jenkins Pipeline 中,我们需要连接 MySQL 数据库,以存储某些状态数据,用于在多个 Job 之间共享。这些数据不属于制品,通过制品管理的方式进行传递是件繁琐的事情,而且存在问题。然而,通过数据库进行传递是最好的方式。因此,我们需要解决在 Jenkins Pipline 中连接数据库的问题。

但是,事情往往没有看起来那样简单。我们难以像「Groovy 连接数据库」那样使用 mysql-connector-java 类库,原因在于 JDBC 驱动的加载方式以及 Jenkins Pipeline 类加载器的设计,这两点导致我们无法直接使用。但是,我们终究是找到解决方案。

该笔记将记录:在 Jenkins Pipeline 中,如何连接 MySQL 数据库,以及常见问题处理。

针对大多数构建任务,没有必要使用 Jenkins Pipeline 访问数据库,这也许是种小众需求。如果在设计中出现这种需求,也许应该重新考虑。我们之所以需要连接数据库,是因为我们要用 Jenkins 做些复杂的事情;

方案一、通过系统类加载器(废弃)

java – Where to put external jars in jdk10 – Stack Overflow
connecting to mysql db from jenkins pipeline not wroking – Stack Overflow
How to add a JDBC driver to Jenkins’s jobs & load the parameter values from SQL Database
mysql – How to add a JDBC driver to a Jenkins pipeline? – Stack Overflow

好在还有其他的方法:通过 Extension 机制,能加载自定义 Jar 包;

首先,在 Jenkins Pipeline 中,执行如下语句,以获取扩展加载位置:

println System.getProperty("java.ext.dirs"),

在我们的环境中,如上语句输出:/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.302.b08-0.el7_9.x86_64/jre/lib/ext:/usr/java/packages/lib/ext

然后,下载 mysql-connector-java.jar 类库,并放入 /usr/java/packages/lib/ext/ 中,此时 mysql-connector-java 将由系统类加载器进行加载;

最后,访问数据库的代码如下(这与「Groovy 连接数据库」基本类似,但无需 Grab 引入驱动):

import java.sql.*;
import groovy.sql.Sql

def connectionString = "jdbc:mysql://<ip-address>:<port-number>/<database>"
def username = "<username>"
def password = "<password>"
def driver = "com.mysql.jdbc.Driver"
def sqlInstance = Sql.newInstance(connectionString, username, password, driver)

// 查询第一条数据
def firstRow = sqlInstance.firstRow("select * from <database>.<table>")
print firstRow

如果采用该方法,(1)建议不再使用 YUM 提供的 OpenJDK 包,(2)而应该到 OpenJDK 官网下载,然后放入自定义的 Jar 包,并单独维护;

# 08/14/2021 在 Java 10 中,移除 Extension 机制,因此不能再使用该方法,只能使用 databse 插件的方式;

方案二、通过 database 插件(推荐)

database | Jenkins plugindatabase-h2 | Jenkins pluginMySQL Database | Jenkins plugin

# 09/24/2020 官方提供 Database ⇒ MySQL API Plugin ⇒ MySQL Database Plugin 扩展,但是没有演示程序,文档也少的可怜(近乎没有),所以我们也没有办法使用官方插件(经过反复的实验,并没有成功,也许是我们的操作方式不对);

# 03/24/2021 但是,我们现在找到相关的文档说明,并通过测试。该部分将记录这些内容,介绍连接 MySQL 数据库的方法;

https://plugins.jenkins.io/database/

第一步、安装 database 与 MySQL Database Plugin 插件

其中,插件 database 是抽象层,对“底层驱动”进行封装。而这里的“底层驱动”便是“MySQL Database Plugin”插件。除了连接 MySQL 数据库的插件,还有 PostgreSQL Database plugin、H2 Database plugin 等等(这里不再赘述插件的安装方法);

官网:https://plugins.jenkins.io/database-mysql/
文档:https://www.jenkins.io/doc/pipeline/steps/database/
仓库:https://github.com/jenkinsci/database-mysql-plugin

第二步、添加 MySQL 数据库配置

在 Manage Jenkins ⇒ Configure System ⇒ Global Database 中,填写数据库信息:
1)选择 MySQL 数据库;
2)填写 Username、Password 等等信息;
3)填写 Validation Query 字段,例如 select version(); 语句;
4)最后,点击按钮 Test Connection 测试连接,最后 Save 保存;

第三步、在 Pipeline 中,连接数据库

pipeline {
    agent any
    stages {
        stage('# 构建开始') {
            steps {
                script{
                    getDatabaseConnection(type: 'GLOBAL') {
                        def result = sql(sql: "SHOW SESSION VARIABLES LIKE '%char%'")
                        println "Class of result: ${result.getClass().toString()}"
                        println "Value of result: ${result}"
                    }
                }
            }
        }
    }
}

下面是执行的输出结果(省略无关内容),正如我们所见,返回 ArrayList 对象:

...
Class of result: class java.util.ArrayList
Value of result: [[SCHEMA_NAME:information_schema], [SCHEMA_NAME:test_db]]
...

关于连接数据库的相关参数,参考 Pipeline Steps Reference/database 文档;

中文乱码

hibernate – JDBC MySQL character encoding: Why is useUnicode required? – Stack Overflow

但是,数据库写入的中文是乱码,需要指定 useUnicode=true&characterEncoding=UTF-8 属性,解决方法如下:
1)在 $JENKINS_HOME/jdbc-drivers/ 中,保存 JDBC 驱动,比如 mysql-connector-java.jar 文件,然后重启 Jenkins 服务;
2)在 Manage Jenkins ⇒ Configure System ⇒ Global Database 中,Database: Generic
3)JDBC Driver Class: com.mysql.jdbc.Driver
4)JDBC Connection URL: jdbc:mysql://hostname:3306/db?useUnicode=true&characterEncoding=UTF-8
5)User Name、Passowrd
6)最后,点击按钮 Test Connection 测试连接,最后 Save 保存;

java.sql.SQLException: Incorrect string value: ‘\xAC\xED\x00\x05sr…’ for column…

mysql – java.sql.SQLException: Incorrect string value: ‘\xAC\xED\x00\x05sr…’ – Stack Overflow

在 Jenkins Pipeline 中,我们使用 PreparedStatement 执行 SQL 语句,产生如下错误:

java.sql.SQLException: Incorrect string value: '\xAC\xED\x00\x05sr...' for column 'content' at row 1

问题原因

传入 PreparedStatement 的 Sql 参数并非 String 类型,而是 org.codehaus.groovy.runtime.GStringImpl 类型。通过如下代码验证:

pipeline {
    agent {
        node {
            label 'NODE03-UBUNTU-20.04'
        }
    }
    stages {
        stage ("# 构建开始") {
            steps {
                script {
                    def str1 = "1"
                    def str2 = "2"
                    println "${str1}/${str2}".getClass().toString()
                }
            }
        }
    }
}

解决办法

解决办法很简单,转换成 String 类型即可:

// 解决办法
println "${str1}/${str2}".toString()

// 验证为 String 类型
println "${str1}/${str2}".toString().getClass().toString()