设计模式综合实例分析之数据库同步系统

转:http://blog.csdn.net/lovelion/article/details/8671196

 

       为了在数据库发生故障的情况下不影响核心业务的运行,需要将生产数据库定期备份到应急数据库,以备生产数据库发生故障时,能切换到应急数据库,保证业务的正常运行。由于移动公司的数据量非常大,所以只需要对基础数据和关键数据进行备份,为了确保切换到应急数据库时保证核心业务能够运行,还需要备份整个数据库结构。

      系统目前需求仅要求支持Oracle数据库的同步,但系统设计时需要考虑以后可以方便地支持其他数据库。Oracle数据库的结构由各种数据库对象组成,要求完成对各种数据库对象的同步,包括表(包括约束)、索引、触发器、分区表、视图、存储过程、函数、包、数据库连接、序列、物化视图和同义词。各类数据库对象的同步有一定的顺序关系,总体流程如图1所示:

数据库同步流程图

 

       数据库同步系统界面如图2所示:

图2 数据库同步系统界面

       用户在操作界面指定源数据库、目标数据库、控制数据库(用于读取配置信息)的数据库连接串,同时选取需要同步的数据库对象类型,对象类型存储在配置文件database_syn_config.xml中,通过输入SQL语句可以获取需要同步的表数据。

       数据库对象同步的处理逻辑描述如下:

       (1) 对于一般的数据库对象,同步时先取出源数据库与目标数据库该类数据库对象进行对比,然后将对象更新到目标数据库。

       (2) 对于DBLink对象,由于数据库环境发生变化,需要手工调整,同步过程只记录新增的DBLink信息,而不执行创建操作。

       (3) 表的同步处理由于其包含数据,因此较为特殊,需先对表结构变化进行分析,再同步数据。表数据的同步有三种方式:增量同步、先DeleteInsert方式、临时表方式。

       (I) 增量同步。适用于可确定最后修改时间戳字段的情况。

       (II) DeleteInsert方式。即先删除表的数据,再将源数据库的该表数据插入到目标数据库,为确保数据安全,要求在一个事务内完成。

       (III) 临时表方式。用于最大限度保证数据的完整性,是一种在发生意外情况时,不丢失数据而使用的较为复杂的方式。

       由于对数据库结构修改无法做事务回滚,因此如果后面的步骤发生异常,需要通过手工编码方式来实现目标数据库结构变化的回滚。

       在本系统实现过程中使用了多种设计模式,下面对其进行简要分析(为了简化代码和类图,省略了关于包的描述,在实际应用中已将不同的类封装在不同包中

 

1. 建造者模式

       在本系统实现时提供了一个数据库同步流程管理器DBSynchronizeManager类,它用于负责控制数据库同步的具体执行步骤。用户在前台界面可以配置同步参数,程序运行时,需要根据这些参数来创建DBSynchronizeManager对象,创建完整DBSynchronizeManager对象的过程由类DBSynchronizeManagerBuilder负责,此时可以使用建造者模式来一步一步构造一个完整的复杂对象,类图如图3所示:

图3 建造者模式实例类图

       在图3中省略了抽象建造者,DBSynchronizeManagerDirector充当指挥者类,DBSynchronizeManagerBuilder充当建造者,DBSynchronizeManager充当复杂产品。

 

2. 简单工厂模式

       DBSynchronizeManagerBuilder类的buildLife()方法可以创建一个初始的DBSynchronizeManager实例,再一步一步为其设置属性,为了保证在更换数据库时无须修改DBSynchronizeManagerBuilder类的源代码,在此处使用简单工厂模式进行设计,将数据库类型存储在配置文件中,如下片段代码所示:

……
<dbSynchronizeManager dbType="oracle" class="com. chinacreator.dbSyn.oracle.OracleDB SynchronizeManager"/>	
……

 类图如图4所示:

图4 简单工厂模式实例类图

       使用简单工厂模式设计的工厂类DBSynchronizeManagerFactory代码如下所示:

public class DBSynchronizeManagerFactory {	
	public static DBSynchronizeManager factory(String dbType) throws Exception {
		String className = DBSynConfigParser.getSynchronizeManagerClass(dbType);
		return (DBSynchronizeManager)Class.forName(className).newInstance();
	}
}
  其中DBSynConfigParser类用于读取配置文件,在图4中,DBSynchronizeManagerFactory类充当数据库同步流程管理器的简单工厂,DBSynchronizeManager是抽象产品,而OracleDBSynchronizeManager为具体产品。


3. 享元模式和单例模式

       在数据库同步系统中,抽象类DBObjectSynchronizer表示需要同步的数据库对象,对于不同的数据库对象类型,提供了不同的子类实现,在数据库同步时可能有多个线程在同时进行同步工作,为了节省系统资源,可以使用享元模式来共享DBObjectSynchroizer对象,提供了享元工厂类DBObjectSynchronizerFlyweightFactory,且享元工厂类使用单例模式实现,类图如图5所示:

享元模式和单例模式实例类图

       在图5中,DBObjectSynchronizerFlyweightFactory充当数据库对象同步执行者的享元工厂,同步对象执行类DBObjectSynchronizer充当抽象享元,其间接子类OracleDBlinkDBSynchronizerOracleTableDBSynchronizer等充当具体享元(由于篇幅问题,未将所有数据库对象类一一列出)。

       在实现DBObjectSynchronizerFlyweightFactory时使用了单例模式(饿汉式单例),其代码片段如下所示:

public class DBObjectSynchronizerFlyweightFactory {
	private static DBObjectSynchronizerFlyweightFactory instance = new DBObjectSynchronizerFlyweightFactory();
	private Map<String, DBObjectSynchronizer> map = new HashMap<String, DBObjectSynchronizer>();
	private DBObjectSynchronizerFlyweightFactory(){	}
	public static DBObjectSynchronizerFlyweightFactory getInstance(){
	return instance;
……
}

4. 观察者模式

       在数据库同步系统中,用户可以自行决定需要同步哪些对象,需要同步的DBObjectSynchronizer子类对象将注册到DBSynchronizeManager中,DBSynchronizeManager类的代码片段如下所示:

public abstract class DBSynchronizeManager{	
    ……
	public void attachDBSynchronizer(DBObjectSynchronizer dbSynchronizer) {
		synchronizers.add(dbSynchronizer);
	}
	public void detachDBSynchronizer(DBObjectSynchronizer dbSynchronizer) {
		synchronizers.remove(dbSynchronizer);
	}
	public abstract void executeSyn() throws Exception;
}

 其中attachDBSynchronizer(DBObjectSynchronizerdbSynchronizer)为注册方法,detachDBSynchronizer(DBObjectSynchronizerdbSynchronizer)为注销方法,executeSyn()为抽象的通知方法,其子类OracleDBSynchronizeManagerexecuteSyn()方法提供了具体实现,类图如图6所示:

观察者模式实例类图

       在数据库同步时,如果DBSynchronizeManagerexecuteSyn()方法被调用,将遍历观察者集合,调用每一个DBObjectSynchronizer对象的executeSyn()方法和compileDBObject()方法,此时DBSynchronizeManager充当抽象观察目标,OracleDBSynchronizeManager充当具体观察目标,DBObjectSynchronizer充当抽象观察者,OracleTableDBSynchronizer充当具体观察者。


5. 模板方法模式

       在执行同步时,OracleDBSynchronizeManagerexecuteSyn()方法将依次调用synDBObject()compileDBObject()方法,并在这两个方法中分别调用DBObjectSynchronizerprocessSyn()compile()方法,在OracleDBSynchronizeManager的子类中可以覆盖synDBObject()compileDBObject()方法,如图7所示:

图7 模板方法模式实例类图

       在图7中,OracleDBSynchronizeManager充当抽象父类,其中定义了模板方法executeSyn()NewOracleDBSynchronizeManager充当具体子类,其中OracleDBSynchronize Manager的代码片段如下所示:

public class OracleDBSynchronizeManager extends DBSynchronizeManager {
	public void executeSyn() throws Exception {
		synDBObject();
		compileDBObject();
	}
	protected void synDBObject(){
		for (DBObjectSynchronizer dbSynchronizer : synchronizers) {
			try {
				dbSynchronizer.processSyn(this);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
	protected void compileDBObject(){
		for (DBObjectSynchronizer dbSynchronizer : synchronizers) {
			try {
				dbSynchronizer.compile(this);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
	}
}
由于 Oracle 数据库对象类型较多,而大部分对象的处理逻辑大同小异,只有少部分对象类型同步结构后需要重新编译,因此在设计 DefaultOracleObjectSynchronizer 类时也可以使用模板方法模式,在其中定义一个钩子方法 getCompileable() ,由子类决定是否要调用编译逻辑,代码片段如下所示:
public class DefaultOracleObjectSynchronizer extends DBObjectSynchronizer {
	......
	public void compile(DBSynchronizeManager dbSynchronizeManager)
			throws Exception {
		if (getCompileable()){
			Database destDB = dbSynchronizeManager.getDestDB();
			String[] compileObjs = findAllObjects(destDB);
			int iLen = compileObjs.length;
			for (int i = 0; i < iLen; i++) {
				compileObject(destDB, compileObjs[i]);
			}
		}
		
	}
	......
}

 6. 策略模式

       由于表数据的同步方式有三种,分别是增量同步、先DeleteInsert方式、临时表方式,因此可以定义一个同步策略接口DataSynStrategy,并提供三个具体实现类:IncSynStrategyDelAndInsSynStrategyTempTableSynStrategy。类图如图8所示:

策略模式实例类图

       在图8中,Oracle表同步对象类OracleTableDBSynchronizer充当环境类,DataSynStrategy充当抽象策略类,其子类IncSynStrategyDelAndInsSynStrategyTempTableSynStrategy充当具体策略类。

       在OracleTableDBSynchronizer中将DataSynStrategy作为方法synSingleTable()的局部变量,因此OracleTableDBSynchronizerDataSynStrategy为依赖关系,如果为全局变量,则为关联关系。

 

7. 组合模式、命令模式和职责链模式

         在使用临时表方式实现表同步时可以定义一系列命令对象,这些命令封装对数据库的操作,由于有些操作修改了数据库结构,因此传统的JDBC事务控制起不到作用,需要自己实现操作失败后的回滚逻辑。此时可以使用命令模式进行设计,在设计时还可以提供宏命令MacroCommand,用于将一些具体执行数据库操作的命令组合起来,形成复合命令。类图如图9所示(由于原始类图比较复杂,考虑到可读性,图9进行了适当简化,不过简化了之后还是挺复杂的):

组合模式、命令模式和职责链模式实例类图

       在图9中,TempTableSynCommand充当抽象命令,MacroCommand充当宏命令类,RenameTableCommandSynTableDataCommandRenameTableConstraintCommand充当具体命令,TempTableSynStrategy充当请求调用者,DataSynHelper充当请求接收者,在DataSynHelper中定义了辅助实现临时表方式同步的一些方法,在命令类中将调用这些方法。在TempTableSynCommand中声明了公共的execute()方法,并提供了回滚方法undo(),其子类实现具体的执行操作与恢复操作。DataSynHelper接口声明了进行数据库操作的方法,在其子类DataSynHelperImpl中实现了这些方法。

       在TempTableSynCommand中还定义了两个子类型的变量previousCommandnextCommnad用于保存前一个命令和后一个命令,其中nextCommnad用于在执行完当前命令的业务逻辑后,再执行下一个命令的业务逻辑;而previousCommand用于在出现异常时,调用上一个命令的undo()方法实现恢复操作。此时使用了职责链模式,nextCommnad.execute()实现正向职责链,而previousCommand.undo()加上Java的异常处理机制实现反向职责链。

       MacroCommand是宏命令,其代码片段如下所示:

public class MacroCommand extends TempTableSynCommand {
	TempTableSynCommand lastCommand = this;
	public void add(TempTableSynCommand tempTableSynCommand) {
		tempTableSynCommand.setPreviousCommand(lastCommand);
		lastCommand = tempTableSynCommand;		//创建命令链
	}
    protected void execute() throws Exception {
……
}
protected void undo() throws Exception {
……
}
}
在请求调用者类 TempTableSynStrategy 中通过如下代码片段来调用宏命令对象的 execute() 方法:
public class TempTableSynStrategy extends DataSynStrategy {
	public String processSyn() {
		//其他代码省略	
		String tempTableName = generateTempTableName();
		String backupTableName =  "BAK_" + tempTableName;
		DataSynHelper dataSynHelper = new DataSynHelperImpl();
		MacroCommand marcoCommand = new MacroCommand();
		marcoCommand.add(new RenameTableConstraintCommand(dataSynHelper, tableName, destDB));
		marcoCommand.add(new SynTableDataCommand(dataSynHelper, tableName, tempTableName, srcDB, destDB));
		marcoCommand.add(new RenameTableCommand(dataSynHelper, tableName, backupTableName, destDB));
		marcoCommand.add(new RenameTableCommand(dataSynHelper, tempTableName, tableName, destDB));
		try{
			marcoCommand.execute();			
			try {
				//其他代码省略	
			} catch (Exception e) {
				e.printStackTrace();
			}
		} catch (Exception e){
			e.printStackTrace();
		}
//其他代码省略
	}
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值