zoukankan      html  css  js  c++  java
  • SpringBoot+MyBatis+Mysql+Durid实现动态多数据源

    亲测有效,网上其他不是报错就是唠海嗑,所以自用在别人基础上改进报错,封装一套。

    一、项目重点有:

    (1)、SpringBoot+Mybatis+Mysql+Durid整合
    (2)、错误后跳转到指定页面
    (3)、多数据源动态切换
    (4)、mybatis分页
    (5)、durid监控
    (6)、集成log4j2日志
    (7)、通过mybatis拦截器,在控制台打印完整的sql

    二、项目截图:

     

     三、SpringBoot+Mybatis+Mysql+Durid整合

    (1)、application.yml:

    spring:
      dynamic-datasource:
        druid:
          # 连接池的配置信息
          # 初始化大小,最小,最大
          initial-size: 5
          min-idle: 5
          maxActive: 20
          # 配置获取连接等待超时的时间
          maxWait: 60000
          # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
          timeBetweenEvictionRunsMillis: 60000
          # 配置一个连接在池中最小生存的时间,单位是毫秒
          minEvictableIdleTimeMillis: 300000
          validationQuery: SELECT 1
          testWhileIdle: true
          testOnBorrow: false
          testOnReturn: false
          # 打开PSCache,并且指定每个连接上PSCache的大小
          poolPreparedStatements: true
          maxPoolPreparedStatementPerConnectionSize: 20
          # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
          filters: stat,wall,slf4j
          # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
          connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
          # 配置DruidStatFilter
          web-stat-filter:
            enabled: true
            url-pattern: "/*"
            exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
          # 配置DruidStatViewServlet
          stat-view-servlet:
            url-pattern: "/druid/*"
            # IP白名单(没有配置或者为空,则允许所有访问)
            allow: 127.0.0.1
            # IP黑名单 (存在共同时,deny优先于allow)
            deny: 192.168.1.73
            #  禁用HTML页面上的“Reset All”功能
            reset-enable: false
            # 登录名
            login-username: admin
            # 登录密码
            login-password: 123456
          filter:
            stat:
              log-slow-sql: true
              slow-sql-millis: 1000
              merge-sql: true
            wall:
              config:
                multi-statement-allow: true
        druid-datasources:
          jwpd:
            driver-class-name: com.mysql.cj.jdbc.Driver
            url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
            username: root
            password: 137972zc
          lkj:
            driver-class-name: com.mysql.cj.jdbc.Driver
            url: jdbc:mysql://localhost:3306/test2?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC
            username: root
            password: 137972zc
      mvc:
        view:
          prefix: /WEB-INF/page/
          suffix: .jsp
    server:
      port: 9090
    logging:
      config: classpath:log4j2-spring-dev.xml
    mybatis:
      type-aliases-package: com.base.springboot.entity
    

     说明:配置druid数据连接池,配置jdbc连接(两个数据源)

    (2)、配置数据源(DataSourceProperties.class)

    @Configuration
    public class DataSourceProperties {
    
        @ConfigurationProperties(prefix = "spring.dynamic-datasource.druid-datasources.jwpd")
        @Bean(name = "JWPDDataSource")
        public DataSource JWPDDataSource(StandardEnvironment env){
            DruidDataSource druidDataSource = DruidDataSourceBuilder.create().build();
            return common(env,druidDataSource);
        }
    
    
    
        @ConfigurationProperties(prefix = "spring.dynamic-datasource.druid-datasources.lkj")
        @Bean(name = "LKJDataSource")
        public DataSource LKJDataSource(StandardEnvironment env){
            DruidDataSource druidDataSource = DruidDataSourceBuilder.create().build();
            return common(env,druidDataSource);
        }
    
        public DataSource common(StandardEnvironment env, DruidDataSource druidDataSource){
            Properties properties = new Properties();
            PropertySource<?> appProperties =  env.getPropertySources().get("applicationConfig: [classpath:/application.yml]");
            Map<String,Object> source = (Map<String, Object>) appProperties.getSource();
            properties.putAll(source);
            druidDataSource.configFromPropety(properties);
            return druidDataSource;
        }
    }
    

    说明:配置数据源,common(env,druidDataSource)方法,是为了继续设置为null的属性(durid配置属性,最大最小连接数、监控地址等等)

    (3)、Spring和Mybatis的整合配置文件(MybatisConfig.class)

    @Configuration
    public class MybatisConfig {
    
        //注入数据源JWPDDataSource
        @Autowired
        @Qualifier("JWPDDataSource")
        public DataSource JWPDDataSource;
    
        //注入数据源LKJDataSource
        @Autowired
        @Qualifier("LKJDataSource")
        public DataSource LKJDataSource;
    
    
        //声明动态数据源,默认值为JWPDDataSource
        @Bean("dynamicDataSource")
        @Primary
        public DynamicDataSource dynamicDataSource(){
            //动态数据源集合
            Map<Object, Object> targetDataSourcesMap = new HashMap<>(2);
            targetDataSourcesMap.put(DataSourceEnum.jwpd.name(),JWPDDataSource);
            targetDataSourcesMap.put(DataSourceEnum.lkj.name(),LKJDataSource);
            DynamicDataSource dynamicDataSource = new DynamicDataSource(targetDataSourcesMap,JWPDDataSource);
    
            return dynamicDataSource;
        }
    
    
        @Bean(name="pageHelper")
        public PageHelper pageHelper() {
            PageHelper pageHelper = new PageHelper();
            Properties p = new Properties();
            p.setProperty("offsetAsPageNum", "true");
            p.setProperty("rowBoundsWithCount", "true");
            p.setProperty("reasonable", "true");
            p.setProperty("dialect", "mysql");
            pageHelper.setProperties(p);
    
            return pageHelper;
        }
    
    
        //sql打印插件
        @Bean(name="fullSqlInterceptor")
        public FullSqlInterceptor fullSqlInterceptor(){
            return new FullSqlInterceptor();
        }
    
        /**
         * 声明sql会话
         * @return
         */
        @Bean(name = "sqlSessionFactory")
        public SqlSessionFactory sqlSessionFactory(@Qualifier("pageHelper")PageHelper pageHelper,@Qualifier("fullSqlInterceptor")FullSqlInterceptor fullSqlInterceptor) throws Exception{
            //声明sql会话工厂
            SqlSessionFactoryBean factoryBean=new SqlSessionFactoryBean();
            //设置数据源
            factoryBean.setDataSource(dynamicDataSource());
            //设置扫描mybatisXml的路径
            factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:**/dao/*/*.xml"));
            factoryBean.setPlugins(new Interceptor[] {pageHelper(), fullSqlInterceptor});//添加分页插件
            //返回sql会话
            return factoryBean.getObject();
        }
    
    
        /**
         * 声明事务管理器
         * @return PlatformTransactionManager
         */
        @Bean(name = "transactionManager")
        public PlatformTransactionManager transactionManager() {
            return new DataSourceTransactionManager(dynamicDataSource());
        }
    
        /**
         * 声明sqlSession模板
         * @param sqlSessionFactory
         * @return
         */
        @Bean(name = "sqlSessionTemplate")
        public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory){
            return new  SqlSessionTemplate(sqlSessionFactory);
        }
    }

    说明:

    1. 注入多个数据源
    2. 声明动态数据源,声明动态数据源和目标数据源(多个用于切换的数据源)
    3. 声明一些插件,比如分页插件和打印全文sql的插件
    4. 声明sql会话
    5. 声明事务管理器
    6. 声明sqlSession模板

    (4)、创建动态数据源对象(DynamicDataSource.class)

    public class DynamicDataSource extends AbstractRoutingDataSource {
    
    
        /**
         * 有参构造方法,声明对象的时候执行,调用父类AbstractRoutingDataSource的方法
         * @param targetDataSources  数据源Map集合
         * @param defaultTargetDataSource  默认数据源
         */
        public DynamicDataSource(Map<Object,Object> targetDataSources, DataSource defaultTargetDataSource) {
            //将数据源的 key 放到数据源上下文的 key 集合中,用于切换时判断数据源是否有效
            DynamicDataSourceContextHolder.addDataSourceKeys(targetDataSources.keySet());
            //设置数据源集合
            super.setTargetDataSources(targetDataSources);
            //设置默认数据源
            super.setDefaultTargetDataSource(defaultTargetDataSource);
        }
    
        /**
         * 重写determineCurrentLookupKey方法,这个方法返回一个key值,
         * 通过这个key值执行determineTargetDataSource方法,获取当前的数据源
         * @return
         */
        @Override
        protected Object determineCurrentLookupKey() {
            return DynamicDataSourceContextHolder.getDataSourceKey();
        }
    }

    说明:继承AbstractRoutingDataSource抽象类,实现setTargetDataSources(设置目标数据源集合)、setDefaultTargetDataSource(设置默认数据源)、determineCurrentLookupKey(返回当前数据源)等方法。

    (5)、动态数据源操作上下文类(DynamicDataSourceContextHolder.class)

    public class DynamicDataSourceContextHolder {
    
        /**
         * 静态ThreadLocal常量contextHolder,用来装当前线程的数据源key
         */
        public static final ThreadLocal<String> contextHolder=new ThreadLocal<>();
    
    
    
        /**
         * 数据源的 key集合,用于切换时判断数据源是否存在
         */
        public static List<Object> dataSourceKeys = new ArrayList<>();
    
    
        /**
         * 获取contextHolder值(数据源key)的方法(获取当前数据源)
         */
        public static String getDataSourceKey(){
            return contextHolder.get();
        }
    
    
        /**
         * 写入contextHolder值(数据源key)的方法(写入当前数据源)
         */
        public static void setDataSourceKey(String key){
            contextHolder.set(key);
        }
    
    
        /**
         * 清除contextHolder值(数据源key)的方法(写入当前数据源)
         */
        public static void clearDataSourceKey(){
            contextHolder.remove();
        }
    
    
        /**
         * 判断是否包含数据源
         * @param key 数据源key
         * @return boolean
         */
        public static boolean containDataSourceKey(String key) {
            return dataSourceKeys.contains(key);
        }
    
        /**
         * 添加数据源keys
         * @param keys
         * @return boolean
         */
        public static boolean addDataSourceKeys(Collection<? extends Object> keys) {
            return dataSourceKeys.addAll(keys);
        }
    }

    说明:声明set、get当前数据源key、判断是否包含当前数据源、添加数据源key的方法

    (6)、设置当前数据源注解(TargetDs.class)

    @Target({ElementType.METHOD, ElementType.TYPE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface TargetDs {
    
        /**
         * 数据源key值
         * @return
         */
        String value();
    }
    

    (7)、设置动态数据源切换类(DynamicDataSourceAspect.class)

    @Aspect
    @Order(-1)  // 该切面应当先于 @Transactional 执行
    @Component
    public class DynamicDataSourceAspect {
    
    
        /**
         * 前置通知,进入切点之前,先切换数据源
         * @param point
         * @param targetDs
         */
        @Before("@annotation(targetDs)")
        public void switchDataSource(JoinPoint point, TargetDs targetDs) {
            //判断,如果没有此数据源
            if (!DynamicDataSourceContextHolder.containDataSourceKey(targetDs.value())){
                System.out.println("没有找到key为[{}]的数据源,所以当前还是使用默认数据源!"+targetDs.value());
            }else {
                DynamicDataSourceContextHolder.setDataSourceKey(targetDs.value());
                System.out.println("方法"+point.getSignature().getName()+"上发现@TargetDs注解,"+"当前数据源已经切换为[{}]!"+targetDs.value());
            }
        }
    
    
        /**
         * 后置通知,切合方法执行完成之后,重置数据源
         * @param point
         * @param targetDs
         */
        @After("@annotation(targetDs)")
        public void restoreDataSource(JoinPoint point, TargetDs targetDs) {
            System.out.println("重置数据源 [" + DynamicDataSourceContextHolder.getDataSourceKey()
                    + "] in Method [" + point.getSignature() + "]");
            // 将数据源置为默认数据源
            DynamicDataSourceContextHolder.clearDataSourceKey();
        }
    }

    说明:设置切面类,切点是带有@targetDs注解的方法,当遇到这种方法,执行前将数据源切换到对应的key对应的数据源,执行完成后还原到默认数据源。

    (8)、事务配置(TransactionAdviceConfig.class)

    @Aspect
    @Configuration
    public class TransactionAdviceConfig {
    
        //声明切面
        private static final String AOP_POINTCUT_EXPRESSION = "execution (* com.base.springboot.service.*.impl.*.*(..))";
    
    
        //事务管理器
        @Autowired
        @Qualifier("transactionManager")
        private PlatformTransactionManager transactionManager;
    
        //声明通知
        @Bean(name = "txInterceptor")
        public TransactionInterceptor txInterceptor(){
    
            Properties attributes = new Properties();
            attributes.setProperty("insert*",   "PROPAGATION_REQUIRED");
            attributes.setProperty("add*",      "PROPAGATION_REQUIRED");
            attributes.setProperty("update*",   "PROPAGATION_REQUIRED");
            attributes.setProperty("delete*",   "PROPAGATION_REQUIRED");
            attributes.setProperty("deploy*",   "PROPAGATION_REQUIRED");
            attributes.setProperty("select*",   "PROPAGATION_REQUIRED,readOnly");
            attributes.setProperty("get*",  	"PROPAGATION_REQUIRED,readOnly");
            attributes.setProperty("query*",    "PROPAGATION_REQUIRED,readOnly");
            return new TransactionInterceptor(transactionManager, attributes);
        }
    
        @Bean
        public AspectJExpressionPointcutAdvisor pointcutAdvisor(@Qualifier("txInterceptor") TransactionInterceptor txInterceptor){
            AspectJExpressionPointcutAdvisor pointcutAdvisor = new AspectJExpressionPointcutAdvisor();
            pointcutAdvisor.setAdvice(txInterceptor);
            pointcutAdvisor.setExpression(AOP_POINTCUT_EXPRESSION);
            return pointcutAdvisor;
        }
    }

    说明:注入事务管理器,声明切面(控制的范围),声明事务拦截器(设置不同的方法对应的事务策略),声明AspectJExpressionPointcutAdvisor,传入切面和事务拦截通知,完成aop切入。

    以上8个步骤,就完成了SpringBoot+MyBatis+Mysql+Durid的配置,多数据源通过自定义注解,动态切换,没有数据源都被事务管控,实现多数据源动态切换核心点就是AbstractRoutingDataSource。

    四、集成log4j2日志

    (1)、log4j2-spring-dev.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <configuration status="OFF" monitorInterval="30">
        <Properties>
            <!-- 日志默认存放的位置,这里设置为项目根路径下,也可指定绝对路径 -->
            <property name="basePath">D://log4j2Logs</property>
            <!-- 控制台默认输出格式,"%-5level":日志级别,"%l":输出完整的错误位置,是小写的L,因为有行号显示,所以影响日志输出的性能 -->
            <property name="console_log_pattern">%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %l - %m%n</property>
            <!-- 日志文件默认输出格式,不带行号输出(行号显示会影响日志输出性能);%C:大写,类名;%M:方法名;%m:错误信息;%n:换行 -->
            <property name="log_pattern">%d{yyyy-MM-dd HH:mm:ss.SSS} [%-5level] %C.%M - %m%n</property>
        </Properties>
    
        <appenders>
            <Console name="Console" target="SYSTEM_OUT">
                <!--只接受程序中INFO级别的日志进行处理-->
                <ThresholdFilter level="DEBUG" onMatch="ACCEPT" onMismatch="DENY"/>
                <PatternLayout pattern="${console_log_pattern}"/>
            </Console>
    
            <!--处理DEBUG级别的日志,并把该日志放到logs/debug.log文件中-->
            <!--打印出DEBUG级别日志,每次大小超过size,则这size大小的日志会自动存入按年份-月份建立的文件夹下面并进行压缩,作为存档-->
            <RollingFile name="RollingFileDebug" fileName="${basePath}/logs/debug.log"
                         filePattern="${basePath}/logs/$${date:yyyy-MM}/debug-%d{yyyy-MM-dd}-%i.log.gz">
                <Filters>
                    <ThresholdFilter level="DEBUG"/>
                    <ThresholdFilter level="INFO" onMatch="DENY" onMismatch="NEUTRAL"/>
                </Filters>
                <PatternLayout pattern="${log_pattern}"/>
                <Policies>
                    <SizeBasedTriggeringPolicy size="500 MB"/>
                    <TimeBasedTriggeringPolicy/>
                </Policies>
            </RollingFile>
    
            <!--处理INFO级别的日志,并把该日志放到logs/info.log文件中-->
            <RollingFile name="RollingFileInfo" fileName="${basePath}/logs/info.log"
                         filePattern="${basePath}/logs/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log.gz">
                <Filters>
                    <!--只接受INFO级别的日志,其余的全部拒绝处理-->
                    <ThresholdFilter level="INFO"/>
                    <ThresholdFilter level="WARN" onMatch="DENY" onMismatch="NEUTRAL"/>
                </Filters>
                <PatternLayout pattern="${log_pattern}"/>
                <Policies>
                    <SizeBasedTriggeringPolicy size="500 MB"/>
                    <TimeBasedTriggeringPolicy/>
                </Policies>
            </RollingFile>
    
            <!--处理WARN级别的日志,并把该日志放到logs/warn.log文件中-->
            <RollingFile name="RollingFileWarn" fileName="${basePath}/logs/warn.log"
                         filePattern="${basePath}/logs/$${date:yyyy-MM}/warn-%d{yyyy-MM-dd}-%i.log.gz">
                <Filters>
                    <ThresholdFilter level="WARN"/>
                    <ThresholdFilter level="ERROR" onMatch="DENY" onMismatch="NEUTRAL"/>
                </Filters>
                <PatternLayout pattern="${log_pattern}"/>
                <Policies>
                    <SizeBasedTriggeringPolicy size="500 MB"/>
                    <TimeBasedTriggeringPolicy/>
                </Policies>
            </RollingFile>
    
            <!--处理error级别的日志,并把该日志放到logs/error.log文件中-->
            <RollingFile name="RollingFileError" fileName="${basePath}/logs/error.log"
                         filePattern="${basePath}/logs/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log.gz">
                <ThresholdFilter level="ERROR"/>
                <PatternLayout pattern="${log_pattern}"/>
                <Policies>
                    <SizeBasedTriggeringPolicy size="500 MB"/>
                    <TimeBasedTriggeringPolicy/>
                </Policies>
            </RollingFile>
    
            <!--druid的日志记录追加器-->
            <RollingFile name="druidSqlRollingFile" fileName="${basePath}/logs/druid-sql.log"
                         filePattern="${basePath}/logs/$${date:yyyy-MM}/api-%d{yyyy-MM-dd}-%i.log.gz">
                <PatternLayout pattern="${log_pattern}"/>
                <Policies>
                    <SizeBasedTriggeringPolicy size="500 MB"/>
                    <TimeBasedTriggeringPolicy/>
                </Policies>
            </RollingFile>
        </appenders>
    
        <loggers>
            <root level="DEBUG">
                <appender-ref ref="Console"/>
                <appender-ref ref="RollingFileInfo"/>
                <appender-ref ref="RollingFileWarn"/>
                <appender-ref ref="RollingFileError"/>
                <appender-ref ref="RollingFileDebug"/>
            </root>
    
            <!--记录druid-sql的记录-->
            <logger name="druid.sql.Statement" level="info" additivity="false">
                <appender-ref ref="druidSqlRollingFile"/>
            </logger>
    
            <!--过滤掉spring和hibernate的一些无用的debug信息-->
            <logger name="org.springframework" level="INFO"></logger>
            <!--输出sql语句-->
            <logger name="com.base.springboot.dao" level="info" additivity="false">
                <appender-ref ref="Console" />
            </logger>
        </loggers>
    </configuration>
    

     (2)、application.yml:

    logging:
      config: classpath:log4j2-spring-dev.xml

    说明:配置log4j2日志,记录不同的级别的日志到不同的文件。

    五、不同的错误跳转到错误页面

    1、创建错误配置类(ErrorPageConfig.class),继承HandlerInterceptorAdapter。

    @Component
    public class ErrorPageConfig extends HandlerInterceptorAdapter {
    
        private List<Integer> errorList= Arrays.asList(404, 405, 500);
    
    
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
            if (errorList.contains(response.getStatus())){
                response.sendRedirect("/error/"+response.getStatus());
                return false;
            }
            return super.preHandle(request, response, handler);
        }
    }
    

     2、在Controller里面写错误页面跳转方法(BaseController.class)

    @Controller
    public class BaseController {
    
        @RequestMapping("/error/{code}")
        public String errorController(@PathVariable("code") String code, Model model){
            String errorStr="";
            switch (code){
                case "404":
                    errorStr="找不到页面!";
                    break;
                case "405":
                    errorStr="405错误!";
                    break;
                case "500":
                    errorStr="服务器错误!";
                    break;
            }
            model.addAttribute("errorStr",errorStr);
            return "errorPage";
        }
    }

    说明:继承拦截器,重写preHandle方法(执行之前拦截),当response.getStatus()状态码为错误码时,重定向到"/error/"+response.getStatus()方法,这个方法里面做不同的处理。

    六、mybatis分页

    (1)、声明列表视图类ListVo.class

    public class ListVo<T> {
        //数据量
        private int totalSize = 0;
        //数据列表
        private List<T> list = new ArrayList();
    
        public int getTotalSize() {
            return totalSize;
        }
    
        public void setTotalSize(int totalSize) {
            this.totalSize = totalSize;
        }
    
        public List<T> getList() {
            return list;
        }
    
        public void setList(List<T> list) {
            this.list = list;
        }
    }

    说明:属性有数量和list列表

    (2)、MybatisConfig.class配置类里面声明pageHelper对象,并且在sql会话里面插入插件。

    @Bean(name="pageHelper")
    public PageHelper pageHelper() {
        PageHelper pageHelper = new PageHelper();
        Properties p = new Properties();
        p.setProperty("offsetAsPageNum", "true");
        p.setProperty("rowBoundsWithCount", "true");
        p.setProperty("reasonable", "true");
        p.setProperty("dialect", "mysql");
        pageHelper.setProperties(p);
    
        return pageHelper;
    }
    
    	/**
         * 声明sql会话
         * @return
         */
        @Bean(name = "sqlSessionFactory")
        public SqlSessionFactory sqlSessionFactory(@Qualifier("pageHelper")PageHelper pageHelper,@Qualifier("fullSqlInterceptor")FullSqlInterceptor fullSqlInterceptor) throws Exception{
            //声明sql会话工厂
            SqlSessionFactoryBean factoryBean=new SqlSessionFactoryBean();
            //设置数据源
            factoryBean.setDataSource(dynamicDataSource());
            //设置扫描mybatisXml的路径
            factoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:**/dao/*/*.xml"));
            factoryBean.setPlugins(new Interceptor[] {pageHelper(), fullSqlInterceptor});//添加分页插件
            //返回sql会话
            return factoryBean.getObject();
        }
    

     (3)、Dao层的base操作类里面(BaseDaoImpl.class)

    @Repository("baseDao")
    public class BaseDaoImpl implements IBaseDao {
    
        @Autowired
        public SqlSessionTemplate sqlSessionTemplate;
    
    
        @Override
        public List<Object> getObjectList(String statement, Map<String, Object> paramMap) {
    
            List<Object> list=null;
            try {
                list=sqlSessionTemplate.selectList(statement,paramMap);
            }catch (Exception e){
                e.printStackTrace();
            }
            return list;
        }
    
    
        @Override
        public <T> ListVo<T> getObjectPage(String start,String limit,String statement, Map<String, Object> paramMap) {
            try {
    //            RowBounds rowBounds=new RowBounds(Integer.parseInt(start),Integer.parseInt(limit));
    //            List<T> list=sqlSessionTemplate.selectList(statement,paramMap,rowBounds);
                ListVo<T> listVo = new ListVo<T>();
                PageHelper.startPage(Integer.parseInt(start),Integer.parseInt(limit),true);
                List<T> list=sqlSessionTemplate.selectList(statement,paramMap);
                listVo.setList(list);
                Page<T> page = (Page<T>)list;
                listVo.setTotalSize((int)page.getTotal());
                return listVo;
            }catch (Exception e){
                e.printStackTrace();
            }
            return null;
        }
    
        @Override
        public void insertObject(String statement, Object object) {
            try{
                sqlSessionTemplate.insert(statement,object);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    }
    

     说明:

    ListVo<T> listVo = new ListVo<T>();
    PageHelper.startPage(Integer.parseInt(start),Integer.parseInt(limit),true);
    List<T> list=sqlSessionTemplate.selectList(statement,paramMap);
    listVo.setList(list);
    Page<T> page = (Page<T>)list;
    listVo.setTotalSize((int)page.getTotal());
    return listVo;
    
    • PageHelper.startPage:进行物理分页
    • listVo.setList(list);:将list设置进listVo类的list属性
    • Page page = (Page)list;
    • listVo.setTotalSize((int)page.getTotal());:设置进listVo类的totalSize属性。

    七、通过mybatis拦截器,在控制台打印完整的sql(FullSqlInterceptor.class)

    @Intercepts({
            @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }),
            @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
                    RowBounds.class, ResultHandler.class }) })
    public class FullSqlInterceptor implements Interceptor {
    
    
        public Object intercept(Invocation invocation) throws Throwable {
            //获取<select> or <update> or <delete>节点
            MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
            Object parameter = null;
            if (invocation.getArgs().length > 1) {
                //获取参数
                parameter = invocation.getArgs()[1];
            }
            //获取方法id
            String sqlId = mappedStatement.getId();
            //获取sql语句
            BoundSql boundSql = mappedStatement.getBoundSql(parameter);
            //获取连接信息
            Configuration configuration = mappedStatement.getConfiguration();
            Object returnValue = null;
            //获取系统时间
            long start = System.currentTimeMillis();
            //获取返回数据
            returnValue = invocation.proceed();
            long end = System.currentTimeMillis();
            long time = (end - start);
            if (time > 1) {
                //调用getSql方法,获取处理后的sql语句
                String sql = getSql(configuration, boundSql, sqlId, time);
                System.err.println(sql);
            }
            return returnValue;
        }
    
        public static String getSql(Configuration configuration, BoundSql boundSql, String sqlId, long time) {
            String sql = showSql(configuration, boundSql);
            StringBuilder str = new StringBuilder(100);
            str.append("执行方法: ");
            str.append(sqlId);
            str.append("
    ");
            str.append("执行语句:  ");
            str.append(sql);
            str.append("   :耗时");
            str.append(time);
            str.append("ms");
            return str.toString();
        }
    
        //这个方法是用来转换参数值的
        private static String getParameterValue(Object obj) {
            String value = null;
            if (obj instanceof String) {
                value = "'" + obj.toString() + "'";
            } else if (obj instanceof Date) {
                DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
                value = "'" + formatter.format(new Date()) + "'";
            } else {
                if (obj != null) {
                    value = obj.toString();
                } else {
                    value = "";
                }
    
            }
            return value;
        }
    
        public static String showSql(Configuration configuration, BoundSql boundSql) {
            Object parameterObject = boundSql.getParameterObject();
            List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
            String sql = boundSql.getSql().replaceAll("[\s]+", " ");
            //如果有参数
            if (parameterMappings.size() > 0 && parameterObject != null) {
                TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
                //如果是基础类型
                if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                    sql = sql.replaceFirst("\?", getParameterValue(parameterObject));
                } else {
                    //如果参数不是基础类型
                    MetaObject metaObject = configuration.newMetaObject(parameterObject);
                    for (ParameterMapping parameterMapping : parameterMappings) {
                        String propertyName = parameterMapping.getProperty();
                        if (metaObject.hasGetter(propertyName)) {
                            Object obj = metaObject.getValue(propertyName);
                            sql = sql.replaceFirst("\?", getParameterValue(obj));
                        } else if (boundSql.hasAdditionalParameter(propertyName)) {
                            Object obj = boundSql.getAdditionalParameter(propertyName);
                            sql = sql.replaceFirst("\?", getParameterValue(obj));
                        }
                    }
                }
            }
            return sql;
        }
    
    }

    说明:主要是在执行mybatis方法的时候,替换掉原生语句中的参数#{},替换成具体的值,方便在控制台直接复制到数据库查看。

    以上就是SpringBoot+MyBatis+Mysql+Durid动态多数据源项目搭建的主要代码和过程。

  • 相关阅读:
    解决UITableView中Cell重用机制导致内容出错的方法总结
    Hdu 1052 Tian Ji -- The Horse Racing
    Hdu 1009 FatMouse' Trade
    hdu 2037 今年暑假不AC
    hdu 1559 最大子矩阵
    hdu 1004 Let the Balloon Rise
    Hdu 1214 圆桌会议
    Hdu 1081 To The Max
    Hdu 2845 Beans
    Hdu 2955 Robberies 0/1背包
  • 原文地址:https://www.cnblogs.com/47Gamer/p/13390267.html
Copyright © 2011-2022 走看看