spring boot学习3之mybatis+druid+事务+PageHelper分页插件+sql打印插件整合

       上篇博文学习了spring boot下对于配置文件的读取。这篇博文学习下spring boot怎样整合mybatis,并使用指定的数据库连接池,事务,分页插件的整合。

      该例子配置开启事务,所以先说下事务的一点注意事项。

注:虽然在配置类中开启了事务,但是没有指定哪些类哪些方法的执行要开启事务(除非定义个AOP),所以需要自行在service类上面或方法上面@Transactional。在类上面加@Transactional,说明类内的每个public方法都默认使用它,除非方法上自行定义@Transactional覆盖类上的定义。

   如果类或方法名上都没有@Transactional,那么执行了数据库操作后,后续代码抛出了异常,数据库也不会回滚,因为没声明开启事务,数据库默认自动提交了。

    事务嵌套:@Transactional时,默认Propagation.REQUIRED (有事务就处于当前事务中,没事务就创建一个事务),所以如果A01Service.a()调用A01Service.b()同时调用A02Service.c(),同类内部的事务方法或不同类的事务方法,spring处理时,会将它们都置于同一个事务中,也就是要么都失败,要么都成功,不会出现多个事务,一个提交了,另一个回滚了。这个进行框架搭建时,可自行测试。


      本例子用的是maven结构。例子源码下载在github上,点击下载

项目结构


mapper接口和mapper.xml文件,我都是放在dao文件夹下

其中pom.xml文件

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.fei.springboot</groupId>
  <artifactId>springboot-mybatis</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  
  
  <properties>
  <!-- 文件拷贝时的编码 -->
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<!-- 编译时的编码 -->
		<maven.compiler.encoding>UTF-8</maven.compiler.encoding>
		
   <spring_versin>1.5.2.RELEASE</spring_versin>
  </properties>
	<dependencies>
	    <dependency>
	        <groupId>org.springframework.boot</groupId>
	        <artifactId>spring-boot-starter-web</artifactId>
	        <version>${spring_versin}</version>

	        <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions> 

	    </dependency>
	    <dependency>
        	<groupId>org.springframework.boot</groupId>
       	 	<artifactId>spring-boot-starter-tomcat</artifactId>
       	    <version>${spring_versin}</version>
        	<scope>provided</scope>
		</dependency>
		
		  <dependency>
   			 <groupId>org.mybatis.spring.boot</groupId>
    		 <artifactId>mybatis-spring-boot-starter</artifactId>
   			 <version>1.3.0</version>
		</dependency> 

		
		<dependency>
    			<groupId>mysql</groupId>
    			<artifactId>mysql-connector-java</artifactId>
    		<version>5.1.38</version>
		</dependency>
		<dependency>
    		<groupId>com.alibaba</groupId>
    		<artifactId>druid</artifactId>
    		<version>1.0.29</version>
		</dependency>
		
		<!-- 分页插件 -->
		<dependency>
    		<groupId>com.github.pagehelper</groupId>
    		<artifactId>pagehelper</artifactId>
    		<version>4.1.6</version>
		</dependency>

		</dependencies>
		
		<build>
		    <sourceDirectory>src</sourceDirectory>
		    <plugins>
		      <plugin>
		        <artifactId>maven-compiler-plugin</artifactId>
		        <version>3.1</version>
		        <configuration>
		          <source>1.7</source>
		          <target>1.7</target>
		          <encoding>UTF-8</encoding>
		        </configuration>
		      </plugin>
		    </plugins>
		  </build>
</project>

application.yml文件

logging:
  config: classpath:logback.xml
  path: d:/logs
server:
  port: 80
  session-timeout: 60


