Práctica de entrada de base cero MyBatis-Plus

MyBatis: práctica de entrada de base cero de MyBatis
Información de MyBatis-Plus:

链接:https://pan.baidu.com/s/16YWDS60ubD4MX70799TzrA?pwd=tdzy 
提取码:tdzy 

MyBatis-Plus

1. Introducción a MyBatis-Plus

1. Introducción

MyBatis-Plus (MP para abreviar) es una herramienta de mejora de MyBatis.Basada en MyBatis, solo se realizan mejoras sin cambios, y nace para simplificar el desarrollo y mejorar la eficiencia.

Visión
Nuestra visión es convertirnos en el mejor socio de MyBatis, al igual que el 1P y 2P en Contra, y la eficiencia se duplica cuando se combina con amigos.
inserte la descripción de la imagen aquí

2. Características

Sin intrusiones : solo mejora y sin cambios, su introducción no afectará a los proyectos existentes, es tan suave como la seda
Baja pérdida : el CURD básico se inyectará automáticamente al inicio, el rendimiento es básicamente sin pérdidas y las
potentes operaciones CRUD están directamente orientadas a objetos : Mapeador general incorporado y servicio general, solo una pequeña cantidad de configuración puede realizar la mayoría de las operaciones CRUD de una sola tabla, y constructores condicionales más potentes para satisfacer diversas necesidades de uso Admite llamadas de formulario Lambda: a través de expresiones Lambda,
es conveniente escribe varias condiciones de consulta, no hay necesidad de preocuparse por los campos escritos incorrectamente
Admite la generación automática de claves principales : admite hasta 4 estrategias de clave principal (incluido un generador de ID único distribuido - Secuencia), que se puede configurar libremente para resolver perfectamente el problema de la clave principal Admite el modo ActiveRecord:
admite llamadas de formulario ActiveRecord, las clases de entidad solo necesitan heredar la clase Model para realizar potentes operaciones CRUD.
Admite operaciones generales globales personalizadas : admite la inyección de métodos generales globales (escribir una vez, usar en cualquier lugar
) Complementos de Maven para generar rápidamente códigos de capa de mapeador, modelo, servicio y controlador, los motores de plantilla son compatibles y hay muchas configuraciones personalizadas esperando que use el complemento de paginación incorporado: basado en la paginación física de MyBatis,
los desarrolladores no necesita preocuparse por operaciones específicas. Después de configurar el complemento, escribir paginación es equivalente a una consulta de lista normal. El
complemento de paginación admite varias bases de datos.: Admite MySQL, MariaDB, Oracle, DB2, H2, HSQL, SQLite, Postgre, SQLServer y otros
complementos de análisis de rendimiento incorporados en la base de datos : puede generar declaraciones SQL y su tiempo de ejecución, se recomienda habilitar esta función al desarrollar y prueba, y puede descubrir rápidamente la consulta lenta
del complemento de intercepción global incorporado : proporcione análisis inteligente y bloqueo de operaciones de eliminación y actualización en toda la tabla, y también personalice las reglas de intercepción para evitar el mal funcionamiento

3. Base de datos de soporte

Cualquier base de datos que pueda usar MyBatis para CRUD y soporte SQL estándar, el soporte específico es el siguiente

MySQL, Oracle, DB2, H2, HSQL, SQLite, PostgreSQL, SQLServer, Phoenix, Gauss, ClickHouse, Sybase, OceanBase, Firebird, Cubrid, Goldilocks, base de datos csiidb Dameng, base de datos Xugu, base de datos Renda Jincang, base de datos Nanda General (biblioteca Hua
) , Base de datos general de NTU, Base de datos de Shentong, Base de datos de Hangao

4. Estructura del marco

inserte la descripción de la imagen aquí

5. Código y dirección del documento

Dirección oficial: http://mp.baomidou.com
Dirección de publicación del código:
Github: https://github.com/baomidou/mybatis-plus
Gitee: https://gitee.com/baomidou/mybatis-plus
Dirección de publicación del documento: https://baomidou.com/pages/24112f

2. Caso introductorio

1. Entorno de desarrollo

IDE: idea 2020.3
JDK: JDK8+
Herramienta de compilación: maven 3.6.1
Versión de MySQL: MySQL 5.7
Spring Boot: 2.6.3
MyBatis-Plus: 3.5.1

2. Crear base de datos y tablas

a> crear tabla

