Tabla de contenido
1. Introducción a MyBatis-Plus
2. Crear base de datos y tabla
Tres, agregar, eliminar, modificar y verificar
2. Llame a la capa Mapper para implementar CRUD
4. Capa de servicio de llamadas para operar datos
2.3 El atributo de valor de @TableId
2.4 El atributo de tipo de @TableId
1. Introducción a la envoltura
6.Contenedor de actualización de Lambda
Seis complementos de uso común
3.2 Bloqueo optimista y bloqueo pesimista
3.3 Simular conflictos de modificación
3.4 El bloqueo optimista resuelve el problema
Nueve, múltiples fuentes de datos
1. Crear base de datos y tabla
2. Los nuevos proyectos introducen dependencias
3. Escribir archivos de configuración
6. Escriba el método de prueba
1. Instala el complemento MyBatisX
1. Introducción a MyBatis-Plus
1. Introducción
MyBatis-Plus (se abre en ventana nueva) (MP para abreviar) es una herramienta de mejora para MyBatis (se abre en ventana nueva) Sobre la base de MyBatis, solo se realizan mejoras sin cambios, y nació para simplificar el desarrollo y mejorar la eficiencia.
Nuestra visión es convertirnos en el mejor socio de MyBatis, al igual que el 1P y 2P en Contra, la eficiencia se duplica cuando se combina con amigos.
2. Características
-
Sin intrusión : solo mejora y sin cambios, la introducción no afectará el proyecto existente, tan suave como la seda.
-
Baja pérdida : el CURD básico se inyectará automáticamente cuando se inicie, el rendimiento es básicamente sin pérdidas y la operación orientada a objetos se realiza directamente
-
Potentes operaciones CRUD : mapeador general y servicio general incorporados, la mayoría de las operaciones CRUD en una sola tabla se pueden realizar con solo una pequeña cantidad de configuración, y hay un potente constructor de condiciones para satisfacer diversas necesidades de uso
-
Admite llamada de formulario Lambda : a través de expresiones Lambda, es conveniente escribir varias condiciones de consulta, sin necesidad de preocuparse por los errores tipográficos de campo
-
Admite la generación automática de claves principales : admite hasta 4 estrategias de claves principales (incluido un generador de ID único distribuido - Secuencia), que se pueden configurar libremente para resolver perfectamente el problema de la clave principal
-
Admite el modo ActiveRecord : admite la llamada de formulario ActiveRecord, la clase de entidad solo necesita heredar la clase Model para realizar potentes operaciones CRUD
-
Admite operaciones generales globales personalizadas : admite inyección de método general global (escribir una vez, usar en cualquier lugar)
-
Generador de código incorporado : use complementos de código o Maven para generar rápidamente códigos de capa de mapeador, modelo, servicio y controlador, admitir motores de plantillas y tener muchas configuraciones personalizadas esperando que las use
-
Complemento de paginación incorporado : basado en la paginación física de MyBatis, los desarrolladores no necesitan 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 múltiples bases de datos : admite MySQL, MariaDB, Oracle, DB2, H2, HSQL, SQLite, Postgre, SQLServer y otras bases de datos
-
Complemento de análisis de rendimiento incorporado : puede generar declaraciones SQL y su tiempo de ejecución. Se recomienda habilitar esta función durante el desarrollo y las pruebas, que pueden descubrir rápidamente las consultas lentas.
-
Complemento de intercepción global incorporado : proporciona análisis inteligente y bloqueo de operaciones de eliminación y actualización en toda la tabla, y también puede personalizar las reglas de intercepción para evitar operaciones incorrectas
3. Base de datos de soporte
Cualquier base de datos que pueda
MyBatis
usar CRUD y admita SQL estándar, el soporte específico es el siguiente, si no está en la lista a continuación, consulte la parte de paginación del tutorial PR para su soporte.
-
MySQL, Oracle, DB2, H2, HSQL, SQLite, PostgreSQL, SQLServer, Phoenix, Gauss, ClickHouse, Sybase, OceanBase, Firebird, Cubrid, Goldilocks, csiidb
-
Base de datos Dameng, Base de datos Xugu, Base de datos Jincang de la Universidad Renmin, Base de datos general Nanda (Huaku), Base de datos general Nanda, Base de datos Shentong, Base de datos Hangao
4. Estructura del marco
Primero escanee la clase de entidad (entidad de escaneo), extraiga los atributos en la clase de entidad a través de la extracción de reflexión (extracción de reflexión) y luego analice quién es la tabla en la que se operará, quién es el atributo en la clase de entidad que se operará y quien es el campo Luego genere la declaración sql e inyéctela en el contenedor mybati La tabla que queremos operar debe estar determinada por la clase de entidad y los atributos de la clase de entidad.
5. Dirección oficial
Web Oficial: MyBatis-Plus
Documento oficial: Introducción | MyBatis-Plus
2. Caso introductorio
1. Entorno de desarrollo
-
IDEA: IDEA 2019.3.5
-
JDK:JDK8+
-
Herramientas de compilación: Maven 3.5.4
-
MySQL:MySQL 8.0.24
-
Navicat:Navicat Premium 15
-
Bota de resorte: 2.6.7
-
MyBatis-Plus:3.5.1
2. Crear base de datos y tabla
-
Abra Navicat y ejecute el siguiente script SQL para crear una base de datos y una 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;
-
Insertar algunos datos de prueba
INSERT INTO user (id, name, age, email) VALUES (1, 'Jone', 18, '[email protected]'), (2, 'Jack', 20, '[email protected]'), (3, 'Tom', 28, '[email protected]'), (4, 'Sandy', 21, '[email protected]'), (5, 'Billie', 24, '[email protected]');
3. Crea un proyecto
-
Use
Spring Initializer
la inicialización rápida de un proyecto Spring Boot -
MyBatis-Plus
Dependencias importadas<!--mybatis-plus启动器--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.1</version> </dependency> <!-- 数据库驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.27</version> </dependency>
-
El paquete de dependencias jar más completo Si el último paso de la operación de captura de pantalla anterior no está marcado, puede copiar directamente todas las siguientes dependencias en el archivo prom
<dependencies> <!--web场景开发启动器--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--lombok:简化实体类开发--> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!--开启测试启动器--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- 数据库驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.27</version> </dependency> <!-- mybatis-plus启动器--> <!-- mybatis-plus 是自己开发,并非官方的! --> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.1</version> </dependency> <!--多数据源依赖--> <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.5.0</version> </dependency> </dependencies>
-
instalar
Lombok
complemento
4. Configurar la codificación
-
application.yaml
archivo de configuraciónspring: #配置数据库 datasource: # 配置连接数据库信息 driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8 username: root password: 123456 mybatis-plus: # 配置MyBatis日志 configuration: log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #全局配置 global-config: db-config: #配置统一的主键策略为自增,如果不设置默认为雪花算法 id-type: auto # 设置实体类所对应的表的统一前缀,为实体类所对应的表名设置默认的前缀 table-prefix: t_ #配置mapper映射文件路径,名字要和mapper接口名字一致 这是默认路径不写也行 mapper-locations: classpath*:/mapper/**/*.xml #配置类型别名所对应的包 type-aliases-package: com.yka.boot02mybatis_plus.pojo # 扫描通用枚举的包 type-enums-package: com.yka.boot02mybatis_plus.enums
-
Cree un paquete de configuración para crear una clase de configuración, agregue la anotación @MapperScan`, escanee la carpeta Mapper y agregue la anotación @Configuration**
@Configuration //告诉SpringBoot这是一个配置类 == spring配置文件 @MapperScan("com.yka.boot02mybatis_plus.mapper")//扫描接口包 在当前配置类(spring.xml)中填写注解最合适 //配置mapper接口的扫描配置 //由mybatis-spring提供,可以将指定包下所有的mapper接口创建代理实现类 //并将这些动态代理作为IOC容器的bean管理,接口就可以自动装配了,直接可以调用接口中的方法 public class MyConfig { }
-
Escriba la clase de entidad
User.java
(Lombok se usa aquí para simplificar el código)@Data//自动提供get set方法、tosString方法,equals方法 @AllArgsConstructor//有参构造器 @NoArgsConstructor//无参构造器 @TableName("user")//绑定表 yaml文件中设置了全局配置这里可以不用注解了 yaml文件配置文件中:db-config:table-prefix: t_ public class User { private Long id; private String name; private Integer age; private String email; }
-
Escriba
UserMapper
la interfaz -
1.BaseMapper<T>
ilustrar:
-
General CRUD encapsula la interfaz BaseMapper, resuelve automáticamente el mapeo de relaciones de la tabla de entidades al
Mybatis-Plus
inicio y lo convierte enMybatis
un contenedor de inyección de objetos internos -
Los genéricos
T
son cualquier objeto de entidad -
El parámetro
Serializable
es cualquier tipo de clave principalMybatis-Plus
. No se recomienda utilizar una clave principal compuesta. Cada tabla tiene su propia claveid
principal . -
el objeto
Wrapper
es un inicializador condicional
El CRUD básico en MyBatis-Plus se ha implementado en el BaseMapper incorporado, por lo que podemos usarlo directamente después de heredar esta interfaz.
La operación CRUD demostrada esta vez no incluye métodos con constructores condicionales como parámetros, y los constructores condicionales se demostrarán en un capítulo separado. Métodos CRUD proporcionados en BaseMapper:
-
-
// 在对应的Mapper上面继承基本的类 BaseMapper,就能直接用BaseMapper接口里面的sql语句了 //MyBatis-Plus中的基本CRUD在内置的BaseMapper中都已得到了实现,因此我们继承该接口以后可以直接使用。 @Repository // 代表持久层 public interface UserMapper extends BaseMapper<User> { // 所有的CRUD操作都已经编写完成了 // 你不需要像以前的配置一大堆文件了! @Select("select * from user where id = #{id}") public User selById(Integer id); }
5. Consulta de prueba
-
escribir una clase de prueba
MyBatisPlusTest.java
@SpringBootTest public class MyBatisPlusTest { @Resource private UserMapper userMapper; /** * 测试查询所有数据 */ @Test void testSelectList(){ //通过条件构造器查询一个list集合,若没有条件,则可以设置null为参数 List<User> users = userMapper.selectList(null); users.forEach(System.out::println); } }
-
La consola imprime los resultados de la consulta.
Tres, agregar, eliminar, modificar y verificar
1.BaseMapper<T>
ilustrar:
General CRUD encapsula la interfaz BaseMapper, resuelve automáticamente el mapeo de relaciones de la tabla de entidades al
Mybatis-Plus
inicio y lo convierte enMybatis
un contenedor de inyección de objetos internosLos genéricos
T
son cualquier objeto de entidadEl parámetro
Serializable
es cualquier tipo de clave principalMybatis-Plus
. No se recomienda utilizar una clave principal compuesta. Cada tabla tiene su propia claveid
principal .el objeto
Wrapper
es un inicializador condicional
El CRUD básico en MyBatis-Plus se ha implementado en el BaseMapper incorporado, por lo que podemos usarlo directamente después de heredar esta interfaz.
La operación CRUD demostrada esta vez no incluye métodos con constructores condicionales como parámetros, y los constructores condicionales se demostrarán en un capítulo separado.
Métodos CRUD proporcionados en BaseMapper:
-
Agregado: Insertar
// 插入一条记录 int insert(T entity);
-
Eliminar: Eliminar
// 根据 entity 条件,删除记录 int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper); // 删除(根据ID 批量删除) int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); // 根据 ID 删除 int deleteById(Serializable id); // 根据 columnMap 条件,删除记录 int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
-
Modificación: Actualizar
// 根据 whereWrapper 条件,更新记录 int update(@Param(Constants.ENTITY) T updateEntity, @Param(Constants.WRAPPER) Wrapper<T> whereWrapper); // 根据 ID 修改 int updateById(@Param(Constants.ENTITY) T entity);
-
Consulta: Seleccionar
// 根据 ID 查询 T selectById(Serializable id); // 根据 entity 条件,查询一条记录 T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查询(根据ID 批量查询) List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList); // 根据 entity 条件,查询全部记录 List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 查询(根据 columnMap 条件) List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap); // 根据 Wrapper 条件,查询全部记录 List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值 List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 根据 entity 条件,查询全部记录(并翻页) IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 根据 Wrapper 条件,查询全部记录(并翻页) IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper); // 根据 Wrapper 条件,查询总记录数 Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
2. Llame a la capa Mapper para implementar CRUD
2.1 insertar
El resultado de la ejecución final, la identificación obtenida es 1527206783590903810
Esto se debe a que MyBatis-Plus generará una identificación basada en la estrategia del algoritmo de copo de nieve de forma predeterminada al insertar datos
/**
* 测试插入一条数据 INSERT INTO user ( name, age, email ) VALUES ( ?, ?, ? )
* MyBatis-Plus在实现插入数据时,会默认基于雪花算法的策略生成id,后面常用注解可以修改
*/
@Test
public void testInsert(){
User user = new User();
user.setName("Vz");
user.setAge(21);
user.setEmail("[email protected]");
int result = userMapper.insert(user);
System.out.println(result > 0 ? "添加成功!" : "添加失败!");
System.out.println("受影响的行数为:" + result);
//1527206783590903810(当前 id 为雪花算法自动生成的id)
System.out.println("id自动获取" + user.getId());
}
2.2 eliminar
a. Eliminar datos según ID
Método de llamada: int deleteById (ID serializable);
/**
* 测试根据id删除一条数据 DELETE FROM user WHERE id=?
*/
@Test
public void testDeleteById(){
int result = userMapper.deleteById(1527206783590903810L);
System.out.println(result > 0 ? "删除成功!" : "删除失败!");
System.out.println("受影响的行数为:" + result);
}
B. Eliminar datos en lotes según ID
Método de llamada: int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
/**
* 测试通过id批量删除数据 DELETE FROM user WHERE id IN ( ? , ? )
*/
@Test
public void testDeleteBatchIds(){
List<Long> ids = Arrays.asList(6L,7L,8L);
int result = userMapper.deleteBatchIds(ids);
System.out.println(result > 0 ? "删除成功!" : "删除失败!");
System.out.println("受影响的行数为:" + result);
}
//删除所有数据 DELETE FROM user
userMapper.delete(null);
c. Eliminar datos de acuerdo con las condiciones del mapa
调用方法:int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
/**
* 测试根据Map集合中所设置的条件删除数据
*/
@Test
public void testDeleteByMap(){
//当前演示为根据name和age删除数据
//执行SQL为:DELETE FROM user WHERE name = ? AND age = ?
Map<String,Object> map = new HashMap<>();
map.put("name","Vz");
map.put("age",21);
int result = userMapper.deleteByMap(map);
System.out.println(result > 0 ? "删除成功!" : "删除失败!");
System.out.println("受影响的行数为:" + result);
}
2.3 Modificaciones
Método de llamada: int updateById(@Param(Constants.ENTITY) T entidad);
/**
* 测试根据id修改用户信息
*/
@Test
public void testUpdateById(){
//执行SQL为: UPDATE user SET name=?, age=?, email=? WHERE id=?
User user = new User();
user.setId(6L);
user.setName("VzUpdate");
user.setAge(18);
user.setEmail("[email protected]");
int result = userMapper.updateById(user);
System.out.println(result > 0 ? "修改成功!" : "修改失败!");
System.out.println("受影响的行数为:" + result);
}
2.4 Consulta
a. Consultar información de usuario basada en ID
Método de llamada: T selectById (ID serializable);
/**
* 测试根据id查询用户数据 SELECT id,name,age,email FROM user WHERE id=?
*/
@Test
public void testSelectById(){
User user = userMapper.selectById(1L);
System.out.println(user);
}
b. Consultar información de varios usuarios en función de varios ID
调用方法:List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);
/**
* 根据多个id查询用户数据
*/
@Test
public void testSelectBatchIds(){
//执行SQL为:SELECT id,name,age,email FROM user WHERE id IN ( ? , ? , ? )
List<Long> ids = Arrays.asList(1L,2L,3L);
List<User> users = userMapper.selectBatchIds(ids);
users.forEach(System.out::println);
}
c. Consultar la información del usuario según las condiciones del Mapa
调用方法:List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);
/**
* 根据Map所设置的条件查询用户
*/
@Test
public void testSelectByMap(){
//执行SQL为:SELECT id,name,age,email FROM user WHERE age = ?
Map<String,Object> map = new HashMap<>();
map.put("age",18);
List<User> users = userMapper.selectByMap(map);
users.forEach(System.out::println);
}
D. Consultar toda la información del usuario
Método de llamada: List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);
/**
* 测试查询所有数据 SELECT id,name,age,email FROM user
*/
@Test
void testSelectList(){
List<User> users = userMapper.selectList(null);
users.forEach(System.out::println);
}
3. Servicios generales
ilustrar:
Interfaz de encapsulación CRUD de servicio general
IService
, encapsulación adicional CRUD utiliza el método de nomenclaturaget 查询单行
remove 删除
list 查询集合
page 分页
de prefijo para distinguirMapper
capas para evitar confusiones,Los genéricos
T
son cualquier objeto de entidadSe recomienda que, si existe la posibilidad de personalizar el método de servicio general, cree su
IBaseService
propiaMybatis-Plus
clase base heredada de la proporcionadael objeto
Wrapper
es un inicializador condicional
Hay una interfaz IService
y ServiceImpl
, que encapsula la lógica de la capa empresarial común; consulte el código fuente IService y ServiceImpl para obtener más información.
Por lo tanto, cuando lo usamos, solo necesitamos Service
heredar IService
la interfaz en la interfaz que definimos, implementar nuestro propio Servicio en nuestra propia clase de implementación y heredarla ServiceImpl
.
Métodos CRUD en IService
-
Agregado: Guardar, Guardar o Actualizar
// 插入一条记录(选择字段,策略插入) boolean save(T entity); // 插入(批量) boolean saveBatch(Collection<T> entityList); // 插入(批量) boolean saveBatch(Collection<T> entityList, int batchSize); // TableId 注解存在更新记录,否插入一条记录 //saveOrUpdate:会去表里查该id,如果表里有该id修改,无id是增加,新增不需要id,更改需要id boolean saveOrUpdate(T entity); // 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法 boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper); // 批量修改插入 会去表里查该id,如果表里有该id修改,无id是增加,新增不需要id,更改需要id boolean saveOrUpdateBatch(Collection<T> entityList); // 批量修改插入 有id修改无id是增加 boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);
-
Eliminar: Quitar
// 根据 entity 条件,删除记录 boolean remove(Wrapper<T> queryWrapper); // 根据 ID 删除 boolean removeById(Serializable id); // 根据 columnMap 条件,删除记录 boolean removeByMap(Map<String, Object> columnMap); // 删除(根据ID 批量删除) boolean removeByIds(Collection<? extends Serializable> idList);
-
Modificación: Actualizar
// 根据 UpdateWrapper 条件,更新记录 需要设置sqlset boolean update(Wrapper<T> updateWrapper); // 根据 whereWrapper 条件,更新记录 boolean update(T updateEntity, Wrapper<T> whereWrapper); // 根据 ID 选择修改 boolean updateById(T entity); // 根据ID 批量更新 有id修改无id是增加 boolean updateBatchById(Collection<T> entityList); // 根据ID 批量更新 有id修改无id是增加 boolean updateBatchById(Collection<T> entityList, int batchSize);
-
Consulta: Obtener, Listar, Contar
// 根据 ID 查询 T getById(Serializable id); // 根据 Wrapper,查询一条记录。结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1") T getOne(Wrapper<T> queryWrapper); // 根据 Wrapper,查询一条记录 T getOne(Wrapper<T> queryWrapper, boolean throwEx); // 根据 Wrapper,查询一条记录 Map<String, Object> getMap(Wrapper<T> queryWrapper); // 根据 Wrapper,查询一条记录 <V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper); // 查询所有 List<T> list(); // 查询列表 List<T> list(Wrapper<T> queryWrapper); // 查询(根据ID 批量查询) Collection<T> listByIds(Collection<? extends Serializable> idList); // 查询(根据 columnMap 条件) Collection<T> listByMap(Map<String, Object> columnMap); // 查询所有列表 List<Map<String, Object>> listMaps(); // 查询列表 List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper); // 查询全部记录 List<Object> listObjs(); // 查询全部记录 <V> List<V> listObjs(Function<? super Object, V> mapper); // 根据 Wrapper 条件,查询全部记录 List<Object> listObjs(Wrapper<T> queryWrapper); // 根据 Wrapper 条件,查询全部记录 <V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper); // 查询总记录数 int count(); // 根据 Wrapper 条件,查询总记录数 int count(Wrapper<T> queryWrapper);
-
Paginación: Página
default <E extends IPage<T>> E page(E page, Wrapper<T> queryWrapper) {}
4. Capa de servicio de llamadas para operar datos
En nuestra interfaz de Servicio, al heredar la interfaz IService proporcionada por MyBatis-Plus, no solo podemos obtener los métodos CRUD que proporciona, sino también utilizar los métodos definidos por nosotros mismos.
-
crear
UserService
y heredarIService
/** * UserService继承IService模板提供的基础功能 */ public interface UserService extends IService<User> {}
-
UserService
Clase de implementación creada y heredada.ServiceImpl
/** * ServiceImpl实现了IService,提供了IService中基础功能的实现 * 若ServiceImpl无法满足业务需求,则可以使用自定的UserService定义方法,并在实现类中实现 */ @Service public class UserServiceImpl extends ServiceImpl<UserMapper,User> implements UserService{}
-
Número de registros de consulta de prueba
Método de llamada: int cuenta();
@Test public void testGetCount(){ //查询总记录数 //执行的SQL为:SELECT COUNT( * ) FROM user long count = userService.count(); System.out.println("总记录数:" + count); }
Cuatro, anotaciones comunes
Las anotaciones que proporciona MyBatis-Plus pueden ayudarnos a resolver algunos problemas de mapeo entre bases de datos y entidades.
1. @NombreDeLaTabla
Después de las pruebas anteriores, al usar MyBatis-Plus para implementar CRUD básico, no especificamos la tabla a operar, sino que configuramos el Usuario genérico cuando la interfaz Mapper heredó BaseMapper, y la tabla operada es la tabla de usuario, por lo que se concluye. que cuando MyBatis-Plus determina la tabla a operar, lo determina el tipo genérico de BaseMapper, es decir, el tipo de entidad, y el nombre de tabla de la operación por defecto es consistente con el nombre de clase del tipo de entidad.
1.1 Conduciendo al problema
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
user
nombre de la tablat_user
y probamos la función de consulta. -
El programa lanza una excepción, la tabla 'mybatis_plus.user' no existe , porque el nombre de la tabla actual
t_user
y el nombre de la tabla de la operación predeterminada es consistente con el nombre de la clase del tipo de entidad, es decir,user
la tabla
1.2 Resolución de problemas
a. Usar anotaciones para resolver problemas
Agréguelo al tipo de clase de entidad
@TableName("t_user")
para identificar la tabla correspondiente a la clase de entidad, y la declaración SQL se puede ejecutar con éxito
@Data
@TableName("t_user")
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
B. Utilice la configuración global para resolver el problema
En el proceso de desarrollo, a menudo nos encontramos con los problemas anteriores, es decir, las tablas correspondientes a las clases de entidad tienen prefijos fijos, por ejemplo
t_
otbl_
. correspondiente a la clase de entidad Establezca el prefijo predeterminado, luego no es necesario identificar la tabla correspondiente a la clase de entidad a través de @TableName en cada clase de entidad
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.
2.1 Conduciendo al problema
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?
-
Los atributos en nuestra clase de entidad
id
se cambianuid
y los campos en la tablaid
también se cambianuid
para probar la función de suma. -
El programa lanza una excepción, el campo 'uid' no tiene un valor por defecto , indicando que MyBatis-Plus no ha asignado
uid
un valor como clave principal
2.2 Resolución de problemas
Al identificar el atributo uid como la clave principal en la clase de entidad
@TableId
, la declaración SQL se puede ejecutar con éxito
@Date
public class User {
//将属性所对应的字段指定为主键
@TableId
private Long uid;
private String name;
private Integer age;
private String email;
}
2.3 El 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 especificar la clave principal campo clave en la tabla a través del atributo de valor de la anotación @TableId,
@TableId("uid")
o@TableId(value="uid")
2.4 El atributo de tipo de @TableId
El atributo de tipo se utiliza para definir la estrategia de clave principal: el algoritmo de copo de nieve predeterminado
Estrategias de clave principal de uso común:
valor | describir |
---|---|
IdType.ASSIGN_ID (predeterminado) | La identificación de datos se genera en función de la estrategia del algoritmo de copo de nieve, independientemente de si la identificación de la base de datos está configurada para incrementar automáticamente |
IdType.AUTO | Utilice la estrategia de incremento automático de la base de datos. Tenga en cuenta que para este tipo, asegúrese de que la base de datos esté configurada con incremento automático de id. |
//如果在.yaml文件中设置了全局配置这里就不用再写注解了
//IdType.ASSIGN_ID(默认)基于雪花算法的策略生成数据id,与数据库id是否设置自增无关
//@TableId(type = IdType.AUTO,value = "id")//使用数据库的自增策略,注意,该类型请确保数据库设置了id自增,
// value:如果属性名和字段主键名不一致,可以指定主键字段
private Long id;
Configure la estrategia de clave principal global: después de la configuración, no es necesario escribir anotaciones en la clase de entidad
#MyBatis-Plus相关配置
mybatis-plus:
configuration:
#配置日志
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
global-config:
db-config:
#配置统一的主键策略为自增
id-type: auto
# 设置实体类所对应的表的统一前缀
table-prefix: t_
3.@TbaleField
Después de las pruebas anteriores, podemos encontrar que cuando MyBatis-Plus ejecuta sentencias 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 el nombre del atributo y el nombre del campo en la clase de entidad son inconsistentes, ¿qué sucederá?
3.1 Caso 1
Si los atributos en la clase de entidad usan el estilo de nomenclatura de mayúsculas y minúsculas y los campos en la tabla usan el estilo de nomenclatura de guión bajo
Por ejemplo, atributos de clase de entidad userName
, campos en tablasuser_name
En este punto, MyBatis-Plus convertirá automáticamente el estilo de nombre de guión bajo al estilo de nombre de joroba.
Equivale a configurar en MyBatis
3.2 Caso 2
Si los atributos de la clase de entidad y los campos de la tabla no cumplen la condición 1
Por ejemplo, atributos de clase de entidad
name
, campos en tablasusername
@TableField("username")
En este momento, debe usar el nombre de campo correspondiente al atributo de configuración en el atributo de clase de entidad
public class User {
@TableId("uid")
private Long id;
@TableField("username")
private String name;
private Integer age;
private String email;
}
4.@TableLogic
4.1 Lápida
Eliminación física: eliminación real, los datos correspondientes se eliminan de la base de datos y los datos eliminados no se pueden consultar después
Eliminación lógica: Eliminación falsa, modifica el estado del campo que representa si se elimina en los datos correspondientes a "estado eliminado", y luego este registro de datos aún se puede ver en la base de datos
Escenario de uso: la recuperación de datos es posible
4.2 Realizar borrado lógico
-
Cree una columna de estado de lápida en la base de datos, establezca el valor predeterminado en 0, 0 no eliminado, 1 eliminado
-
Agregar atributo de lápida en la clase de entidad
-
Prueba la función de borrado, lo que realmente se realiza es la modificación
public void testDeleteById(){ int result = userMapper.deleteById(1527472864163348482L); System.out.println(result > 0 ? "删除成功!" : "删除失败!"); System.out.println("受影响的行数为:" + result); }
-
Ejecute el método de consulta en este momento, y el resultado de la consulta es agregar condiciones automáticamente
is_deleted=0,查询的是未删除的数据
5. Constructor de condiciones
1. Introducción a la envoltura
-
Wrapper
: clase abstracta construida condicionalmente, la clase principal más alta -
AbstractWrapper
: Se utiliza para encapsular condiciones de consulta y generar condiciones where para sql-
QueryWrapper
: Encapsulación de condiciones de consulta -
UpdateWrapper
: Actualizar paquete condicional -
AbstractLambdaWrapper
: Usar sintaxis Lambda-
LambdaQueryWrapper
: Query Wrapper para el uso de la sintaxis de Lambda -
LambdaUpdateWrapper
: Envoltorio del paquete de actualización de Lambda
-
-
2.Contenedor de consulta
-
Montaje de condiciones de consulta
@Test public void test01(){ //SELECT id,name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (name LIKE ? AND age BETWEEN ? AND ? AND email IS NOT NULL) //查询用户名包含a,年龄在20到30之间,邮箱信息不为null的用户信息 QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.like("name","a").between("age",20,30).isNotNull("email"); List<User> users = userMapper.selectList(queryWrapper); users.forEach(System.out::println); }
-
Condiciones de clasificación de conjuntos
@Test public void test02(){ //SELECT id,name,age,email,is_deleted FROM t_user WHERE is_deleted=0 ORDER BY age DESC,id ASC //查询用户信息,按照年龄的降序排序,若年龄相同,则按照id升序排序 QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.orderByDesc("age").orderByAsc("id"); List<User> users = userMapper.selectList(queryWrapper); users.forEach(System.out::println); }
-
Condición de eliminación de ensamblaje
执行SQL: ACTUALIZAR t_user SET is_deleted=1 DONDE is_deleted=0 Y (el correo electrónico ES NULO)
public void test03(){ //删除邮箱地址为null的用户信息 QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.isNull("email"); int result = userMapper.delete(queryWrapper); System.out.println(result > 0 ? "删除成功!" : "删除失败!"); System.out.println("受影响的行数为:" + result); }
-
modificar función
@Test public void test04(){ //将(年龄大于20并且用户名中包含有a)或邮箱为null的用户信息修改 //UPDATE t_user SET name=?, email=? WHERE is_deleted=0 AND (age > ? AND 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 > 0 ? "修改成功!" : "修改失败!"); System.out.println("受影响的行数为:" + result); }
Prioridad de las condiciones
@Test public void test05(){ //将用户名中包含有a并且(年龄大于20或邮箱为null)的用户信息修改 //UPDATE t_user SET name=?, email=? WHERE is_deleted=0 AND (name LIKE ? AND (age > ? OR email IS NULL)) //lambda中优先执行,i就是条件构造器queryWrapper .and()和.or()都有lambda表达式 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 > 0 ? "修改成功!" : "修改失败!"); System.out.println("受影响的行数为:" + result); }
-
Montaje de la cláusula select
执行SQL: SELECCIONE nombre de usuario, edad, correo electrónico DESDE t_user DONDE is_deleted=0
@Test public void test06(){ //查询用户的用户名、年龄、邮箱信息 //SELECT 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); maps.forEach(System.out::println); }
-
implementar la subconsulta
@Test public void test07(){ //查询id小于等于100的用户信息 //SELECT id,name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (id IN (select id from t_user where id <= 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. Actualizar contenedor
UpdateWrapper no solo tiene la función de condición de ensamblaje de QueryWrapper, sino que también proporciona el método set para modificar la información de la base de datos de la condición correspondiente
@Test
public void test08(){
//将用户名中包含有a并且(年龄大于20或邮箱为null)的用户信息修改
//UPDATE t_user SET name=?,email=? WHERE is_deleted=0 AND (name LIKE ? AND (age > ? OR email IS NULL))
UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
updateWrapper.like("name","a")
.and( i -> i.gt("age",20).or().isNull("email"))
.set("name","小黑").set("email","[email protected]");//设置修改的字段
int result = userMapper.update(null, updateWrapper);
System.out.println(result > 0 ? "修改成功!" : "修改失败!");
System.out.println("受影响的行数为:" + result);
}
4. parámetro de condición
Si se cumple la condición del parámetro de condición, se ejecuta la condición en el constructor de condición
Simule los parámetros pasados del cliente al servidor durante el desarrollo y la prueba
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 (null), no debe ser ensamblada, para no afectar el resultado de la ejecución del SQL
-
Idea 1 Escritura complicada
执行SQL: SELECCIONE uid COMO id,nombre_de_usuario COMO nombre,edad,correo electrónico,se_eliminó DE t_usuario DONDE se_eliminó=0 Y (nombre_de_usuario COMO ? Y edad <= ?)
//复杂写法,模拟开发中客户端传到服务器的参数就行测试 @Test public void test09(){ //SELECT id,name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (name LIKE ? AND age <= ?) String username = "a"; Integer ageBegin = null; 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 Manera simple de escribir: use el parámetro de condición
-
Si se cumple la condición del parámetro de condición, se ejecuta la condición en el constructor de condición
-
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.
//简单写法:使用condition参数 @Test public void test10(){ //SELECT id,name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (name LIKE ? AND age <= ?) 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",20) .le(ageEnd!=null,"age",20); List<User> list = userMapper.selectList(queryWrapper); list.forEach(System.out::println); }
5.LambdaQueryWrapper
La función es equivalente a QueryWrapper y se proporciona la sintaxis de la expresión Lambda para evitar completar nombres de columna incorrectos. Haga coincidir automáticamente los nombres de los campos con los nombres de los atributos
//LambdaQueryWrapper:提供了Lambda表达式的语法可以避免填错列名。通过属性名自动匹配字段名
//SELECT id,name,age,email,is_deleted FROM t_user WHERE is_deleted=0 AND (name LIKE ? AND age <= ?)
@Test
public void test11(){
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,20) //User::getAge:通过属性名自动匹配字段名
.le(ageEnd!=null,User::getAge,20);
List<User> list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println);
}
6.Contenedor de actualización de Lambda
La función es equivalente a UpdateWrapper y se proporciona la sintaxis de la expresión Lambda para evitar completar nombres de columna incorrectos.
//LambdaUpdateWrapper:功能等同于UpdateWrapper,提供了Lambda表达式的语法可以避免填错列名。
//UPDATE t_user SET name=?,email=? WHERE is_deleted=0 AND (name LIKE ? AND (age > ? OR email IS NULL))
@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);
}
Seis complementos de uso común
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
-
Importar dependencias de mybatis-plus
<!--mybatis-plus启动器--> <dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.1</version> </dependency> <!-- 数据库驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.27</version> </dependency>
-
Agregar clase de configuración
MyBatisPlusConfig
@Configuration @MapperScan("com.atguigu.mybatisplus.mapper") public class MyBatisPlusConfig { //配置MybatisPlus的插件的 Interceptor:拦截器 @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); //添加分页插件 DbType:数据库类型 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } }
-
método de prueba de escritura
@Test public void testPage(){ //new Page()中的两个参数分别是当前页码,每页显示数量 //SELECT id,name,age,email,is_deleted FROM t_user WHERE is_deleted=0 LIMIT ?,? Page<User> page = new Page<>(2,2); Page<User> userPage = userMapper.selectPage(page, null); System.out.println(userPage); List<User> users = page.getRecords();//分页后的数据 users.forEach(System.out::println); System.out.println("总页数:"+userPage.getPages()); System.out.println("总条数:"+userPage.getTotal()); System.out.println("当前页:"+userPage.getCurrent()); System.out.println("当前页显示条数:"+userPage.getSize()); System.out.println("是否有下一页:"+userPage.hasNext()); System.out.println("是否有上一页:"+userPage.hasPrevious()); }
2. Paginación personalizada
La llamada anterior es el método con paginación proporcionado por MyBatis-Plus, entonces, ¿cómo implementamos la paginación en el método definido por nosotros mismos?
-
UserMapper
definir un método en la interfaz/** * 根据年龄大于20的用户查询用户列表,分页显示 * @param page 分页对象,xml中可以从里面进行取值,传递参数 Page 即自动分页,必须放在第一位 * @param age 年龄 * @return */ Page<User> selectPageVo(@Param("page") Page<User> page,@Param("age") Integer age);
-
UserMapper.xml
Escriba SQL para implementar este método en<select id="selectPageVo" resultType="User"> select id,username as name,age,email from t_user where age > #{age} </select>
-
método de prueba de escritura
@Test public void testPageVo(){ Page<User> page = userMapper.selectPageVo(new Page<User>(1,2), 20); List<User> users = page.getRecords(); users.forEach(System.out::println); }
segunda forma de escribir
//自定义分页 根据年龄大于20的用户查询用户列表,分页显示 @Test public void testPageVo(){ Page<User> page = new Page<>(1,2); QueryWrapper<User> queryWrapper = new QueryWrapper<>(); queryWrapper.gt("age",20); userMapper.selectPage(page,queryWrapper); List<User> users = page.getRecords(); users.forEach(System.out::println); }
3. Cerradura optimista
Función: cuando se va a actualizar un registro, se espera que este registro no haya sido actualizado por otros
Implementación de bloqueo optimista:
-
Al buscar registros, obtener la versión actual
-
Al actualizar, traer esta versión
-
Al realizar una actualización, ACTUALIZAR t_product SET nombre=?, precio=100+50, versión(nuevo valor después del cambio+1)=1 DONDE id=?Y versión(valor anterior)=0.
-
Satisfaga la condición, newVersion: oldVersion El valor anterior será +1 cada vez
-
Si la versión es incorrecta, la actualización fallará
3.1 Escenarios
-
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.
3.2 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.
3.3 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) );
-
agregar un dato
INSERT INTO t_product (id, NAME, price) VALUES (1, '外星人笔记本', 100);
-
añadir una clase de entidad
Product
@Data public class Product { private Long id; private String name; private Integer price; private Integer version; }
-
Agregar una interfaz de mapeador
ProductMapper
public interface ProductMapper extends BaseMapper<Product> {}
-
Métodos de prueba
//模拟修改冲突 @Test public void testProduct01(){ //1.小李获取商品价格 //SELECT id,name,price,version FROM t_product WHERE id=? Product productLi = productMapper.selectById(1); System.out.println("小李获取的商品价格为:" + productLi.getPrice());//100 //2.小王获取商品价格 //SELECT id,name,price,version FROM t_product WHERE id=? Product productWang = productMapper.selectById(1); System.out.println("小王获取的商品价格为:" + productWang.getPrice());//100 //3.小李修改商品价格+50 productLi.setPrice(productLi.getPrice()+50); //UPDATE t_product SET name=?, price=100+50, version=? WHERE id=? productMapper.updateById(productLi);//100+50=150 //4.小王修改商品价格-30 productWang.setPrice(productWang.getPrice()-30); //UPDATE t_product SET name=?, price=100-30, version=? WHERE id=? productMapper.updateById(productWang);//100-30=70 //5.老板查询商品价格 //SELECT id,name,price,version FROM t_product WHERE id=? Product productBoss = productMapper.selectById(1); System.out.println("老板获取的商品价格为:" + productBoss.getPrice());//70 }
-
Resultados de la
3.4 El bloqueo optimista resuelve el problema
-
version
Agregar anotaciones a campos de clase de entidad@Version
@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
@Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); //添加分页插件 interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); //添加乐观锁插件 interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return interceptor; }
-
Ejecute el método de prueba nuevamente, prueba de bloqueo optimista
Xiao Li pregunta sobre la información del producto:
SELECCIONE id, nombre, precio, versión DESDE t_product DONDE id=?
Xiao Wang pregunta sobre la información del producto:
SELECCIONE id, nombre, precio, versión DESDE t_product DONDE id=?
Xiao Li modifica el precio del producto y agrega automáticamente la versión+1
ACTUALIZAR t_product SET nombre=?, precio=?, versión=? DONDE id=? Y versión =?
Parámetros: cuaderno alienígena (cadena), 150 (entero), 1 (entero), 1 (largo), 0 (entero)
Xiao Wang modifica el precio del producto. En este momento, la versión se ha actualizado. Si la condición no se cumple, la modificación falla.
ACTUALIZAR t_product SET nombre=?, precio=?, versión=? DONDE id=? Y versión =?
Parámetros: cuaderno alienígena (cadena), 70 (entero), 1 (entero), 1 (largo), 0 (entero)
Al final, Xiao Wang no pudo modificar y el precio de la consulta: 150
SELECCIONE id, nombre, precio, versión DESDE t_product DONDE id=?
-
//乐观锁测试 @Test public void testProduct02(){ //1.小李获取商品价格 //SELECT id,name,price,version FROM t_product WHERE id=? Product productLi = productMapper.selectById(1); System.out.println("小李获取的商品价格为:" + productLi.getPrice());//100 //2.小王获取商品价格 //SELECT id,name,price,version FROM t_product WHERE id=? Product productWang = productMapper.selectById(1); System.out.println("小王获取的商品价格为:" + productWang.getPrice());//100 //3.小李修改商品价格+50 productLi.setPrice(productLi.getPrice()+50);//100+50=150 //UPDATE t_product SET name=?, price=100+50, version=1 WHERE id=? AND version=0 productMapper.updateById(productLi);//现在版本号已经变成1了 //4.小王修改商品价格-30 productWang.setPrice(productWang.getPrice()-30); //100-30=70 //UPDATE t_product SET name=?, price=100-30, version=1 WHERE id=? AND version=0 productMapper.updateById(productWang);//现在版本号已经变成1了,所以这里没有修改成功 //5.老板查询商品价格 //SELECT id,name,price,version FROM t_product WHERE id=? Product productBoss = productMapper.selectById(1); System.out.println("老板获取的商品价格为:" + productBoss.getPrice());//150 }
-
Optimizar el proceso de ejecución
//优化乐观锁测试 @Test public void testProduct03(){ //1.小李获取商品价格 //SELECT id,name,price,version FROM t_product WHERE id=? Product productLi = productMapper.selectById(1); System.out.println("小李获取的商品价格为:" + productLi.getPrice());//100 //2.小王获取商品价格 //SELECT id,name,price,version FROM t_product WHERE id=? Product productWang = productMapper.selectById(1); System.out.println("小王获取的商品价格为:" + productWang.getPrice());//100 //3.小李修改商品价格+50 productLi.setPrice(productLi.getPrice()+50);//100+50=150 //UPDATE t_product SET name=?, price=100+50, version=1 WHERE id=? AND version=0 productMapper.updateById(productLi);//现在版本号已经变成1了 //4.小王修改商品价格-30 productWang.setPrice(productWang.getPrice()-30); //100-30=70 //UPDATE t_product SET name=?, price=100-30, version=1 WHERE id=? AND version=0 int result = productMapper.updateById(productWang);//现在版本号已经变成1了,所以这里没有修改成功 if(result == 0){ //操作失败,重试 //重新查询一次,这是小李修改玩的数据 Product productNew = productMapper.selectById(1); //再次更新,现在价格是150-30 productNew.setPrice(productNew.getPrice()-30); //再次修改 productMapper.updateById(productNew); } //5.老板查询商品价格 //SELECT id,name,price,version FROM t_product WHERE id=? Product productBoss = productMapper.selectById(1); System.out.println("老板获取的商品价格为:" + productBoss.getPrice());//120 }
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
-
Agregar campo a la tabla de la base de datos
sex
-
Crear un tipo de enumeración genérico
@Getter public enum SexEnum { MALE(1, "男"), FEMALE(2, "女"); @EnumValue //将注解所标识的属性的值存储到数据库中 private int sex; private String sexName; SexEnum(Integer sex, String sexName) { this.sex = sex; this.sexName = sexName; } }
-
Agregar atributo sexo a la clase de entidad de usuario
public class User { private Long id; @TableField("username") private String name; private Integer age; private String email; @TableLogic private int isDeleted; //逻辑删除 private SexEnum sex; }
-
Configurar la enumeración genérica de escaneo
#MyBatis-Plus相关配置 mybatis-plus: #指定mapper文件所在的地址 mapper-locations: classpath:mapper/*.xml configuration: #配置日志 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl global-config: banner: off db-config: #配置mp的主键策略为自增 id-type: auto # 设置实体类所对应的表的统一前缀 table-prefix: t_ #配置类型别名所对应的包 type-aliases-package: com.atguigu.mybatisplus.pojo # 扫描通用枚举的包 type-enums-package: com.atguigu.mybatisplus.enums
-
Ejecutar el método de prueba
@Test public void test(){ User user = new User(); user.setName("admin"); user.setAge(33); user.setSex(SexEnum.MALE); 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>//freemarker引擎模板依赖
<version>2.3.31</version>
</dependency>
2. Generación rápida
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", "123456")
.globalConfig(builder -> {
builder.author("atguigu") // 设置作者
//.enableSwagger() // 开启 swagger 模式
.fileOverride() // 覆盖已生成文件
.outputDir("D://mybatis_plus"); // 指定输出目录
})
.packageConfig(builder -> {
builder.parent("com.atguigu") // 设置父包名
.moduleName("mybatisplus") // 设置父包模块名
// 设置mapperXml映射文件生成路径
.pathInfo(Collections.singletonMap(OutputFile.mapperXml, "D://mybatis_plus"));
})
.strategyConfig(builder -> {
builder.addInclude("t_user") // 设置需要生成的表名
.addTablePrefix("t_", "c_"); // 设置过滤表前缀
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
}
}
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.
Descripción del escenario:
Creamos dos bibliotecas, respectivamente: mybatis_plus
(la biblioteca anterior no se mueve) y (nueva), y movemos las tablas mybatis_plus_1
de la biblioteca mybatis_plus a la biblioteca mybatis_plus_1, para que cada biblioteca tenga una tabla, y se obtengan los datos de usuario y de producto a través de un caso de prueba product
, si se obtiene, significa que la simulación multibiblioteca es exitosa
1. Crear base de datos y tabla
-
Crear base de datos
mybatis_plus_1
y tabla `productoCREATE 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);
-
mesa desplegable
mybatis_plus
en la bibliotecaproduct
use mybatis_plus; DROP TABLE IF EXISTS product;
2. Los nuevos proyectos introducen dependencias
Cree un nuevo proyecto Spring Boot usted mismo y seleccione el controlador MySQL y las dependencias de Lombok
Introducir dependencias en múltiples fuentes de datos
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.0</version>
</dependency>
3. Escribir archivos de configuración
spring:
# 配置数据源信息
datasource:
dynamic:
# 设置默认的数据源或者数据源组,默认值即为master
primary: master
# 严格匹配数据源,默认false.true未匹配到指定数据源时抛异常,false使用默认数据源
strict: false
# 配置多数据源信息
datasource:
#主数据源
master:
url: jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
#从数据源
slave_1:
url: jdbc:mysql://localhost:3306/mybatis_plus_1?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
4. Crear clase de entidad
-
Cree una nueva
User
clase de entidad (si el nombre de la tabla de la base de datos tiene un prefijo t_, recuerde configurarlo)@Data @TableName("t_user")//指定表 public class User { private Long id; private String name; private Integer age; private Integer sex; private String email; private Integer isDeleted; }
-
Crear una nueva clase de entidad
Product
@Data @AllArgsConstructor @NoArgsConstructor public class Product { private Long id; private String name; private Integer price; private Integer version; }
5. Crear Mapeador y Servicio
-
nueva interfaz
UserMapper
@Repository public interface UserMapper extends BaseMapper<User> {}
-
nueva interfaz
ProductMapper
@Repository public interface ProductMapper extends BaseMapper<Product> {}
-
Cree una nueva interfaz de servicio
UserService
para especificar el origen de datos para la operaciónpublic interface UserService extends IService<User> {}
-
Cree una nueva interfaz de servicio
ProductService
para especificar el origen de datos para la operaciónpublic interface ProductService extends IService<Product> {}
-
UserService
Clase de implementación creada@Service @DS("master")//多数据源操作,指定要操作的数据源,master指定的是mybatis_plus数据库里面有t_user表,不写注解使用默认数据源 public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { }
ProductService
Clase de implementación creada@Service @DS("slave_1")//多数据源操作,指定要操作的数据源,slave_1指定的是mybatis_plus1数据库里面有product表,不写注解使用默认数据源 public class ProductServiceImpl extends ServiceImpl<ProductMapper,Product> implements ProductService{ }
6. Escriba el método de prueba
Recuerde agregar anotaciones a la clase de inicio
@MapperScan()
class TestDatasourceApplicationTests {
@Resource
UserService userService;
@Resource
ProductService productService;
@Test
void contextLoads() {
User user = userService.getById(1L);
Product product = productService.getById(1L);
System.out.println("User = " + user);
System.out.println("Product = " + product);
}
}
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 tablas múltiples, necesitamos escribir el código y la declaración SQL por nosotros mismos, ¿cómo podemos resolver este problema rápidamente? vez, puedes usar el complemento MyBatisX.
MyBatisX es un complemento de desarrollo rápido basado en IDEA, nacido para la eficiencia.
1. Instala el complemento MyBatisX
Abra IDEA, Archivo-> Configuración-> Complementos-> MyBatisX, busque MyBatisX en la barra de búsqueda e instálelo.
2. Genera código rápidamente
-
Cree un nuevo proyecto Spring Boot para introducir dependencias (recuerde verificar los controladores lombok y mysql al crear el proyecto)
<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.5.1</version> </dependency> <dependency> <groupId>com.baomidou</groupId> <artifactId>dynamic-datasource-spring-boot-starter</artifactId> <version>3.5.0</version> </dependency>
-
Configurar la información de la fuente de datos
spring: #配置数据库 datasource: # 配置连接数据库信息 driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/mybatis_plus?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8 username: root password: 123456
-
Establecer un enlace con la base de datos en IDEA
-
Complete la información de la base de datos y guarde
-
?characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false?characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false
-
Encuentre la tabla que necesitamos generar y haga clic derecho
-
Siguiente paso después de completar la información
-
continúa completando la información
-
Ya terminaste (muy fácil de usar yyds)
3. Genera CRUD rápidamente
MyBaitsX puede ayudarnos a generar rápidamente la instrucción sql correspondiente de acuerdo con el nombre del método que ingresamos en la interfaz de Mapper
public interface UserMapper extends BaseMapper<User> {
//mybatisX快速生成CRUD 方法名都是见名实意
//增加
int insertSelective(User user);
//删除 通过id和年龄和姓名删除
int deleteByIdAndAgeAndName(@Param("id") Long id, @Param("age") Integer age, @Param("name") String name);
//修改 通过id修改年龄跟性别
int updateAgeAndSexById(@Param("age") Integer age, @Param("sex") Integer sex, @Param("id") Long id);
//查询 通过开始年龄和结束年龄查询年龄和性别
List<User> selectAgeAndSexByAgeBetween(@Param("beginAge") Integer beginAge, @Param("endAge") Integer endAge);
//查询全部
List<User> selectAll();
//通过年龄降序查询全部
List<User> selectAllOrderByAgeDesc();
}
prueba
@SpringBootTest
class Boot04MybatisxDemoApplicationTests {
@Autowired
UserMapper userMapper;
@Test
void contextLoads() {
List<User> users = userMapper.selectAll();
System.out.println(users);
List<User> users1 = userMapper.selectAgeAndSexByAgeBetween(10, 20);
System.out.println(users1);
}
}