基于JavaConfig实现Spring5集成MyBatis3(上)

基于JavaConfig实现Spring5集成MyBatis3

工程模块划分

目前在企业应用开发中为了能够更加方便开发、测试和部署,通常都会根据不同的业务以及功能来把项目划分为多个模块,这里在实现Spring5集成MyBaits3之前先划分下项目模块,模块名称及其说明如下所示

模块名称 模块说明
mybatis-practices-pojo 存放项目开发中的各种POJO对象
mybatis-practices-utils 存放项目开发中的各种第三方工具类
mybatis-practices-spring5-integration 基于javaconfig实现Spring5集成Mybatis3
mybatis-practices-common 存放项目中常用的通用工具

项目依赖说明

mybatis-practices-spring5-integration模块依赖总览图如下所示
mybatis-practices-spring5-integration-dependencies
主要包含了mybatis-practices-pojo,mybatis-practices-utils,mybatis-practices-common三个基础模块,mybatis,spring集成相关依赖以及常用工具包的依赖,详细的依赖说明可以参考mybatis-practices的pom.xml文件中的描述。

集成Log4j2完成日志记录

首先添加Log4j2的依赖,pom.xml文件内容如下

    <properties>
        <log4j2.version>2.9.0</log4j2.version>
    </properties>

  <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>${log4j2.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>${log4j2.version}</version>
        </dependency>

然后在模块mybatis-practices-spring5-integration的src/main/resources目录下添加log4j2的配置文件log4j2.xml,实现如下

<?xml version="1.0" encoding="UTF-8"?>
<!--设置log4j2的自身log级别为warn -->
<configuration status="warn" monitorInterval="3600" shutdownHook="disable">
    <properties>

        <Property name="app_name">mybatis-practices-spring5-integration</Property>
        <Property name="log_path">logs/${app_name}</Property>
        <Property name="log_file_size">100MB</Property>

    </properties>
    <appenders>
        <!--
          Console 的target是SYSTEM_OUT是输出到统一的输出流,没有指定日志文件
          配置文件log4j2.xml 中的<Console name="Console" target="SYSTEM_OUT">表示 log4j2将日志配置到System.out输入到控制到输出流。
      -->
        <console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="[%d{yyyy-MM-dd HH:mm:ss SSS}][%t][%p][%l] %m%n" />
        </console>


        <!--info级别的日志记录配置-->

        <RollingFile name="RollingFileInfo" fileName="${log_path}/info.log"
                     filePattern="${log_path}/$${date:yyyy-MM}/info-%d{yyyy-MM-dd}-%i.log.gz">
            <Filters>
                <ThresholdFilter level="INFO" />
                <ThresholdFilter level="WARN" onMatch="DENY"
                                 onMismatch="NEUTRAL" />
            </Filters>
            <PatternLayout pattern="[%d%d{yyyy-MM-dd HH:mm:ss SSS}][%t][%p][%c:%L] %m%n" />
            <Policies>
                <!-- 归档每天的文件 -->
                <TimeBasedTriggeringPolicy interval="1" modulate="true" />
                <!-- 限制单个文件大小 -->
                <SizeBasedTriggeringPolicy size="${log_file_size}" />
            </Policies>
            <!-- 限制每天文件个数 -->
            <DefaultRolloverStrategy compressionLevel="0" max="10"/>
        </RollingFile>

        <!--warn级别的日志记录配置-->
        <RollingFile name="RollingFileWarn" fileName="${log_path}/warn.log"
                     filePattern="${log_path}/$${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="[%d%d{yyyy-MM-dd HH:mm:ss SSS}][%t][%p][%c:%L] %m%n" />
            <Policies>
                <!-- 归档每天的文件 -->
                <TimeBasedTriggeringPolicy interval="1" modulate="true" />
                <!-- 限制单个文件大小 -->
                <SizeBasedTriggeringPolicy size="${log_file_size}" />
            </Policies>
            <!-- 限制每天文件个数 -->
            <DefaultRolloverStrategy compressionLevel="0" max="10"/>
        </RollingFile>

        <!-- error级别的日志配置-->
        <RollingFile name="RollingFileError" fileName="${log_path}/error.log"
                     filePattern="${log_path}/$${date:yyyy-MM}/error-%d{yyyy-MM-dd}-%i.log.gz">
            <ThresholdFilter level="ERROR" />
            <PatternLayout pattern="[%d%d{yyyy-MM-dd HH:mm:ss SSS}][%t][%p][%c:%L] %m%n" />
            <Policies>
                <!-- 归档每天的文件 -->
                <TimeBasedTriggeringPolicy interval="1" modulate="true" />
                <!-- 限制单个文件大小 -->
                <SizeBasedTriggeringPolicy size="${log_file_size}" />
            </Policies>
            <!-- 限制每天文件个数 -->
            <DefaultRolloverStrategy compressionLevel="0" max="10"/>
        </RollingFile>

    </appenders>

    <loggers>
        <!--第三方框架显示的日志级别-->
        <logger name="org.springframework" level="INFO"/>
        <logger name ="org.ibatis" level="info"/>



        <root level="info">
            <appender-ref ref="Console" />
            <appender-ref ref="RollingFileInfo" />
            <appender-ref ref="RollingFileWarn" />
            <appender-ref ref="RollingFileError" />
        </root>

    </loggers>

</configuration>

程序中应用

    //类中实例化Logger对象 
    private static final Logger LOGGER= LogManager.getLogger();

    //方法中调用对应的方法
    LOGGER.info("");
    LOGGER.error("");

集成P6Spy显示程序运行时的真实SQL

项目地址:https://github.com/p6spy

配置说明:http://p6spy.readthedocs.io/en/latest/configandusage.html

首先添加P6Spy和阿里巴巴数据库连接池Druid的依赖,pom.xml文件的内容如下

<properties>
        <p6spy.version>3.6.0</p6spy.version>
        <druid.version>1.1.6</druid.version>
    </properties>

  <dependency>
            <groupId>p6spy</groupId>
            <artifactId>p6spy</artifactId>
            <version>${p6spy.version}</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>${druid.version}</version>
        </dependency>

然后将p6spy的配置文件spy.properties放置项目的src/main/resources目录下,该文件只需要修改logfile,logMessageFormat,dateformat属性即可,如下图所示:

logfile=mybatis-practices-core.log
logMessageFormat= net.ittimeline.mybatis.practices.common.p6spy.CustomizeLineFormat
dateformat=yyyy-MM-dd HH:mm:ss

完整的spy.properties文件已经在mybatis-practice-spring5-integration模块中给出,仅供参考。

然后实现自定义的SQL输出格式

为了输出的内容足够的简洁,这里只保留了当前时间,执行SQL的耗时以及执行的SQL语句,具体实现如下所示

package net.ittimeline.mybatis.practices.common.p6spy;

import com.alibaba.druid.sql.SQLUtils;
import com.p6spy.engine.spy.appender.MessageFormattingStrategy;

/**
 * 自定义SQL格式
 * @author tony [email protected]
 * @date 2018-01-30-下午10:45
 * @website wwww.ittimeline.net
 * @see
 * @since JDK8u162
 */
public class CustomizeLineFormat implements MessageFormattingStrategy {

    public String buildMessage(String now, long elapsed, String sql) {
        StringBuffer content = new StringBuffer();
        if (org.apache.commons.lang3.StringUtils.isNotEmpty(now) && org.apache.commons.lang3.StringUtils.isNotEmpty(Long.valueOf(elapsed).toString())
                && org.apache.commons.lang3.StringUtils.isNotEmpty(sql)) {
            content.append("当前时间:" + now);
            content.append(" SQL执行耗时(毫秒)为" + elapsed);
            content.append(" SQL执行的语句是\n" + SQLUtils.formatMySql(sql)+"\n\n");
        }
        return content.toString();
    }

    @Override
    public String formatMessage(int connectionId, String now, long elapsed, String category, String prepared, String sql) {
        return buildMessage(now, elapsed, sql);
    }

}

然后增加一个database.properties文件,配置内容如下

相对于未添加P6Spy之前,数据库的驱动类和URL不同,同时该文件还配置了阿里巴巴数据库连接池Druid的相关属性

#############数据库连接池druid配置########
datasource.druid.driverClassName=com.p6spy.engine.spy.P6SpyDriver
datasource.druid.url=jdbc:p6spy:mysql://127.0.0.1:3306/mybatis?useUnicode=true&characterEncoding=UTF-8&useSSL=false
datasource.druid.username=root
datasource.druid.password=guanglei

datasource.druid.initialSize=1
datasource.druid.minIdle=1
datasource.druid.maxActive=20
datasource.druid.maxWait=60000
datasource.druid.timeBetweenEvictionRunsMillis=60000
datasource.druid.minEvictableIdleTimeMillis=300000
datasource.druid.validationQuery=SELECT 'x'
datasource.druid.testWhileIdle=true
datasource.druid.testOnBorrow=false
datasource.druid.testOnReturn=false
datasource.druid.poolPreparedStatements=false
datasource.druid.maxPoolPreparedStatementPerConnectionSize=20
datasource.druid.filters=stat

集成阿里巴巴Druid数据库连接池

项目地址:https://github.com/alibaba/druid

完整实现如下所示

package net.ittimeline.mybatis.practices.spring5.integration.configuration;

import com.alibaba.druid.pool.DruidDataSource;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import javax.sql.DataSource;
import java.sql.SQLException;

/**
 * 阿里巴巴数据库连接池Druid配置
 * @author tony [email protected]
 * @date 2018-02-26-上午11:30
 * @website wwww.ittimeline.net
 * @see
 * @since JDK8u162
 */
@Configuration
@PropertySource("classpath:database.properties")
public class DruidDataSourceConfiguraiton {

    private static final Logger LOGGER= LogManager.getLogger();


    /**********************读取位于ClassPath路径下的database.properties属性配置文件,然后将属性值设置到对应的成员变量中********************/
    @Value("${datasource.druid.driverClassName}")
    private String driverClassName;

    @Value("${datasource.druid.url}")
    private String url;

    @Value("${datasource.druid.username}")
    private String userName;

    @Value("${datasource.druid.password}")
    private String password;

    @Value("${datasource.druid.initialSize}")
    private int initialSize;

    @Value("${datasource.druid.minIdle}")
    private int minIdle;


    @Value("${datasource.druid.maxActive}")
    private int maxActive;


    @Value("${datasource.druid.maxWait}")
    private long maxWait;

    @Value("${datasource.druid.timeBetweenEvictionRunsMillis}")
    private long timeBetweenEvictionRunsMillis;


    @Value("${datasource.druid.minEvictableIdleTimeMillis}")
    private long minEvictableIdleTimeMillis;

    @Value("${datasource.druid.validationQuery}")
    private String validationQuery;

    @Value("${datasource.druid.testWhileIdle}")
    private boolean testWhileIdle;


    @Value("${datasource.druid.testOnBorrow}")
    private boolean testOnBorrow;


    @Value("${datasource.druid.testOnReturn}")
    private boolean testOnReturn;



    @Value("${datasource.druid.poolPreparedStatements}")
    private boolean poolPreparedStatements;


    @Value("${datasource.druid.maxPoolPreparedStatementPerConnectionSize}")
    private int maxPoolPreparedStatementPerConnectionSize;


    @Value("${datasource.druid.filters}")
    private String filters;


    /**
     * 基于阿里巴巴的Druid数据库连接池实现
     * @return
     */
    @Bean
    public DataSource dataSource(){


        DruidDataSource dataSource =new DruidDataSource();
        dataSource.setUsername(userName);
        dataSource.setPassword(password);
        dataSource.setUrl(url);
        dataSource.setDriverClassName(driverClassName);


        dataSource.setInitialSize(initialSize);
        dataSource.setMinIdle(minIdle);
        dataSource.setMaxActive(maxActive);
        dataSource.setMaxWait(maxWait);
        dataSource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
        dataSource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
        dataSource.setValidationQuery(validationQuery);
        dataSource.setTestWhileIdle(testWhileIdle);
        dataSource.setTestOnBorrow(testOnBorrow);
        dataSource.setTestOnReturn(testOnReturn);
        dataSource.setPoolPreparedStatements(poolPreparedStatements);
        dataSource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
        try {
            dataSource.setFilters(filters);
        } catch (SQLException e) {
            e.printStackTrace();

            LOGGER.info("build datasource fail "+e.getMessage());
        }
        return dataSource;
    }

}

其中@PropertySource注解用于读取类路径下的数据库配置文件database.properties,同时使用@Value注解结合Spring表达式赋值于对应的成员变量。

@Bean注解的作用就是相当于传统spring的xml配置的<bean/>

基于JavaConfig的MyBatis3集成Spring5

系统全局配置类

目前只配置了包扫描器

package net.ittimeline.mybatis.practices.spring5.integration.configuration;

import net.ittimeline.mybatis.practices.spring5.integration.constants.SystemConstants;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * 系统全局配置类
 * @author tony [email protected]
 * @date 2018-02-24-上午11:03
 * @website wwww.ittimeline.net
 * @see
 * @since JDK8u162
 */
@Configuration
@ComponentScan(basePackages = {SystemConstants.COMPONENT_SCAN_PACKAGE_NAME})
public class ApplicationConfiguartion {



}

系统常量类

package net.ittimeline.mybatis.practices.spring5.integration.constants;

/**
 * 系统常量配置
 * @author tony [email protected]
 * @date 2018-02-24-上午11:09
 * @website wwww.ittimeline.net
 * @see
 * @since JDK8u162
 */
public class SystemConstants {


    public static final String COMPONENT_SCAN_PACKAGE_NAME="net.ittimeline.mybatis.practices.spring5";

    public static final String MAPPER_BASE_PACKAGE="net.ittimeline.mybatis.practices.spring5.integration.mapper";
}

MyBatis3集成spring5的核心配置类

package net.ittimeline.mybatis.practices.spring5.integration.configuration;

import net.ittimeline.mybatis.practices.spring5.integration.constants.SystemConstants;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

import javax.sql.DataSource;

/**
 * @author tony [email protected]
 * @date 2018-02-24-上午11:13
 * @website wwww.ittimeline.net
 * @see
 * @since JDK8u162
 */
@Configuration
@Import(DruidDataSourceConfiguraiton.class)
@MapperScan(basePackages = SystemConstants.MAPPER_BASE_PACKAGE)
public class MyBatisConfiguartion {



    private static final Logger LOGGER= LogManager.getLogger();


    private static final String TYPE_ALIASES_PACKAGE= "net.ittimeline.mybatis.practices.pojo.persist";




    @Bean(name = "sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactoryBean(DataSource dataSource){

        SqlSessionFactoryBean sqlSessionFactoryBean=new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setTypeAliasesPackage(TYPE_ALIASES_PACKAGE);


        //TODO 添加分页插件

        //添加mapper的XML目录
        ResourcePatternResolver resourcePatternResolver=new PathMatchingResourcePatternResolver();
        try {
            sqlSessionFactoryBean.setMapperLocations(resourcePatternResolver.getResources("classpath:mappers/*.xml"));

            return sqlSessionFactoryBean.getObject();
        } catch (Exception e) {
            e.printStackTrace();
            LOGGER.info("build sqlSessionFactory fail "+e.getMessage());
            throw new RuntimeException(e);
        }
    }

    /**
     *
     * @param sqlSessionFactory
     * @return
     */
    @Bean
    public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory){
        return new SqlSessionTemplate(sqlSessionFactory);
    }




}