mybatis:
     mapperLocations: classpath:/com/fei/springboot/dao/*.xml
     typeAliasesPackage: com.fei.springboot.dao    
     mapperScanPackage: com.fei.springboot.dao
     configLocation: classpath:/mybatis-config.xml

spring:
    datasource:
        name: db
        type: com.alibaba.druid.pool.DruidDataSource
        url: jdbc:mysql://127.0.0.1:3306/test?useUnicode=true&characterEncoding=utf-8
        username: root
        password: root
        driver-class-name: com.mysql.jdbc.Driver
        minIdle: 5
        maxActive: 100
        initialSize: 10
        maxWait: 60000
        timeBetweenEvictionRunsMillis: 60000
        minEvictableIdleTimeMillis: 300000
        validationQuery: select 'x'
        testWhileIdle: true
        testOnBorrow: false
        testOnReturn: false
        poolPreparedStatements: true
        maxPoolPreparedStatementPerConnectionSize: 50
        removeAbandoned: true
        filters: stat # ,wall,log4j # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
        connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000 # 通过connectProperties属性来打开mergeSql功能;慢SQL记录
        useGlobalDataSourceStat: true # 合并多个DruidDataSource的监控数据
        druidLoginName: wjf # 登录druid的账号
        druidPassword: wjf # 登录druid的密码  

mybatis-config.xml

<?xml version="1.0" encoding="UTF-8"?>  
<!DOCTYPE configuration  
     PUBLIC "-//mybatis.org//DTD Config 3.0//EN"  
     "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
	<settings>
	    <!-- 使全局的映射器启用或禁用缓存。 -->
		<setting name="cacheEnabled" value="true" />
		<!-- 全局启用或禁用延迟加载。当禁用时,所有关联对象都会即时加载。 -->
		<setting name="lazyLoadingEnabled" value="true" />
		<!-- 当启用时,有延迟加载属性的对象在被调用时将会完全加载任意属性。否则,每种属性将会按需要加载。 -->        
         <setting name="aggressiveLazyLoading" value="true"/>        
         <!-- 是否允许单条sql 返回多个数据集  (取决于驱动的兼容性) default:true -->
		<setting name="multipleResultSetsEnabled" value="true" />
		<!-- 是否可以使用列的别名 (取决于驱动的兼容性) default:true -->
		<setting name="useColumnLabel" value="true" />
		<!-- 允许JDBC 生成主键。需要驱动器支持。如果设为了true,这个设置将强制使用被生成的主键,有一些驱动器不兼容不过仍然可以执行。  default:false  -->
		<setting name="useGeneratedKeys" value="false" />
		<!-- 指定 MyBatis 如何自动映射 数据基表的列 NONE:不隐射 PARTIAL:部分  FULL:全部  -->
		<setting name="autoMappingBehavior" value="PARTIAL" />
		<!-- 这是默认的执行类型  (SIMPLE: 简单; REUSE: 执行器可能重复使用prepared statements语句;BATCH: 执行器可以重复执行语句和批量更新)  -->
		<setting name="defaultExecutorType" value="SIMPLE" />
		
		<setting name="defaultStatementTimeout" value="25" />
		
		<setting name="defaultFetchSize" value="100" />
		
		<setting name="safeRowBoundsEnabled" value="false" />
		<!-- 使用驼峰命名法转换字段。 -->
		<setting name="mapUnderscoreToCamelCase" value="true" />
		<!-- 设置本地缓存范围 session:就会有数据的共享  statement:语句范围 (这样就不会有数据的共享 ) defalut:session -->
		<setting name="localCacheScope" value="SESSION" />
		<!-- 默认为OTHER,为了解决oracle插入null报错的问题要设置为NULL -->
		<setting name="jdbcTypeForNull" value="NULL" />
		<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString" />
	</settings>
	
</configuration>
配置文件大概就这样
由于数据库连接池用的是druid,所以需要自己写下代码

DatasourceConfig.java代码

package com.fei.springboot.mybatisconfig;

import java.sql.SQLException;

import javax.sql.DataSource;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;

import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;

@Configuration
public class DatasourceConfig {
	private Logger logger = LoggerFactory.getLogger(DatasourceConfig.class);
	
    @Value("${spring.datasource.url}")
    private String dbUrl;
    
    @Value("${spring.datasource.type}")
    private String dbType;
    
    @Value("${spring.datasource.username}")
    private String username;
    
    @Value("${spring.datasource.password}")
    private String password;
    
    @Value("${spring.datasource.driver-class-name}")
    private String driverClassName;
    
    @Value("${spring.datasource.initialSize}")
    private int initialSize;
    
    @Value("${spring.datasource.minIdle}")
    private int minIdle;
    
    @Value("${spring.datasource.maxActive}")
    private int maxActive;
    
    @Value("${spring.datasource.maxWait}")
    private int maxWait;
    
    @Value("${spring.datasource.timeBetweenEvictionRunsMillis}")
    private int timeBetweenEvictionRunsMillis;
    
    @Value("${spring.datasource.minEvictableIdleTimeMillis}")
    private int minEvictableIdleTimeMillis;
    
    @Value("${spring.datasource.validationQuery}")
    private String validationQuery;
    
    @Value("${spring.datasource.testWhileIdle}")
    private boolean testWhileIdle;
    
    @Value("${spring.datasource.testOnBorrow}")
    private boolean testOnBorrow;
    
    @Value("${spring.datasource.testOnReturn}")
    private boolean testOnReturn;
    
    @Value("${spring.datasource.poolPreparedStatements}")
    private boolean poolPreparedStatements;
    
    @Value("${spring.datasource.filters}")
    private String filters;
    
    @Value("${spring.datasource.connectionProperties}")
    private String connectionProperties;
    
    @Value("${spring.datasource.useGlobalDataSourceStat}")
    private boolean useGlobalDataSourceStat;
    
    @Value("${spring.datasource.druidLoginName}")
    private String druidLoginName;
	
    @Value("${spring.datasource.druidPassword}")
    private String druidPassword;
    
    @Bean(name="dataSource",destroyMethod = "close", initMethod="init")
    @Primary //不要漏了这
    public DataSource dataSource(){  
        DruidDataSource datasource = new DruidDataSource();  
        try {  
	        datasource.setUrl(this.dbUrl);  
	        datasource.setDbType(dbType);
	        datasource.setUsername(username);  
	        datasource.setPassword(password);  
	        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.setFilters(filters);  
            datasource.setConnectionProperties(connectionProperties);
            datasource.setUseGlobalDataSourceStat(useGlobalDataSourceStat);
        } catch (SQLException e) {  
            logger.error("druid configuration initialization filter", e);  
        }  
        return datasource;  
    }
    
    /  下面是druid 监控访问的设置  /
    @Bean
    public ServletRegistrationBean druidServlet() {
	    ServletRegistrationBean reg = new ServletRegistrationBean();
	    reg.setServlet(new StatViewServlet());
	    reg.addUrlMappings("/druid/*");  //url 匹配
	    reg.addInitParameter("allow", "192.168.16.110,127.0.0.1"); // IP白名单 (没有配置或者为空,则允许所有访问)
	    reg.addInitParameter("deny", "192.168.16.111"); //IP黑名单 (存在共同时,deny优先于allow)
	    reg.addInitParameter("loginUsername", this.druidLoginName);//登录名
	    reg.addInitParameter("loginPassword", this.druidPassword);//登录密码
	    reg.addInitParameter("resetEnable", "false"); // 禁用HTML页面上的“Reset All”功能
	    return reg;
    }

    @Bean(name="druidWebStatFilter")
    public FilterRegistrationBean filterRegistrationBean() {
	    FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
	    filterRegistrationBean.setFilter(new WebStatFilter());
	    filterRegistrationBean.addUrlPatterns("/*");
	    filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"); //忽略资源
	    filterRegistrationBean.addInitParameter("profileEnable", "true");
	    filterRegistrationBean.addInitParameter("principalCookieName", "USER_COOKIE");
	    filterRegistrationBean.addInitParameter("principalSessionName", "USER_SESSION");
	    return filterRegistrationBean;
    }
   
}

MybatisConfiguration.java  (注:PageHelper不要使用5.0以上版本,因为5.0以上没实现Interceptor接口,导致SqlSessionFactoryBean添加插件时,失败)

package com.fei.springboot.mybatisconfig;

import java.io.IOException;
import java.util.Properties;

import javax.sql.DataSource;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.plugin.Interceptor;
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.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;

import com.github.pagehelper.PageHelper;
/**
 * mybatis的相关配置设置
 * @author Jfei
 *
 */
