公司内部推广DevOps,全部项目配置CI/CD流水线(Jenkins+SVN+ANT),在代码实现持续集成后,SQL语句自动执行的实现提上日程

一、环境

  • Linux环境
  • 安装ANT工具,包括ant扩展包---ant-contrib-1.0b3.jar,maven链接
  • 下载oracle连接ojdbc5.jar包。

二、思路

步骤1:  从SVN的QL脚本提交路径上,下载所有SQL脚本

步骤2:【shell脚本】:获取日期文件夹名为今天的文件夹下的sql脚本,拷贝到待执行文件夹中。将其中已执行成功的脚本(即存在于bak文件夹中)删掉;

步骤3:【ant脚本】:对待执行脚本排序后,循环执行每条sql语句,执行成功的sql脚本备份到bak文件夹,方便后续执行时排除重复。

三、具体实现

3.1 源码管理

3.2 执行shell

if [ $# -ne 1 ]
then
echo "Usage: $0 foldername"
echo " $0 Exec SQL"
exit
fi Workspace=$1
year=`date +%Y`
today=`date +%Y%m%d`
sqlfolder=${Workspace}/svn/${year}/${today}
bakfolder=${Workspace}/sqlbak/${year}/${today}
logfolder=${Workspace}/sqllog #如果待执行sql文件夹已存在就清空该文件夹,否则创建
if [ -d "runSqlInFolder" ];then
echo "-----Clean the executed sql folder:/runSqlInFolder-----"
rm -rf runSqlInFolder/*
else
mkdir -p ${Workspace}/runSqlInFolder
fi if [ ! -d "${logfolder}" ];then
mkdir ${logfolder}
else
echo "-----Dir ${logfolder} is exist"
fi #将今天提交到SVN的sql脚本中还未执行过的拷贝到runSqlInFolder文件夹
if [ -d "${sqlfolder}" ];then
echo '-----Copy the sql file into folder'
cp -r ${sqlfolder}/* runSqlInFolder
find runSqlInFolder/ -name '*.jar' |xargs rm -rf
find runSqlInFolder/ -type d | grep .svn$ | xargs rm -rf
if [ -d "${bakfolder}" ];then
cd ${bakfolder}
find * -type f -name *.sql|xargs -i rm -f ../../../runSqlInFolder/{}
else
echo "-----create back folder: ${bakfolder}"
mkdir -p ${bakfolder}
fi
else
echo dir ${sqlfolder} not exist
fi

3.3 Invoke ANT

3.3.1 定义一个target:runSqlInFolder

使用try catch包裹for 循环,for循环中调用execSQL标签(自定义公共方法块,即宏),顺序逐条执行SQL脚本。

    <taskdef resource="net/sf/antcontrib/antlib.xml" classpath="${lib}/ant-contrib-1.0b3.jar"/>
<target name="runSqlInFolder">
<echo>Run the SQL at Folder: ${sqlfolder}</echo>
<echo>DB Host: ${v7uatdb.host}</echo>
<echo>DB Name: ${v7uatdb.name}</echo>
<echo>DB User: ${v7uatdb.user}</echo>
<trycatch property="errMsg">
<try>
<for param="folder">
<path>
<sort xmlns:rcmp="antlib:org.apache.tools.ant.types.resources.comparators">
<dirset dir="${sqlfolder}" includes="*" />
</sort>
</path>
<sequential>
<echo>SQL Folder: @{folder}</echo>
<for param="file">
<path>
<sort xmlns:rcmp="antlib:org.apache.tools.ant.types.resources.comparators">
<fileset dir="@{folder}" includes="*/*.sql" casesensitive="false"/>
</sort>
</path>
<sequential>
<echo>SQL: @{file}</echo>
<execsql
dbhost="${v7uatdb.host}"
dbport="${v7uatdb.port}"
dbname="${v7uatdb.name}"
dbuser="${v7uatdb.user}"
dbpwd="${v7uatdb.pwd}"
sqlfile="@{file}"
logfile="${Sqllogfile}"/>
</sequential>
<!--<move file="@{file}" todir="${sqlbakdir}/@{folder}"/>
folder 包含路径和文件名,所以直接复制file还有点问题,需要截取文件名--目前待研究 -->
</for>
<move file="@{folder}" todir="${sqlbakdir}"/>
</sequential>
</for>
<echo>Finished running all SQL</echo>
<echo>File moved to backup folder:</echo>
<echo>${sqlbakdir}</echo>
</try>
<catch>
<echo>Error found when running SQL</echo>
<echo>Log file can be found in:</echo>
<echo>${sqlbakdir}/err</echo>
<move file="${Sqllogfile}" todir="${sqlbakdir}/err"/>
<fail>Error Occur</fail>
</catch>
<finally>
</finally>
</trycatch>
</target>

3.3.2 定义execsql标签

通过sql标签执行sql文件,通过record标签记录对应的执行日志并输出。

注意:如果执行procedure就需要设置delimiter,本例中通过SQL文件的命名来区分是不同的SQL类型(procedure,declare等)。

    <macrodef name="execsql" description="Run single SQL file.">
<attribute name="dbhost" description="Host Name/ IP of the DB"/>
<attribute name="dbport" description="DB Port"/>
<attribute name="dbname" description="DB name"/>
<attribute name="dbuser" description="DB User name"/>
<attribute name="dbpwd" description="DB Password"/>
<attribute name="sqlfile" description="SQL file to be run"/>
<attribute name="logfile" default="sql.log" description="Log file"/>
<sequential>
<echo>Log file @{logfile}</echo>
<record name="@{logfile}" action="start"/>
<if>
<contains string="@{sqlfile}" substring="PROCEDURE"/>
<then>
<sql driver="${oracleDriver}"
url="jdbc:oracle:thin:@@@{dbhost}:@{dbport}:@{dbname}"
userid="@{dbuser}"
password="@{dbpwd}"
classpathref="classpath"
encoding="${encoding}"
print="true"
autocommit="true"
delimiter="/"
delimitertype="row">
<transaction src="@{sqlfile}"/>
</sql>
</then>
<else>
<sql driver="${oracleDriver}"
url="jdbc:oracle:thin:@@@{dbhost}:@{dbport}:@{dbname}"
userid="@{dbuser}"
password="@{dbpwd}"
encoding="${encoding}"
classpathref="classpath"
autocommit="true"
print="true">
<transaction src="@{sqlfile}"/>
</sql>
</else>
</if>
<record name="@{logfile}" action="stop"/>
</sequential>
</macrodef>

更新后

    <macrodef name="execsql" description="Run single SQL file.">
<attribute name="dbhost" description="Host Name/ IP of the DB"/>
<attribute name="dbport" description="DB Port"/>
<attribute name="dbname" description="DB name"/>
<attribute name="dbuser" description="DB User name"/>
<attribute name="dbpwd" description="DB Password"/>
<attribute name="sqlfile" description="SQL file to be run"/>
<attribute name="logfile" default="sql.log" description="Log file"/>
<sequential>
<echo>Log file @{logfile}</echo>
<record name="@{logfile}" action="start"/>
<if>
<contains string="@{sqlfile}" casesensitive="no" substring="PROCEDURE"/>
<then>
<sql driver="${oracleDriver}"
url="jdbc:oracle:thin:@@@{dbhost}:@{dbport}:@{dbname}"
userid="@{dbuser}"
password="@{dbpwd}"
classpathref="classpath"
encoding="${encoding}"
print="true"
autocommit="true"
delimiter="/"
delimitertype="row">
<transaction src="@{sqlfile}"/>
</sql>
</then>
<elseif>
<contains string="@{sqlfile}" casesensitive="no" substring="DECLARE"/>
<then>
<sql driver="${oracleDriver}"
url="jdbc:oracle:thin:@@@{dbhost}:@{dbport}:@{dbname}"
userid="@{dbuser}"
password="@{dbpwd}"
classpathref="classpath"
encoding="${encoding}"
print="true"
autocommit="true"
delimiter=";;">
<transaction src="@{sqlfile}"/>
</sql>
</then>
</elseif>
<else>
<sql driver="${oracleDriver}"
url="jdbc:oracle:thin:@@@{dbhost}:@{dbport}:@{dbname}"
userid="@{dbuser}"
password="@{dbpwd}"
encoding="${encoding}"
classpathref="classpath"
autocommit="true"
print="true">
<transaction src="@{sqlfile}"/>
</sql>
</else>
</if>
<record name="@{logfile}" action="stop"/>
</sequential>
</macrodef>

3.4  SVN中存放SQL的文件夹设置

3.5 成功执行效果

四、遇到的问题处理

4.1【问题】如果执行的SQL语句中存在中文,执行完在数据库中保存的数据是乱码

目前常用的两种数据库编码格式为:GBK、UTF-8,也是两种编码风格(ANSI和UNICODE)中的代表

经确认公司数据库采用GBK编码,所以将encoding改为GBK,乱码问题解决,ant文件头设置

<property name="encoding" value="GBK" /><!--UTF-8-->

4.2【问题】如果执行的SQL语句中,有declare,声明变量的,执行declare过程中会报错

delete zssys.WEB_APP_TEMPLATE where C_INTERFACE_ID = '0000-GDCARVA';;
declare
response clob;
begin
response := 'aaa'; insert into zssys.WEB_APP_TEMPLATE (C_PK_ID, C_INTERFACE_ID, C_SYSCODE, C_APP_NAME, C_TEMPLATE_REQUEST, C_TEMPLATE_RESPONSE)
values (SYS_GUID(), '0000-GDCARVA', '*', 'test', null, response);
end;

4.3【问题】目前备份脚本是按照模块整体备份的,没有做到单个sql脚本备份,后期优化

五、 参考资料

使用ANT脚本批量执行SQL,并且结合Jenkins自动化构建

Jenkins系列之-—08 实现SQL脚本批量执行的更多相关文章

  1. PL/SQL中批量执行SQL脚本(不可把所有的语句都复制到New SQL Windows)

    PL/SQL中批量执行SQL脚本,不可把所有的语句都复制到New SQL Window,因为这样会导致缓冲区过大而进程卡死! 最好的办法是将要执行的SQL脚本存放到指定文件中,如C:\insert.s ...

  2. 通过SqlClr制作Sql自动化批量执行脚本

    原文:通过SqlClr制作Sql自动化批量执行脚本 通过SqlClr制作Sql自动化批量执行脚本 在与同事一起做项目时,看到同事用sqlclr做批量执行脚本,感觉挺新奇的就上网搜集资料自己模仿跟做了个 ...

  3. shell脚本批量执行命令----必需判断上一步执行结果--没有捷径

    # 注意:shell脚本批量执行命令,不能只写一个函数,然后把所有命令复制进去,之前试过这样是不行的.必须要有一个判断命令执行成功与否的语句 # 简单的命令可以不加结果判断符号,但是遇到解压包.sed ...

  4. shell脚本-批量执行机器命令

    场景:通过跳板机,批量获取线上机器日志 使用方式:run2 host 'ls -al /home/admin/' #! /bin/sh USER_NAME=$USER if [ $# -ne 2 ]; ...

  5. 脚本批量执行Redis命令

    1.将命令写在文件中 数据量比较大的话,建议用程序去生成文件.例如: List<String> planIdList = planDao.findAll().parallelStream( ...

  6. Jenkins系列之-—06 Ant构建

    一.Ant 简介&构建环境 Apache Ant 是由 Java 语言开发的工具 构建ant环境: 1). 安装jdk,设置JAVA_HOME ,PATH ,CLASS_PATH 2). 下载 ...

  7. SQL Server自动化运维系列——批量执行SQL脚本(Power Shell)

    需求描述 一般在生产环境中,在投产的情况下,需要批量的来执行SQL脚本文件,来完成整个投产,如果投产文件比较多的情况下,无疑这是一个比较痛苦的过程,所以本篇通过PowerShell脚本来批量完成. 监 ...

  8. jenkins jmeter持续集成批处理jmx脚本

    这篇文章介绍jenkis jmeter的持续集成,利用jenkins定时任务去批处理执行jmeter的jmx脚本文件,并且生成测试报告 1:jmeter的安装这里我就不在赘述了,如有问题可参考我的jm ...

  9. Oracle:从SQL文件批量导入数据

    进入DOS界面. 进入SQL文件目录. 在命令提示下运行SqlPlus,c:\sql>sqlplus user_name/password@net_service_name 指定SQL执行日志文 ...