CREATE DATABASE `mybatis_plus` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;
use `mybatis_plus`;
CREATE TABLE `user` (
`id` bigint(20) NOT NULL COMMENT '主键ID',
`name` varchar(30) DEFAULT NULL COMMENT '姓名',
`age` int(11) DEFAULT NULL COMMENT '年龄',
`email` varchar(50) DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

b>Añadir datos

INSERT INTO user (id, name, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');

3. Crea un proyecto Spring Boot

a>Proyecto de inicialización

Use Spring Initializr para inicializar rápidamente un proyecto Spring Boot
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

b>Introducir dependencias

<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

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

        <!--lombok用于简化实体类开发-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <!--mysql驱动-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-generator</artifactId>
            <version>3.5.1</version>
        </dependency>
       
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

Instala el complemento lombok en c>idea

inserte la descripción de la imagen aquí

4. Escribir código

a> aplicación de implementación.yml

spring:
  # 配置数据源信息
  datasource:
    # 配置数据源类型
    type: com.zaxxer.hikari.HikariDataSource
    # 配置连接数据库的各个信息
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/mybatis_plus1?characterEncoding=utf-8&userSSL=false
    # url: jdbc:mysql://localhost:3306/mybatis_plus?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8&useSSL=false
    username: root
    password: root

Aviso:

1. Clase de controlador driver-class-name
spring boot 2.0 (controlador jdbc5 incorporado), uso de clase de controlador:
driver-class-name: com.mysql.jdbc.Driver
spring boot 2.1 y superior (controlador jdbc8 incorporado), uso de clase de controlador:
nombre-clase-controlador: com.mysql.cj.jdbc.Driver
De lo contrario, habrá información de ADVERTENCIA cuando se ejecute el caso de prueba
2. URL de la dirección de conexión
URL de la versión MySQL5.7:
jdbc:mysql://localhost: 3306/mybatis_plus?characterEncoding =utf-8&useSSL=false
MySQL8.0 versión url:
jdbc:mysql://localhost:3306/mybatis_plus?
serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false
De lo contrario, ejecute el caso de prueba e informe lo siguiente error:
java.sql.SQLException: el valor de la zona horaria del servidor 'Öйú±ê׼ʱ¼ä' no se reconoce o representa más

b>comenzar clase

Agregue la anotación @MapperScan a la clase de inicio de Spring Boot para escanear el paquete del mapeador

@SpringBootApplication
@MapperScan("com/xusheng/mybatisplus/mapper")
public class MybatisplusApplication {
    
    

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

}

c>Añadir entidad

@Data //lombok注解
public class User {
    
    
	private Long id;
	private String name;
	private Integer age;
	private String email;
}

El resultado después de compilar la clase Usuario:
inserte la descripción de la imagen aquí

d>Agregar asignador

BaseMapper es un mapeador de plantillas proporcionado por MyBatis-Plus, que contiene métodos CRUD básicos, y el tipo genérico es el tipo de entidad de la operación.

@Repository
public interface UserMapper extends BaseMapper<User> {
    
    

}

e> prueba

@SpringBootTest
public class MyBatisPlusTest {
    
    

    @Autowired
    private UserMapper userMapper;

    @Test
    public void testSelectList(){
    
    
        //通过条件构造器查询一个list集合,若没有条件,则可以设置null为参数
        //selectList()根据MP内置的条件构造器查询一个list集合,null表示没有条件,即查询所有
        //userMapper.selectList(null).forEach(System.out::println);
        List<User> list = userMapper.selectList(null);
        list.forEach(System.out::println);
    }
 }

resultado:
inserte la descripción de la imagen aquí

Aviso:

IDEA informa un error en userMapper porque no se puede encontrar el objeto inyectado, porque la clase se crea dinámicamente, pero el programa se puede ejecutar correctamente.
Para evitar informes de errores, puede agregar la anotación @Repository a la interfaz del mapeador

f> agregar registro

Configure la salida del registro en application.yml

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  

inserte la descripción de la imagen aquí

3. CRUD básico

1, Mapeador de base

El CRUD básico en MyBatis-Plus se ha implementado en el BaseMapper incorporado, podemos usarlo directamente, la interfaz es la siguiente:

/*
 * Copyright (c) 2011-2022, baomidou ([email protected]).
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.baomidou.mybatisplus.core.mapper;

import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
import org.apache.ibatis.annotations.Param;

import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;



/**
 * Mapper 继承该接口后,无需编写 mapper.xml 文件,即可获得CRUD功能
 * <p>这个 Mapper 支持 id 泛型</p>
 *
 * @author hubin
 * @since 2016-01-23
 */
public interface BaseMapper<T> extends Mapper<T> {
    
    

    /**
     * 插入一条记录
     *
     * @param entity 实体对象
     */
    int insert(T entity);

    /**
     * 根据 ID 删除
     *
     * @param id 主键ID
     */
    int deleteById(Serializable id);

    /**
     * 根据实体(ID)删除
     *
     * @param entity 实体对象
     * @since 3.4.4
     */
    int deleteById(T entity);

    /**
     * 根据 columnMap 条件,删除记录
     *
     * @param columnMap 表字段 map 对象
     */
    int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

    /**
     * 根据 entity 条件,删除记录
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
     */
    int delete(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 删除(根据ID或实体 批量删除)
     *
     * @param idList 主键ID列表或实体列表(不能为 null 以及 empty)
     */
    int deleteBatchIds(@Param(Constants.COLLECTION) Collection<?> idList);

    /**
     * 根据 ID 修改
     *
     * @param entity 实体对象
     */
    int updateById(@Param(Constants.ENTITY) T entity);

    /**
     * 根据 whereEntity 条件,更新记录
     *
     * @param entity        实体对象 (set 条件值,可以为 null)
     * @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
     */
    int update(@Param(Constants.ENTITY) T entity, @Param(Constants.WRAPPER) Wrapper<T> updateWrapper);

    /**
     * 根据 ID 查询
     *
     * @param id 主键ID
     */
    T selectById(Serializable id);

    /**
     * 查询(根据ID 批量查询)
     *
     * @param idList 主键ID列表(不能为 null 以及 empty)
     */
    List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);

    /**
     * 查询(根据 columnMap 条件)
     *
     * @param columnMap 表字段 map 对象
     */
    List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);

    /**
     * 根据 entity 条件,查询一条记录
     * <p>查询一条记录,例如 qw.last("limit 1") 限制取一条记录, 注意:多条数据会报异常</p>
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    default T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper) {
    
    
        List<T> ts = this.selectList(queryWrapper);
        if (CollectionUtils.isNotEmpty(ts)) {
    
    
            if (ts.size() != 1) {
    
    
                throw ExceptionUtils.mpe("One record is expected, but the query result is multiple records");
            }
            return ts.get(0);
        }
        return null;
    }

    /**
     * 根据 Wrapper 条件,判断是否存在记录
     *
     * @param queryWrapper 实体对象封装操作类
     * @return
     */
    default boolean exists(Wrapper<T> queryWrapper) {
    
    
        Long count = this.selectCount(queryWrapper);
        return null != count && count > 0;
    }

    /**
     * 根据 Wrapper 条件,查询总记录数
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    Long selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 根据 entity 条件,查询全部记录
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 根据 Wrapper 条件,查询全部记录
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 根据 Wrapper 条件,查询全部记录
     * <p>注意: 只返回第一个字段的值</p>
     *
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 根据 entity 条件,查询全部记录(并翻页)
     *
     * @param page         分页查询条件(可以为 RowBounds.DEFAULT)
     * @param queryWrapper 实体对象封装操作类(可以为 null)
     */
    <P extends IPage<T>> P selectPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);

    /**
     * 根据 Wrapper 条件,查询全部记录(并翻页)
     *
     * @param page         分页查询条件
     * @param queryWrapper 实体对象封装操作类
     */
    <P extends IPage<Map<String, Object>>> P selectMapsPage(P page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
}

2. Insertar

 @Test
    public void testInsert(){
    
    
        //实现新增用户信息
        //INSERT INTO user ( id, name, age, email ) VALUES ( ?, ?, ?, ? )
        User user = new User();
        user.setId(100L);
        user.setName("xusheng");
        user.setAge(23);
        user.setEmail("[email protected]");
        int result = userMapper.insert(user);
        System.out.println("result:"+result);
        System.out.println("id:"+user.getId());

    }

Cuando MyBatis-Plus inserta datos, generará una identificación basada en la estrategia del algoritmo de copo de nieve por defecto

3. Eliminar

a> Eliminar registro por id

@Test
public void testDeleteById(){
    
    
	//通过id删除用户信息
	//DELETE FROM user WHERE id=?
	int result = userMapper.deleteById(1475754982694199298L);
	System.out.println("受影响行数:"+result);
}

b> Eliminar registros por lotes por ID

@Test
public void testDeleteBatchIds(){
    
    
	//通过多个id批量删除
	//DELETE FROM user WHERE id IN ( ? , ? , ? )
	List<Long> idList = Arrays.asList(1L, 2L, 3L);
	int result = userMapper.deleteBatchIds(idList);
	System.out.println("受影响行数:"+result);
}

c>Eliminar registros a través de las condiciones del mapa

@Test
public void testDeleteByMap(){
    
    
	//根据map集合中所设置的条件删除记录
	//DELETE FROM user WHERE name = ? AND age = ?
	Map<String, Object> map = new HashMap<>();
	map.put("age", 23);
	map.put("name", "张三");
	int result = userMapper.deleteByMap(map);
	System.out.println("受影响行数:"+result);
}

4. Modificar

 @Test
    public void testUpdate(){
    
    
        //修改用户信息
        //UPDATE user SET name=?, email=? WHERE id=?
        User user = new User();
        user.setId(4L);
        user.setName("李四");
        user.setEmail("[email protected]");
        int result = userMapper.updateById(user);
        System.out.println("result:"+result);
    }

5. Consulta

a> Consultar información de usuario según id

@Test
public void testSelectById(){
    
    
	//根据id查询用户信息
	//SELECT id,name,age,email FROM user WHERE id=?
	User user = userMapper.selectById(4L);
	System.out.println(user);
}

b> Consultar información de múltiples usuarios basada en múltiples identificaciones

@Test
public void testSelectBatchIds(){
    
    
	//根据多个id查询多个用户信息
	//SELECT id,name,age,email FROM user WHERE id IN ( ? , ? )
	List<Long> idList = Arrays.asList(4L, 5L);
	List<User> list = userMapper.selectBatchIds(idList);
	list.forEach(System.out::println);
}

c> Consultar información del usuario a través de condiciones del mapa

@Test
public void testSelectByMap(){
    
    
	//通过map条件查询用户信息
	//SELECT id,name,age,email FROM user WHERE name = ? AND age = ?
	Map<String, Object> map = new HashMap<>();
	map.put("age", 22);
	map.put("name", "admin");
	List<User> list = userMapper.selectByMap(map);
	list.forEach(System.out::println);
}

d>Consultar todos los datos

@Test
public void testSelectList(){
    
    
	//查询所有用户信息
	//SELECT id,name,age,email FROM user
	List<User> list = userMapper.selectList(null);
	list.forEach(System.out::println);
}

Al observar los métodos en BaseMapper, la mayoría de los métodos tienen parámetros formales de tipo Wrapper. Este es un constructor condicional, que puede establecer diferentes condiciones para las declaraciones SQL. Si no hay condición, puede asignar nulo al parámetro formal, es decir, consultar (eliminar/modificar) todos los datos

6. Servicios generales

Descripción:
Servicio general CRUD encapsula la interfaz IService y encapsula aún más CRUD. Use get para consultar una sola línea remove para eliminar una lista para consultar una página de colección. El método de nomenclatura del prefijo de paginación distingue la capa Mapper para evitar confusiones. El genérico Se recomienda T para cualquier objeto de entidad. Si existe la posibilidad de personalizar el método de servicio general
, cree su propio IBaseService para heredar la clase base proporcionada por Mybatis-Plus
Dirección del sitio web oficial: https://baomidou.com/pages/49cc81 /#service-crud-%E6%8E%A5%E5%8F%A3

a>IServicio

Hay una interfaz IService y su clase de implementación ServiceImpl en MyBatis-Plus, que encapsula la lógica de la capa de negocio común.Para más detalles, consulte el código fuente IService y ServiceImpl

b>Crear interfaz de servicio y clase de implementación

package com.xusheng.mybatisplus.service;

import com.xusheng.mybatisplus.entity.User;
import com.baomidou.mybatisplus.extension.service.IService;

/**
 * <p>
 *  服务类
 * </p>
 *UserService继承IService模板提供的基础功能
 * @author xusheng
 * @since 2023-04-01
 */
public interface UserService extends IService<User> {
    
    

}

package com.xusheng.mybatisplus.service.impl;

import com.xusheng.mybatisplus.entity.User;
import com.xusheng.mybatisplus.mapper.UserMapper;
import com.xusheng.mybatisplus.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

/**
 * <p>
 *  服务实现类
 * </p>
 *ServiceImpl实现了IService,提供了IService中基础功能的实现
 * * 若ServiceImpl无法满足业务需求,则可以使用自定的UserService定义方法,并在实现类中实现
 * @author xusheng
 * @since 2023-04-01
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    
    

}

c> Número de registros de consulta de prueba

package com.xusheng.mybatisplus;


import com.xusheng.mybatisplus.entity.User;
import com.xusheng.mybatisplus.service.UserService;
import com.xusheng.mybatisplus.service.impl.UserServiceImpl;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.ArrayList;
import java.util.List;

@SpringBootTest
public class MyBatisPlusServiceTest {
    
    

    @Autowired
    private UserService userService;

    @Test
    public void testGetCount(){
    
    
        //查询总记录数
        //SELECT COUNT( * ) FROM user
        long count = userService.count();
        System.out.println("总记录数:"+count);
    }
   
}

d> inserto de lote de prueba

 @Test
    public void testInsertMore(){
    
    
        //批量添加
        // SQL长度有限制,海量数据插入单条SQL无法实行,
        // 因此MP将批量插入放在了通用Service中实现,而不是通用Mapper
        //INSERT INTO user ( id, name, age ) VALUES ( ?, ?, ? )
        ArrayList<User> list = new ArrayList<>();
        for (int i = 1; i < 10; i++) {
    
    
            User user = new User();
            user.setName("ybc"+i);
            user.setAge(20+i);
            list.add(user);
        }
        boolean b = userService.saveBatch(list);
        System.out.println(b);

    }

Cuatro, anotaciones comunes

1. @NombreDeLaTabla

Después de las pruebas anteriores, cuando usamos MyBatis-Plus para implementar CRUD básico, no especificamos la tabla a operar, sino que configuramos el Usuario genérico cuando la interfaz de Mapper heredó BaseMapper, y la tabla operada es la tabla de usuario
. MyBatis-Plus determina la tabla a operar, está determinada por el tipo genérico de BaseMapper, es decir, el tipo de entidad, y el nombre de la tabla de la operación por defecto es consistente con el nombre de la clase del tipo de entidad

a> pregunta

Si el nombre de clase del tipo de clase de entidad es inconsistente con el nombre de la tabla que se va a operar, ¿qué problemas ocurrirán?
Cambiamos el nombre del usuario de la tabla a t_user, y el
programa de función de consulta de prueba lanzó una excepción, la tabla 'mybatis_plus.user' no existe, porque el nombre de la tabla actual es t_user, y el nombre de la tabla de la operación predeterminada es el mismo que el nombre de clase del tipo de entidad, es decir, superficie de usuario
inserte la descripción de la imagen aquí

b> resuelve el problema con @TableName

Agregue @TableName("t_user") al tipo de clase de entidad para identificar la tabla correspondiente a la clase de entidad, y la instrucción SQL se puede ejecutar con éxito
inserte la descripción de la imagen aquí

c> Resuelva el problema a través de la configuración global

En el proceso de desarrollo, a menudo nos encontramos con los problemas anteriores, es decir, las tablas correspondientes a clases de entidad tienen prefijos fijos, como t_ o tbl_
En este momento, puede usar la configuración global proporcionada por MyBatis-Plus para corresponder a clases de entidad El prefijo predeterminado se establece para el nombre de la tabla, por lo que no es necesario identificar la tabla correspondiente a la clase de entidad a través de @TableName en cada clase de entidad.

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  # 设置MyBatis-Plus的全局配置
  global-config:
    db-config:
      # 设置实体类所对应的表的统一前缀
      table-prefix: t_

2, @TableId

Después de las pruebas anteriores, cuando MyBatis-Plus implemente CRUD, utilizará id como la columna de clave principal de forma predeterminada, y al insertar datos, generará id en función de la estrategia del algoritmo de copo de nieve de forma predeterminada.

a> pregunta

Si la clase de entidad y la tabla representan la clave principal en lugar de id, pero otros campos, como uid, ¿MyBatis-Plus reconocerá automáticamente uid como la columna de clave principal?
El atributo id en nuestra clase de entidad se cambia a uid, y el campo id en la tabla también se cambia a uid. El
programa de función de adición de prueba arroja una excepción. El campo 'uid' no tiene un valor predeterminado, lo que indica que MyBatis- Plus no usa uid como asignación de clave principal
inserte la descripción de la imagen aquí

b> resuelve el problema por @TableId

En el atributo uid en la clase de entidad, @TableId lo identifica como la clave principal y la instrucción SQL se puede ejecutar con éxito
inserte la descripción de la imagen aquí

c> atributo de valor de @TableId

Si el atributo correspondiente a la clave principal en la clase de entidad es id, y el campo que representa la clave principal en la tabla es uid, en este momento, si solo se agrega la anotación @TableId a la id del atributo, una excepción columna Desconocida ' id' en 'lista de campos', es decir, MyBatis-Plus seguirá usando id como la clave principal de la tabla, y la clave principal en la tabla es el campo uid. En este momento, debe pasar el
valor atributo de la anotación @TableId para especificar el campo de clave principal en la tabla, @TableId("uid") o
@TableId (value="uid")
inserte la descripción de la imagen aquí

d> atributo de tipo de @TableId

El atributo de tipo se usa para definir la estrategia de clave principal
comúnmente utilizada en la estrategia de clave principal:
inserte la descripción de la imagen aquí
configure la estrategia de clave principal global:

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  # 设置MyBatis-Plus的全局配置
  global-config:
    db-config:
      # 设置实体类所对应的表的统一前缀
      table-prefix: t_
      # 设置统一的主键生成策略
      id-type: auto

e>Algoritmo del copo de nieve

fondo

Es necesario elegir una solución adecuada para hacer frente al crecimiento de la escala de datos, a fin de hacer frente al aumento gradual de la presión de acceso y el volumen de datos.
Los métodos de expansión de la base de datos incluyen principalmente: subbase de datos comercial, replicación maestro-esclavo y subtabla de base de datos.

tabla de base de datos

El almacenamiento distribuido de diferentes datos comerciales en diferentes servidores de bases de datos puede respaldar empresas con millones o incluso decenas de millones de usuarios. Sin embargo, si el negocio continúa desarrollándose, los datos de una sola tabla del mismo negocio también alcanzarán el cuello de botella de procesamiento de un solo servidor de base de datos. Por ejemplo, si los cientos de millones de datos de usuario de Taobao se almacenan en una tabla en un servidor de base de datos, definitivamente no podrá cumplir con los requisitos de rendimiento. En este momento, los datos de una sola tabla deben dividirse.
Hay dos formas de dividir los datos de una sola tabla: división de tabla vertical y división de tabla horizontal. El diagrama esquemático es el siguiente:

inserte la descripción de la imagen aquí
mesa vertical

La división vertical de tablas es adecuada para dividir algunas columnas que no se usan comúnmente y ocupan mucho espacio en la tabla.
Por ejemplo, para los campos de apodo y descripción en el diagrama anterior, supongamos que somos un sitio web de citas. Cuando los usuarios filtran a otros usuarios, utilizan principalmente los dos campos de edad y sexo para la consulta, mientras que los dos campos de apodo y descripción son principalmente se usa para mostrar. Generalmente no se usa en consultas comerciales. La descripción en sí es relativamente larga, por lo que podemos separar estos dos campos en otra tabla, de modo que al consultar la edad y el sexo, pueda aportar cierta mejora en el rendimiento.

mesa de nivel

La fragmentación de tablas horizontal es adecuada para tablas con una cantidad particularmente grande de filas. Algunas empresas requieren la fragmentación de tablas si la cantidad de filas en una sola tabla supera los 50 millones. Este número se puede usar como referencia, pero no es un estándar absoluto. La clave depende del rendimiento de acceso de la tabla. Para algunas tablas más complejas, puede ser más de 10 millones para dividir la tabla, y para algunas tablas simples, incluso si los datos almacenados superan los 100 millones de filas, no es necesario dividir la tabla.
Pero en cualquier caso, cuando el volumen de datos de la tabla alcanza decenas de millones, el arquitecto debe estar atento, porque es probable que se trate de un cuello de botella en el rendimiento o de un peligro oculto de la arquitectura.
En comparación con el particionamiento de tablas verticales, el particionamiento de tablas horizontales introducirá más complejidad, por ejemplo, cómo lidiar con el requisito de una identificación de datos única a nivel mundial.

主键自增
①以最常见的用户 ID 为例,可以按照 1000000 的范围大小进行分段,1 ~ 999999 放到表 1中,
1000000 ~ 1999999 放到表2中,以此类推。
②复杂点:分段大小的选取。分段太小会导致切分后子表数量过多,增加维护复杂度;分段太大可能会
导致单表依然存在性能问题,一般建议分段大小在 100 万至 2000 万之间,具体需要根据业务选取合适
的分段大小。
③优点:可以随着数据的增加平滑地扩充新的表。例如,现在的用户是 100 万,如果增加到 1000 万,
只需要增加新的表就可以了,原有的数据不需要动。
④缺点:分布不均匀。假如按照 1000 万来进行分表,有可能某个分段实际存储的数据量只有 1 条,而
另外一个分段实际存储的数据量有 1000 万条。
取模
①同样以用户 ID 为例,假如我们一开始就规划了 10 个数据库表,可以简单地用 user_id % 10 的值来
表示数据所属的数据库表编号,ID 为 985 的用户放到编号为 5 的子表中,ID 为 10086 的用户放到编号
为 6 的子表中。
②复杂点:初始表数量的确定。表数量太多维护比较麻烦,表数量太少又可能导致单表性能存在问题。
③优点:表分布比较均匀。
④缺点:扩充新的表很麻烦,所有数据都要重分布。
雪花算法
雪花算法是由Twitter公布的分布式主键生成算法,它能够保证不同表的主键的不重复性,以及相同表的
主键的有序性。
①核心思想:
长度共64bit(一个long型)。
首先是一个符号位,1bit标识,由于long基本类型在Java中是带符号的,最高位是符号位,正数是0,负
数是1,所以id一般是正数,最高位是041bit时间截(毫秒级),存储的是时间截的差值(当前时间截 - 开始时间截),结果约等于69.73年。
10bit作为机器的ID(5个bit是数据中心,5个bit的机器ID,可以部署在1024个节点)。
12bit作为毫秒内的流水号(意味着每个节点在每毫秒可以产生 4096 个 ID)。

inserte la descripción de la imagen aquí
②Ventajas: en general, se clasifican según el incremento de tiempo, no se producirán colisiones de ID en todo el sistema distribuido y la eficiencia es alta.

3, @TableField

Después de las pruebas anteriores, podemos encontrar que cuando MyBatis-Plus ejecuta declaraciones SQL, debe asegurarse de que los nombres de los atributos en la clase de entidad sean consistentes con los nombres de los campos en la tabla.Si los nombres de los atributos en la clase de entidad son inconsistentes con el nombres de campo, ¿qué problemas ocurrirán
?

a> Caso 1

Si los atributos en la clase de entidad usan el estilo de nomenclatura camel case, y los campos en la tabla usan el estilo
de nomenclatura de guión bajo Equivale a configurar en MyBatis

b> Caso 2

Si los atributos de la clase de entidad y los campos de la tabla no cumplen la condición 1,
como el nombre del atributo de clase de entidad, el nombre de usuario del campo en la tabla
debe usar @TableField("nombre de usuario") en el atributo de clase de entidad para establecer el nombre del campo correspondiente al atributo
inserte la descripción de la imagen aquí

4, @TableLogic

a> Lápida

Eliminación física: eliminación real, elimine los datos correspondientes de la base de datos y luego no se pueden consultar los datos eliminados
Eliminación lógica: eliminación falsa, cambie el estado del campo que representa si los datos correspondientes se han eliminado a "estado eliminado" y luego Todavía puede ver este registro de datos en la base de datos
Escenario de uso: la recuperación de datos es posible

b> implementar la eliminación lógica

paso 1: cree una columna de estado de lápida en la base de datos y establezca el valor predeterminado en 0
inserte la descripción de la imagen aquí
paso 2: agregue el atributo de lápida a la clase de entidad
inserte la descripción de la imagen aquí
paso 3: pruebe

Para probar la función de eliminación, la ejecución real es modificar UPDATE t_user SET is_deleted=1 WHERE id=? AND
is_deleted=0 Para probar la función de consulta, los datos eliminados lógicamente no se consultarán de manera predeterminada SELECCIONE id, nombre de usuario AS
nombre, edad ,email,is_deleted FROM t_user DONDE is_deleted=0

5. Constructores condicionales e interfaces de uso común

1. Introducción a los envoltorios

inserte la descripción de la imagen aquí

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

2, contenedor de consulta

a>Ejemplo 1: Montaje de condiciones de consulta

package com.xusheng.mybatisplus;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.xusheng.mybatisplus.entity.User;
import com.xusheng.mybatisplus.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;
import java.util.Map;

@SpringBootTest
public class MyBatisPlusWrapperTest {
    
    

    @Autowired
    private UserMapper userMapper;

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

b>Ejemplo 2: Montaje de condiciones de clasificación

	@Test
    public void test02(){
    
    
        //查询用户信息,按照年龄的降序排序,若年龄相同,则按照id升序排序
        //SELECT uid AS id,user_name AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 ORDER BY age DESC,uid ASC
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.orderByDesc("age")
                .orderByAsc("id");
        List<User> list = userMapper.selectList(queryWrapper);
        list.forEach(System.out::println);
    }

c>Ejemplo 3: Condición de eliminación de ensamblaje

	@Test
    public void test03(){
    
    
        //删除邮箱地址为null的用户信息
        //UPDATE t_user SET is_deleted=1 WHERE is_deleted=0 AND (email IS NULL)
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.isNotNull("email");
        int result = userMapper.delete(queryWrapper);
        System.out.println("result:"+result);
    }

d>Ejemplo 4: Prioridad de las condiciones

	@Test
    public void test04(){
    
    
        //将(年龄大于20并且用户名中包含有a)或邮箱为null的用户信息修改
        //UPDATE t_user SET user_name=?, email=? WHERE is_deleted=0 AND (age > ? AND user_name LIKE ? OR email IS NULL)
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.gt("age",20)
                .like("name","a")
                .or()
                .isNull("email");
        User user = new User();
        user.setName("小明");
        user.setEmail("[email protected]");
        int result = userMapper.update(user, queryWrapper);
        System.out.println("result:"+result);
    }
	@Test
    public void test05() {
    
    
        //将用户名中包含有a并且(年龄大于20或邮箱为null)的用户信息修改
        //lambda中的条件优先执行
        //UPDATE t_user SET user_name=?, email=? WHERE is_deleted=0 AND (user_name LIKE ? AND (age > ? OR email IS NULL))
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.like("name","a")
                .and(i->i.gt("age",20).or().isNull("email"));
        User user = new User();
        user.setName("安澜");
        user.setEmail("[email protected]");
        int result = userMapper.update(user, queryWrapper);
        System.out.println("result:"+result);
    }

e>Ejemplo 5: Montaje de la cláusula select

	@Test
    public void test06() {
    
    
        //查询用户的用户名、年龄、邮箱信息
        //SELECT user_name,age,email FROM t_user WHERE is_deleted=0
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.select("name","age","email");
        List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
        System.out.println(maps);
    }

f>Ejemplo 6: Implementar subconsulta

	 @Test
    public void test07() {
    
    
        //查询id小于等于100的用户信息
        //SELECT uid AS id,user_name AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (uid IN (select uid from t_user where uid <= 100))
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.inSql("id","select id from t_user where id <= 100");
        List<User> list = userMapper.selectList(queryWrapper);
        list.forEach(System.out::println);
    }

3, envoltorio de actualización

@Test
    public void test08() {
    
    
        //将用户名中包含有a并且(年龄大于20或邮箱为null)的用户信息修改
        //组装set子句以及修改条件
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        //lambda表达式内的逻辑优先运算
        updateWrapper.like("name","a")
                .and(i->i.gt("age","20").or().isNull("email"));
        updateWrapper.set("name","安澜").set("email","[email protected]");
        int result = userMapper.update(null, updateWrapper);
        System.out.println("result:"+result);
    }

4, condición

En el proceso de desarrollo real, ensamblar condiciones es una función común, y estos datos de condiciones provienen de la entrada del usuario y son opcionales. Por lo tanto, cuando ensamblamos estas condiciones, primero debemos determinar si el usuario ha seleccionado estas condiciones. Si es así, necesitamos para ensamblar la condición, si no hay selección, no debe ensamblarse, para no afectar el resultado de la ejecución de SQL

Idea uno:

	@Test
    public void test09() {
    
    
        //SELECT uid AS id,user_name AS name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (user_name LIKE ? AND age <= ?)
        String username = "a";
        Integer ageBegin=20;
        Integer ageEnd = 30;
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        if(StringUtils.isNotBlank(username)){
    
    
            //isNotBlank判断某个字符创是否不为空字符串、不为null、不为空白符
            queryWrapper.like("name",username);
        }
        if(ageBegin != null){
    
    
            queryWrapper.ge("age",ageBegin);
        }
        if(ageEnd != null){
    
    
            queryWrapper.le("age",ageEnd);
        }
        List<User> list = userMapper.selectList(queryWrapper);
        list.forEach(System.out::println);
    }

Idea 2:

No hay problema con el esquema de implementación anterior, pero el código es más complicado. Podemos usar el método sobrecargado con el parámetro de condición para construir la condición de consulta y simplificar la escritura del código.

 	@Test
    public void test10(){
    
    
        String username = "a";
        Integer ageBegin= null;
        Integer ageEnd = 30;
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.like(StringUtils.isNotBlank(username),"name",username)
                .ge(ageBegin != null,"age",ageBegin)
                .le(ageEnd != null,"age",ageEnd);
        List<User> list = userMapper.selectList(queryWrapper);
        list.forEach(System.out::println);
    }

5, envoltorio LambdaQuery

	@Test
    public void test11(){
    
    
        //定义查询条件,有可能为null(用户未输入)
        String username = "a";
        Integer ageBegin= null;
        Integer ageEnd = 30;
        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> list = userMapper.selectList(queryWrapper);
        list.forEach(System.out::println);
    }

6, paquete de actualización de Lambda

	@Test
    public void test12() {
    
    
        //将用户名中包含有a并且(年龄大于20或邮箱为null)的用户信息修改
        LambdaUpdateWrapper<User> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.like(User::getName,"a")
                .and(i->i.gt(User::getAge,20).or().isNull(User::getEmail));
        updateWrapper.set(User::getName,"安澜").set(User::getEmail,"[email protected]");
        int result = userMapper.update(null, updateWrapper);
        System.out.println("result:"+result);

    }

6. Complementos

1. Complemento de paginación

MyBatis Plus tiene su propio complemento de paginación, y la función de paginación se puede realizar con una configuración simple

a>Añadir clase de configuración

package com.xusheng.mybatisplus.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.MyBatisExceptionTranslator;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
//扫描mapper接口所在的包
@MapperScan("com/xusheng/mybatisplus/mapper")
public class MyBatisPlusConfig {
    
    

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
    
    
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //添加分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        

        return interceptor;
    }
}

b> prueba

package com.xusheng.mybatisplus;


import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.xusheng.mybatisplus.entity.Product;
import com.xusheng.mybatisplus.entity.User;
import com.xusheng.mybatisplus.mapper.ProductMapper;
import com.xusheng.mybatisplus.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.List;

@SpringBootTest
public class MyBatisPlusPluginsTest {
    
    
    @Autowired
    private UserMapper userMapper;

    @Autowired
    private ProductMapper productMapper;

    @Test
    public void testPage(){
    
    
        //设置分页参数
        Page<User> page = new Page<>(1, 5);
        userMapper.selectPage(page, null);
        //获取分页数据
        List<User> list = page.getRecords();
        list.forEach(System.out::println);
        System.out.println("当前页:"+page.getCurrent());
        System.out.println("每页显示的条数:"+page.getSize());
        System.out.println("总记录数:"+page.getTotal());
        System.out.println("总页数:"+page.getPages());
        System.out.println("是否有上一页:"+page.hasPrevious());
        System.out.println("是否有下一页:"+page.hasNext());

    }

}

Resultado de la prueba:
Usuario (id=1, nombre=Jone, edad=18, correo electró[email protected], isDeleted=0) Usuario(id=2, nombre=Jack, edad=20, correo electró[email protected] , estáEliminado=0) Usuario(id=3, nombre=Tom,edad=28, correo electró[email protected], estáEliminado=0) Usuario(id=4, nombre=Sandy, edad=21, correo electrónico=prueba4@baomidou .com, isDeleted=0) Usuario (id=5, name=Billie, age=24, email=test5@ba
omidou.com, isDeleted=0) Página actual: 1 Elementos mostrados en cada página: 5 Registros totales: 17 Total páginas: 4 Si hay una página anterior: falso Si hay una página siguiente: verdadero

2. paginación personalizada xml

a> Definir el método de interfaz en UserMapper

 /***
     * 通过年龄查询用户信息并分页
     * MyBatis-Plus所提供的分页对象,必须位于第一个参数的位置
     */
    Page<User> selectPageVo(@Param("page") Page<User> page,@Param("age") Integer age);


Escribir SQL en b>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.xusheng.mybatisplus.mapper.UserMapper">

    <!--Map<String,Object> selectMapById(Long id);-->
    <select id="selectMapById" resultType="map">
        select id,name ,age,email from t_user where id = #{
    
    id}
    </select>
