Skip to content

Latest commit

 

History

History
385 lines (315 loc) · 16.3 KB

框架整合.md

File metadata and controls

385 lines (315 loc) · 16.3 KB

项目构建

项目结构

  1. service 层(业务逻辑层)

  2. dao 层(数据持久层)

  3. common 层(公共组建层)

  4. web 层(请求处理层)

    note: service 层依赖 dao 层和 common 层,web 层依赖于 service 层

pom 文件

  1. 父 pom 原则上都是通过 标签进行依赖包的管理
  2. spring-boot-maven-plugin 插件的作用是打包一个可运行的包,多模块项目仅仅需要在入口类所在的模块添加打包插件,父模块不需要打包运行,因此删除该插件。
  3. dependencies 与 dependencyManagement
    • Maven 通常使用 dependencyManagement 标签提供一种管理依赖的方式,通常出现在父 pom 文件中。这样可以确保各个项目的依赖和版本是一致的,同时,对于依赖版本的修改,只需要在父类容器中修改即可,而不需要逐一修改每个子项目的依赖。如果子项目想要另外一个依赖版本时,只需要修改 标签下的版本号即可,这样会使得子项目优先选择设置了版本号的依赖。
    • 相对于 ,所有声明在 里的依赖都会自动引入,并且默认被所有的子项目继承。

