|
@@ -7,6 +7,7 @@ import com.qmth.boot.data.upgrade.service.DataUpgradeService;
|
|
|
import com.qmth.boot.data.upgrade.utils.ClassHelper;
|
|
|
import com.qmth.boot.data.upgrade.utils.VersionComparator;
|
|
|
import com.zaxxer.hikari.HikariDataSource;
|
|
|
+import org.apache.ibatis.session.TransactionIsolationLevel;
|
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
|
import org.springframework.boot.context.event.ApplicationPreparedEvent;
|
|
@@ -14,6 +15,7 @@ import org.springframework.context.ApplicationListener;
|
|
|
import org.springframework.jdbc.core.BeanPropertyRowMapper;
|
|
|
import org.springframework.jdbc.core.JdbcTemplate;
|
|
|
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
|
|
|
+import org.springframework.transaction.TransactionDefinition;
|
|
|
import org.springframework.transaction.TransactionStatus;
|
|
|
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
|
|
import org.springframework.transaction.support.TransactionTemplate;
|
|
@@ -113,8 +115,8 @@ public class DataUpgradeListener implements ApplicationListener<ApplicationPrepa
|
|
|
throw new RuntimeException("【应用信息表】中未找到匹配的数据!appCode:" + properties.getAppCode());
|
|
|
}
|
|
|
|
|
|
- log.warn("待升级应用码:{} 待升级应用版本号:{} 当前应用码:{} 当前应用版本号:{}", properties.getAppCode(),
|
|
|
- properties.getAppVersion(), appInfo.getAppCode(), appInfo.getAppVersion());
|
|
|
+ log.warn("待升级应用码:{} 待升级应用版本号:{} 当前应用码:{} 当前应用版本号:{} locked:{}", properties.getAppCode(),
|
|
|
+ properties.getAppVersion(), appInfo.getAppCode(), appInfo.getAppVersion(), appInfo.getLocked());
|
|
|
|
|
|
int compareValue = new VersionComparator().compare(appInfo.getAppVersion(), properties.getAppVersion());
|
|
|
if (compareValue > 0) {
|
|
@@ -131,10 +133,19 @@ public class DataUpgradeListener implements ApplicationListener<ApplicationPrepa
|
|
|
List<String> versionKeys = new ArrayList<>(serviceMaps.keySet());
|
|
|
versionKeys.sort(new VersionComparator());
|
|
|
|
|
|
+ // 调整事务隔离级别:串行化(最严格模式加锁读,避免脏读,幻读,不可重复读等)
|
|
|
+ transactionTemplate.setIsolationLevel(TransactionIsolationLevel.SERIALIZABLE.getLevel());
|
|
|
+ transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
|
|
|
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
|
|
|
@Override
|
|
|
protected void doInTransactionWithoutResult(TransactionStatus status) {
|
|
|
try {
|
|
|
+ // 升级锁定
|
|
|
+ if (!tryLock(jdbcTemplate, appInfo.getAppCode())) {
|
|
|
+ throw new RuntimeException("升级锁定中,请稍后再试!");
|
|
|
+ }
|
|
|
+ log.info("upgrade locking...");
|
|
|
+
|
|
|
for (String version : versionKeys) {
|
|
|
log.warn("*************** data upgrade version:{} start ***************", version);
|
|
|
serviceMaps.get(version).process(jdbcTemplate);
|
|
@@ -144,6 +155,10 @@ public class DataUpgradeListener implements ApplicationListener<ApplicationPrepa
|
|
|
status.setRollbackOnly();
|
|
|
// DDL-SQL语句不支持事务,执行失败时请采用其它补偿机制回滚!!!
|
|
|
throw new RuntimeException("SQL语句执行异常!", e);
|
|
|
+ } finally {
|
|
|
+ // 升级解锁
|
|
|
+ unLock(jdbcTemplate, appInfo.getAppCode());
|
|
|
+ log.info("upgrade unlock...");
|
|
|
}
|
|
|
}
|
|
|
});
|
|
@@ -194,7 +209,7 @@ public class DataUpgradeListener implements ApplicationListener<ApplicationPrepa
|
|
|
serviceMaps.put(implVersion, (DataUpgradeService) constructor.newInstance());
|
|
|
}
|
|
|
} catch (Exception e) {
|
|
|
- throw new RuntimeException(e);
|
|
|
+ throw new RuntimeException(e.getMessage(), e);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -207,21 +222,37 @@ public class DataUpgradeListener implements ApplicationListener<ApplicationPrepa
|
|
|
return serviceMaps;
|
|
|
}
|
|
|
|
|
|
+ private boolean tryLock(JdbcTemplate jdbcTemplate, String appCode) {
|
|
|
+ try {
|
|
|
+ return jdbcTemplate.update("update boot_app_info set locked = 1 where app_code = ? and locked = 0", appCode) > 0;
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RuntimeException("升级获取锁失败!appCode:" + appCode, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void unLock(JdbcTemplate jdbcTemplate, String appCode) {
|
|
|
+ try {
|
|
|
+ jdbcTemplate.update("update boot_app_info set locked = 0 where app_code = ?", appCode);
|
|
|
+ } catch (Exception e) {
|
|
|
+ throw new RuntimeException("升级解锁失败!appCode:" + appCode, e);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
private void updateBootAppInfo(JdbcTemplate jdbcTemplate, String appCode, String appVersion) {
|
|
|
try {
|
|
|
jdbcTemplate.update("update boot_app_info set app_version = ? where app_code = ?", appVersion, appCode);
|
|
|
} catch (Exception e) {
|
|
|
- throw new RuntimeException(e);
|
|
|
+ throw new RuntimeException("更新应用版本号失败!appCode:" + appCode, e);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private BootAppInfo queryBootAppInfo(JdbcTemplate jdbcTemplate, String appCode) {
|
|
|
try {
|
|
|
- List<BootAppInfo> list = jdbcTemplate.query("select app_code,app_version from boot_app_info where app_code = ?",
|
|
|
+ List<BootAppInfo> list = jdbcTemplate.query("select app_code,app_version,locked from boot_app_info where app_code = ?",
|
|
|
new String[]{appCode}, new BeanPropertyRowMapper<>(BootAppInfo.class));
|
|
|
return list.isEmpty() ? null : list.get(0);
|
|
|
} catch (Exception e) {
|
|
|
- throw new RuntimeException(e);
|
|
|
+ throw new RuntimeException("获取应用信息失败!appCode:" + appCode, e);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -230,13 +261,14 @@ public class DataUpgradeListener implements ApplicationListener<ApplicationPrepa
|
|
|
String createTableSql = "create table if not exists boot_app_info (" +
|
|
|
" app_code varchar(20) not null," +
|
|
|
" app_version varchar(10) not null," +
|
|
|
+ " locked bit(1) not null default 0," +
|
|
|
" unique key(app_code)" +
|
|
|
")";
|
|
|
jdbcTemplate.execute(createTableSql);
|
|
|
|
|
|
jdbcTemplate.update("insert into boot_app_info (app_code,app_version) values(?,?)", appCode, appVersion);
|
|
|
} catch (Exception e) {
|
|
|
- throw new RuntimeException(e);
|
|
|
+ throw new RuntimeException("初始应用信息表失败!appCode:" + appCode, e);
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -246,7 +278,7 @@ public class DataUpgradeListener implements ApplicationListener<ApplicationPrepa
|
|
|
List<String> exists = jdbcTemplate.queryForList("SHOW TABLES LIKE 'boot_app_info'", String.class);
|
|
|
return !exists.isEmpty();
|
|
|
} catch (Exception e) {
|
|
|
- throw new RuntimeException(e);
|
|
|
+ throw new RuntimeException("查询应用信息表失败!", e);
|
|
|
}
|
|
|
}
|
|
|
|