<!--
    Page<User> selectPageVo(@Param("page") Page<User> page,@Param("age") Integer age);
-->
    <select id="selectPageVo" resultType="User">
        select id,name ,age,email from t_user where age > #{
    
    age}
    </select>



</mapper>

c> prueba

	@Test
    public void testSelectPageVo(){
    
    
        //设置分页参数
        Page<User> page = new Page<>(1, 5);
        userMapper.selectPageVo(page, 20);
        //获取分页数据
        List<User> list = page.getRecords();
        list.forEach(System.out::println);
        System.out.println("当前页:"+page.getCurrent());
        System.out.println("每页显示的条数:"+page.getSize());
        System.out.println("总记录数:"+page.getTotal());
        System.out.println("总页数:"+page.getPages());
        System.out.println("是否有上一页:"+page.hasPrevious());
        System.out.println("是否有下一页:"+page.hasNext());
    }

Resultado:
Usuario (id=3, nombre=Tom, edad=28, correo electró[email protected], isDeleted=null) Usuario(id=4, nombre=Sandy, edad=21, correo electró[email protected], isDeleted=null) Usuario (id=5, name=Billie,age=24, [email protected], isDeleted=null) User(id=8, name=ybc1, age=21,email=null, isDeleted= null) Usuario (id=9, name=ybc2, age=22, email=null, isDeleted=null)
Página actual: 1 Elementos mostrados en cada página: 5 Registros totales: 12 Páginas totales: 3 Si hay una página anterior: falso si hay una página siguiente: verdadero

