-
集成flowable
1.1导入maven依赖
<flowable.version>6.7.0</flowable.version>
<!--flowable 主依赖-->
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-spring-boot-starter</artifactId>
<version>${flowable.version}</version>
</dependency>
-
集成flowable-modeler
2.1 maven依赖
<!-- flowable 集成依赖conf ,包内包含 rest,logic -->
<dependency>
<groupId>org.flowable</groupId>
<artifactId>flowable-ui-modeler-conf</artifactId>
<version>${flowable.version}</version>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</exclusion>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
</exclusion>
</exclusions>
</dependency>
前端下载
直接将github上flowable-engine项目(GitHub - flowable/flowable-engine: A compact and highly efficient workflow and Business Process Management (BPM) platform for developers, system admins and business users.)version 6.7.0 (分支 origin/flowable-release-6.7.0)的前端代码拷贝到自己项目中,路径保持相同:flowable-engine-6.7.0\modules\flowable-ui-modeler\flowable-ui-modeler-app\src\main\resources
2.2 flowable的yml配置
flowable:
# 关闭异步,不关闭历史数据的插入就是异步的,会在同一个事物里面,无法回滚
# 开发可开启会提高些效率,上线需要关闭
database-schema-update: true
process-definition-location-prefix: classpath*:/processes/
process-definition-location-suffixes: "**.bpmn20.xml, **.bpmn"
common:
app:
idm-url: http://localhost:${server.port}/flowable-idm1
idm-admin:
user: admin
password: test
#解决乱码问题
activity-font-name: 宋体
label-font-name: 宋体
annotation-font-name: 宋体
#关闭定时任务JOB
async-executor-activate: false
2.3路径重定向配置
(用于路径跳转直接可免去index.html)

@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("forward:" + indexUrl);
registry.addViewController("/modeler").setViewName("forward:/modeler/index.html");
}
2.4免登录配置
类名随意定,如FlowableSecurityConfiguration.class,包路径与flowable相同:org.flowable.ui.modeler.conf