随机推荐

  1. iOS学习笔记36-Masonry自动布局

    一.Masonry介绍 之前我们在屏幕适配的章节中学习过AutoLayout的使用,但那都是在可视化界面上进行添加约束完成的,我们很多时候都需要在代码中使用AutoLayout约束,苹果也为我们提供了 ...

  2. 【Luogu】P3786萃香抱西瓜(状压DP)

    题目链接 水题,数据范围提示得太明显了吧,不用动脑子都能知道是状压. 不过还是有坑(当然更可能是我脑子有坑) f[i][j][k][l]表示当前是第i秒,萃香在(j,k),已经抱到的西瓜状态是l的最少 ...

  3. 学习 WebService 第四步:利用WSDL(URL)生成WebService客户端<初级>

    我用的是最简单的方法,利用jdk的命令wsimport -keep -p 包路径 -d 代码存放位置 WSDL网址 蓝色是命令,粉色是存放位置,橘色是URL C:\Program Files\IBM\ ...

  4. 面向对象oop思想

    OOP核心思想:封装,继承,多态. 理解: 对象是由数据和容许的操作组成的封装体,与客观实体有直接对应关系,一个对象类定义了具有相似性质的一组对象.而每继承性是对具有层次关系的类的属性和操作进行共享的 ...

  5. java面试题之死锁产生的条件,以及如何避免死锁,银行家算法,产生死锁后如何解决(阿里面试题)

    死锁产生的四个必要条件: 互斥:一个资源每次只能被一个进程使用(资源独立) 请求与保持:一个进程因请求资源而阻塞时,对已获得的资源保持不放(不释放锁) 不剥夺:进程已获得的资源,在未使用之前,不能强行 ...

  6. 按 Tab 在多个 InputField 间切换

    下面这个链接里的有些unity的东西还没搞懂..改天继续看 http://forum.unity3d.com/threads/tab-between-input-fields.263779/ if(I ...

  7. hdu 1979 剪枝暴搜

    Fill the blanks Time Limit: 3000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)T ...

  8. 切糕(bzoj 3144)

    Description Input 第一行是三个正整数P,Q,R,表示切糕的长P. 宽Q.高R.第二行有一个非负整数D,表示光滑性要求.接下来是R个P行Q列的矩阵,第z个 矩阵的第x行第y列是v(x, ...

  9. net1:post,get方式传值,读写cookie,读XML文件,写script语句,跳转页面,response与request类

    原文发布时间为:2008-07-29 -- 来源于本人的百度文章 [由搬家工具导入] using System;using System.Data;using System.Configuration ...

  10. js-classList修改class属性

    classList定义与用法 1)classList属性返回元素的类名,作为DOMTokenList对象 2)该属性用于在元素中添加,移除及切换css类 3)classList属性是只读的,但可以用a ...