3. Cerradura optimista

a> escena

Una mercancía tiene un precio de costo de 80 yuanes y un precio de venta de 100 yuanes. El jefe primero notificó a Xiao Li que debería aumentar el precio del producto en 50 yuanes. Xiao Li estaba jugando y se retrasó una hora. Exactamente una hora después, el jefe sintió que el precio del producto había aumentado a 150 yuanes, lo que era demasiado alto y podría afectar las ventas. También informe a Xiao Wang que reducirá el precio del producto en 30 yuanes.
En este momento, Xiao Li y Xiao Wang operan el sistema de fondo de productos básicos al mismo tiempo. Cuando Xiao Li operó, el sistema primero sacó el precio del producto de 100 yuanes; Xiao Wang también estaba operando, y el precio del producto extraído también fue de 100 yuanes. Xiao Li agregó 50 yuanes al precio y almacenó 100+50=150 yuanes en la base de datos; Xiao Wang redujo el producto en 30 yuanes y almacenó 100-30=70 yuanes en la base de datos. Sí, si no hay bloqueo, la operación de Xiao Li estará completamente cubierta por la de Xiao Wang.
Ahora el precio de la materia prima es de 70 yuanes, que es 10 yuanes más bajo que el precio de costo. Unos minutos más tarde, este producto vendió rápidamente más de 1,000 artículos y el jefe perdió más de 10,000.