@Configuration
@AutoConfigureAfter(DatasourceConfig.class)
@ConfigurationProperties
@EnableTransactionManagement
//@MapperScan("com.fei.springboot.dao")
public class MybatisConfiguration implements TransactionManagementConfigurer{

	private static Log logger = LogFactory.getLog(MybatisConfiguration.class);

    //  配置类型别名
        @Value("${mybatis.typeAliasesPackage}")
        private String typeAliasesPackage;

    //  配置mapper的扫描,找到所有的mapper.xml映射文件
//        @Value("${mybatis.mapperLocations : classpath:com/fei/springboot/dao/*.xml}")
        @Value("${mybatis.mapperLocations}")
        private String mapperLocations;

    //  加载全局的配置文件
        @Value("${mybatis.configLocation}")
        private String configLocation;

        @Autowired
        private DataSource dataSource;

        // 提供SqlSeesion
        @Bean(name = "sqlSessionFactory")
        @Primary
        public SqlSessionFactory sqlSessionFactory() {
            try {
                SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean();
                sessionFactoryBean.setDataSource(dataSource);

                // 读取配置 
                sessionFactoryBean.setTypeAliasesPackage(typeAliasesPackage);
                
                //设置mapper.xml文件所在位置 
                Resource[] resources = new PathMatchingResourcePatternResolver().getResources(mapperLocations);
                sessionFactoryBean.setMapperLocations(resources);
             //设置mybatis-config.xml配置文件位置
                sessionFactoryBean.setConfigLocation(new DefaultResourceLoader().getResource(configLocation));

                //添加分页插件、打印sql插件
                Interceptor[] plugins = new Interceptor[]{pageHelper(),sqlPrintInterceptor()};
                sessionFactoryBean.setPlugins(plugins);
                
                return sessionFactoryBean.getObject();
            } catch (IOException e) {
                logger.error("mybatis resolver mapper*xml is error",e);
                return null;
            } catch (Exception e) {
                logger.error("mybatis sqlSessionFactoryBean create error",e);
                return null;
            }
        }

