项目场景:
在做审批中心的时候,原项目使用的是jpa,我想要改成mybatis-plus,并且自己整理思路重新写一下审批模块,所以有了这篇文章
整合步骤
pom依赖
因为我这里还使用了eureka、feign、sentry,所以引入了这些依赖,如果不需要的话可以去除,第二、第三个依赖是我自己的子模块,注意:整合activiti7和mybatis-plus的话一定要将activiti7中的mybatis依赖给去除,不然会造成版本冲突!
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.ihrm</groupId>
<artifactId>ihrm_common</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.ihrm</groupId>
<artifactId>ihrm_common_model</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.activiti</groupId>
<artifactId>activiti-spring-boot-starter</artifactId>
<version>7.1.0.M1</version>
<exclusions>
<exclusion>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.3</version>
</dependency>
<dependency>
<groupId>io.sentry</groupId>
<artifactId>sentry-spring</artifactId>
<version>1.7.30</version>
</dependency>
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.1.6</version>
</dependency>
yml文件
#spring配置
spring:
#1.应用配置
application:
name: ihrm-myaudit #指定服务名
#2.数据库连接池
datasource:
ihrm:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:3306/ihrm?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
username: root
password: aaaaaa
hikari:
maximum-pool-size: 2
activiti:
driver-class-name: com.mysql.cj.jdbc.Driver
jdbc-url: jdbc:mysql://127.0.0.1:3306/activiti?characterEncoding=utf8&useSSL=false&serverTimezone=UTC&rewriteBatchedStatements=true
username: root
password: aaaaaa
hikari:
aximum-pool-size: 2
activiti:
history-level: full
db-history-used: true
database-schema-update: false
mybatis-plus:
# xml扫描,多个目录用逗号或者分号分隔(告诉 Mapper 所对应的 XML 文件位置)
mapper-locations: classpath:mapper/*.xml
# 以下配置均有默认值,可以不设置
# global-config:
# db-config:
# #主键类型 AUTO:"数据库ID自增" INPUT:"用户输入ID",ID_WORKER:"全局唯一ID (数字类型唯一ID)", UUID:"全局唯一ID UUID";
# id-type: auto
# #字段策略 IGNORED:"忽略判断" NOT_NULL:"非 NULL 判断") NOT_EMPTY:"非空判断"
# field-strategy: NOT_EMPTY
# #数据库类型
# db-type: MYSQL
configuration:
# 是否开启自动驼峰命名规则映射:从数据库列名到Java属性驼峰命名的类似映射
map-underscore-to-camel-case: true
# 如果查询结果中包含空值的列,则 MyBatis 在映射的时候,不会映射这个字段
call-setters-on-nulls: true
# 这个配置会将执行的sql打印出来,在开发或测试的时候可以用
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
redis:
host: 127.0.0.1
port: 6379
#注册到eureka的服务地址
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:6868/eureka/
instance:
preferIpAddress : true
instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}
#服务配置
server:
port: 9007
主数据源配置(设置Activiti为主数据源)
package com.ihrm.myaudit;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;
@Configuration
@MapperScan(basePackages = "com.ihrm.myaudit.mapper", sqlSessionFactoryRef = "activitiSqlSessionFactory")
public class ActivitiDatabaseConfig {
@Bean(name = "activitiDataSource")
@Primary
@ConfigurationProperties(prefix = "spring.datasource.activiti")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "activitiSqlSessionFactory")
@Primary
public SqlSessionFactory basicSqlSessionFactory(@Qualifier("activitiDataSource") DataSource datasource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(datasource);
bean.setMapperLocations(
// 设置mybatis的xml所在位置
new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*.xml"));
return bean.getObject();
}
@Bean(name = "activitiSqlSessionTemplate")
@Primary
public SqlSessionTemplate testSqlSessionTemplate(
@Qualifier("activitiSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
从数据源配置
package com.ihrm.myaudit;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.jdbc.DataSourceBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;
@Configuration
@MapperScan(basePackages ="com.ihrm.myaudit.mapper.ihrm", sqlSessionFactoryRef = "ihrmSqlSessionFactory")
public class IhrmDatabaseConfig {
@Bean(name = "ihrmDataSource")
@ConfigurationProperties(prefix = "spring.datasource.ihrm")
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
@Bean(name = "ihrmSqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(@Qualifier("ihrmDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
factoryBean.setMapperLocations(
new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/ihrm/*.xml"));
return factoryBean.getObject();
}
@Bean(name = "ihrmSqlSessionTemplate")
public SqlSessionTemplate testSqlSessionTemplate(
@Qualifier("ihrmSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
shiro配置
package com.ihrm.myaudit;
import com.ihrm.common.shiro.realm.IhrmRealm;
import com.ihrm.common.shiro.session.CustomSessionManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.crazycake.shiro.RedisCacheManager;
import org.crazycake.shiro.RedisManager;
import org.crazycake.shiro.RedisSessionDAO;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.LinkedHashMap;
import java.util.Map;
@Configuration
public class ShiroConfiguration {
//1.创建realm
@Bean
public IhrmRealm getRealm() {
return new IhrmRealm();
}
//2.创建安全管理器
@Bean
public SecurityManager getSecurityManager(IhrmRealm realm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
securityManager.setRealm(realm);
//将自定义的会话管理器注册到安全管理器中
securityManager.setSessionManager(sessionManager());
//将自定义的redis缓存管理器注册到安全管理器中
securityManager.setCacheManager(cacheManager());
return securityManager;
}
//3.配置shiro的过滤器工厂
/**
* 再web程序中,shiro进行权限控制全部是通过一组过滤器集合进行控制
*
*/
@Bean
public ShiroFilterFactoryBean shiroFilter(SecurityManager securityManager) {
//1.创建过滤器工厂
ShiroFilterFactoryBean filterFactory = new ShiroFilterFactoryBean();
//2.设置安全管理器
filterFactory.setSecurityManager(securityManager);
//3.通用配置(跳转登录页面,未授权跳转的页面)
filterFactory.setLoginUrl("/autherror?code=1");//跳转url地址
filterFactory.setUnauthorizedUrl("/autherror?code=2");//未授权的url
//4.设置过滤器集合
Map<String,String> filterMap = new LinkedHashMap<>();
//anon -- 匿名访问
filterMap.put("/sys/login","anon");
filterMap.put("/autherror","anon");
//注册
//authc -- 认证之后访问(登录)
filterMap.put("/**","authc");
//perms -- 具有某中权限 (使用注解配置授权)
filterFactory.setFilterChainDefinitionMap(filterMap);
return filterFactory;
}
@Value("${spring.redis.host}")
private String host;
@Value("${spring.redis.port}")
private int port;
/**
* 1.redis的控制器,操作redis
*/
public RedisManager redisManager() {
RedisManager redisManager = new RedisManager();
redisManager.setHost(host);
redisManager.setPort(port);
return redisManager;
}
/**
* 2.sessionDao
*/
public RedisSessionDAO redisSessionDAO() {
RedisSessionDAO sessionDAO = new RedisSessionDAO();
sessionDAO.setRedisManager(redisManager());
return sessionDAO;
}
/**
* 3.会话管理器
*/
public DefaultWebSessionManager sessionManager() {
CustomSessionManager sessionManager = new CustomSessionManager();
//sessionManager.setSessionIdCookieEnabled(false);
sessionManager.setSessionIdUrlRewritingEnabled(false);
sessionManager.setSessionDAO(redisSessionDAO());
return sessionManager;
}
/**
* 4.缓存管理器
*/
public RedisCacheManager cacheManager() {
RedisCacheManager redisCacheManager = new RedisCacheManager();
redisCacheManager.setRedisManager(redisManager());
return redisCacheManager;
}
//开启对shior注解的支持
@Bean
public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
AuthorizationAttributeSourceAdvisor advisor = new AuthorizationAttributeSourceAdvisor();
advisor.setSecurityManager(securityManager);
return advisor;
}
}
启动类
注意去除不需要的@Bean
package com.ihrm.myaudit;
import com.ihrm.common.utils.IdWorker;
import com.ihrm.common.utils.JwtUtils;
import io.sentry.Sentry;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.impl.RepositoryServiceImpl;
import org.activiti.engine.impl.RuntimeServiceImpl;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.web.servlet.ServletContextInitializer;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.Bean;
import org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter;
import org.springframework.web.servlet.HandlerExceptionResolver;
//1.配置springboot的包扫描
@SpringBootApplication(scanBasePackages = "com.ihrm",exclude ={ SecurityAutoConfiguration.class})
@EntityScan(value="com.ihrm.entity")
@EnableEurekaClient
@EnableDiscoveryClient
@EnableFeignClients
public class MyAuditApplication {
/**
* 启动方法
*/
public static void main(String[] args) {
Sentry.init("http://0641be2867664b0daed5c615b7a398a6@sentry.itheima.net/16");
SpringApplication.run(MyAuditApplication.class, args);
}
@Bean
public HandlerExceptionResolver sentryExceptionResolver() {
return new io.sentry.spring.SentryExceptionResolver();
}
@Bean
public ServletContextInitializer sentryServletContextInitializer() {
return new io.sentry.spring.SentryServletContextInitializer();
}
@Bean
public IdWorker idWorker() {
return new IdWorker();
}
@Bean
public JwtUtils jwtUtils() {
return new JwtUtils();
}
//解决no session
@Bean
public OpenEntityManagerInViewFilter openEntityManagerInViewFilter() {
return new OpenEntityManagerInViewFilter();
}
}
ActivitiService
package com.ihrm.myaudit.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
public interface ActivitiService extends IService<Object> {
public void deployProcessDefinition(MultipartFile file, String companyId) throws IOException;
}
ActivitiServiceImpl
package com.ihrm.myaudit.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ihrm.myaudit.mapper.ActivitiMapper;
import com.ihrm.myaudit.service.ActivitiService;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@Service
public class ActivitiServiceImpl extends ServiceImpl<ActivitiMapper,Object> implements ActivitiService {
@Autowired
private RepositoryService repositoryService;
@Autowired
private RuntimeService runtimeService;
/**
* 查询所有的流程实例
* 1、根据companyId查询出对应的流程定义
* 2、根据流程定义查询出所有的流程实例
*/
public void getAllProInstance(String companyId){
List<ProcessDefinition> list = repositoryService.createProcessDefinitionQuery().processDefinitionTenantId(companyId).latestVersion().list();
List<ProcessInstance> proList=new ArrayList<>();
for(ProcessDefinition processDefinition:list){
// runtimeService.createProcessInstanceQuery().
}
}
/**
* 部署一个流程
*/
public void deployProcessDefinition(MultipartFile file,String companyId) throws IOException {
//获取文件名称
String fileName=file.getOriginalFilename();
//部署流程定义
DeploymentBuilder builder = repositoryService.createDeployment().addBytes(fileName, file.getBytes()).tenantId(companyId);
Deployment deploy=builder.deploy();
System.out.println(deploy);
}
}
目录结构
遇到的BUG
1、配置双数据源的时候,出现Autowired required a single bean, but 2 were found的异常,意思是只需要一个bean但是找到了两个
2、启动项目的时候遇到org.springframework.beans.factory.BeanCreationException,不知道什么原因
3、启动网关时报错:Failed to configure a DataSource: ‘url’ attribute is not specified and no embedded datasource could be configured.
4、运行登录子模块时,出现和3相同的报错。
5、postman通过网关或者直接访问端口出现404错误
6、访问端口报500错误
7、访问端口报空指针异常
解决方案
1、ngix文件的所在目录包含了中文,将目录换成英文目录
2、后台没有将查询到的数据返回给前端,添加数据即可
3、网关报错的原因可能是由于依赖了另一个子模块,而另一个子模块使用了mybatis-plus依赖,需要配置数据库信息,所以出现这个问题。因为我知道这个项目使用的时jpa,所以这两个子模块应该用不到mybatis,尝试取消子模块的mybatis-plus依赖,但还是出现这个报错。仔细检查发现这个子模块又引用了另一个子模块,另一个子模块中也包含mybatis-plus依赖,尝试将两个子模块的依赖全部注释,问题解决。
4、本来以为也是依赖或者子模块引用的问题,但是这个模块以来的两个子模块的mybatis-plus-boot-stater已经被注释掉了,其本身的pom文件中也没有mybatis-plus依赖,所以推测可能是模块本身的数据库连接配置出的问题。因为我的mysql用的是8.0版本,springboot默认的版本是5.1.47的,尝试修改默认版本为8.0.24,并且修改yml文件中的driver-class-name和时区,没有效果。后来仔细看了一下报错,提示的是url没有值,之前我把url改成了jdbc-url,所以就想是不是这个问题,重新改成url之后,运行项目,问题解决。但是什么时候需要用到jdbc-url什么时候用url?为什么另外的模块可以使用jdbc-url,这个模块就不行呢?
5、在controller中添加一个测试接口,测试是否进入了controller,发现测试接口也无法访问,再写了一个方法,使用@ModelAttribute注解,发现没有被调用。检查端口号,确定为9007,请求地址也没有写错,请求方式正确。Application的位置放置正确,Controller上加了RestController注解。之前请求路径前缀是直接放在@RestContoller中的,参考其他子模块发现,RestController中没有请求路径,而是使用@RequestMapping来定义前缀。修改之后不报404,报500了。
6、自己之前为了方便测试,把shiroConfiguration中的@Configuration注解给注释掉了。
7、debug发现,RepositoryService中的processEngineConfiguration为null,因为我的RepositoryService是在application中添加了一个bean,所以可能是没有配置正确。将自己在application中写的@Bean注解删除,更换Activiti的依赖,之前导入的依赖为activiti-engine,现在更换成activiti-spring-boot-starter,重新运行,访问接口,发现不报错了,但是没进断点,响应也没有任何内容。访问测试接口,报Unauthorized