b> Bloqueo optimista y bloqueo pesimista

En la historia anterior, si se trata de un candado optimista, Xiao Wang comprobará si el precio se ha modificado antes de guardar el precio. Si se ha modificado, se recuperará nuevamente el precio revisado, 150 yuanes, por lo que almacenará 120 yuanes en la base de datos.
Si es un bloqueo pesimista, después de que Xiao Li elimine los datos, Xiao Wang solo puede operar con el precio después de que Xiao Li termine la operación, y se garantizará que el precio final sea de 120 yuanes.

c>Simular conflictos de modificación

Agregar tabla de productos básicos a la base de datos

CREATE TABLE t_product
(
	id BIGINT(20) NOT NULL COMMENT '主键ID',
	NAME VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名称',
	price INT(11) DEFAULT 0 COMMENT '价格',
	VERSION INT(11) DEFAULT 0 COMMENT '乐观锁版本号',
	PRIMARY KEY (id)
);

agregando datos

INSERT INTO t_product (id, NAME, price) VALUES (1, '外星人笔记本', 100);

añadir entidad

package com.xusheng.mybatisplus.entity;

import com.baomidou.mybatisplus.annotation.Version;
import lombok.Data;
@Data
public class Product {
    
    
    private Long id;
    private String name;
    private Integer price;
    private Integer version;
}


