项目示例代码:
依赖引入
mysql驱动引入
<!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql.version}</version>
</dependency>
mybatis-plus驱动引入
官网:mybatis-plus
介绍:
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本CURD,性能基本无损耗,直接面向对象操作
- 强大的 CRUD 操作:内置通用 Mapper、通用Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
- 支持 Lambda形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
- 支持主键自动生成:支持多达 4种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
- 支持 ActiveRecord 模式:支持ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
- 支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
- 内置代码生成器:采用代码或者Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller
层代码,支持模板引擎,更有超多自定义配置等您来使用 - 内置分页插件:基于 MyBatis物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
- 分页插件支持多种数据库:支持MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
- 内置性能分析插件:可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
- 内置全局拦截插件:提供全表 delete、update 操作智能分析阻断,也可自定义拦截规则,预防误操作
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
根目录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>org.example</groupId>
<artifactId>Hello</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>demo</module>
</modules>
<properties>
<spring-framework.version>5.3.39</spring-framework.version>
<spring-security.version>5.8.15</spring-security.version>
<spring-boot.version>2.7.18</spring-boot.version>
<lombok.version>1.18.34</lombok.version>
<mybatis-plus.version>3.5.9</mybatis-plus.version>
<druid.version>1.2.23</druid.version>
<mysql.version>9.1.0</mysql.version>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencyManagement>
<dependencies>
<!-- SpringBoot的依赖配置-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>${spring-boot.version}</version>
<!-- <type>pom</type>-->
<!-- <scope>import</scope>-->
</dependency>
<!-- <!– SpringFramework的依赖配置–>-->
<!-- <dependency>-->
<!-- <groupId>org.springframework</groupId>-->
<!-- <artifactId>spring-framework-bom</artifactId>-->
<!-- <version>${spring-framework.version}</version>-->
<!-- <type>pom</type>-->
<!-- <scope>import</scope>-->
<!-- </dependency>-->
<!-- <!– SpringSecurity的依赖配置–>-->
<!-- <dependency>-->
<!-- <groupId>org.springframework.security</groupId>-->
<!-- <artifactId>spring-security-web</artifactId>-->
<!-- <version>${spring-security.version}</version>-->
<!-- <type>pom</type>-->
<!-- <scope>import</scope>-->
<!-- </dependency>-->
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<scope>provided</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus.version}</version>
</dependency>
<!-- 阿里数据库连接池 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>${mysql.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<repositories>
<repository>
<id>public</id>
<name>aliyun nexus</name>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>public</id>
<name>aliyun nexus</name>
<url>https://maven.aliyun.com/repository/public</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
</project>
项目配置
application.yml
# 开发环境配置
server:
# 服务器的HTTP端口,默认为8080
port: 9090
servlet:
# 应用的访问路径
context-path: /
tomcat:
# tomcat的URI编码
uri-encoding: UTF-8
# 连接数满后的排队数,默认为100
accept-count: 1000
threads:
# tomcat最大线程数,默认为200
max: 800
# Tomcat启动初始化的线程数,默认值10
min-spare: 100
# 日志配置
logging:
level:
org.springframework: warn
# MyBatis配置
mybatis-plus:
# 搜索指定包别名
typeAliasesPackage: org.web.domain
# 配置mapper的扫描,找到所有的mapper.xml映射文件
mapperLocations: classpath*:mybatis/mapper/**/*Mapper.xml
# 加载全局的配置文件
configLocation: classpath:mybatis/mybatis-config.xml
# 数据源配置
spring:
datasource:
druid:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/yootk?useSSL=false&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
username: root
password: root
max-wait: 1000
remove-abandoned-timeout: 580
log-abandoned: true
test-on-borrow: false
test-on-return: false
remove-abandoned: true
# 初始连接数
initialSize: 5
# 最小连接池数量
minIdle: 10
# 最大连接池数量
maxActive: 20
# 配置获取连接等待超时的时间
maxWait: 60000
# 配置连接超时时间
connectTimeout: 30000
# 配置网络超时时间
socketTimeout: 60000
# 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
timeBetweenEvictionRunsMillis: 60000
# 配置一个连接在池中最小生存的时间,单位是毫秒
minEvictableIdleTimeMillis: 300000
# 配置一个连接在池中最大生存的时间,单位是毫秒
maxEvictableIdleTimeMillis: 900000
logback.xml
以我自己的理解,logback.xml在项目中有很大的作用,可以对不同包进行不同级别的日志输出,在上线的项目中,可以通过来查看生成的log文件来发现问题。
logback.xml放在resource目录下SpringBoot自动装配使用
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
<contextName>logback</contextName>
<!-- 定义控制台输出匹配格式 -->
<substitutionProperty name="logging.pattern.console"
value="%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr([%X{requestId}]) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%ewtpc}"/>
<!-- 定义日志文件输出匹配格式 -->
<substitutionProperty name="logging.pattern.file"
value="%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} %clr([%X{requestId}]) ${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%ewtpc}"/>
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<conversionRule conversionWord="wtpc" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
<conversionRule conversionWord="ewtpc" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
<appender name="console" class="ch.qos.logback.core.ConsoleAppender"> <!-- 控制台输出 -->
<layout class="ch.qos.logback.classic.PatternLayout"> <!-- 使用layout节点 -->
<pattern>${logging.pattern.console}</pattern> <!-- 格式引用 -->
</layout>
</appender>
<!-- 将每天的日志保存在一个文件之中 -->
<appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
<Prudent>true</Prudent>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 设置日志保存路径,本次按照月份创建日志目录,而后每天的文件归档到一组 -->
<FileNamePattern>tang-logs/%d{yyyy-MM}/tang_%d{yyyy-MM-dd}.log</FileNamePattern>
<MaxHistory>30</MaxHistory><!-- 删除超过365天的日志文件 -->
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>INFO</level> <!-- 保存ERROR及以上级别的日志 -->
</filter>
<encoder>
<Pattern>${logging.pattern.file}</Pattern> <!-- 格式引用 -->
</encoder>
</appender>
<root level="INFO"> <!-- 全局日志级别 -->
<appender-ref ref="console"/>
<appender-ref ref="file"/>
</root>
<appender name="druidSqlFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
<Prudent>true</Prudent>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 设置日志保存路径,本次按照月份创建日志目录,而后每天的文件归档到一组 -->
<FileNamePattern>
druid-logs/%d{yyyy-MM}/druid_slow_sql_%d{yyyy-MM-dd}.log
</FileNamePattern>
<MaxHistory>30</MaxHistory> <!-- 删除超过365天的日志文件 -->
</rollingPolicy>
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level> <!-- 保存ERROR及以上级别的日志 -->
</filter>
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<pattern>
%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger Line:%-3L - %msg%n
</pattern>
<charset>utf-8</charset>
</encoder>
</appender>
<logger name="com.alibaba.druid.filter.stat.StatFilter" level="ERROR">
<appender-ref ref="druidSqlFile"/>
</logger>
<logger name="org.web.mapper" level="DEBUG"/>
</configuration>
mybatis-config.xml
Mybatis-Plus配置文件
在application.yml中定义配置
<?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" />
<!-- 允许JDBC 支持自动生成主键 -->
<setting name="useGeneratedKeys" value="true" />
<!-- 配置默认的执行器.SIMPLE就是普通执行器;REUSE执行器会重用预处理语句(prepared statements);BATCH执行器将重用语句并执行批量更新 -->
<setting name="defaultExecutorType" value="SIMPLE" />
<!-- 指定 MyBatis 所用日志的具体实现 -->
<setting name="logImpl" value="SLF4J" />
<!-- 使用驼峰命名法转换字段 -->
<!-- <setting name="mapUnderscoreToCamelCase" value="true"/> -->
</settings>
</configuration>
项目结构
在domain中定义实体类与数据库字段映射,mapper与数据库操作实现映射,service主要是业务代码实现。
使用MybatisX生成代码
-
idea安装插件
-
创建数据库,导入sql文件
-
点击数据源
-
选择mysql
-
设置用户名密码,并测试连接
-
连接并选择数据库
-
右键数据表使用插件
-
设置生成配置
点击finish生成
查看插件生成代码(主要部分)
- domain包下Project类
package org.web.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import lombok.Data;
/**
*
* @TableName project
*/
@TableName(value ="project")
@Data
public class Project implements Serializable {
/**
* 项目ID
*/
@TableId(value = "pid", type = IdType.AUTO)
private Long pid;
/**
* 项目名称
*/
@TableField(value = "name")
private String name;
/**
* 项目主管
*/
@TableField(value = "charge")
private String charge;
/**
* 项目描述
*/
@TableField(value = "note")
private String note;
/**
* 项目状态
*/
@TableField(value = "status")
private Integer status;
@TableField(exist = false)
private static final long serialVersionUID = 1L;
@Override
public boolean equals(Object that) {
if (this == that) {
return true;
}
if (that == null) {
return false;
}
if (getClass() != that.getClass()) {
return false;
}
Project other = (Project) that;
return (this.getPid() == null ? other.getPid() == null : this.getPid().equals(other.getPid()))
&& (this.getName() == null ? other.getName() == null : this.getName().equals(other.getName()))
&& (this.getCharge() == null ? other.getCharge() == null : this.getCharge().equals(other.getCharge()))
&& (this.getNote() == null ? other.getNote() == null : this.getNote().equals(other.getNote()))
&& (this.getStatus() == null ? other.getStatus() == null : this.getStatus().equals(other.getStatus()));
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + ((getPid() == null) ? 0 : getPid().hashCode());
result = prime * result + ((getName() == null) ? 0 : getName().hashCode());
result = prime * result + ((getCharge() == null) ? 0 : getCharge().hashCode());
result = prime * result + ((getNote() == null) ? 0 : getNote().hashCode());
result = prime * result + ((getStatus() == null) ? 0 : getStatus().hashCode());
return result;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(getClass().getSimpleName());
sb.append(" [");
sb.append("Hash = ").append(hashCode());
sb.append(", pid=").append(pid);
sb.append(", name=").append(name);
sb.append(", charge=").append(charge);
sb.append(", note=").append(note);
sb.append(", status=").append(status);
sb.append(", serialVersionUID=").append(serialVersionUID);
sb.append("]");
return sb.toString();
}
}
- ProjectMapper.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="org.web.mapper.ProjectMapper">
<resultMap id="BaseResultMap" type="org.web.domain.Project">
<id property="pid" column="pid" jdbcType="BIGINT"/>
<result property="name" column="name" jdbcType="VARCHAR"/>
<result property="charge" column="charge" jdbcType="VARCHAR"/>
<result property="note" column="note" jdbcType="VARCHAR"/>
<result property="status" column="status" jdbcType="INTEGER"/>
</resultMap>
<sql id="Base_Column_List">
pid,name,charge,
note,status
</sql>
</mapper>
在Controller中的实现
ProjectController类
package org.web.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.web.domain.Project;
import org.web.service.ProjectService;
import org.web.utils.AjaxResult;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/project/*")
public class ProjectController {
@Autowired
private ProjectService projectService;
@GetMapping("list")
public AjaxResult list(){
return AjaxResult.success(projectService.list());
}
@GetMapping("listByPid/{pid}")
public AjaxResult listByPid(@PathVariable Long pid){
return AjaxResult.success(projectService.selectByPid(pid));
}
@GetMapping("listByPids")
public AjaxResult listByPids(@RequestBody List<Long> pids){
return AjaxResult.success(projectService.listByIds(pids));
}
@GetMapping("listByLikeName")
public AjaxResult listByLikeName(@RequestParam String name){
return AjaxResult.success(projectService.selectByLikeName(name));
}
@PostMapping("add")
public AjaxResult add(@RequestBody Project project){
boolean save = projectService.save(project);
if (save){
return AjaxResult.success();
}else {
return AjaxResult.error();
}
}
@PutMapping("update")
public AjaxResult update(@RequestBody Project project){
boolean update = projectService.updateById(project);
if (update){
return AjaxResult.success();
}else {
return AjaxResult.error();
}
}
@DeleteMapping("delete/{pid}")
public AjaxResult delete(@PathVariable Long pid){
boolean delete = projectService.removeById(pid);
if (delete){
return AjaxResult.success();
}else {
return AjaxResult.error();
}
}
}
测试
add
list
listByPid
listByLikeName
update
delete
项目中遇到的问题及解决方案
使用阿里的德鲁伊连接池
从ruoyi复制的配置文件,即使配置没有问题,还是会爆识别不到url
解决办法:改成如下配置即可
总结:
MyBatis和MyBatis-Plus在项目中的意义
MyBatis在项目中的意义
- 简化数据库操作:MyBatis是一个开源的Java持久层框架,它允许开发者使用纯SQL语句进行数据库操作,从而简化了数据库操作的编写和管理。
- 提高开发效率:通过配置文件或注解,MyBatis可以轻松实现Java对象和数据库表之间的映射关系,降低了代码的维护成本,提高了开发效率。
- 灵活性和可扩展性:MyBatis提供了动态SQL、缓存机制、事务管理等丰富功能,以满足各种复杂的数据库操作需求。同时,其插件机制也为开发者提供了对SQL执行过程进行拦截和增强的能力。
MyBatis-Plus在项目中的意义
- 进一步增强MyBatis的功能:MyBatis-Plus是一个基于MyBatis的增强工具包,它提供了更多的便捷功能和增强特性,如通用CRUD操作、条件构造器、分页查询等,进一步简化了与关系型数据库的交互。
- 提高开发效率和质量:MyBatis-Plus通过封装MyBatis的底层操作,减少了开发者编写重复代码的工作量。同时,其提供的代码生成器工具可以根据数据库表自动生成实体类、Mapper接口和XML映射文件,进一步提高了开发效率。此外,MyBatis-Plus还支持逻辑删除、乐观锁等高级功能,有助于提升代码质量和系统的稳定性。
连接池技术的作用
- 减少连接的创建成本:数据库连接的创建涉及网络通信和鉴权等过程,是一项开销较大的操作。连接池可以有效地管理数据库连接的创建和复用,从而减少每次操作时创建连接的时间和资源消耗。
- 提高数据库访问性能:连接池可以对数据库连接进行池化和复用。当数据访问请求到达时,连接池会分配一个闲置的连接给该请求,避免了每次请求都重新创建连接的开销,从而大大提高了数据库访问的性能。
- 管理连接的超时和资源占用:连接池可以对连接的生命周期进行管理,通过设定连接的超时时间,可以防止连接长时间占用而导致资源浪费。同时,连接池还可以监控连接的状态,及时回收处于异常状态的连接,避免出现连接泄漏或无效连接的情况。
- 控制并发连接数:连接池可以限制并发连接的数量,避免过多的连接导致数据库的性能下降。连接池可以根据系统的负载情况动态调整连接池中的连接数量,以适应不同条件下的数据库访问需求。
- 提供连接可靠性和可用性:连接池具备连接自检和重连功能,当连接发生异常或失败时,连接池可以自动重试或重新建立连接,保证连接的可靠性和可用性。