MyBatis + MyBatisPlus

442 阅读20分钟

MyBatis

MyBatis相关网址

MyBatis官网:github.com/mybatis/myb…

MyBatis中文文档:mybatis.net.cn/

MyBatis的核心配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!--下面指定该xml文件的根标签为:configuration-->
<!DOCTYPE configuration					
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--configuration表示配置-->
<configuration>
    <!--enviroment表示环境,default属性指定默认使用的环境-->
    <environments default="development">
        <!--配置一个环境,一个环境对应一个数据库-->
        <environment id="development">
            <!--指定事务管理器,type属性可以有两个值:JDBC、MANAGED,具体查看MyBatis事务管理机制-->
            <transactionManager type="JDBC"/>
            <!--指定数据源,type属性指定数据源类型,type属性值:UNPOOLED | POOLED | JNDI-->
            <!--UNPOOLED:表示不使用数据库连接池技术,每一次请求都创建一个新的Connection对象-->
            <!--POOLED:表示使用MyBatis自己实现的数据库连接池-->
            <!--JNDI:表示使用其它第三方的数据库连接池-->
            <dataSource type="POOLED">
                <!--数据库连接池参数的配置-->
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3308/bjpowernode"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--sql映射⽂件创建好之后,需要将该⽂件路径配置到这⾥-->
        <mapper resource="CarMapper.xml"/>
    </mappers>
</configuration>

MyBatis的Mapper文件

<!--Bean属性名和表字段名的对应关系-->
<resultMap id="userMapper" type="User">
    <result property="id" column="id"/>
    <result property="name" column="name"/>
</resultMap>

