数据库开发的持续集成 - Liquibase的简介和应用

本系列文章目录
    
数据库开发的持续集成 - Sql Server 部署升级工具
    
数据库开发的持续集成 - Sql Server数据库结构比较
   
数据库开发的持续集成 - 方法和流程
   
数据库开发的持续集成 - Liquibase的简介和应用
   
数据库的持续集成 - CruiseControl.Net的项目配置

    
    在数据库开发的持续集成-方法和流程中,谈到了在持续集成中应用Liquibase,这里简单介绍一下,然后说说Liquibase在持续集成过程中的具体应用方法。

Liquibase简介

     Liquibase是一个用于跟踪、管理和应用数据库变化的开源的数据库重构工具。它将所有数据库的变化(包括结构和数据)都保存在XML文件中,便于版本控制。
     Liquibase具备如下特性:
     * 不依赖于特定的数据库,目前支持包括Oracle/Sql Server/DB2/MySql/Sybase/PostgreSQL/Caché等12种数据库,这样在数据库的部署和升级环节可帮助应用系统支持多数据库。
     * 提供数据库比较功能,比较结果保存在XML中,基于该XML你可用Liquibase轻松部署或升级数据库。
     * 以XML存储数据库变化,其中以作者和ID唯一标识一个变化(ChangSet),支持数据库变化的合并,因此支持多开发人员同时工作。
     * 在数据库中保存数据库修改历史(DatabaseChangeHistory),在数据库升级时自动跳过已应用的变化(ChangSet)。
     * 提供变化应用的回滚功能,可按时间、数量或标签(tag)回滚已应用的变化。通过这种方式,开发人员可轻易的还原数据库在任何时间点的状态。
     * 可生成数据库修改文档(HTML格式)
     * 提供数据重构的独立的IDE和Eclipse插件
     
      Liquibase的核心就是存储变化的XML,如例:

<?xml version="1.0" encoding="UTF-8"?> 
<databaseChangeLog
  
xmlns="http://www.liquibase.org/xml/ns/dbchangelog/1.6"
  xmlns:xsi
="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation
="http://www.liquibase.org/xml/ns/dbchangelog/1.6
         http://www.liquibase.org/xml/ns/dbchangelog/dbchangelog-1.6.xsd"
>
 
    
<changeSet id="1" author="bob">
        
<createTable tableName="department">
            
<column name="id" type="int">
                
<constraints primaryKey="true" nullable="false"/>
            
</column>
            
<column name="name" type="varchar(50)">
                
<constraints nullable="false"/>
            
</column>
            
<column name="active" type="boolean" defaultValue="1"/>
        
</createTable>
    
</changeSet> 
</databaseChangeLog>

       其中,changeSet包含不同的数据库变化,几乎涵盖了所有的数据库变化类型,具体支持的类型要看API,我这里给几个例子:
       *  创建和删除表、视图、存储过程、主键、外键、索引等
       *  重命名表、视图、列等
       *  加入列缺省值、唯一约束、非空约束等
       *  合并两个列
       *  在一个表的数据的基础上创建一个字典表
       
       除此之外,Liquibase还允许你运行自己的Sql脚本、执行Shell程序。
       之所以所说”几乎涵盖“,是因为目前我发现不支持用户自定义数据和自定义函数的。由于在我新的数据库开发原则中摒弃了对他们的依赖,所以也不痛苦。

Liquibase在持续集成中的应用

      
如果你基于Java开发,你可能会更喜欢Liquibase,如果基于Ruby On Rails开发,你可能会选择Migration 和 Rake。我基于.Net,考察过Red Gate($),Migration.Net, Machine.Migrations,Power Tools等等,最终还是选择了Liquibase。如何整合到我的开发流程中来,我选择命令行以及基于命令行的MsBuild任务。
      用命令行搞一个bat文件执行Liquibase,用户数据库部署(见我例子中的deploy.bat)。
      Msbuild提供比较、部署、文档生成等等全套功能,用于开发的全过程,当然也用于CC.Net(见例子中的ci.proj)。
      
      具体的应用包括:
      * 开发人员在本地完成一个阶段的开发后,使用ci.proj中的diff target比较本地数据和基线数据,产生changelog.xml,提交到SVN(这里,XML文件中的changeSet中的Author取开发人员本地机器的用户名)
      * CC.Net检测到SVN的变化,触发持续集成过程,调用ci.proj中的deploy在一个测试数据库中创建最新的数据结构,然后对实体类和访问层进行测试(开发人员在本地机器的测试亦如此)。测试成功,使用ci.proj中的doc生成文档,发布在持续集成服务器的IIS中。
      * 如果是trunk项目则在测试完成后需要在SVN上打Tag,然后使用ci.proj中的publish发布数据库部署升级程序(含changelog.xml)
      * 发布的数据库部署升级程序中含deploy.bat给部署人员用以部署或升级现有的产品系统数据库。
     
      关于changelog.xml的合并:
      * 每一次开发人员有新的changelog.xml提交,我会通过SVN的Merge功能将其新的changeSet合并
      * 这个过程可以通过SVN自动完成,但鉴于数据库的变化影响面太大,需要谨慎审核,因此还是选择手工来做
     
      关于test.proj: 这是用MsBuild写的一个测试案例,测试ci.proj提供的数据库比较和升级功能。      

      关于ci.proj的说明:
      * 对dtproperties的特殊处理
         一旦你使用过Sql Server的企业管理器操作过数据库,会自动生成这个表存一些数据库对象的属性。虽然在企业管理器中显示为系统表,但它的object_id很大,使得liquibase将其看做用户表,会影响数据库比较结果,因此在ci.proj中会检查参与比较的两个数据是否存在该表,如果不存在则创建它。
      * <Target Name="CheckExec">
        MsBuild使用Java命令行调用liquibase,如果执行出错,MsBuild不能得到该错误,这样将导致CC.Net的持续集成在失败时报成功。没有找到更好的办法,我将执行Java得到的输出写入exec.log文件,然后用CheckExec检查其中是否有Failed关键字。可能还有其他的关键字需要检查,如果园友发现还请告诉我。

<Target Name="CheckExec">
    
<ReadLinesFromFile File="$(ExecLog)" >
        
<Output TaskParameter="Lines" PropertyName="ExecOutput"/>
    
</ReadLinesFromFile>
    
<Message Text="$(ExecOutput)" Importance="high"/>
    
<StringComparison Comparison="Contains" Param1="$(ExecOutput)" Param2="Failed" >
        
<Output TaskParameter="Result" PropertyName="StringSearchResult"/>
    
</StringComparison>
    
<Delete Files="$(ExecLog)"/>
    
<Error Text="liquibase report error" Condition="$(StringSearchResult)"/>
</Target>
       

        附件:持续集成项目文件          

   
Update20080718  发现Liquibase应用SQL Server生成修改记录的两个Bug
  * 删除一个定义了外键(引用其他表)的列时,删除Constraint的脚本放在了删列的脚本后面,导致部署失败
  * 添加NotNull的Constraint是,脚本中少了columnDataType,多了defaultNullValue,导致部署失败
   


       

      
    

posted @ 2008-06-20 11:41  观无明  阅读(10157)  评论(5编辑  收藏  举报