基于JavaConfig的Spring事务配置

package net.ittimeline.mybatis.practices.spring5.integration.configuration;

import org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
import org.springframework.transaction.interceptor.RollbackRuleAttribute;
import org.springframework.transaction.interceptor.RuleBasedTransactionAttribute;
import org.springframework.transaction.interceptor.TransactionInterceptor;

import javax.sql.DataSource;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;

/**
 * Spring事务配置
 * @author tony [email protected]
 * @date 2018-02-26-下午4:27
 * @website wwww.ittimeline.net
 * @see
 * @since JDK8u162
 */
@Configuration
@Import(DruidDataSourceConfiguraiton.class)
public class SpringTransactionConfiguration {


    /**
     * 自定义事务拦截名称
     */
    private static final String CUSTOMIZE_TRANSACTION_INTERCEPTOR_NAME="customizeTransactionInterceptor";

    /**
     * 默认只对*Service和*ServiceImpl Bean进行事务处理,"*"表示模糊匹配,比如userService,userServiceImpl
     */
    private static final String[] DEFAULT_TRANSACTION_BEAN_NAMES={"*Service","*ServiceImpl"};

    /**
     * 可传播事务配置
     */
    private static final String[] DEFAULT_REQUIRED_METHOD_RULE_TRANSACTION_ATTRIBUTES={"add","save","insert","delete","update","edit","batch","create","remove"};