agregar mapeador

package com.xusheng.mybatisplus.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.xusheng.mybatisplus.entity.Product;
import org.springframework.stereotype.Repository;

@Repository
public interface ProductMapper extends BaseMapper<Product> {
    
    
}

prueba

    @Test
    public void testProduct01(){
    
    
        //小李查询商品价格
        Product productLi = productMapper.selectById(1);
        System.out.println("小李查询商品价格: "+productLi.getPrice());
        //小王查询商品价格
        Product productWang = productMapper.selectById(1);
        System.out.println("小王查询商品价格: "+productWang.getPrice());
        //小李将商品价格+50
        productLi.setPrice(productLi.getPrice()+50);
        productMapper.updateById(productLi);
        //小王将商品价格-30
        productWang.setPrice(productWang.getPrice()-30);
        productMapper.updateById(productWang);
       
        //老板查询商品价格
        Product productLaoban = productMapper.selectById(1);
        System.out.println("老板查询商品价格: "+productLaoban.getPrice());
    }

d>Proceso de implementación de bloqueo optimista

Agregue un campo de versión a la base de datos
para obtener la versión actual al buscar registros

SELECT id,`name`,price,`version` FROM product WHERE id=1

Al actualizar, versión + 1, si la versión en la instrucción where es incorrecta, la actualización fallará