import org.flowable.ui.common.security.SecurityConstants;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
@Configuration(proxyBeanMethods = false)
@EnableWebSecurity
public class FlowableSecurityConfiguration {
@Configuration
@Order(SecurityConstants.TASK_API_SECURITY_ORDER)
public static class ModelerApiWebSecurityConfigurationAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http
//必须要将csrf设置为disable,不然后面发送POST请求时会报403错误
.csrf().disable()
//为了简单起见,简单粗暴方式直接放行modeler下面所有请求
.authorizeRequests().antMatchers("/modeler/**").permitAll();
//解决iframe无法访问问题
http.csrf().disable().headers().frameOptions().sameOrigin();
}
}
}
2.5 数据库链接配置
类名随意定,如DatabaseConfiguration.class
import liquibase.Liquibase;
import liquibase.database.Database;
import liquibase.database.DatabaseConnection;
import liquibase.database.DatabaseFactory;
import liquibase.database.jvm.JdbcConnection;
import liquibase.exception.DatabaseException;
import liquibase.resource.ClassLoaderResourceAccessor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.ui.common.service.exception.InternalServerErrorException;
import org.flowable.ui.common.util.LiquibaseUtil;
import org.flowable.ui.modeler.properties.FlowableModelerAppProperties;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.boot.autoconfigure.MybatisProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
@Configuration
public class DatabaseConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(ModelerDatabaseConfiguration.class);
protected static final String LIQUIBASE_CHANGELOG_PREFIX = "ACT_DE_";
@Autowired
protected FlowableModelerAppProperties modelerAppProperties;
@Autowired
protected MybatisProperties mybatisProperties;
@Autowired
protected ResourceLoader resourceLoader;
protected static Properties databaseTypeMappings = getDefaultDatabaseTypeMappings();
public static final String DATABASE_TYPE_H2 = "h2";
public static final String DATABASE_TYPE_HSQL = "hsql";
public static final String DATABASE_TYPE_MYSQL = "mysql";
public static final String DATABASE_TYPE_ORACLE = "oracle";
public static final String DATABASE_TYPE_POSTGRES = "postgres";
public static final String DATABASE_TYPE_MSSQL = "mssql";
public static final String DATABASE_TYPE_DB2 = "db2";
public static Properties getDefaultDatabaseTypeMappings() {
Properties databaseTypeMappings = new Properties();
databaseTypeMappings.setProperty("H2", DATABASE_TYPE_H2);
databaseTypeMappings.setProperty("HSQL Database Engine", DATABASE_TYPE_HSQL);
databaseTypeMappings.setProperty("MySQL", DATABASE_TYPE_MYSQL);
databaseTypeMappings.setProperty("MariaDB", DATABASE_TYPE_MYSQL);
databaseTypeMappings.setProperty("Oracle", DATABASE_TYPE_ORACLE);
databaseTypeMappings.setProperty("PostgreSQL", DATABASE_TYPE_POSTGRES);
databaseTypeMappings.setProperty("Microsoft SQL Server", DATABASE_TYPE_MSSQL);
databaseTypeMappings.setProperty(DATABASE_TYPE_DB2, DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/NT", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/NT64", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2 UDP", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/LINUX", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/LINUX390", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/LINUXX8664", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/LINUXZ64", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/LINUXPPC64", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/LINUXPPC64LE", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/400 SQL", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/6000", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2 UDB iSeries", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/AIX64", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/HPUX", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/HP64", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/SUN", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/SUN64", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/PTX", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/2", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2 UDB AS400", DATABASE_TYPE_DB2);
return databaseTypeMappings;
}
@Bean
@Qualifier("flowableModeler")
@Primary
public SqlSessionFactory modelerSqlSessionFactory(DataSource dataSource) {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
String databaseType = initDatabaseType(dataSource);
if (databaseType == null) {
throw new FlowableException("couldn't deduct database type");
}
try {
Properties properties = new Properties();
properties.put("prefix", modelerAppProperties.getDataSourcePrefix());
properties.put("blobType", "BLOB");
properties.put("boolValue", "TRUE");
properties.load(this.getClass().getClassLoader().getResourceAsStream("org/flowable/db/properties/" + databaseType + ".properties"));
sqlSessionFactoryBean.setConfigurationProperties(properties);
//这个是原始代码,只添加flowable的扫描路径
// sqlSessionFactoryBean
// .setMapperLocations(ResourcePatternUtils.getResourcePatternResolver(resourceLoader).getResources("classpath:/META-INF/modeler-mybatis-mappings/*.xml"));
//由于后面要设置该SqlSessionFactory为主要的SqlSessionFactory,
// 然后Ruoyi中使用时Mybatis自动注入,所以在这个地方就需要添加Ruoyi配置扫描
ResourcePatternResolver resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
List<Resource> allResourceList = new ArrayList<>();
List<String> mapperLocations = new ArrayList<>();
//先添加
mapperLocations.add("classpath:/META-INF/modeler-mybatis-mappings/*.xml");
mapperLocations.add("classpath*:/mapper/**/*Mapper.xml");
mapperLocations.add("mapper/*/*.xml");
mapperLocations.add("classpath*:mapper/*.xml");
for (String mapperLocation : mapperLocations) {
try {
Resource[] resources = resourcePatternResolver.getResources(mapperLocation);
allResourceList.addAll(Arrays.asList(resources));
} catch (Exception ex) {
throw new FlowableException("Could not create sqlSessionFactory", ex);
}
}
Resource[] allResourceArr = allResourceList.toArray(new Resource[allResourceList.size()]);
//因为若依中使用了类别名基础包,所以需要在这个地方进行添加
sqlSessionFactoryBean.setTypeAliasesPackage(mybatisProperties.getTypeAliasesPackage());
sqlSessionFactoryBean.setMapperLocations(allResourceArr);
sqlSessionFactoryBean.afterPropertiesSet();
return sqlSessionFactoryBean.getObject();
} catch (Exception e) {
throw new FlowableException("Could not create sqlSessionFactory", e);
}
}
@Bean(destroyMethod = "clearCache")
// destroyMethod: see https://github.com/mybatis/old-google-code-issues/issues/778
@Qualifier("flowableModeler")
@Primary
public SqlSessionTemplate modelerSqlSessionTemplate(@Qualifier("flowableModeler") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
@Bean
@Qualifier("flowableModeler")
public Liquibase modelerLiquibase(DataSource dataSource) {
LOGGER.info("Configuring Liquibase");
try {
return LiquibaseUtil.runInFlowableScope(() -> createAndUpdateLiquibase(dataSource));
} catch (Exception e) {
throw new InternalServerErrorException("Error creating liquibase database", e);
}
}
protected Liquibase createAndUpdateLiquibase(DataSource dataSource) {
Liquibase liquibase = null;
try {
DatabaseConnection connection = new JdbcConnection(dataSource.getConnection());
Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(connection);
database.setDatabaseChangeLogTableName(LIQUIBASE_CHANGELOG_PREFIX + database.getDatabaseChangeLogTableName());
database.setDatabaseChangeLogLockTableName(LIQUIBASE_CHANGELOG_PREFIX + database.getDatabaseChangeLogLockTableName());
liquibase = new Liquibase("META-INF/liquibase/flowable-modeler-app-db-changelog.xml", new ClassLoaderResourceAccessor(), database);
liquibase.update("flowable");
return liquibase;
} catch (Exception e) {
throw new InternalServerErrorException("Error creating liquibase database", e);
} finally {
closeDatabase(liquibase);
}
}
protected String initDatabaseType(DataSource dataSource) {
String databaseType = null;
Connection connection = null;
try {
connection = dataSource.getConnection();
DatabaseMetaData databaseMetaData = connection.getMetaData();
String databaseProductName = databaseMetaData.getDatabaseProductName();
LOGGER.info("database product name: '{}'", databaseProductName);
databaseType = databaseTypeMappings.getProperty(databaseProductName);
if (databaseType == null) {
throw new FlowableException("couldn't deduct database type from database product name '" + databaseProductName + "'");
}
LOGGER.info("using database type: {}", databaseType);
} catch (SQLException e) {
LOGGER.error("Exception while initializing Database connection", e);
} finally {
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
LOGGER.error("Exception while closing the Database connection", e);
}
}
return databaseType;
}
private void closeDatabase(Liquibase liquibase) {
if (liquibase != null) {
Database database = liquibase.getDatabase();
if (database != null) {
try {
database.close();
} catch (DatabaseException e) {
LOGGER.warn("Error closing database", e);
}
}
}
}
}
2.6修改框架原有mybatis配置
2.6.1重写自动化读取
类名随意定,
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.boot.autoconfigure.MybatisProperties;
import org.mybatis.spring.boot.autoconfigure.SpringBootVFS;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import javax.sql.DataSource;
public class AbsMybatisAutoConfiguration {
protected SqlSessionFactory getSqlSessionFactory(
DataSource dataSource,
MybatisProperties properties,
ResourceLoader resourceLoader,
Interceptor[] interceptors,
DatabaseIdProvider databaseIdProvider,
ApplicationContext applicationContext
) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setVfs(SpringBootVFS.class);
if (StringUtils.hasText(properties.getConfigLocation())) {
factory.setConfigLocation(resourceLoader.getResource(properties.getConfigLocation()));
}
Configuration configuration = properties.getConfiguration();
if (configuration == null && !StringUtils.hasText(properties.getConfigLocation())) {
configuration = new Configuration();
}
factory.setConfiguration(configuration);
if (properties.getConfigurationProperties() != null) {
factory.setConfigurationProperties(properties.getConfigurationProperties());
}
if (!ObjectUtils.isEmpty(interceptors)) {
factory.setPlugins(interceptors);
}
if (databaseIdProvider != null) {
factory.setDatabaseIdProvider(databaseIdProvider);
}
if (StringUtils.hasLength(properties.getTypeAliasesPackage())) {
factory.setTypeAliasesPackage(properties.getTypeAliasesPackage());
}
if (StringUtils.hasLength(properties.getTypeHandlersPackage())) {
factory.setTypeHandlersPackage(properties.getTypeHandlersPackage());
}
if (!ObjectUtils.isEmpty(properties.resolveMapperLocations())) {
factory.setMapperLocations(properties.resolveMapperLocations());
}
return factory.getObject();
}
private void applyConfiguration(SqlSessionFactoryBean factory, MybatisProperties properties) {
Configuration configuration = properties.getConfiguration();
if (configuration == null && !StringUtils.hasText(properties.getConfigLocation())) {
configuration = new Configuration();
}
factory.setConfiguration(configuration);
}
public SqlSessionTemplate getSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, MybatisProperties properties) {
ExecutorType executorType = properties.getExecutorType();
if (executorType != null) {
return new SqlSessionTemplate(sqlSessionFactory, executorType);
} else {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
}
2.6.2MyBatisConfig.class修改
屏蔽内部所有方法,继承上边的自动化配置类,增加注解和注入bean
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.mybatis.spring.boot.autoconfigure.MybatisProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
/**
* Mybatis支持*匹配扫描包
*
* @author ruoyi
*/
@EnableTransactionManagement(proxyTargetClass = true)
@Configuration
@MapperScan(sqlSessionFactoryRef = "sqlSessionFactory", sqlSessionTemplateRef = "sqlSessionTemplate")
public class MyBatisConfig extends AbsMybatisAutoConfiguration{
@Bean(name = "mySqlSessionFactory")
public SqlSessionFactory sqlSessionFactoryBean(DataSource dataSource,
MybatisProperties properties,
ResourceLoader resourceLoader,
ApplicationContext applicationContext) throws Exception {
return getSqlSessionFactory(dataSource,
properties,
resourceLoader,
null,
null,
applicationContext);
}
@Bean(name = "mySqlSessionTemplate")
public SqlSessionTemplate sqlSessionTemplate(MybatisProperties properties,
@Qualifier("mySqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return getSqlSessionTemplate(sqlSessionFactory, properties);
}
}
2.6.3yml修改mybatis配置
让其支持BLOB
# MyBatis
mybatis:
# 搜索指定包别名
typeAliasesPackage: org.morfly.kdla.pdmp.**.domain
# 配置mapper的扫描,找到所有的mapper.xml映射文件
mapperLocations: mapper/*/*.xml,classpath*:mapper/*.xml,classpath:/META-INF/modeler-mybatis-mappings/*.xml
# 加载全局的配置文件
configLocation: classpath:mybatis/mybatis-config.xml
# 参数配置
configuration-properties:
# 配置流程引擎参数,详情可见 DatabaseConfiguration
blobType: BLOB
boolValue: TRUE
# 不要设置库名,否则会出现双库名 bug
prefix: ''
外记:
1.项目集成mybatis-plus
如果项目中的mybatis升级成mybatis-plus,需要对三个配置类修改:MybatisConfiguration.class和MyBatisConfig.class,DatabaseConfiguration.class,内容如下:
AbstractMybatisPlusConfiguration.class
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;
import com.baomidou.mybatisplus.autoconfigure.SpringBootVFS;
import com.baomidou.mybatisplus.core.MybatisConfiguration;
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.baomidou.mybatisplus.core.incrementer.IKeyGenerator;
import com.baomidou.mybatisplus.core.injector.ISqlInjector;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.ExecutorType;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.context.ApplicationContext;
import org.springframework.core.io.ResourceLoader;
import org.springframework.util.StringUtils;
import javax.sql.DataSource;
import java.util.List;
public class AbstractMybatisPlusConfiguration {
protected SqlSessionFactory getSqlSessionFactory(
DataSource dataSource,
MybatisPlusProperties properties,
ResourceLoader resourceLoader,
Interceptor[] interceptors,
DatabaseIdProvider databaseIdProvider,
ApplicationContext applicationContext
) throws Exception {
MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setVfs(SpringBootVFS.class);
if (StringUtils.hasText(properties.getConfigLocation())) {
factory.setConfigLocation(resourceLoader.getResource(properties.getConfigLocation()));
}
applyConfiguration(factory, properties);
if (properties.getConfigurationProperties() != null) {
factory.setConfigurationProperties(properties.getConfigurationProperties());
}
if (!ObjectUtils.isEmpty(interceptors)) {
factory.setPlugins(interceptors);
}
if (databaseIdProvider != null) {
factory.setDatabaseIdProvider(databaseIdProvider);
}
if (StringUtils.hasLength(properties.getTypeAliasesPackage())) {
factory.setTypeAliasesPackage(properties.getTypeAliasesPackage());
}
// TODO 自定义枚举包
if (StringUtils.hasLength(properties.getTypeEnumsPackage())) {
factory.setTypeEnumsPackage(properties.getTypeEnumsPackage());
}
if (properties.getTypeAliasesSuperType() != null) {
factory.setTypeAliasesSuperType(properties.getTypeAliasesSuperType());
}
if (StringUtils.hasLength(properties.getTypeHandlersPackage())) {
factory.setTypeHandlersPackage(properties.getTypeHandlersPackage());
}
if (!ObjectUtils.isEmpty(properties.resolveMapperLocations())) {
factory.setMapperLocations(properties.resolveMapperLocations());
}
// TODO 此处必为非 NULL
GlobalConfig globalConfig = properties.getGlobalConfig();
//注入填充器
if (applicationContext.getBeanNamesForType(MetaObjectHandler.class,
false, false).length > 0) {
MetaObjectHandler metaObjectHandler = applicationContext.getBean(MetaObjectHandler.class);
globalConfig.setMetaObjectHandler(metaObjectHandler);
}
//注入主键生成器
if (applicationContext.getBeanNamesForType(IKeyGenerator.class, false,
false).length > 0) {
IKeyGenerator keyGenerator = applicationContext.getBean(IKeyGenerator.class);
globalConfig.getDbConfig().setKeyGenerators((List<IKeyGenerator>) keyGenerator);
}
//注入sql注入器
if (applicationContext.getBeanNamesForType(ISqlInjector.class, false,
false).length > 0) {
ISqlInjector iSqlInjector = applicationContext.getBean(ISqlInjector.class);
globalConfig.setSqlInjector(iSqlInjector);
}
factory.setGlobalConfig(globalConfig);
return factory.getObject();
}
private void applyConfiguration(MybatisSqlSessionFactoryBean factory, MybatisPlusProperties properties) {
MybatisConfiguration configuration = properties.getConfiguration();
if (configuration == null && !StringUtils.hasText(properties.getConfigLocation())) {
configuration = new MybatisConfiguration();
}
// if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
// for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
// customizer.customize(configuration);
// }
// }
factory.setConfiguration(configuration);
}
public SqlSessionTemplate getSqlSessionTemplate(SqlSessionFactory sqlSessionFactory, MybatisPlusProperties properties) {
ExecutorType executorType = properties.getExecutorType();
if (executorType != null) {
return new SqlSessionTemplate(sqlSessionFactory, executorType);
} else {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
}
DatabaseConfiguration.class
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import liquibase.Liquibase;
import liquibase.database.Database;
import liquibase.database.DatabaseConnection;
import liquibase.database.DatabaseFactory;
import liquibase.database.jvm.JdbcConnection;
import liquibase.exception.DatabaseException;
import liquibase.resource.ClassLoaderResourceAccessor;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.session.SqlSessionFactory;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.ui.common.service.exception.InternalServerErrorException;
import org.flowable.ui.modeler.properties.FlowableModelerAppProperties;
import org.mybatis.spring.SqlSessionTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternUtils;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
@Configuration
@Slf4j
public class DatabaseConfiguration {
protected static final String LIQUIBASE_CHANGELOG_PREFIX = "ACT_DE_";
@Autowired
protected FlowableModelerAppProperties modelerAppProperties;
@Autowired
protected ResourceLoader resourceLoader;
@Autowired
protected MybatisPlusProperties mybatisPlusProperties;
protected static Properties databaseTypeMappings = getDefaultDatabaseTypeMappings();
public static final String DATABASE_TYPE_H2 = "h2";
public static final String DATABASE_TYPE_HSQL = "hsql";
public static final String DATABASE_TYPE_MYSQL = "mysql";
public static final String DATABASE_TYPE_ORACLE = "oracle";
public static final String DATABASE_TYPE_POSTGRES = "postgres";
public static final String DATABASE_TYPE_MSSQL = "mssql";
public static final String DATABASE_TYPE_DB2 = "db2";
public static Properties getDefaultDatabaseTypeMappings() {
Properties databaseTypeMappings = new Properties();
databaseTypeMappings.setProperty("H2", DATABASE_TYPE_H2);
databaseTypeMappings.setProperty("HSQL Database Engine", DATABASE_TYPE_HSQL);
databaseTypeMappings.setProperty("MySQL", DATABASE_TYPE_MYSQL);
databaseTypeMappings.setProperty("Oracle", DATABASE_TYPE_ORACLE);
databaseTypeMappings.setProperty("PostgreSQL", DATABASE_TYPE_POSTGRES);
databaseTypeMappings.setProperty("Microsoft SQL Server", DATABASE_TYPE_MSSQL);
databaseTypeMappings.setProperty(DATABASE_TYPE_DB2, DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/NT", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/NT64", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2 UDP", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/LINUX", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/LINUX390", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/LINUXX8664", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/LINUXZ64", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/LINUXPPC64", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/400 SQL", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/6000", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2 UDB iSeries", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/AIX64", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/HPUX", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/HP64", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/SUN", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/SUN64", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/PTX", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2/2", DATABASE_TYPE_DB2);
databaseTypeMappings.setProperty("DB2 UDB AS400", DATABASE_TYPE_DB2);
return databaseTypeMappings;
}
@Bean
@Qualifier("flowableModeler")
@Primary
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) {
MybatisSqlSessionFactoryBean sqlSessionFactoryBean = new MybatisSqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
String databaseType = initDatabaseType(dataSource);
if (databaseType == null) {
throw new FlowableException("couldn't deduct database type");
}
try {
Properties properties = new Properties();
properties.put("prefix", modelerAppProperties.getDataSourcePrefix());
properties.put("blobType", "BLOB");
properties.put("boolValue", "TRUE");
log.info("this.getClass().getClassLoader():{}",databaseType);
properties.load(this.getClass().getClassLoader().getResourceAsStream("org/flowable/db/properties/" + databaseType + ".properties"));
sqlSessionFactoryBean.setConfigurationProperties(properties);
//这个是原始代码,只添加flowable的扫描路径
// sqlSessionFactoryBean
// .setMapperLocations(ResourcePatternUtils.getResourcePatternResolver(resourceLoader).getResources("classpath:/META-INF/modeler-mybatis-mappings/*.xml"));
//由于后面要设置该SqlSessionFactory为主要的SqlSessionFactory,
// 然后Ruoyi中使用时Mybatis自动注入,所以在这个地方就需要添加Ruoyi配置扫描
ResourcePatternResolver resourcePatternResolver = ResourcePatternUtils.getResourcePatternResolver(resourceLoader);
List<Resource> allResourceList = new ArrayList<>();
List<String> mapperLocations = new ArrayList<>();
//先添加
mapperLocations.add("classpath:/META-INF/modeler-mybatis-mappings/*.xml");
mapperLocations.add("classpath*:/mapper/**/*Mapper.xml");
mapperLocations.add("mapper/*/*.xml");
mapperLocations.add("classpath*:mapper/*.xml");
for (String mapperLocation : mapperLocations) {
try {
Resource[] resources = resourcePatternResolver.getResources(mapperLocation);
allResourceList.addAll(Arrays.asList(resources));
} catch (Exception ex) {
throw new FlowableException("Could not create sqlSessionFactory", ex);
}
}
Resource[] allResourceArr = allResourceList.toArray(new Resource[allResourceList.size()]);
//因为若依中使用了类别名基础包,所以需要在这个地方进行添加
sqlSessionFactoryBean.setTypeAliasesPackage(mybatisPlusProperties.getTypeAliasesPackage());
sqlSessionFactoryBean.setMapperLocations(allResourceArr);
sqlSessionFactoryBean.afterPropertiesSet();
return sqlSessionFactoryBean.getObject();
} catch (Exception e) {
throw new FlowableException("Could not create sqlSessionFactory", e);
}
}
@Primary
@Bean(destroyMethod = "clearCache") // destroyMethod: see https://github.com/mybatis/old-google-code-issues/issues/778
public SqlSessionTemplate SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
@Bean
public Liquibase liquibase(DataSource dataSource) {
log.info("Configuring Liquibase");
Liquibase liquibase = null;
try {
DatabaseConnection connection = new JdbcConnection(dataSource.getConnection());
Database database = DatabaseFactory.getInstance().findCorrectDatabaseImplementation(connection);
database.setDatabaseChangeLogTableName(LIQUIBASE_CHANGELOG_PREFIX + database.getDatabaseChangeLogTableName());
database.setDatabaseChangeLogLockTableName(LIQUIBASE_CHANGELOG_PREFIX + database.getDatabaseChangeLogLockTableName());
liquibase = new Liquibase("META-INF/liquibase/flowable-modeler-app-db-changelog.xml", new ClassLoaderResourceAccessor(), database);
liquibase.update("flowable");
return liquibase;
} catch (Exception e) {
throw new InternalServerErrorException("Error creating liquibase database", e);
} finally {
closeDatabase(liquibase);
}
}
protected String initDatabaseType(DataSource dataSource) {
String databaseType = null;
Connection connection = null;
try {
connection = dataSource.getConnection();
DatabaseMetaData databaseMetaData = connection.getMetaData();
String databaseProductName = databaseMetaData.getDatabaseProductName();
log.info("database product name: '{}'", databaseProductName);
databaseType = databaseTypeMappings.getProperty(databaseProductName);
if (databaseType == null) {
throw new FlowableException("couldn't deduct database type from database product name '" + databaseProductName + "'");
}
log.info("using database type: {}", databaseType);
} catch (SQLException e) {
log.error("Exception while initializing Database connection", e);
} finally {
try {
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
log.error("Exception while closing the Database connection", e);
}
}
return databaseType;
}
private void closeDatabase(Liquibase liquibase) {
if (liquibase != null) {
Database database = liquibase.getDatabase();
if (database != null) {
try {
database.close();
} catch (DatabaseException e) {
log.warn("Error closing database", e);
}
}
}
}
}
MybatisConfig.class改名 MybatisPlusConfig.class
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.autoconfigure.MybatisPlusProperties;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.BlockAttackInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ResourceLoader;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
/**
* Mybatis Plus 配置
*
* @author ruoyi
*/
@EnableTransactionManagement(proxyTargetClass = true)
@Configuration
@MapperScan(sqlSessionFactoryRef = "sqlSessionFactory", sqlSessionTemplateRef = "sqlSessionTemplate")
public class MybatisPlusConfig extends AbstractMybatisPlusConfiguration
{
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor()
{
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 分页插件
interceptor.addInnerInterceptor(paginationInnerInterceptor());
// 乐观锁插件
interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor());
// 阻断插件
interceptor.addInnerInterceptor(blockAttackInnerInterceptor());
return interceptor;
}
/**
* 分页插件,自动识别数据库类型 https://baomidou.com/guide/interceptor-pagination.html
*/
public PaginationInnerInterceptor paginationInnerInterceptor()
{
PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor();
// 设置数据库类型为mysql
paginationInnerInterceptor.setDbType(DbType.MYSQL);
// 设置最大单页限制数量,默认 500 条,-1 不受限制
paginationInnerInterceptor.setMaxLimit(-1L);
return paginationInnerInterceptor;
}
/**
* 乐观锁插件 https://baomidou.com/guide/interceptor-optimistic-locker.html
*/
public OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor()
{
return new OptimisticLockerInnerInterceptor();
}
/**
* 如果是对全表的删除或更新操作,就会终止该操作 https://baomidou.com/guide/interceptor-block-attack.html
*/
public BlockAttackInnerInterceptor blockAttackInnerInterceptor()
{
return new BlockAttackInnerInterceptor();
}
@Bean(name = "mySqlSessionFactory")
public SqlSessionFactory sqlSessionFactoryBean(DataSource dataSource,
MybatisPlusProperties properties,
ResourceLoader resourceLoader,
ApplicationContext applicationContext) throws Exception {
return getSqlSessionFactory(dataSource,
properties,
resourceLoader,
null,
null,
applicationContext);
}
@Bean(name = "mySqlSessionTemplate")
public SqlSessionTemplate sqlSessionTemplate(MybatisPlusProperties properties,
@Qualifier("mySqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return getSqlSessionTemplate(sqlSessionFactory, properties);
}
}
主要修改的是配置获取方法。有时mybatis-plus版本不同也会出现一些问题,比如重写的自动化配置在注入主键生成器:
