迁入core-starter项目

This commit is contained in:
mazhicheng 2020-01-11 16:29:36 +08:00
parent 71dc096d03
commit da1882716a
11 changed files with 628 additions and 0 deletions

View File

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/>
</parent>
<groupId>com.diboot</groupId>
<artifactId>diboot-core-spring-boot-starter</artifactId>
<version>2.0.4</version>
<dependencies>
<!-- 编译需要的依赖 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 添加starter依赖jar -->
<dependency>
<groupId>com.diboot</groupId>
<artifactId>diboot-core</artifactId>
<version>2.0.4</version>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,74 @@
package com.diboot.core.starter;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import com.diboot.core.util.D;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.http.HttpMessageConverters;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.core.env.Environment;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import java.util.ArrayList;
import java.util.List;
/**
* DibootCore自动配置类
* @author Mazhicheng
* @version v2.0
* @date 2019/08/01
*/
@Configuration
@EnableConfigurationProperties(CoreProperties.class)
@ComponentScan(basePackages={"com.diboot.core"})
@MapperScan(basePackages = {"com.diboot.core.mapper"})
@Order(1)
public class CoreAutoConfiguration{
@Autowired
Environment environment;
@Autowired
CoreProperties coreProperties;
@Bean
@ConditionalOnMissingBean(CorePluginManager.class)
public CorePluginManager corePluginManager(){
// 初始化SCHEMA
SqlHandler.init(environment);
CorePluginManager pluginManager = new CorePluginManager() {};
// 检查数据库字典是否已存在
if(coreProperties.isInitSql() && SqlHandler.checkIsDictionaryTableExists() == false){
SqlHandler.initBootstrapSql(pluginManager.getClass(), environment, "core");
}
return pluginManager;
}
@Bean
public HttpMessageConverters fastJsonHttpMessageConverters() {
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
//处理中文乱码问题
List<MediaType> fastMediaTypes = new ArrayList<>();
fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
converter.setSupportedMediaTypes(fastMediaTypes);
// 配置转换格式
FastJsonConfig fastJsonConfig = new FastJsonConfig();
// 设置fastjson的序列化参数禁用循环依赖检测数据兼容浏览器端避免JS端Long精度丢失问题
fastJsonConfig.setSerializerFeatures(SerializerFeature.DisableCircularReferenceDetect,
SerializerFeature.BrowserCompatible);
fastJsonConfig.setDateFormat(D.FORMAT_DATETIME_Y4MDHM);
converter.setFastJsonConfig(fastJsonConfig);
HttpMessageConverter<?> httpMsgConverter = converter;
return new HttpMessageConverters(httpMsgConverter);
}
}

View File

@ -0,0 +1,7 @@
package com.diboot.core.starter;
import com.diboot.core.plugin.PluginManager;
public class CorePluginManager implements PluginManager {
}

View File

@ -0,0 +1,26 @@
package com.diboot.core.starter;
import org.springframework.boot.context.properties.ConfigurationProperties;
/**
* diboot-core配置文件类
* @author Mazhicheng
* @version v2.0
* @date 2019/08/06
*/
@ConfigurationProperties(prefix = "diboot.core")
public class CoreProperties {
/**
* 是否初始化默认true自动安装SQL
*/
private boolean initSql = true;
public boolean isInitSql() {
return initSql;
}
public void setInitSql(boolean initSql) {
this.initSql = initSql;
}
}

View File

@ -0,0 +1,341 @@
package com.diboot.core.starter;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.toolkit.JdbcUtils;
import com.diboot.core.util.ContextHelper;
import com.diboot.core.util.S;
import com.diboot.core.util.SqlExecutor;
import com.diboot.core.util.V;
import org.apache.commons.io.IOUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.env.Environment;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* SQL处理类
* @author Mazhicheng
* @version v2.0
* @date 2019/08/01
*/
public class SqlHandler {
private static final Logger logger = LoggerFactory.getLogger(SqlHandler.class);
// 数据字典SQL
private static final String DICTIONARY_SQL = "SELECT id FROM ${SCHEMA}.dictionary WHERE id=0";
private static final String MYBATIS_PLUS_SCHEMA_CONFIG = "mybatis-plus.global-config.db-config.schema";
private static String dbType;
private static String CURRENT_SCHEMA = null;
private static Environment environment;
/**
* 初始化
* @param env
*/
public static void init(Environment env) {
environment = env;
String jdbcUrl = environment.getProperty("spring.datasource.url");
dbType = SqlHandler.extractDatabaseType(jdbcUrl);
}
/***
* 初始化安装SQL
* @return
*/
public static void initBootstrapSql(Class inst, Environment environment, String module){
if(dbType == null){
init(environment);
}
String sqlPath = "META-INF/sql/init-"+module+"-"+dbType+".sql";
extractAndExecuteSqls(inst, sqlPath);
}
/**
* 检查是否dictionary表已存在
* @return
*/
public static boolean checkIsDictionaryTableExists(){
return checkIsTableExists(DICTIONARY_SQL);
}
/**
* 检查SQL文件是否已经执行过
* @param sqlStatement
* @return
*/
public static boolean checkIsTableExists(String sqlStatement){
// 获取SqlSessionFactory实例
SqlSessionFactory sqlSessionFactory = (SqlSessionFactory) ContextHelper.getBean(SqlSessionFactory.class);
if(sqlSessionFactory == null){
logger.warn("无法获取SqlSessionFactory实例安装SQL将无法执行请手动安装");
return false;
}
sqlStatement = buildPureSqlStatement(sqlStatement);
try(SqlSession session = sqlSessionFactory.openSession(); Connection conn = session.getConnection(); PreparedStatement stmt = conn.prepareStatement(sqlStatement)){
ResultSet rs = stmt.executeQuery();
ResultSetMetaData meta = rs.getMetaData();
if(meta.getColumnCount() > 0){
rs.close();
}
return true;
}
catch(Exception e){
return false;
}
}
/***
* 从SQL文件读出的行内容中 提取SQL语句并执行
* @param sqlPath
* @return
*/
public static boolean extractAndExecuteSqls(Class inst, String sqlPath){
List<String> sqlFileReadLines = readLinesFromResource(inst, sqlPath);
if(V.isEmpty(sqlFileReadLines)){
return false;
}
// 解析SQL
List<String> sqlStatementList = new ArrayList<>();
StringBuilder sb = new StringBuilder();
for(String line : sqlFileReadLines){
if(line.contains("--")){
line = line.substring(0, line.indexOf("--"));
}
sb.append(" ");
if(line.contains(";")){
// 语句结束
sb.append(line.substring(0, line.indexOf(";")));
String cleanSql = buildPureSqlStatement(sb.toString());
sqlStatementList.add(cleanSql);
sb.setLength(0);
if(line.indexOf(";") < line.length()-1){
String leftSql = line.substring(line.indexOf(";")+1);
if(V.notEmpty(leftSql)){
sb.append(leftSql);
}
}
}
else if(V.notEmpty(line)){
sb.append(line);
}
}
if(sb.length() > 0){
String cleanSql = buildPureSqlStatement(sb.toString());
sqlStatementList.add(cleanSql);
sb.setLength(0);
}
// 返回解析后的SQL语句
return executeMultipleUpdateSqls(sqlStatementList);
}
/**
* 构建纯净可执行的SQL语句: 去除注释替换变量
* @param sqlStatement
* @return
*/
protected static String buildPureSqlStatement(String sqlStatement){
sqlStatement = clearComments(sqlStatement);
// 替换sqlStatement中的变量{SCHEMA}
if(sqlStatement.contains("${SCHEMA}")){
if(dbType.equals(DbType.SQL_SERVER.getDb())){
sqlStatement = S.replace(sqlStatement, "${SCHEMA}", getSqlServerCurrentSchema());
}
else if(dbType.equals(DbType.ORACLE.getDb())){
sqlStatement = S.replace(sqlStatement, "${SCHEMA}", getOracleCurrentSchema());
}
else{
sqlStatement = S.replace(sqlStatement, "${SCHEMA}.", "");
}
}
return sqlStatement;
}
/***
* 执行多条批量更新SQL
* @param sqlStatementList
* @return
*/
public static boolean executeMultipleUpdateSqls(List<String> sqlStatementList){
if(V.isEmpty(sqlStatementList)){
return false;
}
for(String sqlStatement : sqlStatementList){
try{
boolean success = SqlExecutor.executeUpdate(sqlStatement, null);
if(success){
logger.info("初始化SQL执行完成: "+ S.substring(sqlStatement, 0, 60) + "...");
}
}
catch (Exception e){
logger.error("初始化SQL执行异常请检查或手动执行。SQL => "+sqlStatement, e);
}
}
return true;
}
/***
* 获取
* @param inst
* @return
*/
protected static List<String> readLinesFromResource(Class inst, String sqlPath){
List<String> lines = null;
try{
InputStream is = inst.getClassLoader().getResourceAsStream(sqlPath);
lines = IOUtils.readLines(is, "UTF-8");
}
catch (FileNotFoundException fe){
logger.warn("暂未发现数据库SQL: "+sqlPath + " 请参考其他数据库定义DDL手动初始化。");
}
catch (Exception e){
logger.warn("读取SQL文件异常: "+sqlPath, e);
}
return lines;
}
/***
* 剔除SQL中的注释提取可执行的实际SQL
* @param inputSql
* @return
*/
private static String clearComments(String inputSql){
String[] sqlRows = inputSql.split("\\n");
List<String> cleanSql = new ArrayList();
for(String row : sqlRows){
// 去除行注释
if(row.contains("--")){
row = row.substring(0, row.indexOf("--"));
}
if(V.notEmpty(row.trim())){
cleanSql.add(row);
}
}
inputSql = S.join(cleanSql, " ");
// 去除多行注释
inputSql = removeMultipleLineComments(inputSql);
// 去除换行
return inputSql.replaceAll("\r|\n", " ");
}
/***
* 去除多行注释
* @param inputSql
* @return
*/
private static String removeMultipleLineComments(String inputSql){
if(inputSql.contains("*/*")){
//忽略此情况避免死循环
return inputSql;
}
if(inputSql.contains("/*") && inputSql.contains("*/")){
inputSql = inputSql.substring(0, inputSql.lastIndexOf("/*")) + inputSql.substring(inputSql.indexOf("*/")+2, inputSql.length());
}
if(inputSql.contains("/*") && inputSql.contains("*/")){
return removeMultipleLineComments(inputSql);
}
return inputSql;
}
/**
* 提取数据库类型
* @param jdbcUrl
* @return
*/
public static String extractDatabaseType(String jdbcUrl){
DbType dbType = JdbcUtils.getDbType(jdbcUrl);
String dbName = dbType.getDb();
if(dbName.startsWith(DbType.SQL_SERVER.getDb()) && !dbName.equals(DbType.SQL_SERVER.getDb())){
dbName = DbType.SQL_SERVER.getDb();
}
return dbName;
}
//SQL Server查询当前schema
public static final String SQL_DEFAULT_SCHEMA = "SELECT DISTINCT default_schema_name FROM sys.database_principals where default_schema_name is not null AND name!='guest'";
/**
* 查询SqlServer当前schema
* @return
*/
public static String getSqlServerCurrentSchema(){
if(CURRENT_SCHEMA == null){
Object firstValue = queryFirstValue(SQL_DEFAULT_SCHEMA, "default_schema_name");
if(firstValue != null){
CURRENT_SCHEMA = (String)firstValue;
}
if(CURRENT_SCHEMA == null){
CURRENT_SCHEMA = environment.getProperty(MYBATIS_PLUS_SCHEMA_CONFIG);
}
// dbo schema兜底
if(CURRENT_SCHEMA == null){
CURRENT_SCHEMA = "dbo";
}
}
return CURRENT_SCHEMA;
}
/**
* 获取当前schemaoracle默认schema=当前user
* @return
*/
public static String getOracleCurrentSchema(){
if(CURRENT_SCHEMA == null){
// 先查找配置中是否存在指定
String alterSessionSql = environment.getProperty("spring.datasource.hikari.connection-init-sql");
if(V.notEmpty(alterSessionSql) && S.containsIgnoreCase(alterSessionSql," current_schema=")){
CURRENT_SCHEMA = S.substringAfterLast(alterSessionSql, "=");
}
if(CURRENT_SCHEMA == null){
CURRENT_SCHEMA = environment.getProperty(MYBATIS_PLUS_SCHEMA_CONFIG);
}
if(CURRENT_SCHEMA == null){
// 然后默认为当前用户名大写
String username = environment.getProperty("spring.datasource.username");
if(username != null){
CURRENT_SCHEMA = username.toUpperCase();
}
}
}
return CURRENT_SCHEMA;
}
/**
* 查询SQL返回第一项
* @return
*/
public static Object queryFirstValue(String sql, String key){
try{
List<Map<String, Object>> mapList = SqlExecutor.executeQuery(sql, null);
if(V.notEmpty(mapList)){
for (Map<String, Object> mapElement : mapList){
if(mapElement.get(key) != null){
return mapElement.get(key);
}
}
}
}
catch(Exception e){
logger.error("获取SqlServer默认Schema异常: {}", e.getMessage());
}
return null;
}
/**
* 获取数据库类型
* @return
*/
public static String getDbType(){
return dbType;
}
}