<insert id="save" parameterType="User">
    insert into user(id, name)
    values (#{id}, #{name})
</insert>
``` ```

SpringBoot整合MyBatis

添加maven依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.17</version>
    <relativePath/>
</parent>


<dependencies>
    <!--添加spring-boot-starter-web依赖-->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.1.3</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.33</version>
    </dependency>
</dependencies>

yaml配置

server:
  port: 8900

mybatis:
  # 配置mapper映射文件
  mapper-locations: classpath:mybatis/mapper/*.xml
  # 配置mybatis配置文件
  config-location: classpath:mybatis/mybatis-config.xml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3308/test?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false
    username: root
    password: 123456

# 日志级别可以是:info、debug、error
logging:
  level:
    com.wenxuan.mapper: debug

mybatis配置文件

mybatis配置文件路径:/resource/mybatis/mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

Controller的编写


package com.wenxuan.controller; 
import com.wenxuan.bean.User; 
import com.wenxuan.service.UserService; 
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.beans.factory.annotation.Autowired; 
import org.springframework.web.bind.annotation.GetMapping; 
import org.springframework.web.bind.annotation.PathVariable; 
import org.springframework.web.bind.annotation.RestController; 

@RestController 
public class UserController { 
    @Autowired private UserService userService; 
    
    @PostMapping("/save") 
    public User save(@RequestBody User user) { 
        return userService.save(user); 
    } 
}

Service的编写

package com.wenxuan.service;


import com.wenxuan.mapper.UserMapper;
import com.wenxuan.bean.User;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

@Service
public class UserService {


    @Resource
    private UserMapper userMapper;

    public void save(User user) {
        userMapper.save(user);
    }
}

Mapper的编写

package com.wenxuan.mapper;

import com.wenxuan.bean.User;
import org.apache.ibatis.annotations.Mapper;


@Mapper
public interface UserMapper {
    void save(User user);
}

Mapper文件

Mapper的路径:mybatis/mapper/UserMapper.xml

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

    <!--Bean属性名和表字段名的对应关系-->
    <resultMap id="userMapper" type="com.wenxuan.bean.User">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
    </resultMap>

    <insert id="save" parameterType="com.wenxuan.bean.User">
        insert into user(id, name)
        values (#{id}, #{name})
    </insert>
</mapper>

启动类

package com.wenxuan;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.wenxuan.mapper")
public class Application {

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

MyBatis的类的别名机制

别名配置:/resource/mybatis/mybatis-config.xml

<!--方式一:给指定类起别名-->
<typeAliases>
    <typeAlias type="com.wenxuan.bean.User" alias="User"/>
</typeAliases>

<!--方式二:给包下所有的类起别名,别名是类的简单类名-->
<typeAliases>
    <package name="com.wenxuan.bean"/>
</typeAliases>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wenxuan.mapper.UserMapper">

    <!--Bean属性名和表字段名的对应关系-->
    <resultMap id="userMapper" type="User">
        <result property="id" column="id"/>
        <result property="name" column="name"/>
    </resultMap>
	
    <!-- 可以在parameterType标签或者resultType标签中使用别名 -->
    <select id="save" parameterType="User">
        insert into user(id, name)
        values (#{id}, #{name})
    </select>
</mapper>

Mapper的映射文件路径

方式一:通过application.yaml配置文件设置

mybatis:
  # 配置mapper映射文件
  mapper-locations: classpath:mybatis/mapper/*.xml

方式二:通过配置文件方式:/resource/mybatis/mybatis-config.xml

<!--1. 相对路径指定Mapper文件:相对于resource目录下-->
<mappers>
    <mapper resource="mybatis/mapper/UserMapper.xml"/>
</mappers>


<!--2. 绝对路径指定Mapper文件:Windows相对于当前盘符下,Linux相对于/目录下-->
<mappers>
    <mapper url="file:///test/test01/src/main/resources/mybatis/mapper/UserMapper.xml"/>
</mappers>


<!--
	3. 相同目录结构:
		SQL映射文件放在resource目录下,Mapper接口放在Java目录下
		SQL映射文件和Mapper接口文件必须是相同的目录结构
		SQL映射文件和Mapper接口文件必须同名
-->
<mappers>
    <mapper class="com.wenxuan.mapper.UserMapper"/>
</mappers>

<!--
    4. 相同目录结构:和 3 的相同条件,当class文件较多,可以通过指定报名方式进行映射
-->
<mappers>
    <package name="com.wenxuan.mapper"/>
</mappers>

MyBatis的参数处理机制

单个简单类型参数

<select id="selectByName" resultType="student" parameterType="java.lang.String">
  select * from t_student where name = #{name}
</select>

多个简单类型参数

<!-- MyBatis底层会使用Map集合保存这些参数值,第一个参数的key是arg0或者param1,第二个参数的key是arg2或者param2 -->
<select id="selectByNameAndSex" resultType="student">
  select * from t_student where name = #{arg0} and sex = #{arg1}
</select>

多参数可以通过@Param标签指定Map集合中的key值

/**
* 根据name和age查询
* @param name
* @param age
* @return
*/
List<Student> selectByNameAndAge(@Param(value="name") String name, @Param("sex") String sex);
<select id="selectByNameAndSex" resultType="student">
  select * from t_student where name = #{name} and sex = #{sex}
</select>

Map参数

/**
* 根据name和age查询
* @param paramMap
* @return
*/
List<Student> selectByParamMap(Map<String,Object> paramMap);
<!-- 手动封装Map集合:将每个条件以key和value的形式存放到集合中。然后在使用的时候通过#{key}来取值 -->
<select id="selectByParamMap" resultType="student">
  select * from t_student where name = #{nameKey} and age = #{ageKey}
</select>

Bean类参数

/**
 * 保存学生数据
 * @param student
 * @return
 */
int insert(Student student);
<!--
    如果没有使用通过别名机制,则parameterType需要填写全类名
    如果使用别名机制则parameterType中可以填写别名
-->
<insert id="insert" parameterType="Student">
  insert into t_student values(null,#{name},#{age},#{height},#{birth},#{sex})
</insert>

MyBatis返回值处理机制

返回简单类型值

<select id="getDeptName" parameterType="java.lang.String" resultType="java.lang.String">
  select dept_name from department
  where id = #{id}
</select>

返回Bean类型

<!--
    如果没有使用通过别名机制,则resultType需要填写全类名
    如果使用别名机制则resultType中可以填写别名
-->
<!--
    如果没有通过resultMap标签进行属性和字段进行映射,则resultMap需要填写全类名
    如果通过resultMap标签进行属性和字段进行映射,则resultMap可以填写resultMap标签对应的id属性值
-->
<!--
    resultMap:当Bean类字段名和数据库表字段名不一致时,可使用resultMap指定返回结果类型
    resultType:SQL所查询的字段必须与相应的pojo中的字段相对应
-->
<select id="selectById" resultMap="car" parameterType="long">
    select * from t_car
    where id = #{id}
</select>

返回多个Bean类型

public interface CarMapper {
    List<Car> selectAll();
}
<!-- resultType不是指定List而是指定List中泛型的类型 -->
<select id="selectAll" resultType="car">
    select id,car_num as carNum,brand,guide_price as guidePrice,produce_time as produceTime,car_type as carType
    from t_car
</select>

返回Bean通过Map接收

<!-- Map集合中key是字段名,value对应的字段值 -->
<select id="selectByIdRetMap" resultType="map">
    select * from t_car
    where id = #{id}
</select>

返回多个Map

<select id="selectAllRetListMap" resultType="map">
    select * from t_car
</select>

MyBatis属性字段映射

使用resultMap标签进行结果映射

<?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">

<!--namespace填写mapper接口名-->
<mapper namespace="com.wenxuan.mybatis.mapper.CarMapper">

    <!--通过resultMap标签指定字段和pojo类属性之间的对应关系-->
    <!--id属性:resultMap标签的唯一标识,每个resultMap都有一个唯一的id值-->
    <!--type属性:指定和哪个pojo类进行映射关系-->
    <resultMap id="carResultMap" type="car">
        <!--如果数据库表有主键,建议将主键字段的映射关系放在id标签中,这样子可以提高mybatis的效率-->
        <id property="id" column="id" />
        <!--property属性:pojo类的属性名-->
        <!--column属性:数据库表的字段名-->
        <result property="carNum" column="car_num" />
        <result property="brand" column="brand" />
        <result property="guidePrice" column="guide_price" />
        <result property="produceTime" column="produce_time" />
        <result property="carType" column="car_type" />
    </resultMap>

	<!--不需要起别名-->
    <select id="selectAllResultMap" resultMap="carResultMap">
        select id,car_num,brand,guide_price,produce_time,car_type
        from t_car
    </select>

</mapper>

开启驼峰命名自动映射

在mybatis-config.xml中配置

<settings>
    <setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

MyBatis的动态SQL

if标签

if标签:动态将某些字符串拼接到SQL语句中

<select id="selectByMultiCondition" resultMap="carResultMap">
    select id,car_num,brand,guide_price,produce_time,car_type from t_car where 1 = 1
    <!--
		1. if标签中test属性是必须的,并且属性值是一个表达式,表达式的结果为布尔类型的值
		2. 如果test属性值是true,则if标签中的SQL语句就会被拼接;否则就不会被拼接
		3. test属性中可以使用的是:
			当Mapper类的函数参数使用了@Param注解,那么test中要出现的是@Param注解指定的参数名
			当Mapper类的函数参数没有使用@Param注解,那么test中可以出现的是param1,param2...
			当Mapper类的函数参数是POJO对象,那么test中可以出现的是POJO类的属性名
    -->
    <if test="brand != null and brand != ''">
        and brand like "%"#{brand}"%"
    </if>
</select>

where标签

where标签:通过和if标签结合使用,来动态的添加where子句,而且where标签可以合理的去掉and或者or关键字

<select id="selectMultiConditionWithWhere" resultMap="carResultMap">
    select * from t_car
    <where>
        <if test="brand != null and brand != ''">
            brand like "%"#{brand}"%"
        </if>
        <if test="guidePrice != null">
            and guide_price > #{guidePrice}
        </if>
        <if test="carType != null and carType != ''">
            and car_type = #{carType}
        </if>
    </where>
</select>

trim标签

trim标签:动态的去掉或者删除前后缀关键字

  • prefix:在trim标签中的语句前添加内容
  • suffix:在trim标签中的语句后添加内容
  • prefixOverrides:前缀删除掉
  • suffixOverrides:后缀删除掉
<select id="selectMultiConditionWithTrim" resultMap="carResultMap">
    select * from t_car
    <!--and | or 表示and 或者 or-->
    <trim prefix="where" suffixOverrides="and | or">
        <if test="brand != null and brand != ''">
            brand like "%"#{brand}"%" and
        </if>
        <if test="guidePrice != null">
            guide_price > #{guidePrice} and
        </if>
        <if test="carType != null and carType != ''">
            car_type = #{carType}
        </if>
    </trim>
</select>

set标签

set标签:一般使用在update语句中,用来生成set关键字,同时去掉多余的逗号

<update id="updateBySet">
    update t_car
    <set>
        <if test="carNum != null and carNum != ''">car_num = #{carNum},</if>
        <if test="brand != null and brand != ''">brand = #{brand},</if>
        <if test="guidePrice != null and guidePrice != ''">guide_price = #{guidePrice},</if>
        <if test="produceTime != null and produceTime != ''">produce_time = #{produceTime},</if>
        <if test="carType != null and carType != ''">car_type = #{carType}</if>
    </set>
    <where>
        id = #{id}
    </where>
</update>

choose when otherwise标签

choose when otherwise标签:三个标签结合使用,类似于if … else if … else if … else …

<select id="selectByChoose" resultMap="carResultMap">
    select * from t_car
    <where>
        <choose>
            <when test="brand != null and brand != ''">
                brand like "%"#{brand}"%"
            </when>
            <when test="guidePrice != null">
                guide_price > #{guidePrice}
            </when>
            <otherwise>
                car_type = #{carType}
            </otherwise>
        </choose>
    </where>
</select>

foreach标签

foreach标签:遍历数组或者集合,并且遍历过程中可以使用某个分隔符分隔开这些元素

  • collection:指定遍历的是数组还是集合(array、list)
  • item:取出数组或者集合的每个元素放在哪个变量中
  • separator:拼接的SQL语句使用什么分隔符分隔每个元素
  • open:指定遍历拼接的SQL语句中最前面添加的内容
  • close:指定遍历拼接的SQL语句中最后面添加的内容
<delete id="deleteByForeach">
    delete from t_car where id in
    <foreach collection="ids" item="id" separator="," open="(" close=")">
        #{id}
    </foreach>
</delete>

MyBatis的逆向工程

添加插件依赖

<!--定制构建过程-->
<build>
  <!--可配置多个插件-->
  <plugins>
    <!--其中的一个插件:mybatis逆向工程插件-->
    <plugin>
      <!--插件的GAV坐标-->
      <groupId>org.mybatis.generator</groupId>
      <artifactId>mybatis-generator-maven-plugin</artifactId>
      <version>1.4.1</version>
      <!--允许覆盖-->
      <configuration>
        <overwrite>true</overwrite>
      </configuration>
      <!--插件的依赖-->
      <dependencies>
        <!--mysql驱动依赖-->
        <dependency>
          <groupId>mysql</groupId>
          <artifactId>mysql-connector-java</artifactId>
          <version>8.0.30</version>
        </dependency>
      </dependencies>
    </plugin>
  </plugins>
</build>

配置generatorConfig.xml:放在resource目录下

<?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>
    <!--
        targetRuntime有两个值:
            MyBatis3Simple:生成的是基础版,只有基本的增删改查。
            MyBatis3:生成的是增强版,除了基本的增删改查之外还有复杂的增删改查。
    -->
    <context id="DB2Tables" targetRuntime="MyBatis3">
        <!--防止生成重复代码-->
        <plugin type="org.mybatis.generator.plugins.UnmergeableXmlMappersPlugin"/>

        <commentGenerator>
            <!--是否去掉生成日期-->
            <property name="suppressDate" value="true"/>
            <!--是否去除注释-->
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>

        <!--连接数据库信息-->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3308/test"
                        userId="root"
                        password="123456">
        </jdbcConnection>

        <!-- 生成pojo包名和位置 -->
        <javaModelGenerator targetPackage="com.wenxuan.mybatis.pojo" targetProject="src/main/java">
            <!--是否开启子包-->
            <property name="enableSubPackages" value="true"/>
            <!--是否去除字段名的前后空白-->
            <property name="trimStrings" value="true"/>
        </javaModelGenerator>

        <!-- 生成SQL映射文件的包名和位置 -->
        <sqlMapGenerator targetPackage="com.wenxuan.mybatis.mapper" targetProject="src/main/resources">
            <!--是否开启子包-->
            <property name="enableSubPackages" value="true"/>
        </sqlMapGenerator>

        <!-- 生成Mapper接口的包名和位置 -->
        <javaClientGenerator
                type="xmlMapper"
                targetPackage="com.wenxuan.mybatis.mapper"
                targetProject="src/main/java">
            <property name="enableSubPackages" value="true"/>
        </javaClientGenerator>

        <!-- 表名和对应的实体类名-->
        <table tableName="user" domainObjectName="User"/>

    </context>
</generatorConfiguration>

#{} 和 ${}

  • #{}:先编译sql语句,再给占位符传值,底层是PreparedStatement实现。可以防止sql注入,比较常用。
  • ${}:先进行sql语句拼接,然后再编译sql语句,底层是Statement实现。存在sql注入现象。只有在需要进行sql语句关键字拼接的情况下才会用到。

#{}和${}有不同的使用场景

  1. 当需要进行SQL语句拼接时,必须使用${},其余情况使用#{}
    • 使用#{}:当需要给使用占位符并且给占位符传值时,使用#{},此时传的值会用单引号括起来
    • 使用${}:当需要进行SQL语句拼接时,使用${},此时拼接的值不会有单引号
  2. ${}的使用场景
    • 拼接表名,在select语句中表名不需要单引号括起来
    • 批量删除,in中的值不需要单引号括起来
    • 模糊查询,link单引号中的值不需要单引号括起来
    • 排序查询,asc和desc关键字不需要单引号括起来
  3. 模糊查询一般使用#{},比如:like "%"#{brand}"%"

SQL片段

通过SQL标签定义SQL片段,可以在mapper文件中多个地方使用,实现代码复用,更加容易维护。

<sql id="userField">id,name, password</sql>

<select id="selectById" resultMap="user">
  select <include refid="userField"/> from t_user where id = #{id}
</select>

MyBatis使用PageHelper

  1. 添加PageHelper依赖
<dependency>
  <groupId>com.github.pagehelper</groupId>
  <artifactId>pagehelper</artifactId>
  <version>5.3.1</version>
</dependency>
  1. 在mybatis配置文件中添加插件配置

在mybatis-config.xml中的typeAliases标签后面添加

<plugins>
  <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
  1. 将查询结果封装到PageInfo对象中
  • startPage与PageInfo之间只能有一个查询语句,如果有多个查询语句则会失效。
@Test
public void testPageHelper() throws Exception{
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
    SqlSession sqlSession = sqlSessionFactory.openSession();
    CarMapper mapper = sqlSession.getMapper(CarMapper.class);

    // 开启分页
    PageHelper.startPage(2, 2);

    // 执行查询语句
    List<Car> cars = mapper.selectAll();

    // 获取分页信息对象
    PageInfo<Car> pageInfo = new PageInfo<>(cars);

    System.out.println(pageInfo);
}

MyBatisPlus

SpringBoot整合MyBatisPlus

添加maven依赖

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.7.17</version>
    <relativePath/>
</parent>

<dependencies>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.33</version>
    </dependency>
    <dependency>
        <groupId>com.baomidou</groupId>
        <artifactId>mybatis-plus-boot-starter</artifactId>
        <version>3.5.3.1</version>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
</dependencies>

编写yaml配置

server:
  port: 8902

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3308/test?serverTimezone=UTC
    username: root
    password: 123456

# 开启mybatis-plus日志
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

编写启动类

package com.wenxuan;

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
@MapperScan("com.wenxuan.mapper")    // 扫描指定包
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

编写Controller


package com.wenxuan.controller;

import com.wenxuan.bean.User;
import com.wenxuan.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserController {
    @Autowired
    private UserService userService;


    @GetMapping("/user/{id}")
    public User getUser(@PathVariable String id) {
        return userService.getById(id);
    }
}

编写Service

package com.wenxuan.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.wenxuan.bean.User;


public interface UserService extends IService<User> {
}
package com.wenxuan.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wenxuan.bean.User;
import com.wenxuan.mapper.UserMapper;
import com.wenxuan.service.UserService;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
}

编写Mapper

package com.wenxuan.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.wenxuan.bean.User;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface UserMapper extends BaseMapper<User> {
}

MyBatisPlus实现简单的CRUD

添加操作

public void testInsert() {
    User user = new User();
    user.setAge(19);
    user.setEmail("283748293@qq.com");
    user.setName("张三");

    // INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )
    int count = userMapper.insert(user);
}

删除操作

public void deleteById() {
    // DELETE FROM user WHERE id=?
    int count = userMapper.deleteById(1629136008219594754L);
    System.out.println("count=" + count);
}
// 通过map设置删除条件
public void deleteByMap() {
    HashMap<String, Object> map = new HashMap<>();
    map.put("name", "张三");
    map.put("age", 19);
    // DELETE FROM user WHERE name = ? AND age = ?
    int count = userMapper.deleteByMap(map);
    System.out.println("count=" + count);
}

修改操作

public void testUpdate() {
    User user = new User();
    user.setId(5l);
    user.setName("李四");
    user.setAge(20);
    user.setEmail("2934878293@qq.com");
    // UPDATE user SET name=?, age=?, email=? WHERE id=?
    int count = userMapper.updateById(user);
}

查询操作

public void selectId() {
    // SELECT id,name,age,email FROM user WHERE id=?
    User user = userMapper.selectById(1L);
    System.out.println(user);
}
// 通过map条件查询
public void selectByMap() {
    Map<String, Object> map = new HashMap<>();
    map.put("name", "Tom");
    map.put("age", 28);
    // SELECT id,name,age,email FROM user WHERE name = ? AND age = ?
    List<User> users = userMapper.selectByMap(map);
    users.forEach(System.out::println);
}
// 查询所有记录
public void selectAll() {
    // SELECT id,name,age,email FROM user
    List<User> users = userMapper.selectList(null);
    users.forEach(System.out::println);
}

MyBatisPlus实现自定义SQL

MyBatisPlus实现自定义SQL和Mybatis基本相同

  1. 添加maven依赖
  2. 编写application.yaml
  3. 编写Mapper接口
  4. 编写Mapper配置文件

MyBatisPlus的maven依赖

<dependency>  
    <groupId>com.baomidou</groupId>  
    <artifactId>mybatis-plus-boot-starter</artifactId>  
    <version>3.5.1</version>  
</dependency>

编写application.yml文件

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3308/test?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false
    username: root
    password: 123456


mybatis-plus:
  # 配置MyBatis日志
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  # 指定mapper配置文件所在路径
  mapper-locations: classpath:/mapper/*.xml

编写mapper接口

package com.wenxuan.mapper;  
  
import com.baomidou.mybatisplus.core.mapper.BaseMapper;  
import com.wenxuan.bean.User;  
import org.apache.ibatis.annotations.Mapper;  
  
/**  
* @author 文轩  
* @create 2023-11-23 21:24  
*/  
@Mapper  
public interface UserMapper extends BaseMapper<User> {  
    User selectUserById(String id);  
}

编写Mapper配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.wenxuan.mapper.UserMapper">
    <select id="selectUserById" resultType="com.wenxuan.bean.User" parameterType="string">
        select * from user where id = #{id}
    </select>
</mapper>

MyBatisPlus条件构造器的CRUD

条件构造器:用于构造执行SQL的条件

  • Wrapper : 条件构造抽象类,最顶端父类
  • AbstractWrapper : 用于查询条件封装,生成 sql 的 where 条件
  • QueryWrapper : 查询条件封装
  • UpdateWrapper : Update 条件封装
  • AbstractLambdaWrapper : 使用Lambda 语法
  • LambdaQueryWrapper :用于Lambda语法使用的查询Wrapper
  • LambdaUpdateWrapper : Lambda 更新封装Wrapper

Wrapper抽象类图.png

QueryWrapper

public void test(){
    //查询用户名包含a,年龄在20到30之间,并且邮箱不为null的用户信息
    //SELECT id,username AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (username LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL)
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();
    queryWrapper.like("username", "a")	// 表字段名
        .between("age", 20, 30)
        .isNotNull("email");
    List<User> list = userMapper.selectList(queryWrapper);
    list.forEach(System.out::println);
}

UpdateWrapper

public void test() {
    //将(年龄大于20或邮箱为null)并且用户名中包含有a的用户信息修改
    //组装set子句以及修改条件
    UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
    //lambda表达式内的逻辑优先运算
    updateWrapper
        .set("age", 18)
        .set("email", "user@atguigu.com")
        .like("username", "a")
        .and(i -> i.gt("age", 20).or().isNull("email"));
    //这里必须要创建User对象,否则无法应用自动填充。如果没有自动填充,可以设置为null
    //UPDATE t_user SET username=?, age=?,email=? WHERE (username LIKE ? AND (age > ? OR email IS NULL))
    //User user = new User();
    //user.setName("张三");
    //int result = userMapper.update(user, updateWrapper);
    //UPDATE t_user SET age=?,email=? WHERE (username LIKE ? AND (age > ? OR email IS NULL))
    int result = userMapper.update(null, updateWrapper);
    System.out.println(result);
}

LambdaQueryWrapper

public void test() {
    //定义查询条件,有可能为null(用户未输入)
    String username = "a";
    Integer ageBegin = 10;
    Integer ageEnd = 24;
    LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
    //避免使用字符串表示字段,防止运行时错误
    queryWrapper
        .like(StringUtils.isNotBlank(username), User::getName, username)
        .ge(ageBegin != null, User::getAge, ageBegin)
        .le(ageEnd != null, User::getAge, ageEnd);
    List<User> users = userMapper.selectList(queryWrapper);
    users.forEach(System.out::println);
}

LambdaUpdateWrapper

public void test() {
    //组装set子句
    LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
    updateWrapper
        .set(User::getAge, 18)
        .set(User::getEmail, "user@atguigu.com")
        .like(User::getName, "a")
        .and(i -> i.lt(User::getAge, 24).or().isNull(User::getEmail)); //lambda表达式内的逻辑优先运算
    User user = new User();
    int result = userMapper.update(user, updateWrapper);
    System.out.println("受影响的行数:" + result);
}

MyBatisPlus表和Bean相关设置

@TableName

@TableName注解:设置实体类和数据库对应表之间的对应关系

package com.wenxuan.mp.domain;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;

@Data
@TableName("user")
public class User {

    private Long id;
    private String name;
    private Integer age;
    private String email;
}

@TableId

@TableId注解:设置表的主键的字段

public class User {
    @TableId
    private Long id;
    private String name;
    private Integer age;
    private String email;
}

@TableId注解的属性

  • value属性:指定属性对应的字段名
  • type属性:指定主键策略
    1. IdType.ASSIGN_ID(默认) :基于雪花算法的策略生成数据id,与数据库id是否设置自增无关
    2. IdType.AUTO :使用数据库的自增策略,注意,该类型请确保数据库设置了id自增,否则无效

雪花算法

  • 雪花算法(Snowflake)是Twitter开源的一种分布式唯一ID生成算法,旨在解决分布式系统中唯一ID的生成问题。
  • 雪花算法的核心思想是通过将64位的ID分割成多个部分,从而使生成的ID具有唯一性和有序性。

@TableField

@TableField:设置Bean类属性和表字段的对应关系

public class User {
    @TableField("id")
    private Long id;
    
    @TableField("name")
    private String name;
    
    @TableField("age")
    private Integer age;
    
    @TableField("email")
    private String email;
}

MyBatisPlus中当Bean属性名和表字段名不同时:

  1. 若实体类中的属性使用的是驼峰命名风格,而表中的字段使用的是下划线命名风格,此时MyBatis-Plus会自动将下划线命名风格转化为驼峰命名风格。
  2. 若实体类中的属性和表中的字段不满足驼峰命名风格,例如实体类属性name,表中字段username,此时需要在实体类属性上使用@TableField("username")设置属性所对应的字段名。

@TableLogic

@TableLogic:实现逻辑删除功能,将对应数据中代表是否被删除字段的状态修改为“被删除状态”,之后在数据库中仍旧能看到此条数据记录。

public class User {
    @TableId
    private Long id;
    private String name;
    private Integer age;
    private String email;
    
    @TableLogic(value = "0", delval = "1")
    @TableField("is_delete")
    private Integer isDelete;
}

@TableLogic注解的属性:

  1. value:未删除的标记
  2. delval:已删除的标记

调用BaseMapper的deleteById(id)或者IService的removeById(id)时

  1. 无该注解:直接从表里删除(delete)
  2. 有该注解:会直接更新方法,将指定的字段改为delval的值(update)

@TableLogic注解只对自动注入的SQL起效,不会对手动编写的SQL语句产生影响

  • 插入: 不作限制
  • 查找: 追加where条件过滤掉已删除数据,且使用 wrapper.entity 生成的where条件会忽略该字段
  • 更新: 追加where条件防止更新到已删除数据,且使用 wrapper.entity 生成的where条件会忽略该字段
  • 删除: 转变为更新

配置全局的逻辑删除

mybatis-plus:
  global-config:
    db-config:
      # 配置全局主键
      id-type: auto
      # 配置全局删除的字段名(数据库字段名)
      logic-delete-field: is_delete
      # 逻辑已删除值(默认值为1)
      logic-delete-value: 1
      # 逻辑未删除值(默认值为0)
      logic-not-delete-value: 1

MyBatisPlus的application.yaml配置

开启MyBatisPlus日志

mybatis-plus:
  configuration:
    # 开启mybatis-plus日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

配置MyBatisPlus的Mapper文件路径

mybatis-plus:
  # mybatis-plus的mapper配置文件路径
  mapper-locations: classpath:mapper/*.xml

配置MyBatisPlus的bean类和字段表的默认前缀

mybatis-plus:
  global-config:
    db-config:
      # mybatis-plus的bean类和字段表的默认前缀
      table-prefix: t_

配置MyBatisPlus的全局主键生成策略

主键生成策略:

  1. auto:自动选择生成策略,MySQL默认使用雪花算法。
  2. none:没有主键生成策略,需要手动指定实体类的主键值。
  3. input:手动输入主键值,需要手动指定实体类的主键值。
  4. assign_id:需要手动指定实体类的主键值,并且主键值通常为自增值。
  5. assign_uuid:需要手动指定实体类的主键值,并且主键值为uuid值。
mybatis-plus:
  global-config:
    db-config:
      # 配置全局主键生成策略
      id-type: auto

配置MyBatisPlus逻辑删除功能

mybatis-plus:
  global-config:
      # 配置全局删除的字段名(数据库字段名)
      logic-delete-field: is_delete
      # 逻辑已删除值(默认值为1)
      logic-delete-value: 1
      # 逻辑未删除值(默认值为0)
      logic-not-delete-value: 0

配置MyBatisPlus扫描通用枚举

mybatis-plus:
  # 配置扫描通用枚举
  type-enums-package: com.atguigu.mybatisplus.enums
mybatis-plus:
  # 配置扫描通用枚举
  type-enums-package: com.atguigu.mybatisplus.enums

MyBatisPlus乐观锁的实现

模拟冲突

@Test
public void testConcurrentUpdate() {
    // 假设id为1的产品的初始价格为100
    
    // 小李取出的商品价格为100
    Product p1 = productMapper.selectById(1L);
    // 小王取出的商品价格是100
    Product p2 = productMapper.selectById(1L);
    
    //3、小李将商品价格加了50元,p1对象存储的价格为150,p2对象存储的价格还是150,然后将150存入数据库
    p1.setPrice(p1.getPrice() + 50);
    
    //4、小王将商品价格减了30元,p1对象存储的价格为150,p2对象存储的价格为70,然后将70存入了数据库
    p2.setPrice(p2.getPrice() - 30);
    productMapper.updateById(p2);
    
    //最后价格覆盖,商品的价格本来应该是120,但是数据库中却存储的是70
    Product p3 = productMapper.selectById(1L);
}

MybatisPlus实现乐观锁

冲突的出现主要还是因为线程冲突,当多个线程同时获取初始值对其进行修改,后面更新的值会将前面更新的值给覆盖掉。

解决办法:每次获取数据库中的值时同时将记录的版本号获取,当修改数据库中的值时需要比对当前记录的版本号是否为获取时的版本号,如果是才能进行修改,如果不相同则不能修改。同时如果将记录的值修改成功则将版本号也一并修改掉。

编写实体类,添加version字段

package com.atguigu.mybatisplus.entity;
import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;
@Data
public class Product {
    private Long id;
    private String name;
    private Integer price;
    @Version	// 添加负责版本号的字段
    private Integer version;
}

添加乐观锁插件配置:

// @EnableTransactionManagement:开启 Spring 框架的事务管理功能
@EnableTransactionManagement
@Configuration
public class MyBatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //添加乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return interceptor;
    }
}

乐观锁的实现原理:

-- 取出记录时,获取当前version
SELECT id,`name`,price,`version` FROM product WHERE id=1

-- 更新时,version + 1,如果where语句中的version版本不对,则更新失败
UPDATE product SET price=price+50, `version`=`version` + 1 WHERE id=1 AND `version`=1

MyBatisPlus实现通用枚举

可以通过MyBatisPlus实现某个字段只能固定某些值,并且这些值代表不同的涵义。

设置字段的枚举值

package com.wenxuan.enums;

import com.baomidou.mybatisplus.annotation.EnumValue;

/**
 * @author 文轩
 * @create 2023-11-24 23:23
 */
public enum SexEnum {

    MALE(1, "男"),
    FEMALE(2, "女");

    @EnumValue
    private Integer sex;
    private String sexName;

    SexEnum(Integer sex, String sexName) {
        this.sex = sex;
        this.sexName = sexName;
    }

}

配置MyBatisPlus扫描的通用枚举类

mybatis-plus:
    # 配置扫描通用枚举
    type-enums-package: com.atguigu.mybatisplus.enums

测试MyBatisPlus的通用枚举

@Test
public void testSexEnum(){
    User user = new User();
    user.setName("Enum");
    user.setAge(20);
    //设置性别信息为枚举项,会将@EnumValue注解所标识的属性值存储到数据库
    userMapper.insert(user);
}

MyBatisPlus代码生成器

可以通过MyBatisPlus代码生成器生成表对应的entity、controller、service、mapper等固定代码。

添加maven依赖

<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.5.1</version>
</dependency>
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
    <version>2.3.31</version>
</dependency>

编写代码生成器代码

package com.wenxuan.test;

import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;

import java.util.Collections;

/**
 * @author 文轩
 * @create 2023-11-24 23:55
 */
public class FastAutoGeneratorTest {
    public static void main(String[] args) {
        FastAutoGenerator
                .create("jdbc:mysql://127.0.0.1:3308/test?characterEncoding=utf-8&userSSL=false", "root", "123456")
                .globalConfig(builder -> {
                    builder.author("wenxuan") // 设置作者
                            //.enableSwagger() // 开启 swagger 模式
                            .fileOverride() // 覆盖已生成文件
                            .outputDir("F://tmp//mybatis_plus"); // 指定输出目录
                })
                .packageConfig(builder -> {
                    builder.parent("com.wenxuan") // 设置父包名
                            .moduleName("mybatisplus") // 设置父包模块名
                            .pathInfo(Collections.singletonMap(OutputFile.mapperXml, "F://tmp//mybatis_plus"));			// 设置mapperXml生成路径
                })
                .strategyConfig(builder -> {
                    builder.addInclude("user") // 设置需要生成的表名
                            .addTablePrefix("t_", "c_"); // 设置过滤表前缀
                })
                .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
                .execute();
    }
}

MyBatisPlus插件

MyBatis-Plus提供了强大的mapper和service模板,能够大大的提高开发效率,但是在真正开发过中,MyBatis-Plus不能解决所有问题,例如一些复杂的SQL,多表联查,此时可以通过MyBatisX插件实现负责功能。MyBatisX一款基于 IDEA 的快速开发插件。

MyBatisX插件用法:baomidou.com/pages/ba5b2…