UPDATE product SET price=price+50, `version`=`version` + 1 WHERE id=1 AND
`version`=1

e>Mybatis-Plus implementa bloqueo optimista

Modificar clase de entidad

package com.xusheng.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;
}


Agregar configuración de complemento de bloqueo optimista

package com.xusheng.mybatisplus.config;

import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import org.mybatis.spring.MyBatisExceptionTranslator;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
//扫描mapper接口所在的包
@MapperScan("com/xusheng/mybatisplus/mapper")
public class MyBatisPlusConfig {
    
    

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor(){
    
    
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //添加分页插件
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        //添加乐观锁插件
        interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());

        return interceptor;
    }
}

Conflictos de modificación de prueba

小李查询商品信息:
SELECT id,name,price,version FROM t_product WHERE id=?
小王查询商品信息:
SELECT id,name,price,version FROM t_product WHERE id=?
小李修改商品价格,自动将version+1
UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=?
Parameters: 外星人笔记本(String), 150(Integer), 1(Integer), 1(Long), 0(Integer)
小王修改商品价格,此时version已更新,条件不成立,修改失败
UPDATE t_product SET name=?, price=?, version=? WHERE id=? AND version=?
Parameters: 外星人笔记本(String), 70(Integer), 1(Integer), 1(Long), 0(Integer)
最终,小王修改失败,查询价格:150
SELECT id,name,price,version FROM t_product WHERE id=?

optimizar el proceso

@Test
    public void testProduct01(){
    
    
        //小李查询商品价格
        Product productLi = productMapper.selectById(1);
        System.out.println("小李查询商品价格: "+productLi.getPrice());
        //小王查询商品价格
        Product productWang = productMapper.selectById(1);
        System.out.println("小王查询商品价格: "+productWang.getPrice());
        //小李将商品价格+50
        productLi.setPrice(productLi.getPrice()+50);
        productMapper.updateById(productLi);
        //小王将商品价格-30
        productWang.setPrice(productWang.getPrice()-30);
        int result = productMapper.updateById(productWang);
        if (result == 0){
    
    
            //操作失败,重试
            Product productNew = productMapper.selectById(1);
            productNew.setPrice(productNew.getPrice()-30);
            productMapper.updateById(productNew);
        }
        //老板查询商品价格
        Product productLaoban = productMapper.selectById(1);
        System.out.println("老板查询商品价格: "+productLaoban.getPrice());
    }

Siete, enumeración general

Algunos valores de campo en la tabla son fijos, como el género (masculino o femenino), en este momento podemos usar la enumeración general de MyBatis-Plus para lograr

1. Agregue el campo sexo a la tabla de la base de datos

inserte la descripción de la imagen aquí

2. Crear un tipo de enumeración genérico

package com.xusheng.mybatisplus.enums;

import com.baomidou.mybatisplus.annotation.EnumValue;
import lombok.Getter;

@Getter
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;
    }
}

3. Enumeración general de escaneo de configuración

mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  # 设置MyBatis-Plus的全局配置
  global-config:
    db-config:
      # 设置实体类所对应的表的统一前缀
      table-prefix: t_
      # 设置统一的主键生成策略
      id-type: auto
  # 配置类型别名所对应的包
  type-aliases-package: com.xusheng.mybatisplus.entity
  # 扫描通用枚举的包
  type-enums-package: com.xusheng.mybatisplus.enums