        @Bean
        public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        	return new SqlSessionTemplate(sqlSessionFactory);
        }
        
        //事务管理
        @Bean
        public PlatformTransactionManager annotationDrivenTransactionManager() {
            return new DataSourceTransactionManager(dataSource);
        }

        //将要执行的sql进行日志打印(不想拦截,就把这方法注释掉)
        @Bean
        public SqlPrintInterceptor sqlPrintInterceptor(){
        	return new SqlPrintInterceptor();
        }

        /**
         * 分页插件
         * @param dataSource
         * @return
         */
        
//        <!-- 分页插件 -->
//    	<plugins>        
//                    <plugin interceptor="com.github.pagehelper.PageHelper">            
//                            <property name="dialect" value="mysql"/>
//                            <!-- 该参数默认为false -->
//    							<!-- 设置为true时,会将RowBounds第一个参数offset当成pageNum页码使用 -->
//    							<!-- 和startPage中的pageNum效果一样 -->
//                            <property name="offsetAsPageNum" value="true"/>
//                             <!-- 该参数默认为false -->
//    							<!-- 设置为true时,使用RowBounds分页会进行count查询 -->    
//                            <property name="rowBoundsWithCount" value="true"/>
//                            <!-- 设置为true时,如果pageSize=0或者RowBounds.limit = 0就会查询出全部的结果 -->
//    							<!-- (相当于没有执行分页查询,但是返回结果仍然是Page类型) -->
//                            <property name="pageSizeZero" value="true"/>
//                            <!-- 3.3.0版本可用 - 分页参数合理化,默认false禁用 -->
//    							<!-- 启用合理化时,如果pageNum<1会查询第一页,如果pageNum>pages会查询最后一页 -->
//    							<!-- 禁用合理化时,如果pageNum<1或pageNum>pages会返回空数据 -->
//                            <property name="reasonable" value="false"/>
//                            <!-- 支持通过Mapper接口参数来传递分页参数 -->
//                            <property name="supportMethodsArguments" value="false"/>
//                            <!-- always总是返回PageInfo类型,check检查返回类型是否为PageInfo,none返回Page -->
//                            <property name="returnPageInfo" value="none"/>
//                            
//                   </plugin>    
//          </plugins>
        @Bean
        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("returnPageInfo", "check");
            p.setProperty("params", "count=countSql");
            pageHelper.setProperties(p);
            return pageHelper;
        }
}

打印sql拦截插件

SqlPrintInterceptor.java

package com.fei.springboot.mybatisconfig;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.ParameterMode;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.type.TypeHandlerRegistry;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.Properties;
import java.util.regex.Matcher;

/**
 * MyBatis 将mybatis要执行的sql拦截打印出来
 *
 * @since 1.0.0
 */