    /**
     * 默认的只读事务
     */
    private static final String[] DEFAULT_READ_ONLY_METHOD_RULE_TRANSACTION_ATTRIBUTES={"get","count","find","query","select","list","*"};
    /**
     * 自定义事务 BeanName 拦截
     */
    private String[] customizeTransactionBeanNames                        = {};
    /**
     * 自定义方法名的事务属性相关联,可以使用通配符(*)字符关联相同的事务属性的设置方法; 只读事务
     */
    private String[] customizeReadOnlyMethodRuleTransactionAttributes     = {};
    /**
     * 自定义方法名的事务属性相关联,可以使用通配符(*)字符关联相同的事务属性的设置方法;
     * 传播事务(默认的){@link org.springframework.transaction.annotation.Propagation#REQUIRED}
     */
    private String[] customizeRequiredMethodRuleTransactionAttributes     = {};


    /**
     * 事务管理器
     * @param dataSource
     * @return
     */
    @Bean(name = "transactionManager")
    public PlatformTransactionManager transactionManager(DataSource dataSource){
        return new DataSourceTransactionManager(dataSource);
    }


    /**
     * 配置事务拦截器
     *
     * @param transactionManager : 事务管理器
     */
    @Bean( CUSTOMIZE_TRANSACTION_INTERCEPTOR_NAME )
    public TransactionInterceptor customizeTransactionInterceptor ( PlatformTransactionManager transactionManager ) {
        NameMatchTransactionAttributeSource transactionAttributeSource = new NameMatchTransactionAttributeSource();
        RuleBasedTransactionAttribute       readOnly                   = this.readOnlyTransactionRule();
        RuleBasedTransactionAttribute       required                   = this.requiredTransactionRule();
        // 默认的只读事务配置
        for ( String methodName : DEFAULT_READ_ONLY_METHOD_RULE_TRANSACTION_ATTRIBUTES ) {
            transactionAttributeSource.addTransactionalMethod( methodName , readOnly );
        }
        // 默认的传播事务配置
        for ( String methodName : DEFAULT_REQUIRED_METHOD_RULE_TRANSACTION_ATTRIBUTES ) {
            transactionAttributeSource.addTransactionalMethod( methodName , required );
        }
        // 定制的只读事务配置
        for ( String methodName : customizeReadOnlyMethodRuleTransactionAttributes ) {
            transactionAttributeSource.addTransactionalMethod( methodName , readOnly );
        }
        // 定制的传播事务配置
        for ( String methodName : customizeRequiredMethodRuleTransactionAttributes ) {
            transactionAttributeSource.addTransactionalMethod( methodName , required );
        }
        return new TransactionInterceptor( transactionManager , transactionAttributeSource );
    }