Mybatis Plus

  1. Spring Boot集成 MyBatis Plus

    • 添加对应的 Maven 依赖

    • 添加扫描包配置类

      import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
      import org.mybatis.spring.annotation.MapperScan;
      import org.springframework.context.annotation.Bean;
      import org.springframework.context.annotation.Configuration;
      import org.springframework.transaction.annotation.EnableTransactionManagement;
      
      @EnableTransactionManagement
      @Configuration // 标记为配置类
      @MapperScan("com.zhuawa.course.persistence.user.dao") // 要扫描的包
      public class MybatisPlusConfig {
          @Bean
          public PaginationInterceptor paginationInterceptor() {
              PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
              // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
              // paginationInterceptor.setOverflow(false);
              // 设置最大单页限制数量,默认 500 条,-1 不受限制
              // paginationInterceptor.setLimit(500);
              return paginationInterceptor;
          }
      }
    • 修改配置文件

  2. MyBatis Plus 代码生成器

    • 自动生成 mapper.xml、Mapper.java POJO、dao、service、controller等文件
    import com.baomidou.mybatisplus.annotation.DbType;
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.generator.AutoGenerator;
    import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
    import com.baomidou.mybatisplus.generator.config.GlobalConfig;
    import com.baomidou.mybatisplus.generator.config.PackageConfig;
    import com.baomidou.mybatisplus.generator.config.StrategyConfig;
    import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
    
    public class MyBatisPlusGenerator {
        public static void main(String[] args) {
            // 1. 全局配置
            GlobalConfig config = new GlobalConfig();
            // 是否支持AR模式
            config.setActiveRecord(true)
                    // 作者
                    .setAuthor("mybatis-plus")
                    // TODO 生成路径
                    .setOutputDir("/Users/apple/projects/temp/")
                    // 文件覆盖
                    .setFileOverride(true)
                    // 主键策略
                    .setIdType(IdType.AUTO)
                    // 设置生成的service接口的名字的首字母是否为I,如IEmployeeService
                    .setServiceName("%sService")
                    // 生成基本的resultMap
                    .setBaseResultMap(true)
                    .setBaseColumnList(true)
                    // 生成基本的SQL片段p
                    .setEnableCache(false);
    
            // 2. 数据源配置
            DataSourceConfig dsConfig = new DataSourceConfig();
            dsConfig.setDbType(DbType.MYSQL)
                    // 设置数据库类型
                    .setDriverName("com.mysql.jdbc.Driver")
                    // TODO 数据库连接
                    .setUrl("jdbc:mysql://localhost:3306/effectdb?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=true")
                    // TODO 用户名
                    .setUsername("root")
                    // TODO 密码
                    .setPassword("root@1234");
    
            // 3. 策略配置globalConfiguration中
            StrategyConfig stConfig = new StrategyConfig();
            stConfig.setCapitalMode(true)
                    // 全局大写命名
                    // .setDbColumnUnderline(true)
                    // 指定表名 字段名是否使用下划线
                    // 数据库表映射到实体的命名策略
                    .setNaming(NamingStrategy.underline_to_camel)
                     .setTablePrefix("tb_")
                    // 表的前缀
                    // TODO 要生成的表
                    .setInclude("user")
                    .setEntityLombokModel(false)
                    .setRestControllerStyle(true);
    
            // 4. 包名策略配置
            PackageConfig pkConfig = new PackageConfig();
            pkConfig.setParent("com.zhuawa.course.dao.user")
                    // dao
                    .setMapper("dao")
                    // servcie
    //                .setService("service")
                    // controller
    //                .setController("controller")
                    .setEntity("entity")
                    // mapper.xml
                    .setXml("mapper");
    
            // 5. 整合配置
            AutoGenerator ag = new AutoGenerator();
            ag.setGlobalConfig(config)
                    .setDataSource(dsConfig)
                    .setStrategy(stConfig)
                    .setPackageInfo(pkConfig);
    
            // 6. 执行
            ag.execute();
        }
    
    }
  3. MyBatis Plus AR

    提供 ActiveRecord 的支持,实体类只需继承 Model 类即可实现基本的 CRUD 操作。

    package com.zhuawa.course.biz.user;
    
    import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
    import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
    import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
    import com.zhuawa.course.persistence.user.dao.RoleMapper;
    import com.zhuawa.course.persistence.user.dao.UserMapper;
    import com.zhuawa.course.persistence.user.entity.User;
    import org.junit.runner.RunWith;
    import org.springframework.boot.test.context.SpringBootTest;
    import org.springframework.test.context.junit4.SpringRunner;
    import org.springframework.util.CollectionUtils;
    import org.testng.Assert;
    import org.testng.annotations.Test;
    
    import java.util.List;
    
    import javax.annotation.Resource;
    
    
    @RunWith(SpringRunner.class)
    @SpringBootTest
    public class WrapperTest {
    
        @Resource
        private UserMapper userMapper;
        @Resource
        private RoleMapper roleMapper;
    
        @Test
        public void tests() {
            System.out.println("----- 普通查询 ------");
            List<User> plainUsers = userMapper.selectList(new QueryWrapper<User>().eq("role_id", 2L));
            List<User> lambdaUsers = userMapper.selectList(new QueryWrapper<User>().lambda().eq(User::getRoleId, 2L));
            Assert.assertEquals(plainUsers.size(), lambdaUsers.size());
            print(plainUsers);
    
            System.out.println("----- 带子查询(sql注入) ------");
            List<User> plainUsers2 = userMapper.selectList(new QueryWrapper<User>()
                    .inSql("role_id", "select id from role where id = 2"));
            List<User> lambdaUsers2 = userMapper.selectList(new QueryWrapper<User>().lambda()
                    .inSql(User::getRoleId, "select id from role where id = 2"));
            Assert.assertEquals(plainUsers2.size(), lambdaUsers2.size());
            print(plainUsers2);
    
            System.out.println("----- 带嵌套查询 ------");
            List<User> plainUsers3 = userMapper.selectList(new QueryWrapper<User>()
                    .nested(i -> i.eq("role_id", 2L).or().eq("role_id", 3L))
                    .and(i -> i.ge("age", 20)));
            List<User> lambdaUsers3 = userMapper.selectList(new QueryWrapper<User>().lambda()
                    .nested(i -> i.eq(User::getRoleId, 2L).or().eq(User::getRoleId, 3L))
                    .and(i -> i.ge(User::getAge, 20)));
            Assert.assertEquals(plainUsers3.size(), lambdaUsers3.size());
            print(plainUsers3);
    
            System.out.println("----- 自定义(sql注入) ------");
            List<User> plainUsers4 = userMapper.selectList(new QueryWrapper<User>()
                    .apply("role_id = 2"));
            print(plainUsers4);
    
            UpdateWrapper<User> uw = new UpdateWrapper<>();
            uw.set("email", null);
            uw.eq("id", 4);
            userMapper.update(new User(), uw);
            User u4 = userMapper.selectById(4);
            Assert.assertNull(u4.getEmail());
    
    
        }
    
        @Test
        public void lambdaQueryWrapper() {
            System.out.println("----- 普通查询 ------");
            List<User> plainUsers = userMapper.selectList(new LambdaQueryWrapper<User>().eq(User::getRoleId, 2L));
            List<User> lambdaUsers = userMapper.selectList(new QueryWrapper<User>().lambda().eq(User::getRoleId, 2L));
            Assert.assertEquals(plainUsers.size(), lambdaUsers.size());
            print(plainUsers);
    
            System.out.println("----- 带子查询(sql注入) ------");
            List<User> plainUsers2 = userMapper.selectList(new LambdaQueryWrapper<User>()
                    .inSql(User::getRoleId, "select id from role where id = 2"));
            List<User> lambdaUsers2 = userMapper.selectList(new QueryWrapper<User>().lambda()
                    .inSql(User::getRoleId, "select id from role where id = 2"));
            Assert.assertEquals(plainUsers2.size(), lambdaUsers2.size());
            print(plainUsers2);
    
            System.out.println("----- 带嵌套查询 ------");
            List<User> plainUsers3 = userMapper.selectList(new LambdaQueryWrapper<User>()
                    .nested(i -> i.eq(User::getRoleId, 2L).or().eq(User::getRoleId, 3L))
                    .and(i -> i.ge(User::getAge, 20)));
            List<User> lambdaUsers3 = userMapper.selectList(new QueryWrapper<User>().lambda()
                    .nested(i -> i.eq(User::getRoleId, 2L).or().eq(User::getRoleId, 3L))
                    .and(i -> i.ge(User::getAge, 20)));
            Assert.assertEquals(plainUsers3.size(), lambdaUsers3.size());
            print(plainUsers3);
    
            System.out.println("----- 自定义(sql注入) ------");
            List<User> plainUsers4 = userMapper.selectList(new QueryWrapper<User>()
                    .apply("role_id = 2"));
            print(plainUsers4);
    
            UpdateWrapper<User> uw = new UpdateWrapper<>();
            uw.set("email", null);
            uw.eq("id", 4);
            userMapper.update(new User(), uw);
            User u4 = userMapper.selectById(4);
            Assert.assertNull(u4.getEmail());
        }
    
        private <T> void print(List<T> list) {
            if (!CollectionUtils.isEmpty(list)) {
                list.forEach(System.out::println);
            }
        }
    
        /**
         * SELECT id,name,age,email,role_id FROM user
         * WHERE ( 1 = 1 ) AND ( ( name = ? AND age = ? ) OR ( name = ? AND age = ? ) )
         */
        @Test
        public void testSql() {
            QueryWrapper<User> w = new QueryWrapper<>();
            w.and(i -> i.eq("1", 1))
                    .nested(i ->
                            i.and(j -> j.eq("name", "a").eq("age", 2))
                                    .or(j -> j.eq("name", "b").eq("age", 2)));
            userMapper.selectList(w);
        }
    }
  4. MyBatis Plus 优化

    • 日志

      • Spring 默认使用 Logback 作为日志打印,对应 resource/logback.xml

        <?xml version="1.0" encoding="UTF8"?>
        <configuration>
            <property name="LOG_DIR" value="./log"/>
            <property name="PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{39} - %msg%n"/>
        
            <appender name="ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
                <file>${LOG_DIR}/error.log</file>
                <encoder>
                    <charset>utf-8</charset>
                    <pattern>${PATTERN}</pattern>
                </encoder>
                <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
                    <level>ERROR</level>
                </filter>
                <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
                    <fileNamePattern>${LOG_DIR}/error-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
                    <!--保存日志天数-->
                    <maxHistory>7</maxHistory>
                    <!--单个日志文件大小-->
                    <maxFileSize>200MB</maxFileSize>
                    <totalSizeCap>10GB</totalSizeCap>
                </rollingPolicy>
            </appender>
        
            <appender name="INFO-APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender">
                <!--最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动&ndash;&gt;-->
                <file>${LOG_DIR}/info.log</file>
                <encoder>
                    <charset>utf-8</charset>
                    <pattern>${PATTERN}</pattern>
                </encoder>
                <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
                    <level>INFO</level>
                </filter>
                <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
                    <fileNamePattern>${LOG_DIR}/info-%d{yyyy-MM-dd}-%i.log</fileNamePattern>
                    <!--保存日志天数-->
                    <maxHistory>7</maxHistory>
                    <!--单个日志文件大小-->
                    <maxFileSize>200MB</maxFileSize>
                    <totalSizeCap>10GB</totalSizeCap>
                </rollingPolicy>
            </appender>
            <!-- 控制台输出 -->
            <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
                <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
                    <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
                    <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{39} - %msg%n</pattern>
                </encoder>
            </appender>
        
            <!-- 下面的logger需要修改为你的项目根路径,如com.xxx.uc -->
            <Logger name="com.zhuawa.course" level="debug" additivity="false">
                <appender-ref ref="STDOUT"/>
            </Logger>
        
            <!--这个是自定义日志类型SQLTRACE-->
            <logger name="SQLTRACE" level="info" additivity="false">
                <appender-ref ref="STDOUT"/>
            </logger>
        
        
            <root level="info">
                <appender-ref ref="ERROR"/>
                <appender-ref ref="STDOUT"/>
            </root>
        </configuration>
        
      • log4j 无法实现热加载

      • log4j2 缺少滚动策略

    • 分页

      加入 Page 对象,使用 slectPage()

    • 动态数据源:减少相关数据交互的压力;业务需求多个数据源(分析型数据库、数据存储数据库)。

  5. 数据库连接池

    • 为什么使用
      • 所有的数据库连接都遵守基本的设计规则,实现javax.sql.DataSource 接口,使用 getConnection() 方法获取一个连接, 该连接是一个 TCP 连接,这个连接的建立比较耗时。
      • 通过数据库连接池将建立好的连接缓存起来,可以减少由于建立 TCP 连接而造成的耗时。
      • 数据库连接池可以检测异常的连接,并释放它。
    • HikariCP
      • 特性
        • 代码少且精简
        • 性能
        • 稳定:等待 5 s,如果连接未恢复,则抛出异常。
      • 配置
        • 连接数:核心数*2 + 有效磁盘数