@Intercepts
({
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class SqlPrintInterceptor implements Interceptor {

	private static Log logger = LogFactory.getLog(SqlPrintInterceptor.class);
	
    private static final DateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        Object parameterObject = null;
        if (invocation.getArgs().length > 1) {
            parameterObject = invocation.getArgs()[1];
        }

        long start = System.currentTimeMillis();

        Object result = invocation.proceed();
        
        String statementId = mappedStatement.getId();
        BoundSql boundSql = mappedStatement.getBoundSql(parameterObject);
        Configuration configuration = mappedStatement.getConfiguration();
        String sql = getSql(boundSql, parameterObject, configuration);

        long end = System.currentTimeMillis();
        long timing = end - start;
        if(logger.isInfoEnabled()){
        	logger.info("执行sql耗时:" + timing + " ms" + " - id:" + statementId + " - Sql:" );
        	logger.info("   "+sql);
        }
       
        return result;
    }

    @Override
    public Object plugin(Object target) {
        if (target instanceof Executor) {
            return Plugin.wrap(target, this);
        }
        return target;
    }

    @Override
    public void setProperties(Properties properties) {
    }

    private String getSql(BoundSql boundSql, Object parameterObject, Configuration configuration) {
        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
        if (parameterMappings != null) {
            for (int i = 0; i < parameterMappings.size(); i++) {
                ParameterMapping parameterMapping = parameterMappings.get(i);
                if (parameterMapping.getMode() != ParameterMode.OUT) {
                    Object value;
                    String propertyName = parameterMapping.getProperty();
                    if (boundSql.hasAdditionalParameter(propertyName)) {
                        value = boundSql.getAdditionalParameter(propertyName);
                    } else if (parameterObject == null) {
                        value = null;
                    } else if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
                        value = parameterObject;
                    } else {
                        MetaObject metaObject = configuration.newMetaObject(parameterObject);
                        value = metaObject.getValue(propertyName);
                    }
                    sql = replacePlaceholder(sql, value);
                }
            }
        }
        return sql;
    }

    private String replacePlaceholder(String sql, Object propertyValue) {
        String result;
        if (propertyValue != null) {
            if (propertyValue instanceof String) {
                result = "'" + propertyValue + "'";
            } else if (propertyValue instanceof Date) {
                result = "'" + DATE_FORMAT.format(propertyValue) + "'";
            } else {
                result = propertyValue.toString();
            }
        } else {
            result = "null";
        }
        return sql.replaceFirst("\\?", Matcher.quoteReplacement(result));
    }
}


按照项目开发习惯,写个dao/service/controller来进行测试下

UserMapper.java

package com.fei.springboot.dao;

import java.util.List;

import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

import com.fei.springboot.domain.User;

@Mapper
public interface UserMapper {

	@Insert("insert sys_user(id,user_name) values(#{id},#{userName})")
	void insert(User u);
	
	//注:方法名和要UserMapper.xml中的id一致
	List<User> query(@Param("userName")String userName);
	
	@Delete("delete from sys_user")
	void deleteAll();
}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.fei.springboot.dao.UserMapper">
 
<select id="query"  resultType="com.fei.springboot.domain.User">
    select id ,user_name 
    from sys_user 
    where 1=1
    <if test="userName != null">
      and user_name like CONCAT('%',#{userName},'%')
    </if>
</select>
</mapper>
UserService.java


package com.fei.springboot.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.fei.springboot.dao.UserMapper;
import com.fei.springboot.domain.User;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;

@Service
@Transactional(readOnly=true,rollbackFor=Exception.class)
public class UserService {

	@Autowired
	private UserMapper userMapper;
	
	//注意:方法的@Transactional会覆盖类上面声明的事务
	//Propagation.REQUIRED :有事务就处于当前事务中,没事务就创建一个事务
	//isolation=Isolation.DEFAULT:事务数据库的默认隔离级别
	@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT,readOnly=false)
	public void insertUser(User u){
		this.userMapper.insert(u);
	
		//如果类上面没有@Transactional,方法上也没有,哪怕throw new RuntimeException,数据库也会成功插入数据
	//	throw new RuntimeException("测试插入事务");
	}
	
	public PageInfo<User> queryPage(String userName,int pageNum,int pageSize){
		Page<User> page = PageHelper.startPage(pageNum, pageSize);
		//PageHelper会自动拦截到下面这查询sql
		this.userMapper.query(userName);
		return page.toPageInfo();
	}
	
	
	//测试事务
	//注意:方法的@Transactional会覆盖类上面声明的事务
	//Propagation.REQUIRED :有事务就处于当前事务中,没事务就创建一个事务
	//isolation=Isolation.DEFAULT:事务数据库的默认隔离级别
	@Transactional(propagation=Propagation.REQUIRED,isolation=Isolation.DEFAULT,readOnly=false)
	public void testTransational(){
		
		//删除全部
		this.userMapper.deleteAll();
		//新增
		User u = new User();
		u.setId("123456");
		u.setUserName("张三");
		this.userMapper.insert(u);
		//制造异常
		//如果类上面没有@Transactional,方法上也没有,哪怕throw new RuntimeException,数据库也会提交
		throw new RuntimeException("事务异常测试");
	}
	
}
 
    注:虽然在配置类中开启了事务,但是没有指定哪些类哪些方法的执行要开启事务,所以需要自行在service类上面或方法上面@Transactional。在类上面加@Transactional,说明类内的每个public方法都默认使用它,除非方法上自行定义@Transactional覆盖类上的定义。

   如果类或方法名上都没有@Transactional,那么执行了数据库操作后,后续代码抛出了异常,数据库也不会回滚,因为没声明开启事务,数据库默认自动提交了。

    事务嵌套:@Transactional时,默认Propagation.REQUIRED (有事务就处于当前事务中,没事务就创建一个事务),所以如果A01Service.a()调用A01Service.b()同时调用A02Service.c(),同类内部的事务方法或不同类的事务方法,spring处理时,会将它们都置于同一个事务中,也就是要么都失败,要么都成功,不会出现多个事务,一个提交了,另一个回滚了。这个进行框架搭建时,可自行测试。


