/*
* 文件名称: DefaultDbUpgrade.java
* 版权信息: Copyright 2001-2011 ZheJiang Collaboration Data System Co., LTD. All right reserved.
* ----------------------------------------------------------------------------------------------
* 修改历史:
* ----------------------------------------------------------------------------------------------
* 修改原因: 新增
* 修改人员: LuoJingtian
* 修改日期: 2011-12-24
* 修改内容:
*/
package com.mfh.comn.upgrade;
import com.mfh.comn.Constants;
import com.mfh.comn.utils.IOUtils;
import com.mfh.comn.utils.PathUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
/**
* 抽象的数据库升级实现
* 注意实际的数据库可能是jdbc关系数据库,也可能是其他数据存储实现。
* @since SHK BMP 1.0
*/
public class BaseDbUpgrade implements DbUpgrade {
private UpgradeSupport upgradeSupport = null;
/** 数据库升级配置信息 */
protected UpgradeConfigInfo upgradeConfigInfo;
/** 日志记录器 */
protected Logger logger = LoggerFactory.getLogger(BaseDbUpgrade.class);
@Override
public void init(UpgradeConfigInfo uci, UpgradeSupport support) {
this.upgradeConfigInfo = uci;
this.upgradeSupport = support;
try {
upgradeSupport.init(uci.getDataSourceId());
}
catch (Exception e) {
e.printStackTrace();
}
}
/**
* 构造函数
*/
public BaseDbUpgrade() {
}
/**
* 获取升级的实际数据源
* @return
* @author zhangyz created on 2013-6-20
*/
protected String getUpgradeDsName() {
return upgradeConfigInfo.getDataSourceId();
}
/**
* 获取一个升级文件读取器
*/
@Override
public BufferedReader getUpgradeSqlScriptsReader(int version) {
BufferedReader br = null;
InputStream isSql = null;
String scriptRelativePath = getScriptRelativePath(version);
try {
isSql = upgradeSupport.getUpgradeSqlScriptsStream(scriptRelativePath);
br = new BufferedReader(new InputStreamReader(isSql, Constants.defaultCode));
return br;
}
catch (UnsupportedEncodingException e) {
throw new RuntimeException(scriptRelativePath + "升级脚本文件编码错误:", e);
}
}
/**
* 获取指定版本的数据库升级脚本的相对路径
* @param version 版本
* @return 数据库升级脚本的相对路径
* @author LuoJingtian created on 2012-1-18
* @since SHK BMP 1.0
*/
private String getScriptRelativePath(int version) {
String scriptFilePath = upgradeConfigInfo.getScriptFilePath();
String scriptFilePrefix = upgradeConfigInfo.getScriptFilePrefix();
scriptFilePath = PathUtils.appendEndFileSeparator(scriptFilePath);
String dsType = this.upgradeSupport.getDsType();//根据数据源Id得到数据源的类型,以拼出升级文件目录
if (StringUtils.isNotBlank(dsType)) {
scriptFilePath = scriptFilePath + dsType + "/";
} else {
throw new RuntimeException("找不到升级脚本目录,数据源ID为:"+upgradeConfigInfo.getDataSourceId() + ", 数据源类型为:" + dsType);
}
String filePath = scriptFilePath + scriptFilePrefix + "_" + version + "_" + (version + 1) + ".sql";
return filePath;
}
/**
* 某版本升级前执行的方法
* @return true:成功执行了升级前执行的方法,且需要升级
* false:成功执行了升级前执行的方法,且不需要升级。便于子类做些检测
*
* @author zhangyz created on 2012-11-3
*/
protected boolean doBeforeUpdateVersion(int curVersion){
return true;
}
/**
* 某版本升级后执行的方法
*
* @author zhangyz created on 2012-11-3
*/
protected void doAfterUpdateVersion(int curVersion){
}
@Override
public void directToNewVersion() {
try {
int newVersion = upgradeConfigInfo.getVersin();
if (!upgradeSupport.existTable()) {
// 创建表
upgradeSupport.createVersionTable();
}
updateVersion(newVersion);
}
catch (Exception e) {
rollbackQuietly();
logger.error("执行数据库版本初始化失败:" + e.getMessage(), e);
throw new RuntimeException("执行数据库版本初始化." + upgradeConfigInfo.getDomain(), e);
}
finally {
upgradeSupport.close();
}
}
/**
* 升级主方法,sql语句以分号分隔,不需要创建T_COM_VERSION的sql
*/
@Override
public void upgrade() {
try {
int newVersion = upgradeConfigInfo.getVersin();
int curVersion = 0;
if (upgradeSupport.existTable()) {
curVersion = queryCurrentVersion();
}
else {
// 创建表
upgradeSupport.createVersionTable();
}
while(curVersion < newVersion) {
if (doBeforeUpdateVersion(curVersion)) {
upgradeVersion(curVersion);
upgradeSupport.commit();//先提交,否则后面可能的逻辑因为查询数据库可能会造成等待死锁。缺点是可能产生不一致。
logger.info("---" + upgradeConfigInfo.getDomain() + "数据库由" + (curVersion) + "_" + (curVersion + 1) + "升级成功");
doAfterUpdateVersion(curVersion);
upgradeSupport.commit();
}
if (curVersion == 0)// 某个升级文件的第一次升级
insertFirstVersion();
else
updateVersion(curVersion + 1);
curVersion++;
}
}
catch (Exception e) {
rollbackQuietly();
logger.error("执行数据库升级失败:" + e.getMessage(), e);
throw new RuntimeException("执行数据库升级失败." + upgradeConfigInfo.getDomain(), e);
}
finally {
upgradeSupport.close();
}
}
/**
* 判断当前Sql是否为建版本控制表,true表示是,false表示否
* @param sql 要进行判断的sql
* @return
* <ul>
* <li>true:是建版本控制表语句</li>
* <li>false:不是建版本控制表语句</li>
* </ul>
* @author huangwb created on 2012-1-12
* @since SHK BMP 1.0
*/
private boolean isCreateVersionTableSql(String sql) {
if (StringUtils.isNotBlank(sql)) {
if (sql.toUpperCase().contains("CREATE TABLE T_COM_VERSION")) {
return true;
}
}
return false;
}
/**
* 1.升级一个版本
* 2.修改版本信息
* 3.提示:如果升级成功,而修改版本不成功,这时会造成,下次升级出错
*
* @param version
* @return
* @throws Exception
* @author huangwb created on 2012-1-4
* @since SHK BMP 1.0
*/
private void upgradeVersion(int version) throws Exception {
BufferedReader reader = null;
String sql = null;
StringBuffer sb = new StringBuffer();
try {
reader = getUpgradeSqlScriptsReader(version);
List<String> sqls = new ArrayList<>();
sql = readSql(reader);
while (StringUtils.isNotBlank(sql)) {
if (isCreateVersionTableSql(sql)) {//忽略创建版本表的sql
// 创建版本表的sql,不做处理
}
else {
logger.info(sql);
sqls.add(sql);
sb.append(sql).append(";");
}
sql = readSql(reader);
}
if (sqls.size() > 0) {
this.upgradeSupport.upgradeVersion(sqls);
}
}
catch (Exception e) {
String msg = e.getMessage();
logger.error("---" + upgradeConfigInfo.getDomain() + "数据库由" + (version) + "_" + (version + 1)
+ "升级失败,SQL为:" + sb.toString(), e);
//throw e;
}
finally {
IOUtils.closeQuietly(reader);
}
}
/**
* 读取一条sql语句
* @param reader 升级文件读取
* @return
* @throws IOException
* @author huangwb created on 2012-1-4
* @since SHK BMP 1.0
*/
private String readSql(BufferedReader reader) throws IOException {
StringBuffer sqlBuf = new StringBuffer();
String sql = null;
String line = reader.readLine();
while(line != null){
line = line.trim();
if(StringUtils.isNotBlank(line) && !line.startsWith("--")) {
sqlBuf.append(line).append(" ");
if (line.endsWith(";")) {//一个完整的sql;
sql = sqlBuf.toString().trim();
sql = replaceBlankspace(sql.substring(0, sql.length() - 1));//去掉最后的分号,不然升级不了
if (StringUtils.isNotBlank(sql)) {
break;
}
else {
sqlBuf.delete(0,sqlBuf.length());
}
}
}
line = reader.readLine();
}
return replaceBlankspace(sql);
}
/**
* 回滚数据库操作
* @author huangwb created on 2012-1-12
* @since SHK BMP 1.0
*/
private void rollbackQuietly() {
if (upgradeSupport != null) {
try {
upgradeSupport.rollback();
}
catch (Exception e1) {
logger.error("执行数据库升级失败", e1);
throw new RuntimeException("执行数据库升级失败.", e1);
}
}
}
/**
* 获取域的当前数据升级脚本版本号
* @return
* @author huangwb created on 2011-12-30
* @since SHK BMP 1.0
*/
private int queryCurrentVersion() {
VersionInfo versionInfo = upgradeSupport.queryVersion(upgradeConfigInfo.getDomain());
String curVersionStr = "0";
int curVersion = 0;
if (null != versionInfo) {
curVersionStr = versionInfo.getCurrentVersion();
if (StringUtils.isNotBlank(curVersionStr)) {
curVersion = Integer.parseInt(curVersionStr);
} else {//前次脚本升级时版本号写入有错
logger.error("前次脚本升级时版本号为空");
throw new RuntimeException("前次脚本升级时版本号为空");
}
}
return curVersion;
}
/**
* 初始化版本表为第一个版本
* @return
* @author huangwb created on 2012-1-4
* @since SHK BMP 1.0
*/
private int insertFirstVersion() {
VersionInfo versionInfo = new VersionInfo();
versionInfo.setDomain(upgradeConfigInfo.getDomain());
versionInfo.setCurrentVersion("1");
versionInfo.setLastVersion("0");
versionInfo.setUpgradeDate(new Date());
versionInfo.setComments(upgradeConfigInfo.getDescription());
upgradeSupport.saveVersion(versionInfo);
return 0;
}
/**
* 更新数据库版本
* @param currentVersion 当前要更新到的版本号
* @author huangwb created on 2012-1-12
* @since SHK BMP 1.0
*/
private void updateVersion(int currentVersion) {
VersionInfo versionInfo = upgradeSupport.queryVersion(upgradeConfigInfo.getDomain());
if (versionInfo == null) {
insertFirstVersion();
versionInfo = upgradeSupport.queryVersion(upgradeConfigInfo.getDomain());
}
if (versionInfo == null)
throw new RuntimeException("指定的版本应用域不存在:" + upgradeConfigInfo.getDomain());
versionInfo.setCurrentVersion(String.valueOf(currentVersion));
upgradeSupport.updateVersion(versionInfo);
}
/**
* 替换空白字符为空格
* @param source 要被替换操作的字符串
* @return
* @author huangwb created on 2012-1-4
* @since SHK BMP 1.0
*/
private String replaceBlankspace(String source) {
if (StringUtils.isNotBlank(source)) {
return source.replaceAll("\\s", " ").replaceAll(" +", " ");
} else {
return source;
}
}
}