-
service 层(业务逻辑层)
-
dao 层(数据持久层)
-
common 层(公共组建层)
-
web 层(请求处理层)
note: service 层依赖 dao 层和 common 层,web 层依赖于 service 层
- 父 pom 原则上都是通过 标签进行依赖包的管理
- spring-boot-maven-plugin 插件的作用是打包一个可运行的包,多模块项目仅仅需要在入口类所在的模块添加打包插件,父模块不需要打包运行,因此删除该插件。
- dependencies 与 dependencyManagement
- Maven 通常使用 dependencyManagement 标签提供一种管理依赖的方式,通常出现在父 pom 文件中。这样可以确保各个项目的依赖和版本是一致的,同时,对于依赖版本的修改,只需要在父类容器中修改即可,而不需要逐一修改每个子项目的依赖。如果子项目想要另外一个依赖版本时,只需要修改 标签下的版本号即可,这样会使得子项目优先选择设置了版本号的依赖。
- 相对于 ,所有声明在 里的依赖都会自动引入,并且默认被所有的子项目继承。
-
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; } }
-
修改配置文件
-
-
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(); } }
-
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); } }
-
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"> <!--最常用的滚动策略,它根据时间来制定滚动策略,既负责滚动也负责出发滚动–>--> <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()
-
动态数据源:减少相关数据交互的压力;业务需求多个数据源(分析型数据库、数据存储数据库)。
-
-
数据库连接池
- 为什么使用
- 所有的数据库连接都遵守基本的设计规则,实现
javax.sql.DataSource
接口,使用getConnection()
方法获取一个连接, 该连接是一个 TCP 连接,这个连接的建立比较耗时。 - 通过数据库连接池将建立好的连接缓存起来,可以减少由于建立 TCP 连接而造成的耗时。
- 数据库连接池可以检测异常的连接,并释放它。
- 所有的数据库连接都遵守基本的设计规则,实现
HikariCP
- 特性
- 代码少且精简
- 性能
- 稳定:等待 5 s,如果连接未恢复,则抛出异常。
- 配置
- 连接数:核心数*2 + 有效磁盘数
- 特性
- 为什么使用