View File

@ -0,0 +1 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.diboot.core.starter.CoreAutoConfiguration

View File

@ -0,0 +1,19 @@
-- 建表
CREATE TABLE `dictionary` (
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
`parent_id` int unsigned NOT NULL COMMENT '父ID',
`type` varchar(50) NOT NULL COMMENT '字典类型',
`item_name` varchar(100) NOT NULL COMMENT '显示名',
`item_value` varchar(100) DEFAULT NULL COMMENT '存储值',
`description` varchar(100) DEFAULT NULL COMMENT '描述说明',
`extdata` varchar(200) DEFAULT NULL COMMENT '扩展JSON',
`sort_id` smallint NOT NULL DEFAULT '99' COMMENT '排序号',
`is_editable` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否可改',
`is_deletable` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否可删',
`is_deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除标记',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`)
) AUTO_INCREMENT=10000 DEFAULT CHARSET=utf8 COMMENT '数据字典';
-- 创建索引
create index idx_directory on dictionary(type, item_value);

View File

@ -0,0 +1,19 @@
-- 建表
CREATE TABLE `dictionary` (
`id` int unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
`parent_id` int unsigned NOT NULL COMMENT '父ID',
`type` varchar(50) NOT NULL COMMENT '字典类型',
`item_name` varchar(100) NOT NULL COMMENT '显示名',
`item_value` varchar(100) DEFAULT NULL COMMENT '存储值',
`description` varchar(100) DEFAULT NULL COMMENT '描述说明',
`extdata` varchar(200) DEFAULT NULL COMMENT '扩展JSON',
`sort_id` smallint NOT NULL DEFAULT '99' COMMENT '排序号',
`is_editable` tinyint(1) NOT NULL DEFAULT '1' COMMENT '是否可改',
`is_deletable` tinyint(1) NOT NULL DEFAULT '0' COMMENT '是否可删',
`is_deleted` tinyint(1) NOT NULL DEFAULT '0' COMMENT '删除标记',
`create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
PRIMARY KEY (`id`)
) AUTO_INCREMENT=10000 DEFAULT CHARSET=utf8 COMMENT '数据字典';
-- 创建索引
create index idx_directory on dictionary(type, item_value);

View File

@ -0,0 +1,36 @@
-- 建表
create table ${SCHEMA}.dictionary (
id NUMBER(20) generated as identity ( start with 10000 nocycle noorder),
parent_id NUMBER(20) default 0 not null,
type VARCHAR2(50) not null,
item_name VARCHAR2(100) not null,
item_value VARCHAR2(100),
description VARCHAR2(100),
extdata VARCHAR2(200),
sort_id SMALLINT default 99 not null,
is_deletable NUMBER(1) default 0 not null,
is_editable NUMBER(1) default 1 not null,
is_deleted NUMBER(1) default 0 not null,
create_time TIMESTAMP default CURRENT_TIMESTAMP not null,
constraint PK_dictionary primary key (id)
);
-- 添加备注
comment on column ${SCHEMA}.dictionary.id is 'ID';
comment on column ${SCHEMA}.dictionary.parent_id is '父ID';
comment on column ${SCHEMA}.dictionary.type is '字典类型';
comment on column ${SCHEMA}.dictionary.item_name is '显示名';
comment on column ${SCHEMA}.dictionary.item_value is '存储值';
comment on column ${SCHEMA}.dictionary.description is '备注';
comment on column ${SCHEMA}.dictionary.extdata is '扩展JSON';
comment on column ${SCHEMA}.dictionary.sort_id is '排序号';
comment on column ${SCHEMA}.dictionary.is_editable is '是否可改';
comment on column ${SCHEMA}.dictionary.is_deletable is '是否可删';
comment on column ${SCHEMA}.dictionary.is_deleted is '删除标记';
comment on column ${SCHEMA}.dictionary.create_time is '创建时间';
comment on table ${SCHEMA}.dictionary is '数据字典';
-- 创建索引
create index idx_directory on ${SCHEMA}.dictionary (type, item_value);

View File

@ -0,0 +1,33 @@
-- 建表
create table dictionary (
id serial not null,
parent_id int not null,
type VARCHAR(50) not null,
item_name VARCHAR(100) not null,
item_value VARCHAR(100) null,
description VARCHAR(100) null,
extdata VARCHAR(200) null,
sort_id SMALLINT not null default 99,
is_deletable BOOLEAN not null default FALSE,
is_editable BOOLEAN not null default TRUE,
is_deleted BOOLEAN not null default FALSE,
create_time DATE not null default CURRENT_TIMESTAMP,
constraint PK_dictionary primary key (id)
);
-- 添加备注
comment on column dictionary.id is 'ID';
comment on column dictionary.parent_id is '父ID';
comment on column dictionary.type is '字典类型';
comment on column dictionary.item_name is '显示名';
comment on column dictionary.item_value is '存储值';
comment on column dictionary.description is '描述说明';
comment on column dictionary.extdata is '扩展JSON';
comment on column dictionary.sort_id is '排序号';
comment on column dictionary.is_editable is '是否可改';
comment on column dictionary.is_deletable is '是否可删';
comment on column dictionary.is_deleted is '删除标记';
comment on column dictionary.create_time is '创建时间';
comment on table dictionary is '数据字典';
-- 创建索引
create index idx_directory on dictionary(type, item_value);

View File

@ -0,0 +1,33 @@
-- 建表
create table ${SCHEMA}.dictionary (
id int identity,
parent_id int not null,
type varchar(50) not null,
item_name varchar(100) not null,
item_value varchar(100) null,
description varchar(100) null,
extdata varchar(200) null,
sort_id smallint not null default 99,
is_deletable tinyint not null default 0,
is_editable tinyint not null default 1,
is_deleted tinyint not null default 0,
create_time datetime not null default CURRENT_TIMESTAMP,
constraint PK_dictionary primary key (id)
);
-- 添加备注
execute sp_addextendedproperty 'MS_Description', N'ID', 'SCHEMA', '${SCHEMA}', 'table', dictionary, 'column', 'id';
execute sp_addextendedproperty 'MS_Description', N'父ID','SCHEMA', '${SCHEMA}', 'table', dictionary, 'column', 'parent_id';
execute sp_addextendedproperty 'MS_Description', N'字典类型','SCHEMA', '${SCHEMA}', 'table', dictionary, 'column', 'type';
execute sp_addextendedproperty 'MS_Description', N'显示名','SCHEMA', '${SCHEMA}', 'table', dictionary, 'column', 'item_name';
execute sp_addextendedproperty 'MS_Description', N'存储值','SCHEMA', '${SCHEMA}', 'table', dictionary, 'column', 'item_value';
execute sp_addextendedproperty 'MS_Description', N'备注','SCHEMA', '${SCHEMA}', 'table', dictionary, 'column', 'description';
execute sp_addextendedproperty 'MS_Description', N'扩展JSON','SCHEMA', '${SCHEMA}', 'table', dictionary, 'column', 'extdata';
execute sp_addextendedproperty 'MS_Description', N'排序号','SCHEMA', '${SCHEMA}', 'table', dictionary, 'column', 'sort_id';
execute sp_addextendedproperty 'MS_Description', N'是否可删除','SCHEMA', '${SCHEMA}', 'table', dictionary, 'column', 'is_deletable';
execute sp_addextendedproperty 'MS_Description', N'是否可编辑','SCHEMA', '${SCHEMA}', 'table', dictionary, 'column', 'is_editable';
execute sp_addextendedproperty 'MS_Description', N'删除标记','SCHEMA', '${SCHEMA}', 'table', dictionary, 'column', 'is_deleted';
execute sp_addextendedproperty 'MS_Description', N'创建时间','SCHEMA', '${SCHEMA}', 'table', dictionary, 'column', 'create_time';
execute sp_addextendedproperty 'MS_Description', N'数据字典','SCHEMA', '${SCHEMA}', 'table', dictionary, null, null;
-- 创建索引
create nonclustered index idx_directory on ${SCHEMA}.dictionary(type, item_value);