    /**
     * 配置事务拦截
     * <p>
     * {@link #customizeTransactionInterceptor(PlatformTransactionManager)}
     */
    @Bean
    public BeanNameAutoProxyCreator customizeTransactionBeanNameAutoProxyCreator () {
        BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
        // 设置定制的事务拦截器
        beanNameAutoProxyCreator.setInterceptorNames( CUSTOMIZE_TRANSACTION_INTERCEPTOR_NAME );
        List< String > transactionBeanNames = new ArrayList<>( DEFAULT_TRANSACTION_BEAN_NAMES.length + customizeTransactionBeanNames.length );
        // 默认
        transactionBeanNames.addAll( Arrays.asList( DEFAULT_TRANSACTION_BEAN_NAMES ) );
        // 定制
        transactionBeanNames.addAll( Arrays.asList( customizeTransactionBeanNames ) );
        // 归集
        for ( String transactionBeanName : transactionBeanNames ) {
            beanNameAutoProxyCreator.setBeanNames( transactionBeanName );
        }
        beanNameAutoProxyCreator.setProxyTargetClass( true );
        return beanNameAutoProxyCreator;
    }

    /**
     * 支持当前事务;如果不存在创建一个新的
     * {@link org.springframework.transaction.annotation.Propagation#REQUIRED}
     */
    private RuleBasedTransactionAttribute requiredTransactionRule () {
        RuleBasedTransactionAttribute required = new RuleBasedTransactionAttribute();
        required.setRollbackRules( Collections.singletonList( new RollbackRuleAttribute( Exception.class ) ) );
        required.setPropagationBehavior( TransactionDefinition.PROPAGATION_REQUIRED );
        required.setTimeout( TransactionDefinition.TIMEOUT_DEFAULT );
        return required;
    }

    /**
     * 只读事务
     * {@link org.springframework.transaction.annotation.Propagation#NOT_SUPPORTED}
     */
    private RuleBasedTransactionAttribute readOnlyTransactionRule () {
        RuleBasedTransactionAttribute readOnly = new RuleBasedTransactionAttribute();
        readOnly.setReadOnly( true );
        readOnly.setPropagationBehavior( TransactionDefinition.PROPAGATION_NOT_SUPPORTED );
        return readOnly;
    }
}

项目地址:https://github.com/ittimeline/mybatis-practices

猜你喜欢

转载自blog.csdn.net/ittechnologyhome/article/details/79385660
今日推荐