UserController.java

package com.fei.springboot.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import com.fei.springboot.domain.User;
import com.fei.springboot.service.UserService;
import com.github.pagehelper.PageInfo;

@Controller
@RequestMapping("/user")
public class UserController {

	
	@Autowired
	private UserService userService;
	
	@RequestMapping("/hello")
	@ResponseBody
	public String hello(){
		return "hello";
	}
	/**
	 * 测试插入
	 * @return
	 */
	@RequestMapping("/add")
	@ResponseBody
	public String add(){
		User u = new User();
		double i = Math.random()*100;
		u.setId(String.valueOf(i));
		u.setUserName("test"+i);
		this.userService.insertUser(u);
		return "success";
	}
	/**
	 * 测试分页插件
	 * @return
	 */
	@RequestMapping("/queryPage")
	@ResponseBody
	public String queryPage(){
		PageInfo<User> page = this.userService.queryPage("tes", 1, 2);
		System.out.println("总页数=" + page.getPages());
		System.out.println("总记录数=" + page.getTotal()) ;
		for(User u : page.getList()){
			System.out.println(u.getId() + " \t " + u.getUserName());
		}
		return "success";
	}
	/**
	 * 测试事务
	 * @return
	 */
	@RequestMapping("/testTransational")
	@ResponseBody
	public String test(){
		try {
			this.userService.testTransational();
			return "success";
		} catch (Exception e) {
			return e.getMessage();
		}
		
	}
}
Application.java

package com.fei.springboot;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.embedded.ConfigurableEmbeddedServletContainer;
import org.springframework.boot.context.embedded.EmbeddedServletContainerCustomizer;
import org.springframework.boot.web.support.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;

@EnableAutoConfiguration
@ComponentScan(basePackages={"com.fei.springboot"})
@SpringBootApplication
public class Application extends SpringBootServletInitializer implements EmbeddedServletContainerCustomizer{

	 @Override  
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {  
        return application.sources(Application.class);  
    }  

	 
	 public static void main(String[] args) throws Exception {
	        SpringApplication.run(Application.class, args);
	    }

	public void customize(ConfigurableEmbeddedServletContainer configurableEmbeddedServletContainer) {
	//	configurableEmbeddedServletContainer.setPort(9090);
	}
}

执行Application.java,然后浏览器访问controller里的url即可进行测试

比如访问查询时,可以看到日志中打印了sql,并且PageHelper也正常工作

2017-05-20 14:26:03.932  INFO 101936 --- [p-nio-80-exec-2] c.f.s.mybatisconfig.SqlPrintInterceptor  :    select id ,user_name from sys_user where 1=1 and user_name like CONCAT('%','tes','%')
2017-05-20 14:26:03.932 [http-nio-80-exec-2] INFO  c.f.s.m.SqlPrintInterceptor -    select id ,user_name from sys_user where 1=1 and user_name like CONCAT('%','tes','%')
总页数=3
总记录数=5
45.83897583806164 	 test45.83897583806164
5.580134425508676 	 test5.580134425508676


习惯JPA的同学,发现insert对象都需要写sql,会觉得麻烦。那可以把通用Mapper整合进去。通用Mapper比较成熟了,它以整合了jpa,具体可以百度通用mapper,git上作者已经提供spring boot+mybatis+通用mapper的整合demo了


druid 监控

访问 http://127.0.0.1/druid/  输入账号/密码(配置文件那配置的druidLoginName)



完整的例子源码下载










  • 9
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值