代码改变世界

自动化数据库更新

2012-03-13 13:23  康杜  阅读(745)  评论(0编辑  收藏  举报
简介

在我们的开发和运维过程中,通常会维护不同的数据库。由于在某个时间点,各个数据库的结构和状态是不一样的,所以我们开发人员需要在不同的数据库中运行相同的数据库脚本(SQL),比如我们修改了某个字段的长度并编写了一段SQL语句,我们需要在开发数据库、测试数据库、客户测试数据库、生产数据库中运行这段相同的SQL脚本。重复的工作不仅增加我们的工作量同时还增加我们出错的机会。

为了让我们的工作更加轻松,我们需要通过某种自动化的手段来解决这个问题。本文就我在工作中的做法进行阐述。

 

解决方案

自动化编译服务器+自动化数据库脚本。 在这种方案下,每个数据库都知道它自己已经运行了什么样的数据库脚本。通过在数据库中维护表DB_SCRIPT来解决,如下面表所示

SQL> desc DB_SCRIPT;
Name     Type          Nullable Default Comments 
-------- ------------- -------- ------- -------- 
ID       NUMBER(9)                               
EXE_DATE DATE          Y        sysdate          
VERSION  VARCHAR2(20)  Y                         
REMARK   VARCHAR2(800) Y   

在build-all工程里,我们按照日期加编号的形式添加数据库脚本。

QQ截图未命名2

然后DbScript.groovy将查询数据库,根据当前数据库所运行的SQL语句来决定在build-all功能中哪些数据库SQL语句应该运行。具体的Groovy脚本请看下节。

QQ截图未命名

Groovy脚本
import groovy.sql.*;
def sql = Sql.newInstance("jdbc:oracle:thin:@192.168.141.128:1521:orcl", "root",
"111111", "oracle.jdbc.driver.OracleDriver")
sql.withTransaction { 
	def createDBScriptTableClosure = {
		row -> 
		if (row[0] == 0){
			//如果找不到表'DB_SCRIPT', 那么就建立一个表
			println '数据库没有表DB_SCRIPT, 现在建立一个表' 
			def createSeq = "CREATE SEQUENCE DB_SCRIPTSEQ INCREMENT BY 1 START WITH 1 MAXVALUE 99999"
			sql.execute createSeq
			def creatTableSql = "create table DB_SCRIPT(ID number(9) primary key not null, EXE_DATE date default sysdate, VERSION varchar2(20), REMARK varchar2(800))";
			sql.execute creatTableSql
		}
	}
	sql.eachRow("select count(*) from tab where tname ='DB_SCRIPT'", createDBScriptTableClosure)
}
//最后执行的SQL语句
def lastExecutedSql
sql.withTransaction {
	def firstRow = sql.firstRow("SELECT VERSION FROM DB_SCRIPT WHERE ID = (SELECT MAX(ID) FROM DB_SCRIPT)")
	
	if (firstRow !=null ){
		lastExecutedSql = firstRow.getAt(0)
	}
}
//把dbscript目录中的脚本缓存起来下来
def scripts = [:]
def maxFile = ''
Thread.currentThread().getContextClassLoader().getResource("").each {
	it ->  
	new File(it.getPath()+"\\dbscript").eachFile { 
		//把所有SQL文件放到MAP中
		file -> 
		def currentFileName = file.getName()
		scripts.put currentFileName, file
		if (currentFileName > maxFile){
			maxFile = currentFileName
		}
	}
}
def insertSql = "INSERT INTO DB_SCRIPT(ID, VERSION, REMARK) values(DB_SCRIPTSEQ.NEXTVAL, ?, ?)"
def keys = scripts.keySet().sort()
def sqlExecutionClosure = {
	key -> 
	println key
	def sqlFile = scripts.get(key)
	def sqlCommand = sqlFile.text
	def sqlCds = sqlCommand.split(';')
	sqlCds.each {
		sqlCd ->
		println 'executing ' + sqlCd
		sql.execute sqlCd
	}						
	sql.execute insertSql, [key, sqlCommand]
}
//如果还没有语句执行
sql.withTransaction{
	if (lastExecutedSql == null) {
		println '在这个数据库中还没有任何数据库补丁脚本运行,将依次运行如下脚本'
		keys.each {
			key -> 
			sqlExecutionClosure(key)
		}
	}else{
		keys.each {
			key ->
			println lastExecutedSql
			if (key > lastExecutedSql){
				sqlExecutionClosure(key)
			}
		}
	}
}