4. prueba

package com.xusheng.mybatisplus;


import com.xusheng.mybatisplus.entity.User;
import com.xusheng.mybatisplus.enums.SexEnum;
import com.xusheng.mybatisplus.mapper.UserMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class MyBatisPlusEnumTest {
    
    

    @Autowired
    private UserMapper userMapper;

    @Test
    public  void test(){
    
    
        User user = new User();
        user.setName("admin");
        user.setAge(33);
        //设置性别信息为枚举项,会将@EnumValue注解所标识的属性值存储到数据库
        user.setSex(SexEnum.MALE);
        //INSERT INTO t_user ( username, age, sex ) VALUES ( ?, ?, ? )
        //Parameters: Enum(String), 20(Integer), 1(Integer)
        int result = userMapper.insert(user);
        System.out.println("result:"+result);
    }
}

8. Generador de código

1. Introducir dependencias

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

2. Generación rápida

package com.xusheng.mybatisplus;

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

import java.util.Collections;

/**
 * Date:2022/2/15
 * Author:xusheng
 * Description:
 */
public class FastAutoGeneratorTest {
    
    

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


Nueve, múltiples fuentes de datos

Aplicable a una variedad de escenarios: multibiblioteca pura, separación de lectura y escritura, un maestro y varios esclavos, modo mixto, etc.
Ahora simularemos un escenario multibiblioteca puro y otros escenarios son
similares
. dos bibliotecas, respectivamente: mybatis_plus (la biblioteca anterior no se mueve) y mybatis_plus_1 (nueva), mover la tabla de productos de la biblioteca mybatis_plus a la biblioteca mybatis_plus_1, para que cada biblioteca tenga una tabla, y obtener datos de usuario y datos de productos a través de
un caso de prueba Si obtiene más instrucciones Simulación de biblioteca exitosa

1. Crear base de datos y tablas

Crear base de datos mybatis_plus_1 y tabla producto

CREATE DATABASE `mybatis_plus_1` /*!40100 DEFAULT CHARACTER SET utf8mb4 */;
use `mybatis_plus_1`;
CREATE TABLE product
(
	id BIGINT(20) NOT NULL COMMENT '主键ID',
	name VARCHAR(30) NULL DEFAULT NULL COMMENT '商品名称',
	price INT(11) DEFAULT 0 COMMENT '价格',
	version INT(11) DEFAULT 0 COMMENT '乐观锁版本号',
	PRIMARY KEY (id)
);

Agregar datos de prueba

INSERT INTO product (id, NAME, price) VALUES (1, '外星人笔记本', 100);

Eliminar la tabla de productos de la biblioteca mybatis_plus

use mybatis_plus;
DROP TABLE IF EXISTS product;

2. Introducir dependencias

<dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>dynamic-datasource-spring-boot-starter</artifactId>
            <version>3.5.0</version>
        </dependency>

3. Configurar múltiples fuentes de datos

Descripción: comentar la conexión de base de datos anterior y agregar una nueva configuración

spring:
  # 配置数据源信息
  datasource:
    dynamic:
      # 设置默认的数据源或者数据源组,默认值即为master
      primary: master
      # 严格匹配数据源,默认false.true未匹配到指定数据源时抛异常,false使用默认数据源
      strict: false
      datasource:
        master:
          url: jdbc:mysql://localhost:3306/mybatis_plus1?characterEncoding=utf-8&useSSL=false
          driver-class-name: com.mysql.cj.jdbc.Driver
          username: root
          password: root
        slave_1:
          url: jdbc:mysql://localhost:3306/mybatis_plus_1?characterEncoding=utf-8&useSSL=false
          driver-class-name: com.mysql.cj.jdbc.Driver
          username: root
          password: root

4. Crear servicio de usuario

package com.xusheng.mybatisplus.service;

import com.xusheng.mybatisplus.entity.User;
import com.baomidou.mybatisplus.extension.service.IService;

/**
 * <p>
 *  服务类
 * </p>
 *
 * @author xusheng
 * @since 2023-04-04
 */
public interface UserService extends IService<User> {
    
    

}

package com.xusheng.mybatisplus.service.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.xusheng.mybatisplus.entity.User;
import com.xusheng.mybatisplus.mapper.UserMapper;
import com.xusheng.mybatisplus.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author xusheng
 * @since 2023-04-04
 */
@Service
@DS("master")
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    
    

}

5. Crear un servicio básico

package com.xusheng.mybatisplus.service;

import com.xusheng.mybatisplus.entity.Product;
import com.baomidou.mybatisplus.extension.service.IService;

/**
 * <p>
 *  服务类
 * </p>
 *
 * @author xusheng
 * @since 2023-04-04
 */
public interface ProductService extends IService<Product> {
    
    

}

package com.xusheng.mybatisplus.service.impl;

import com.baomidou.dynamic.datasource.annotation.DS;
import com.xusheng.mybatisplus.entity.Product;
import com.xusheng.mybatisplus.mapper.ProductMapper;
import com.xusheng.mybatisplus.service.ProductService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author xusheng
 * @since 2023-04-04
 */
@Service
@DS("slave_1")
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product> implements ProductService {
    
    

}

6. prueba

package com.xusheng.mybatisplus;


import com.xusheng.mybatisplus.service.ProductService;
import com.xusheng.mybatisplus.service.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class MybatisPlusDatasourceApplicationTests {
    
    

    @Test
    void contextLoads() {
    
    
    }

    @Autowired
    private UserService userService;

    @Autowired
    private ProductService productService;

    @Test
    public void test(){
    
    
        System.out.println(userService.getById(1));
        System.out.println(productService.getById(1));
    }

}

Resultados:
1. Si el objeto se puede obtener con éxito, la prueba es exitosa
2. Si nos damos cuenta de la separación de lectura y escritura, agregue el método de operación de escritura a la fuente de datos de la base de datos maestra y el método de operación de lectura para agregar la fuente de datos de la base de datos esclava, y cambiar automáticamente, ¿es posible lograr la separación de lectura y escritura?

10. Complemento MyBatisX

MyBatis-Plus nos proporciona potentes plantillas de mapas y servicios, que pueden mejorar en gran medida la eficiencia del desarrollo;
pero en el proceso de desarrollo real, MyBatis-Plus no puede resolver todos los problemas por nosotros, como algunos SQL complejos, consultas conjuntas de varias tablas, necesitamos escribir código y sentencias SQL por nosotros mismos.¿Cómo podemos solucionar este problema rápidamente?En este momento, podemos utilizar el complemento MyBatisX,
MyBatisX es un complemento de desarrollo rápido basado en IDEA, nacido para la eficiencia.

Uso del complemento MyBatisX: https://baomidou.com/pages/ba5b24/

Supongo que te gusta

Origin blog.csdn.net/m0_52435951/article/details/129968371
Recomendado
Clasificación