• springboot学习笔记:8. springboot+druid+mysql+mybatis+通用mapper+pagehelper+mybatis-generator+freemarker+layui


    前言:

    开发环境:IDEA+jdk1.8+windows10

    目标:使用springboot整合druid数据源+mysql+mybatis+通用mapper插件+pagehelper插件+mybatis-generator+freemarker+layui

    使用springboot开发web项目,尤其是web后台管理类项目,推荐使用上面的组合;

    原因:

    首先,druid数据源提供了方便的sql监控视图,而且性能也很好;

    mysql开源且小巧强大,使用方便;

    mybatis可以手动定制sql,十分方便;通用mapper插件可以让你免去基础的单表增删改查等基础代码的编写工作;pagehelper使用拦截器智能分页;mybatis-generator配置后可以全面自动生成持久层大部分代码;

    freemarker模板效率较好,宏的使用十分方便;

    layui更是当前java后端程序员的福音;

    下面开始跟我一起整合,源代码在最后:


    项目整合:

    1、项目依赖

    完整的pom.xml文件如下:

    <?xml version="1.0" encoding="UTF-8"?>
    <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.zjt.learn</groupId>
        <artifactId>springboot-mybatis</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <packaging>jar</packaging>
    
        <name>springboot-mybatis</name>
        <description>Demo project for Spring Boot</description>
    
        <parent>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-parent</artifactId>
            <version>1.5.9.RELEASE</version>
        </parent>
    
        <properties>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
    
            <!--使用freemarker的starter,里面包含spring-boot-starter-web组件-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-freemarker</artifactId>
            </dependency>
    
            <!--springboot测试组件-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
            </dependency>
    
            <!--springboot热部署组件-->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-devtools</artifactId>
                <optional>true</optional> <!-- 这个需要为 true 热部署才有效 -->
            </dependency>
    
            <!--mybatis依赖-->
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>1.3.1</version>
            </dependency>
    
            <!--通用mapper-->
            <dependency>
                <groupId>tk.mybatis</groupId>
                <artifactId>mapper-spring-boot-starter</artifactId>
                <version>1.1.5</version>
            </dependency>
    
            <!--pagehelper 分页插件-->
            <dependency>
                <groupId>com.github.pagehelper</groupId>
                <artifactId>pagehelper-spring-boot-starter</artifactId>
                <version>1.2.3</version>
            </dependency>
    
            <!--mysql连接驱动包-->
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>5.1.44</version>
            </dependency>
    
            <!--druid连接池依赖-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>1.1.6</version>
            </dependency>
    
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.31</version>
            </dependency>
    
            <!--aspectj组件-->
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.8.0</version>
            </dependency>
    
            <!--部署外部tomcat容器的时候再开启-->
            <!--<dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-tomcat</artifactId>
            </dependency>-->
        </dependencies>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <configuration>
                        <!--fork :  如果没有该项配置,这个devtools不会起作用,即应用不会restart -->
                        <fork>true</fork>
                        <!--支持静态文件热部署-->
                        <addResources>true</addResources>
                    </configuration>
                </plugin>
    
                <!--要打包了这个生成代码要禁止掉,本地开发开启-->
                <plugin>
                    <groupId>org.mybatis.generator</groupId>
                    <artifactId>mybatis-generator-maven-plugin</artifactId>
                    <version>1.3.5</version>
                    <dependencies>
                        <!--配置这个依赖主要是为了等下在配置mybatis-generator.xml的时候可以不用配置classPathEntry这样的一个属性,避免代码的耦合度太高-->
                        <dependency>
                            <groupId>mysql</groupId>
                            <artifactId>mysql-connector-java</artifactId>
                            <version>5.1.44</version>
                        </dependency>
                        <dependency>
                            <groupId>tk.mybatis</groupId>
                            <artifactId>mapper</artifactId>
                            <version>3.4.0</version>
                        </dependency>
                    </dependencies>
                    <executions>
                        <execution>
                            <id>Generate MyBatis Artifacts</id>
                            <phase>package</phase>
                            <goals>
                                <goal>generate</goal>
                            </goals>
                        </execution>
                    </executions>
                    <configuration>
                        <!--允许移动生成的文件 -->
                        <verbose>true</verbose>
                        <!-- 是否覆盖 -->
                        <overwrite>true</overwrite>
                        <!-- 自动生成的配置 -->
                        <configurationFile>src/main/resources/mybatis-generator.xml</configurationFile>
                    </configuration>
                </plugin>
    
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                    </configuration>
                </plugin>
            </plugins>
        </build>
    
    
    </project>

    解释:

    spring-boot-starter-freemarker 依赖会包含springboot web组件,所以web相关其他的组件就不用引入了;

    mybatis-spring-boot-starter引入mybatis相关依赖;

    mapper-spring-boot-starter是mybatis的通用mapper;该插件可以极其方便的使用Mybatis单表的增删改查;(通用mapper原作者文档)

    pagehelper-spring-boot-starter是pagehelper 分页插件,使用拦截器的原理;

    druid-spring-boot-starter是druid连接池组件依赖;(druid连接池中文文档)

     此外,在plugin中添加了mybatis-generator插件,配合mybatis-generator.xml来自动生成实体、mapper、xml;

     2、项目包结构

    3、配置数据持久层

    3.1数据库表:

    我们本文仅使用T_CLASS表,因为仅仅演示如何整合,故业务字段仅有1个name;

     3.2在application-dev.properties配置文件中,配置数据源和连接池、mybatis和分页插件的配置:

    ## 数据库访问配置
    spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
    spring.datasource.driver-class-name=com.mysql.jdbc.Driver
    spring.datasource.url=jdbc:mysql://localhost:3306/springboot-mybatis?useUnicode=true&characterEncoding=utf-8
    spring.datasource.username=root
    spring.datasource.password=root
    
    # 下面为连接池的补充设置,应用到上面所有数据源中
    # 初始化大小,最小,最大
    spring.datasource.initialSize=5
    spring.datasource.minIdle=5
    spring.datasource.maxActive=20
    # 配置获取连接等待超时的时间
    spring.datasource.maxWait=60000
    # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
    spring.datasource.timeBetweenEvictionRunsMillis=60000
    # 配置一个连接在池中最小生存的时间,单位是毫秒
    spring.datasource.minEvictableIdleTimeMillis=300000
    spring.datasource.validationQuery=SELECT 1 FROM DUAL
    spring.datasource.testWhileIdle=true
    spring.datasource.testOnBorrow=false
    spring.datasource.testOnReturn=false
    # 打开PSCache,并且指定每个连接上PSCache的大小
    spring.datasource.poolPreparedStatements=true
    spring.datasource.maxPoolPreparedStatementPerConnectionSize=20
    # 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
    spring.datasource.filters=stat,wall,log4j
    # 合并多个DruidDataSource的监控数据
    #spring.datasource.useGlobalDataSourceStat=true
    
    #mybatis
    mybatis.type-aliases-package=com.zjt.entity
    mybatis.mapper-locations=classpath:mapper/*.xml
    #mappers 多个接口时逗号隔开
    #mapper.mappers=tk.mybatis.mapper.common.Mapper
    mapper.mappers=com.zjt.util.MyMapper
    mapper.not-empty=false
    mapper.identity=MYSQL
    
    #pagehelper
    pagehelper.helperDialect=mysql
    pagehelper.reasonable=true
    pagehelper.supportMethodsArguments=true
    pagehelper.params=count=countSql

     3.3在DruidConfig.java中使用druid数据源配置参数生产数据源,并配置druid监控相关配置:

    package com.zjt.config;
    
    import com.alibaba.druid.filter.Filter;
    import com.alibaba.druid.filter.stat.StatFilter;
    import com.alibaba.druid.pool.DruidDataSource;
    import com.alibaba.druid.support.http.StatViewServlet;
    import com.alibaba.druid.support.http.WebStatFilter;
    import com.alibaba.druid.wall.WallConfig;
    import com.alibaba.druid.wall.WallFilter;
    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 javax.sql.DataSource;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * Druid配置
     *
     * @author zhaojiatao
     */
    @Configuration
    public class DruidConfig {
        private Logger logger = LoggerFactory.getLogger(DruidConfig.class);
    
        @Value("${spring.datasource.url:#{null}}")
        private String dbUrl;
        @Value("${spring.datasource.username: #{null}}")
        private String username;
        @Value("${spring.datasource.password:#{null}}")
        private String password;
        @Value("${spring.datasource.driverClassName:#{null}}")
        private String driverClassName;
        @Value("${spring.datasource.initialSize:#{null}}")
        private Integer initialSize;
        @Value("${spring.datasource.minIdle:#{null}}")
        private Integer minIdle;
        @Value("${spring.datasource.maxActive:#{null}}")
        private Integer maxActive;
        @Value("${spring.datasource.maxWait:#{null}}")
        private Integer maxWait;
        @Value("${spring.datasource.timeBetweenEvictionRunsMillis:#{null}}")
        private Integer timeBetweenEvictionRunsMillis;
        @Value("${spring.datasource.minEvictableIdleTimeMillis:#{null}}")
        private Integer minEvictableIdleTimeMillis;
        @Value("${spring.datasource.validationQuery:#{null}}")
        private String validationQuery;
        @Value("${spring.datasource.testWhileIdle:#{null}}")
        private Boolean testWhileIdle;
        @Value("${spring.datasource.testOnBorrow:#{null}}")
        private Boolean testOnBorrow;
        @Value("${spring.datasource.testOnReturn:#{null}}")
        private Boolean testOnReturn;
        @Value("${spring.datasource.poolPreparedStatements:#{null}}")
        private Boolean poolPreparedStatements;
        @Value("${spring.datasource.maxPoolPreparedStatementPerConnectionSize:#{null}}")
        private Integer maxPoolPreparedStatementPerConnectionSize;
        @Value("${spring.datasource.filters:#{null}}")
        private String filters;
        @Value("{spring.datasource.connectionProperties:#{null}}")
        private String connectionProperties;
    
        @Bean
        @Primary
        public DataSource dataSource(){
            DruidDataSource datasource = new DruidDataSource();
    
            datasource.setUrl(this.dbUrl);
            datasource.setUsername(username);
            datasource.setPassword(password);
            datasource.setDriverClassName(driverClassName);
            //configuration
            if(initialSize != null) {
                datasource.setInitialSize(initialSize);
            }
            if(minIdle != null) {
                datasource.setMinIdle(minIdle);
            }
            if(maxActive != null) {
                datasource.setMaxActive(maxActive);
            }
            if(maxWait != null) {
                datasource.setMaxWait(maxWait);
            }
            if(timeBetweenEvictionRunsMillis != null) {
                datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
            }
            if(minEvictableIdleTimeMillis != null) {
                datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
            }
            if(validationQuery!=null) {
                datasource.setValidationQuery(validationQuery);
            }
            if(testWhileIdle != null) {
                datasource.setTestWhileIdle(testWhileIdle);
            }
            if(testOnBorrow != null) {
                datasource.setTestOnBorrow(testOnBorrow);
            }
            if(testOnReturn != null) {
                datasource.setTestOnReturn(testOnReturn);
            }
            if(poolPreparedStatements != null) {
                datasource.setPoolPreparedStatements(poolPreparedStatements);
            }
            if(maxPoolPreparedStatementPerConnectionSize != null) {
                datasource.setMaxPoolPreparedStatementPerConnectionSize(maxPoolPreparedStatementPerConnectionSize);
            }
    
            if(connectionProperties != null) {
                datasource.setConnectionProperties(connectionProperties);
            }
    
            List<Filter> filters = new ArrayList<>();
            filters.add(statFilter());
            filters.add(wallFilter());
            datasource.setProxyFilters(filters);
    
            return datasource;
        }
    
        @Bean
        public ServletRegistrationBean druidServlet() {
            ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
    
            //控制台管理用户,加入下面2行 进入druid后台就需要登录
            //servletRegistrationBean.addInitParameter("loginUsername", "admin");
            //servletRegistrationBean.addInitParameter("loginPassword", "admin");
            return servletRegistrationBean;
        }
    
        @Bean
        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");
            return filterRegistrationBean;
        }
    
        @Bean
        public StatFilter statFilter(){
            StatFilter statFilter = new StatFilter();
            statFilter.setLogSlowSql(true); //slowSqlMillis用来配置SQL慢的标准,执行时间超过slowSqlMillis的就是慢。
            statFilter.setMergeSql(true); //SQL合并配置
            statFilter.setSlowSqlMillis(1000);//slowSqlMillis的缺省值为3000,也就是3秒。
            return statFilter;
        }
    
        @Bean
        public WallFilter wallFilter(){
            WallFilter wallFilter = new WallFilter();
            //允许执行多条SQL
            WallConfig config = new WallConfig();
            config.setMultiStatementAllow(true);
            wallFilter.setConfig(config);
            return wallFilter;
        }
    }

     3.4在MybatisDatasourceConfig.java中使用3.3中生成的数据源生成sqlSessionFactory,并指定要扫描的mapper包,生成该数据源的事务管理器:

    package com.zjt.config;
    
    import com.zjt.util.MyMapper;
    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.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.Primary;
    import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
    import org.springframework.core.io.support.ResourcePatternResolver;
    import org.springframework.jdbc.datasource.DataSourceTransactionManager;
    
    import javax.sql.DataSource;
    
    /**
     * @author <a href="zhaojiatao"></a>
     * @version 1.0, 2017/11/24
     * @description
     */
    @Configuration
    // 精确到 mapper 目录,以便跟其他数据源隔离
    @MapperScan(basePackages = "com.zjt.mapper", markerInterface = MyMapper.class, sqlSessionFactoryRef = "sqlSessionFactory")
    public class MybatisDatasourceConfig {
    
        @Autowired
        @Qualifier("dataSource")
        private DataSource ds;
    
        @Bean
        public SqlSessionFactory sqlSessionFactory() throws Exception {
            SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
            factoryBean.setDataSource(ds);
            //指定mapper xml目录
            ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
            factoryBean.setMapperLocations(resolver.getResources("classpath:mapper/*.xml"));
            return factoryBean.getObject();
    
        }
    
        @Bean
        public SqlSessionTemplate sqlSessionTemplate() throws Exception {
            SqlSessionTemplate template = new SqlSessionTemplate(sqlSessionFactory()); // 使用上面配置的Factory
            return template;
        }
    
        //关于事务管理器,不管是JPA还是JDBC等都实现自接口 PlatformTransactionManager
        // 如果你添加的是 spring-boot-starter-jdbc 依赖,框架会默认注入 DataSourceTransactionManager 实例。
        //在Spring容器中,我们手工注解@Bean 将被优先加载,框架不会重新实例化其他的 PlatformTransactionManager 实现类。
        @Bean(name = "transactionManager")
        @Primary
        public DataSourceTransactionManager masterTransactionManager() {
            //MyBatis自动参与到spring事务管理中,无需额外配置,只要org.mybatis.spring.SqlSessionFactoryBean引用的数据源
            // 与DataSourceTransactionManager引用的数据源一致即可,否则事务管理会不起作用。
            return new DataSourceTransactionManager(ds);
    
        }
    
    }

     3.5使用3.4生成的事务管理器通过AOP技术为指定包下的service方法提供事务支持,并根据不同的方法提供不同的事务隔离级别:

    package com.zjt.interceptor;
    
    import org.aspectj.lang.annotation.Aspect;
    import org.springframework.aop.Advisor;
    import org.springframework.aop.aspectj.AspectJExpressionPointcut;
    import org.springframework.aop.support.DefaultPointcutAdvisor;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.transaction.PlatformTransactionManager;
    import org.springframework.transaction.TransactionDefinition;
    import org.springframework.transaction.interceptor.*;
    
    import java.util.Collections;
    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * @author <a href="zhaojiatao"></a>
     * @version 1.0, 2017/11/29
     * @description
     * 注解声明式事务
     */
    @Aspect
    @Configuration
    public class TxAdviceInterceptor {
        private static final int TX_METHOD_TIMEOUT = 50000;//单位秒
        private static final String AOP_POINTCUT_EXPRESSION = "execution (* com.zjt.service.*.*(..))";
    
        @Autowired
        @Qualifier("transactionManager")
        private PlatformTransactionManager transactionManager;
    
        @Bean
        public TransactionInterceptor txAdvice() {
            NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
             /*只读事务,不做更新操作*/
            RuleBasedTransactionAttribute readOnlyTx = new RuleBasedTransactionAttribute();
            readOnlyTx.setReadOnly(true);
            readOnlyTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_NOT_SUPPORTED );
            /*当前存在事务就使用当前事务,当前不存在事务就创建一个新的事务*/
            RuleBasedTransactionAttribute requiredTx = new RuleBasedTransactionAttribute();
            requiredTx.setRollbackRules(
                    Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
            requiredTx.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
            requiredTx.setTimeout(TX_METHOD_TIMEOUT);
            Map<String, TransactionAttribute> txMap = new HashMap<>();
            txMap.put("add*", requiredTx);
            txMap.put("save*", requiredTx);
            txMap.put("insert*", requiredTx);
            txMap.put("update*", requiredTx);
            txMap.put("delete*", requiredTx);
            txMap.put("get*", readOnlyTx);
            txMap.put("select*", readOnlyTx);
            txMap.put("query*", readOnlyTx);
            source.setNameMap( txMap );
        TransactionInterceptor txAdvice = new TransactionInterceptor(transactionManager, source);
            return txAdvice;
    }
    
        @Bean
        public Advisor txAdviceAdvisor() {
            AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
            pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
            return new DefaultPointcutAdvisor(pointcut, txAdvice());
        }
    }

    3.6使用mybatis-generator自动生成entity+mapper+xml:

    首先,在util包下添加:MyMapper接口:

    package com.zjt.util;
    
    import tk.mybatis.mapper.common.Mapper;
    import tk.mybatis.mapper.common.MySqlMapper;
    /**
     * 继承自己的MyMapper
     *
     * @author
     * @since 2017-06-26 21:53
     */
    public interface MyMapper<T> extends Mapper<T>, MySqlMapper<T> {
        //FIXME 特别注意,该接口不能被扫描到,否则会出错
        //FIXME 最后在启动类中通过MapperScan注解指定扫描的mapper路径:
    }

    然后,在resources目录下添加mybatis-generator.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE generatorConfiguration
            PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
            "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
    <generatorConfiguration>
        <!--加载配置文件,为下面读取数据库信息准备-->
        <properties resource="application.properties"/>
    
        <context id="Mysql" targetRuntime="MyBatis3Simple" defaultModelType="flat">
    
            <plugin type="tk.mybatis.mapper.generator.MapperPlugin">
                <!--其中tk.mybatis.mapper.generator.MapperPlugin很重要,用来指定通用Mapper对应的文件,这样我们生成的mapper都会继承这个通用Mapper-->
                <property name="mappers" value="com.zjt.util.MyMapper" />
                <!--caseSensitive默认false,当数据库表名区分大小写时,可以将该属性设置为true-->
                <property name="caseSensitive" value="false"/>
            </plugin>
    
            <!-- 阻止生成自动注释 -->
            <commentGenerator>
                <property name="javaFileEncoding" value="UTF-8"/>
                <property name="suppressDate" value="true"/>
                <property name="suppressAllComments" value="true"/>
            </commentGenerator>
    
            <!--数据库链接地址账号密码,这里由于我使用的是根据开发和生产分离的配置文件,所以这里直接写上了-->
            <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                            connectionURL="jdbc:mysql://localhost:3306/springboot-mybatis?useUnicode=true&amp;characterEncoding=utf-8"
                            userId="root"
                            password="root">
            </jdbcConnection>
    
            <javaTypeResolver>
                <property name="forceBigDecimals" value="false"/>
            </javaTypeResolver>
    
            <!--生成Model类存放位置-->
            <javaModelGenerator targetPackage="com.zjt.entity" targetProject="src/main/java">
                <property name="enableSubPackages" value="true"/>
                <property name="trimStrings" value="true"/>
            </javaModelGenerator>
    
            <!--生成映射文件存放位置-->
            <sqlMapGenerator targetPackage="mapper" targetProject="src/main/resources">
                <property name="enableSubPackages" value="true"/>
            </sqlMapGenerator>
    
            <!--生成Dao类存放位置-->
            <!-- 客户端代码,生成易于使用的针对Model对象和XML配置文件 的代码
                    type="ANNOTATEDMAPPER",生成Java Model 和基于注解的Mapper对象
                    type="XMLMAPPER",生成SQLMap XML文件和独立的Mapper接口
            -->
            <javaClientGenerator type="XMLMAPPER" targetPackage="com.zjt.mapper" targetProject="src/main/java">
                <property name="enableSubPackages" value="true"/>
            </javaClientGenerator>
    
            <!--生成对应表及类名
            去掉Mybatis Generator生成的一堆 example
            -->
            <!--<table tableName="LEARN_RESOURCE" domainObjectName="LearnResource" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false">
                <generatedKey column="id" sqlStatement="Mysql" identity="true"/>
            </table>-->
            <!--<table tableName="T_STUDENT" domainObjectName="Student" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false">
                <generatedKey column="id" sqlStatement="Mysql" identity="true"/>
            </table>
            <table tableName="T_TEACHER" domainObjectName="Teacher" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false">
                <generatedKey column="id" sqlStatement="Mysql" identity="true"/>
            </table>-->
            <table tableName="T_CLASS" domainObjectName="TClass" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false">
                <generatedKey column="id" sqlStatement="Mysql" identity="true"/>
            </table>
        </context>
    </generatorConfiguration>

     因为我们已经在pom的plugin中添加了插件,所以我们可以在IDEA中使用maven插件命令执行:

    执行成功之后会生成:TClass.java+TClassMapper.java+TClassMapper.xml

    3.7使用通用IService接口实现service层构建

    由于spring4支持泛型注入,所以通用mapper作者提供了一种通用mapper在spring4中的最佳实践:

    首先,编写通用接口:

    package com.zjt.service;
    
    import org.springframework.stereotype.Service;
    
    import java.util.List;
    
    /**
     * 通用接口
     */
    @Service
    public interface IService<T> {
    
        T selectByKey(Object key);
    
        int save(T entity);
    
        int saveNotNull(T entity);
    
        int delete(Object key);
    
        int updateAll(T entity);
    
        int updateNotNull(T entity);
    
        List<T> selectByExample(Object example);
    
        //TODO 其他...
    }

     然后,编写抽象类BaseService实现通用接口:

    package com.zjt.service.impl;
    
    import com.zjt.service.IService;
    import org.springframework.beans.factory.annotation.Autowired;
    import tk.mybatis.mapper.common.Mapper;
    
    import java.util.List;
    
    /**
     * 通用Service
     * @param <T>
     */
    public abstract class BaseService<T> implements IService<T> {
    
        @Autowired
        protected Mapper<T> mapper;
        public Mapper<T> getMapper() {
            return mapper;
        }
    
        @Override
        public T selectByKey(Object key) {
            //说明:根据主键字段进行查询,方法参数必须包含完整的主键属性,查询条件使用等号
            return mapper.selectByPrimaryKey(key);
        }
    
        @Override
        public int save(T entity) {
            //说明:保存一个实体,null的属性也会保存,不会使用数据库默认值
            return mapper.insert(entity);
        }
    
        @Override
        public int saveNotNull(T entity) {
            //说明:保存一个实体,属性不为null的值
            return mapper.insertSelective(entity);
        }
    
        @Override
        public int delete(Object key) {
            //说明:根据主键字段进行删除,方法参数必须包含完整的主键属性
            return mapper.deleteByPrimaryKey(key);
        }
    
        @Override
        public int updateAll(T entity) {
            //说明:根据主键更新实体全部字段,null值会被更新
            return mapper.updateByPrimaryKey(entity);
        }
    
        @Override
        public int updateNotNull(T entity) {
            //根据主键更新属性不为null的值
            return mapper.updateByPrimaryKeySelective(entity);
        }
    
        @Override
        public List<T> selectByExample(Object example) {
            //说明:根据Example条件进行查询
            //重点:这个查询支持通过Example类指定查询列,通过selectProperties方法指定查询列
            return mapper.selectByExample(example);
        }
    }

     这样,其他的业务service实现类serviceImpl就可以继承这个抽象类BaseService来简化基础数据库操作的代码;

    如,我们建立TClassService业务接口,使其继承通用接口并指定泛型类型IService<TClass>:

    package com.zjt.service;
    
    import com.zjt.entity.TClass;
    import com.zjt.model.QueryTClassList;
    import com.zjt.util.Page;
    
    import java.util.List;
    import java.util.Map;
    
    public interface TClassService  extends IService<TClass>{
        public List<TClass> queryTClassList(Page<QueryTClassList> page);
    
        public Map<String,Object> saveOrUpdateTClass(TClass tclass);
    
    
    }

     然后在建立该业务接口的实现类TClassServiceImpl,使其继承并指定泛型类型BaseService<TClass>,并实现TClassService:

    package com.zjt.service.impl;
    
    import com.github.pagehelper.PageHelper;
    import com.zjt.entity.TClass;
    import com.zjt.mapper.TClassMapper;
    import com.zjt.model.QueryTClassList;
    import com.zjt.service.TClassService;
    import com.zjt.util.Page;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import tk.mybatis.mapper.util.StringUtil;
    
    import java.util.LinkedHashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.UUID;
    
    @Service("tclassServiceImpl")
    public class TClassServiceImpl extends BaseService<TClass> implements TClassService {
        @Autowired
        private TClassMapper tclassMapper;
    
        @Override
        public List<TClass> queryTClassList(Page<QueryTClassList> page) {
            PageHelper.startPage(page.getPage(), page.getRows());
    //        return tclassMapper.queryTClassList(page.getCondition());
            return tclassMapper.selectAll();
        }
    
        @Override
        public Map<String, Object> saveOrUpdateTClass(TClass tclass) {
            LinkedHashMap<String,Object> resultMap=new LinkedHashMap<String,Object>();
            if(tclass!=null){
                if(StringUtil.isNotEmpty(tclass.getId())){//编辑
                    if(StringUtil.isNotEmpty(tclass.getName())){
                        updateNotNull(tclass);
                        resultMap.put("state","success");
                        resultMap.put("message","修改班级成功");
                        return resultMap;
                    }else{
                        resultMap.put("state","fail");
                        resultMap.put("message","修改失败,缺少字段");
                        return resultMap;
                    }
                }else{//新建
                    if(StringUtil.isNotEmpty(tclass.getName())){
                        tclass.setId(UUID.randomUUID().toString().replaceAll("-",""));
                        saveNotNull(tclass);
                        resultMap.put("state","success");
                        resultMap.put("message","新建班级成功");
                        return resultMap;
                    }else{
                        resultMap.put("state","fail");
                        resultMap.put("message","新建失败,缺少字段");
                        return resultMap;
                    }
                }
            }else{
                resultMap.put("state","fail");
                resultMap.put("message","失败");
                return resultMap;
            }
    
        }
    }

    最后,我们就可以在controller中调用该业务service的方法,并使用业务实现类去执行:

    package com.zjt.web;
    
    import com.zjt.entity.TClass;
    import com.zjt.mapper.PageRusult;
    import com.zjt.model.QueryTClassList;
    import com.zjt.service.TClassService;
    import com.zjt.util.Page;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    import tk.mybatis.mapper.util.StringUtil;
    
    import java.util.LinkedHashMap;
    import java.util.List;
    import java.util.Map;
    
    @Controller
    @RequestMapping("/tclass")
    public class TClassController {
    
        @Autowired
        @Qualifier("tclassServiceImpl")
        private TClassService tclassService;
    
    
        @ResponseBody
        @RequestMapping("/queryTClassList")
        public PageRusult selectByPages(Page<QueryTClassList> page){
            List<TClass> tclassList=tclassService.queryTClassList(page);
            //PageInfo<TClass> pageInfo =new PageInfo<TClass>(tclassList);
            PageRusult<TClass> pageRusult =new PageRusult<TClass>(tclassList);
            pageRusult.setCode(0);
            return pageRusult;
        }
    
        @ResponseBody
        @RequestMapping("/saveOrUpdateTClass")
        public Map<String,Object> saveOrUpdateTClass(TClass tclass){
            LinkedHashMap<String,Object> resultMap=new LinkedHashMap<String,Object>();
            try {
                return tclassService.saveOrUpdateTClass(tclass);
            }catch (Exception e){
                resultMap.put("state","fail");
                resultMap.put("message","操作失败");
                return resultMap;
            }
        }
    
    
        @ResponseBody
        @RequestMapping("/deleteTClass")
        public Map<String,Object> deleteTClass(String id){
            LinkedHashMap<String,Object> resultMap=new LinkedHashMap<String,Object>();
            try {
                if(StringUtil.isNotEmpty(id)){
                    tclassService.delete(id);
                    resultMap.put("state","success");
                    resultMap.put("message","删除班级成功");
                    return resultMap;
                }else{
                    resultMap.put("state","fail");
                    resultMap.put("message","删除班级失败");
                    return resultMap;
                }
            }catch (Exception e){
                resultMap.put("state","fail");
                resultMap.put("message","操作异常,删除班级失败");
                return resultMap;
            }
        }
    
    
    
    }

     3.8 调整项目启动类SpringbootMybatisApplication.java中的配置:

    开启@EnableTransactionManagement声明事务管理,并将spring boot自带的DataSourceAutoConfiguration禁掉:

    package com.zjt;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
    import org.springframework.boot.builder.SpringApplicationBuilder;
    import org.springframework.boot.web.support.SpringBootServletInitializer;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    @EnableTransactionManagement  // 启注解事务管理,等同于xml配置方式的
    //首先要将spring boot自带的DataSourceAutoConfiguration禁掉,因为它会读取application.properties文件的spring.datasource.*属性并自动配置单数据源。在@SpringBootApplication注解中添加exclude属性即可:
    @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
    public class SpringbootMybatisApplication  extends SpringBootServletInitializer {
        @Override
        protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
            return application.sources(SpringbootMybatisApplication.class);
        }
        public static void main(String[] args) {
            SpringApplication.run(SpringbootMybatisApplication.class, args);
        }
    }

    至此,业务持久层和业务逻辑层全部完成;

     4、配置web层

    4.1配置MyWebMvcConfigurerAdapter.java

    package com.zjt.config;
    
    import org.springframework.context.annotation.Configuration;
    import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
    import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
    
    /**
     * Created by tengj on 2017/3/13.
     */
    @Configuration
    public class MyWebMvcConfigurerAdapter extends WebMvcConfigurerAdapter {
    
    
        /**
         * 以前要访问一个页面需要先创建个Controller控制类,在写方法跳转到页面
         * 在这里配置后就不需要那么麻烦了,直接访问http://localhost:8080/toLogin就跳转到login.html页面了
         *
         * @param registry
         */
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController("/toTest").setViewName("test");
            super.addViewControllers(registry);
        }
    
    }

     4.2 编写controller

    package com.zjt.web;
    
    import com.zjt.entity.TClass;
    import com.zjt.mapper.PageRusult;
    import com.zjt.model.QueryTClassList;
    import com.zjt.service.TClassService;
    import com.zjt.util.Page;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    import tk.mybatis.mapper.util.StringUtil;
    
    import java.util.LinkedHashMap;
    import java.util.List;
    import java.util.Map;
    
    @Controller
    @RequestMapping("/tclass")
    public class TClassController {
    
        @Autowired
        @Qualifier("tclassServiceImpl")
        private TClassService tclassService;
    
    
        @ResponseBody
        @RequestMapping("/queryTClassList")
        public PageRusult selectByPages(Page<QueryTClassList> page){
            List<TClass> tclassList=tclassService.queryTClassList(page);
            //PageInfo<TClass> pageInfo =new PageInfo<TClass>(tclassList);
            PageRusult<TClass> pageRusult =new PageRusult<TClass>(tclassList);
            pageRusult.setCode(0);
            return pageRusult;
        }
    
        @ResponseBody
        @RequestMapping("/saveOrUpdateTClass")
        public Map<String,Object> saveOrUpdateTClass(TClass tclass){
            LinkedHashMap<String,Object> resultMap=new LinkedHashMap<String,Object>();
            try {
                return tclassService.saveOrUpdateTClass(tclass);
            }catch (Exception e){
                resultMap.put("state","fail");
                resultMap.put("message","操作失败");
                return resultMap;
            }
        }
    
    
        @ResponseBody
        @RequestMapping("/deleteTClass")
        public Map<String,Object> deleteTClass(String id){
            LinkedHashMap<String,Object> resultMap=new LinkedHashMap<String,Object>();
            try {
                if(StringUtil.isNotEmpty(id)){
                    tclassService.delete(id);
                    resultMap.put("state","success");
                    resultMap.put("message","删除班级成功");
                    return resultMap;
                }else{
                    resultMap.put("state","fail");
                    resultMap.put("message","删除班级失败");
                    return resultMap;
                }
            }catch (Exception e){
                resultMap.put("state","fail");
                resultMap.put("message","操作异常,删除班级失败");
                return resultMap;
            }
        }
    
    
    
    }

     4.3 在配置文件application-dev.properties中增加freemarker的配置和tomcat相关配置:

    ########################################################
    ###FREEMARKER (FreeMarkerAutoConfiguration)
    ########################################################
    spring.freemarker.allow-request-override=false
    #本机调试时,配置项template_update_delay=0,这样就关闭了模板缓存。注意线上环境要开启缓存
    spring.freemarker.cache=false
    spring.freemarker.settings.template_update_delay=0
    spring.freemarker.check-template-location=true
    spring.freemarker.charset=UTF-8
    spring.freemarker.content-type=text/html
    spring.freemarker.expose-request-attributes=false
    spring.freemarker.expose-session-attributes=false
    spring.freemarker.expose-spring-macro-helpers=false
    spring.freemarker.prefix=
    #若在freemarker获取request对象,在spring boot 在application.properties可以这么配置
    spring.freemarker.request-context-attribute=request
    #spring.freemarker.settings.*=
    spring.freemarker.suffix=.ftl
    #template-loader-path表示所有的模板文件都放在该目录下
    spring.freemarker.template-loader-path=classpath:/templates/ 
    #spring.freemarker.view-names= #whitelistofviewnamesthatcanberesolved
    
    #static-locations可以自定义静态资源路径,不过会覆盖springboot默认路径
    #在这个最末尾的file:${web.upload-path}之所有要加file:是因为指定的是一个具体的硬盘路径,其他的使用classpath指的是系统环境变量
    #spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/,file:${web.upload-path}
    spring.resources.static-locations=classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/
    
    spring.freemarker.settings.auto_import=common/common.ftl as com
    spring.freemarker.settings.datetime_format=yyyy-MM-dd
    #兼容传统模式
    spring.freemarker.settings.classic_compatible=true
    #表示访问该路径时代表请求静态资源,用户可以直接访问该请求路径中的静态资源
    spring.mvc.static-path-pattern=/static/**
    server.port=8080
    server.context-path=/test
    server.session.timeout=10000

     4.4 编写测试页面:test.ftl

    <@com.head title="">
        <base id="base" href="${basePath!}">
        <link href="${basePath!}/static/plugins/layui/css/layui.css" type="text/css" media="screen" rel="stylesheet"/>
        <script src="${basePath!}/static/plugins/layui/layui.js" type="text/javascript"></script>
    
    <script>
        //一般直接写在一个js文件中
        layui.use(['layer', 'form','table'], function(){
            var layer = layui.layer
                ,form = layui.form
                ,$ = layui.$
                ,laytpl = layui.laytpl
                ,table = layui.table;
    
            var tableTClass =table.render({
                id: 'idTClass'
                ,elem: '#TClass'
                ,height: 315
                ,url: '${basePath!}/tclass/queryTClassList' //数据接口
                ,page: true //开启分页
                ,cols: [[ //表头
                    {type:'numbers', title: '序号', 80,sort: true}
                    ,{field: 'id', title: 'ID', 300, unresize:true}
                    ,{field: 'name', title: '班级名称', 200,unresize:true}
                    ,{fixed: 'right', 150, align:'center', toolbar: '#barDemo'} //这里的toolbar值是模板元素的选择器
                ]],
                method: 'post',
                request: {
                    pageName: 'page' //页码的参数名称,默认:page
                    ,limitName: 'rows' //每页数据量的参数名,默认:limit
                },
                response: {
                    statusName: 'code'
                    ,statusCode: 0
                    ,countName: 'total' //数据总数的字段名称,默认:count
                    ,dataName: 'list' //数据列表的字段名称,默认:data
                },
                skin: 'line' //行边框风格
            });
    
            var TClassInsertLayerIndex;
    
            //新建
            $("#TClassInsert").click(function(){
                //置空表单
                $("#TClassInsertForm").find(":input[name='name']").val("");
                $("#TClassInsertForm").find(":input[name='id']").val("");
                TClassInsertLayerIndex = layer.open({
                    title:"新建",
                    type: 1,
                    content: $('#TClassInsertDiv')
                });
            });
    
            form.on('submit(TClassInsertFormSubmit)', function(data){
                $.ajax({
                    type: "POST",
                    url: "${basePath!}/tclass/saveOrUpdateTClass",
                    data: $("#TClassInsertForm").serialize(),
                    async: false,
                    error: function (request) {
                        layer.alert("与服务器连接失败/(ㄒoㄒ)/~~");
                        return false;
                    },
                    success: function (data) {
                        if (data.state == 'fail') {
                            layer.alert(data.message);
                            layer.close(TClassInsertLayerIndex);
                            return false;
                        }else if(data.state == 'success'){
                            layer.alert(data.message);
                            layer.close(TClassInsertLayerIndex);
                            tableTClass.reload({
                                where: { //设定异步数据接口的额外参数,任意设
                                    /*aaaaaa: 'xxx'
                                    ,bbb: 'yyy'*/
                                    //
                                }
                                ,page: {
                                    curr: 1 //重新从第 1 页开始
                                }
                            });
                        }
                    }
                });
    
                return false; //阻止表单跳转。如果需要表单跳转,去掉这段即可。
            });
    
    
            //监听工具条
            table.on('tool(TClass)', function(obj){ //注:tool是工具条事件名,TClass是table原始容器的属性 lay-filter="对应的值"
                var data = obj.data; //获得当前行数据
                var layEvent = obj.event; //获得 lay-event 对应的值(也可以是表头的 event 参数对应的值)
                var tr = obj.tr; //获得当前行 tr 的DOM对象
    
                if(layEvent === 'detail'){ //查看
                    //do somehing
                } else if(layEvent === 'del'){ //删除
                    layer.confirm('真的删除该行数据吗', function(index){
                        obj.del(); //删除对应行(tr)的DOM结构,并更新缓存
                        layer.close(index);
                        //向服务端发送删除指令
                        $.ajax({
                            type: "POST",
                            url: "${basePath!}/tclass/deleteTClass",
                            data: {id:data.id},
                            async: false,
                            error: function (request) {
                                layer.alert("与服务器连接失败/(ㄒoㄒ)/~~");
                                return false;
                            },
                            success: function (data) {
                                if (data.state == 'fail') {
                                    layer.alert(data.message);
                                    return false;
                                }else if(data.state == 'success'){
                                }
                            }
                        });
                    });
                } else if(layEvent === 'edit'){ //编辑
                    //do something
                    //置空表单
                    $("#TClassInsertForm").find(":input[name='name']").val("");
                    $("#TClassInsertForm").find(":input[name='id']").val("");
                    //添加值
                    $("#TClassInsertForm").find(":input[name='name']").val(data.name);
                    $("#TClassInsertForm").find(":input[name='id']").val(data.id);
                    TClassInsertLayerIndex = layer.open({
                        title:"编辑",
                        type: 1,
                        content: $('#TClassInsertDiv')
                    });
                }
            });
    
    
    
    
    
    
        });
    </script>
    
    </@com.head>
    <@com.body>
    
    
    
    <fieldset class="layui-elem-field">
        <legend>班级信息</legend>
        <div class="layui-field-box">
            <div class="layui-fluid">
                <div class="layui-row">
                    <button class="layui-btn" id="TClassInsert">新建</button>
                </div>
                <div class="layui-row">
                    <table id="TClass" lay-filter="TClass"></table>
                </div>
            </div>
        </div>
    </fieldset>
    
    
    <div id="TClassInsertDiv" style="display: none">
        <form class="layui-form" action="" id="TClassInsertForm">
            <input type="hidden" id="TClassInsertFormId" name="id"/>
            <div class="layui-form-item">
                <label class="layui-form-label">班级名称</label>
                <div class="layui-input-block">
                    <input type="text" name="name" required  lay-verify="required" placeholder="请输入班级名称" autocomplete="off" class="layui-input">
                </div>
            </div>
            <div class="layui-form-item">
                <div class="layui-input-block">
                    <button class="layui-btn" lay-submit lay-filter="TClassInsertFormSubmit">立即提交</button>
                    <button type="reset" class="layui-btn layui-btn-primary" id="TClassInsertFormReset">重置</button>
                </div>
            </div>
        </form>
    </div>
    
    
    <script type="text/html" id="barDemo">
        <#--<a class="layui-btn layui-btn-xs" lay-event="detail">查看</a>-->
        <a class="layui-btn layui-btn-xs" lay-event="edit">编辑</a>
        <a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del">删除</a>
    </script>
    
    <!-- 序号监听事件 -->
    <script type="text/html" id="indexTpl">
        {{d.LAY_TABLE_INDEX+1}}
    </script>
    
    </@com.body>

    测试:

    1.基本业务增删改查测试

    启动项目成功后,输入地址:http://localhost:8080/test/toTest

    得到如下页面,可以进行班级的增删改查,并进行分页:

    这里我解释一下,是如何做到分页的,我们找到TClassServiceImpl.java,找到queryTClassList(Page<QueryTClassList> page)方法 :

    @Override
        public List<TClass> queryTClassList(Page<QueryTClassList> page) {
            PageHelper.startPage(page.getPage(), page.getRows());
    //        return tclassMapper.queryTClassList(page.getCondition());
            return tclassMapper.selectAll();
        }

     我们会发现:PageHelper.startPage(page.getPage(), page.getRows());设置了请求页码和每页记录数,然后在这句仅接着的下一句执行select请求就会执行分页,记住必须是紧挨着的下一句;(原理不懂的话请移步官方文档)

    2.druid监控

    地址栏输入:http://localhost:8080/test/druid

    可以看到十分详细的sql监控信息

    3.事务测试:

     我们在TClassServiceImpl.java的saveOrUpdateTClass方法中,在执行更新的代码之后,人工制造一个RuntimeException ,来验证其事务是否回滚;

     

    再执行编辑操作:

    编辑原来的一班,将一班名称改为一班121,点击提交:

    如果你debug的话,会发现1/0抛出运行时异常后,进入:

    回滚事务,并在controller被catch,并返回前端错误提示;

    检查数据库发现一班并没有改变名称:

    在druid中也可以发现本次更新操作的事务回滚:

    至此,事务已经证明生效了;大家可以自己尝试一下;


     后记

    本文项目源代码(含mysql数据库脚本):

    https://github.com/zhaojiatao/springboot-zjt-chapter08-springboot-mybatis

    相关文档(强烈推荐大家先阅读相关文档之后再动手整合,否则一些细节可能不了解,以后也是个坑):

    通用mapper文档:https://gitee.com/free/Mapper

    pagehelper文档:https://github.com/pagehelper/Mybatis-PageHelper/blob/master/wikis/zh/HowToUse.md

    layui文档:http://www.layui.com/doc/

  • 相关阅读:
    为什么硬链接不能链接目录、文件inode 和目录 dentry 的区别联系
    LVM 详解
    pwd 命令详解
    type 命令详解
    查看文件中字符出现次数
    lesson
    xml linq
    新系统配置
    空合并运算符(??):
    dos.ORM配置和使用
  • 原文地址:https://www.cnblogs.com/zhaojiatao/p/8392071.html
Copyright © 2020-2023  润新知