MyBatisPlus mybatis-plus-join
一、MyBatis Plus 介绍
MyBatis Plus 是国内人员开发的 MyBatis 增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。MybatisX 是一款基于 IDEA 的快速开发插件,为效率而生。
MyBatis Plus 的核心功能有:支持通用的 CRUD、代码生成器与条件构造器。
- 通用 CRUD:定义好 Mapper 接口后,只需要继承
BaseMapper<T>
接口即可获得通用的增删改查功能,无需编写任何接口方法与配置文件 - 条件构造器:通过
EntityWrapper<T>
(实体包装类),可以用于拼接 SQL 语句,并且支持排序、分组查询等复杂的 SQL - 代码生成器:支持一系列的策略配置与全局配置,比 MyBatis的代码生成更好用
/***
* 通用删除操作 deleteBatchIds 通过多个ID进行删除
*/
@Test
public void testDeleteBatchIds() {
List<Integer> idList = new ArrayList<Integer>();
idList.add(5);
idList.add(6);
int result = employeeMapper.deleteBatchIds(idList);
System.out.println("*******************" + result);
}
public void selectIn() {
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.setSqlSelect(WinAgent.COL_ID, WinAgent.COL_AG_NUM);
wrapper.in(User.COL_AG_NUM, new String[]{"11","22","33","44"});
List agreeList = Arrays.asList(agreeMan.split(",|,"));
if (agreeList.size() > 1) {
wrapper.in(User.COL_CREATE_USER, agreeList);
} else {
wrapper.eq(User.COL_CREATE_USER, agreeList.get(0));
}
userMapper.selectList(wrapper);
}
/**
* 根据条件构造器删除
*/
public void testDeleteByWrapper(){
QueryWrapper<User> wrapper = new QueryWrapper<>();
// 参数1-字段名,参数2-匹配的值
wrapper.eq("user_name", "lisi");
// count代表删除成功的记录数
int count = userMapper.delete(wrapper);
}
/***
* 通用更新操作
*/
@Test
public void testUpdateOrInsert() {
Employee entity = new Employee();
entity.setId(1);
entity.setName("更新测试成功");
int result = employeeMapper.updateById(entity);
//int result = employeeMapper.insert(entity);
System.out.println("插入后:employee id="+entity.getId());
}
public void testUpdate() {
Employee entity = new Employee();
entity.setName("更新测试成功");
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("id", "1");
int result = employeeMapper.update(entity, queryWrapper);
}
/**
* 查询所有数据
* UserMapper 中的 selectList() 方法的参数
* 为 MP 内置的条件封装器 Wrapper,所以不填写就是无任何条件(条件构造器)
*/
@Test
public void selectList() {
List<User> list = userMapper.selectList(null);
list.forEach(System.out::println);
}
/**
* 根据 entity 条件,查询一条记录
* 其实 QueryWrapper 条件构造器(相当于给SQL的条件语句)
* selectOne() 方法查询必须有且只能查询一条记录,多一条会报错。
*/
@Test
public void selectOne() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
//查询名字为 Tom 的一条记录
queryWrapper.eq("name", "Tom");
User user = userMapper.selectOne(queryWrapper);
System.out.println(user);
}
/**
* 根据主键ID查询数据
* 查询主键id=2 的一条数据,只能查询一个主键的数据不能多个
*/
@Test
public void selectById() {
User user = userMapper.selectById(1);
System.out.println(user);
}
/**
* 根据 List 的 ID 集合 查询对应的用户list
*/
@Test
public void selectBatchIds() {
List<User> list = userMapper.selectBatchIds(Arrays.asList(new String[] { "1", "2" }));
list.forEach(System.out::println);
}
/**
* 查询(根据 columnMap 条件),查询年龄为20岁的所有记录
* <p>
* 注意:建议尽量减少使用map这种方式。
* 因为可能字段名可能存在修改的情况,
* 如果,项目开发一段时间后,再修改,影响太大
*/
@Test
public void selectByMap() {
Map<String, Object> map = new HashMap<>();
map.put("age", "20");
List<User> list = userMapper.selectByMap(map);
list.forEach(System.out::println);
}
/**
* 查询大于20岁的学生,名称中包含“J”的人,带条件判断的,可以采用这种方式
* SELECT * FROM user WHERE (name LIKE ? AND age > ?)
* gt()方法 相当于:大于 > 但没有等于=
*/
@Test
public void selectListWrapper() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name", "J");//格式:(字段,值)
queryWrapper.gt("age", "19"); //查询不小于20岁
List<User> list = userMapper.selectList(queryWrapper);
list.forEach(System.out::println);
}
/**
* 根据 Wrapper 条件,查询全部记录。
* 查询名字含有 a 的,且年龄大于等于20。
* ge()方法 相当于: 大于等于 >= 。
* like()方法 相当于: NOT LIKE '%值%' 。
*/
@Test
public void selectMaps() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.like("name", "a");
queryWrapper.ge("age", "20");
List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper);
maps.forEach(System.out::println);
}
/**
* 根据 Wrapper 条件,查询总记录数
* 查询一共有多少条数据记录
*/
@Test
public void selectCount() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
Integer count = userMapper.selectCount(queryWrapper);
System.out.printf("一共有" + String.valueOf(count) + "条记录");
}
不更新名称,前提没有配置FieldStrategy.IGNORED
Employee entity = new Employee();
entity.setId(1);
entity.setName(null);
employeeMapper.updateById(entity);
括号
//有很多中Wrapper实现类,我也不是很了解,这里我使用的是EntityWrapper
EntityWrapper wrapper = new EntityWrapper();
wrapper.eq("status", status);
wrapper.andNew().gt("gmt_end", now).or().isNotNull("days");
Integer count = couponMapper.selectCount(wrapper);
SELECT COUNT(1) FROM unimall_coupon WHERE status = 1 AND (gmt_end > '2019-07-14 16:14:23.51' OR days IS NOT NULL)
分页查询
/**
* 根据 entity 条件,查询全部记录(并翻页)。
* 查询年龄大于 18 且 小于等于30 岁的所有记录,分 3 条数记录为一页
* gt()方法 相当于: 大于 > 。
* le()方法 相当于: 小于等于 <= 。
*/
@Test
public void selectPage() {
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.gt("age", "18");
queryWrapper.le("age", "30");
//第一个参数表示当前页
//第二个参数表示当前页显示多少条
//第三个参数是否查询count
Page<User> page = new Page<>(1, 3);
Page<User> userIPage = userMapper.selectPage(page, queryWrapper);
List<User> records = userIPage.getRecords();
records.forEach(System.out::println);
}
public void selectPage1() {
Wrapper<User> wrapper = new EntityWrapper<>();
wrapper.last("limit 0,30");
List<User> list = this.selectList(wrapper);
}
/**
* 根据 Wrapper 条件,查询全部记录(并翻页)
*/
@Test
public void selectMapsPage() {
QueryWrapper<User> query = new QueryWrapper<User>();
//第一个参数表示当前页
//第二个参数表示当前页显示多少条
Page<Map<String, Object>> page = new Page<>(2, 3);
Page<Map<String, Object>> maps = userMapper.selectMapsPage(page, query);
maps.getRecords().forEach(System.out::println);
}
queryWrapper是mybatis plus中实现查询的对象封装操作类.。注意这是因为我的mybatisplus的版本是3.2版本,只有3.x的版本才有QueryWrapper,而如果是2.x版本,是没有QueryWrapper的,如果找不到QueryWrapper,可以在配置里将mybatisplus的版本改为3.x
<mybatis-plus.version>2.2.0</mybatis-plus.version>
或者使用EntityWrapper,两者的功能其实差不多。
条件参数说明
二、MyBatis Plus 集成 Spring
数据表结构
CREATE TABLE user
(
id BIGINT(20) NOT NULL COMMENT '主键ID',
name VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
age INT(11) NULL DEFAULT NULL COMMENT '年龄',
email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (id)
);
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');
创建一个 SpringBoot 项目。直接使用 IDE 工具创建一个。 Spring Initializer。
注意选择Type,JDK等.
输入Group、Artifact、Package的对应的信息,再点击next
注意:这里必须得选择web,否则demo不能使用controller层。 在我们的实际项目中,最好还是要引用spring-boot release版本的
添加 MyBatis-Plus 依赖(mybatis-plus-boot-starter)
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
使用 mysql,需要引入 mysql 相关依赖。为了简化代码,引入 lombok 依赖
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
<!-- 工具类相关 -->
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId> <!-- use mapstruct-jdk8 for Java 8 or higher -->
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
</dependency>
<!-- 测试相关 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
完整pom.xml
文件
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.lyh.test</groupId>
<artifactId>test-mybatis-plus</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>test-mybatis-plus</name>
<description>测试 -- 测试 MyBatis-Plus 功能</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>3.3.1</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
在 resources/application.yml 文件中配置 mysql 数据源信息。
server:
port: 2525
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
url: jdbc:mysql://localhost:3306/testMyBatisPlus?useUnicode=true&characterEncoding=utf8
mybatis-plus:
mapper-locations: classpath:mappers/*.xml
三、快速体验 MyBatis Plus
实体类
package entity;
import lombok.Data;
@Data
public class User {
private Long id;
private String name;
private int age;
private String email;
}
mapper 接口,这是 mybatis-plus 封装好的类。
@Select @Update
package mapper;
import entity.User;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import java.util.List;
public interface UserMapper extends BaseMapper<User> {
//@Select("SELECT * FROM user where name = #{name}")
//List<User> selectByName(@Param("name") String name);
@Update({"UPDATE user SET supplier_fee_id=null,category_id=#{categoryId},supplier_id=#{supplierId} WHERE number_type=#{numberType} AND number=#{phone}"})
boolean updateUserByPhone(@Param("phone") String phone, @Param("numberType") Integer numberType, @Param("supplierId") Integer supplierId, @Param("categoryId") Integer categoryId);
//传入对象参数xml方式
//List<User> selectAllUsers(@Param("user") User user, @Param("bm") String bm);
}
实体类、Mapper 类都写好了,就可以使用了。接口绑定有两种实现方式,一种是通过注解绑定,就是在接口的方法上面加上@Select @Update 等注解里面包含 Sql 语句来绑定,另外一种就是通过xml 里面写 SQL 来绑定,在这种情况下,要指定 xml 映射文件里面的namespace 必须为接口的全路径名。当 Sql 语句比较简单时候,用注解绑定;当 SQL 语句比较复杂时候,用 xml 绑定,一般用 xml 绑定的比较多
添加UserMapper.xml
文件(可省略)
对于$和#的用法区别
简单来说 #{} 会在将参数加上引号,而${}并不会在给参数加上引号。mybatis对参数没有进行任何的处理。通常${}用于GROUP BY,ORDER BY,IN等的后面
但是,实际应用中,并不提倡使用 ${},因为使用 #{} 写法,除了可以防止sql注入以外,还能在参数里含有单引号的时候自动转义
<?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="UserMapper">
<select id="findUserCount" resultType="java.lang.Integer">
select count(1) from user
</select>
<select id="selectAllUsers" resultMap="BaseResultMap">
select * from user where number_type = ${@net.test.api.constants.ProductEnum@PHONE400.type}
<if test="user.name != null and user.name != ''">
and name like '%${user.name}%'
</if>
<if test="user.startTime != null ">
AND start_time>='${user.startTime} 00:00:00' and <![CDATA[ start_time<='${user.startTime} 23:59:59' ]]>
</if>
</select>
</mapper>
ServiceImpl层 通用的IService实现crud功能
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
@Autowired
private UserMapper userMapper;
public Integer getUserNum() {
return userMapper.findUserCount();
}
// 增
@Override
public int insertUser(User user) {
return insert(user);
}
// 改
@Override
public int updateUser(User user) {
return updateById(user);
}
// 删
@Override
public int deleteUser(User user) {
Integer id = user.getUserId();
return deleteById(id);
}
// 查
@Override
public User findUserByName(String userName) {
return baseMapper.getUserByName(userName);
}
// 分页查
@Override
public IPage getUserPage(Page page, User user) {
return baseMapper.getUsersPage( page, user );
}
}
Service层
public interface UserService extends IService<User> {
Integer getUserNum();
int insertUser( User user );
int updateUser( User user );
int deleteUser( User user );
User findUserByName( String userName );
IPage getUserPage( Page page, User user );
Step1:先得在启动类里扫描 Mapper 类,即添加 @MapperScan 注解
package com.lyh.test.testmybatisplus;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@MapperScan("mapper")
//@MapperScan({"com.kfit.*.mapper","org.kfit.*.mapper"})
@SpringBootApplication
public class TestMybatisPlusApplication {
public static void main(String[] args) {
SpringApplication.run(TestMybatisPlusApplication.class, args);
}
}
多module项目扫描失败解决: Consider defining a bean of type 'xxx' in your configuration
@SpringBootApplication(scanBasePackages = {"com.xx.xx.xx.A", "com.xx.xx.common"})
Step2:写一个测试类测试一下。
package com.lyh.test.testmybatisplus;
import entity.User;
import mapper.UserMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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
class TestMybatisPlusApplicationTests {
@Autowired
private UserMapper userMapper;
@Test
public void testSelect() {
System.out.println(("----- selectAll method test ------"));
List<User> userList = userMapper.selectList(null);
for(User user:userList) {
System.out.println(user);
}
}
@Test
public void testSelectBySql() {
LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper();
wrapper.like("name" , "k").lt("age", 40).last("limit 4");
List<User> userList = userMapper.selectBySql(wrapper);
userList.forEach(System.out::println);
}
}
上面几个例子中,并没有在 UserMapper 接口中定义任何方法,也没有在配置文件中编写 SQL 语句,而是通过继承 BaseMapper 接口获得通用的的增删改查方法,复杂的 SQL 也可以使用条件构造器拼接。通过这两种方式已经能够满足很多的开发需求了,不过复杂的业务需求还是要编写 SQL 语句的,流程和 MyBatis 一样。
若遇到 @Autowired 标记的变量出现 红色下划线,但是不影响 正常运行。可以进入 Settings,找到 Inspection,并选择其中的 Spring Core -> Code -> Autowiring for Bean Class,将 Error 改为 Warning,即可。
四、代码生成器
package com.example.demo;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
/**
* MybatisPlus代码生成器
*/
public class MybatisPlusGeneratorRunner {
public static final String MYSQL_DRIVER = "com.mysql.cj.jdbc.Driver";
//模块名 如果有模块名,则需在模块名前加. 例:log
public static final String MODULE_NAME = "";
public static final String AUTHOR = "danny";
public static final String MYSQL_URL = "jdbc:mysql://locallhost:3306/test?serverTimezone=UTC";
public static final String USERNAME = "root";
public static final String PASSWORD = "0rb!t";
public static final String[] DB_TABLE_NAMES = {"user", "role"};
public static final String PACKAGE_NAME = "cn.kerninventory.mybatis";
public static void main(String[] args) {
AutoGenerator generator = new AutoGenerator();
//配置数据库连接参数
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setDriverName(MYSQL_DRIVER);
dataSourceConfig.setUrl(MYSQL_URL);
dataSourceConfig.setUsername(USERNAME);
dataSourceConfig.setPassword(PASSWORD);
generator.setDataSource(dataSourceConfig);
//配置文件生成路径参数
PackageConfig packageConfig = new PackageConfig();
packageConfig.setModuleName(MODULE_NAME);
packageConfig.setParent(PACKAGE_NAME);
packageConfig.setEntity("entity");
packageConfig.setXml("xml");
packageConfig.setMapper("mapper");
//packageConfig.setServiceImpl("serviceimpl");
packageConfig.setService("service");
packageConfig.setController("controller");
generator.setPackageInfo(packageConfig);
//策略配置
StrategyConfig strategyConfig = new StrategyConfig();
//表名转驼峰
strategyConfig.setNaming(NamingStrategy.underline_to_camel);
//字段驼峰命名
strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel);
//set方法builder模式
strategyConfig.setChainModel(true);
//使用lombok注解
strategyConfig.setEntityLombokModel(true);
//不生成serial version uuid
//strategyConfig.setEntitySerialVersionUID(false);
//要生成的表名
strategyConfig.setInclude(DB_TABLE_NAMES);
//rest风格
strategyConfig.setRestControllerStyle(true);
//是否生成字段常量
strategyConfig.setEntityColumnConstant(true);
//是否生成实体时,生成字段注解
strategyConfig.setEntityTableFieldAnnotationEnable(true);
generator.setStrategy(strategyConfig);
//全局配置
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setServiceName("%sService");
globalConfig.setXmlName("mapper");
globalConfig.setBaseResultMap(true); //XML中的ResultMap标签
globalConfig.setBaseColumnList(true); //XML标签
globalConfig.setOpen(false); //不弹出生成目录
//输出目录
globalConfig.setOutputDir(System.getProperty("user.dir") + "/src/main/java");
globalConfig.setAuthor(AUTHOR);
generator.setGlobalConfig(globalConfig);
generator.execute();
}
}
五、@TableField注解
- @TableField(exist = false) 注解加载bean属性上,表示当前属性不是数据库的字段,但在项目中必须使用,这样在新增等使用bean的时候,mybatis-plus就会忽略这个,不会报错
- @TableField(.. , update="%s+1") 其中 %s 会填充为字段。输出 SQL 为:update 表 set 字段=字段+1 @TableField(.. , update="now()") 输出 SQL 为:update 表 set 字段=now() where
- @TableField(condition = SqlCondition.LIKE) 输出 SQL 为:select 表 where name LIKE CONCAT('%',值,'%')
- @TableField(fill = FieldFill.INSERT)的字段进行自动填充
- @TableField(select = false) 把一些敏感数据查询到返回给前端,这个时候我们就需要限制哪些字段默认不要进行查询。如:密码
@TableName注解
- @TableName(value = "my_table_name") 表名和类名不一致时指定
Mybatis-plus批量插入
saveBatch方法分析:底层也是通过for来完成,默认是一个事务一次提交N条数据。即伪批量性能比较差。引入依赖,3.4.0之上的版本都可以
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
insertBatchSomeColumn
自定义SQL注入器
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.extension.injector.methods.InsertBatchSomeColumn;
import java.util.List;
/**
* 批量插入 SQL 注入器.
**/
public class InsertBatchSqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass, TableInfo tableInfo) {
// super.getMethodList() 保留 Mybatis Plus 自带的方法
List<AbstractMethod> methodList = super.getMethodList(mapperClass, tableInfo);
// 添加自定义方法:批量插入,方法名为 insertBatchSomeColumn
methodList.add(new InsertBatchSomeColumn());
return methodList;
}
}
把SQL注入器交给Spring
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class MybatisPlusConfig {
/**
* 自定义批量插入 SQL 注入器.
*/
@Bean
public InsertBatchSqlInjector insertBatchSqlInjector() {
return new InsertBatchSqlInjector();
}
}
到此定义完毕,继承下面定义的 MyBaseMapper
,你就可以撒手不管了,直接调用就行。或者直接在ServiceImpl通过Mapper调用insertBatchSomeColumn,然后ALT+回车生成此方法
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface MyBaseMapper<T> extends BaseMapper<T> {
/**
* 执行批量插入操作,针对指定列表中的对象插入数据库。 此方法专注于通过批量操作提高数据插入的效率,而不是逐个插入对象。这有助于减少数据库访问次数,从而提高性能。
*
* @param batchList 待插入数据库的对象列表。列表中的每个对象都将被插入到数据库中, 注意,对象的属性和数据库表的字段需要对应,以便正确插入数据。
* @return 返回插入操作影响的行数。这可以用于判断插入操作的成功与否,或者了解操作的范围。
*/
int insertBatchSomeColumn(@Param("list") List<T> batchList);
}
测试代码,调用insertBatchSomeColumn方法
@Test
void testInsertBatch() {
List<User> users = new ArrayList<>();
for (int i = 0; i < 3; i++) {
User user = new User();
user.setName("犬小哈" + i);
user.setAge(i);
user.setGender(1);
users.add(user);
}
userMapper.insertBatchSomeColumn(users);
}
mybatis-plus-join用法
mybatis plus 封装的 mapper 不支持 join,如果需要支持就必须自己去实现。但是对于大部分的业务场景来说,都需要多表 join。mybatis-plus-join是mybatis plus的一个多表插件,上手简单,十分钟不到就能学会全部使用方式,只要会用mp就会用这个插件,仅仅依赖了lombok,而且是扩展mp的构造器并非更改原本的构造器,不会对原有项目产生一点点影响,相信大多数项目都有这俩插件,四舍五入就是没依赖。需要 Mybatis-plus version >= 3.4.0
安装
- maven
<dependency>
<groupId>com.github.yulichang</groupId>
<artifactId>mybatis-plus-join</artifactId>
<version>1.2.4</version>
</dependency>
使用
- mapper继承MPJBaseMapper (必选)
- service继承MPJBaseService (可选)
- serviceImpl继承MPJBaseServiceImpl (可选)
分页查询
public class testMPJQueryWrapperPage{
@Autowired
private UserMapper userMapper;
public void mpjQueryWrapperTestPage(){
IPage<UserDTO> page = userMapper.selectJoinPage(new Page<>(1, 10), UserDTO.class,
new MPJQueryWrapper<UserDO>()
.selectAll(UserDO.class)
.select("addr.tel", "addr.address")
.select("a.province")
.leftJoin("user_address addr on t.id = addr.user_id")
.rightJoin("area a on addr.area_id = a.id"));
}
}
对应sql
SELECT
t.id,
t.name,
t.sex,
t.head_img,
addr.tel,
addr.address,
a.province
FROM
user t
LEFT JOIN user_address addr on t.id = addr.user_id
RIGHT JOIN area a on addr.area_id = a.id
LIMIT ?,?
其他
Java计时StopWatch的使用方法详解
StopWatch 是位于 org.springframework.util 包下的一个工具类,通过它可方便的对程序部分代码进行计时(ms级别),适用于同步单线程代码块。简单总结一句,Spring提供的计时器StopWatch对于秒、毫秒为单位方便计时的程序,尤其是单线程、顺序执行程序的时间特性的统计输出支持比较好。也就是说假如我们手里面有几个在顺序上前后执行的几个任务,而且我们比较关心几个任务分别执行的时间占用状况,希望能够形成一个不太复杂的日志输出,StopWatch提供了这样的功能。而且Spring的StopWatch基本上也就是仅仅为了这样的功能而实现。
想要使用它,首先你需要在你的 Maven 中引入 Spring 核心包,当然 Spring MVC 和 Spring Boot 都已经自动引入了该包:
<!-- https://mvnrepository.com/artifact/org.springframework/spring-core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
对一切事物的认知,都是从使用开始,那就先来看看它的用法,会如下所示:
public static void main(String[] args) throws InterruptedException {
StopWatch stopWatch = new StopWatch();
// 任务一模拟休眠3秒钟
stopWatch.start("TaskOneName");
Thread.sleep(1000 * 3);
System.out.println("当前任务名称:" + stopWatch.currentTaskName());
stopWatch.stop();
// 任务一模拟休眠10秒钟
stopWatch.start("TaskTwoName");
Thread.sleep(1000 * 10);
System.out.println("当前任务名称:" + stopWatch.currentTaskName());
stopWatch.stop();
// 任务一模拟休眠10秒钟
stopWatch.start("TaskThreeName");
Thread.sleep(1000 * 10);
System.out.println("当前任务名称:" + stopWatch.currentTaskName());
stopWatch.stop();
// 打印出耗时
System.out.println(stopWatch.prettyPrint());
System.out.println(stopWatch.shortSummary());
// stop后它的值为null
System.out.println(stopWatch.currentTaskName());
// 最后一个任务的相关信息
System.out.println(stopWatch.getLastTaskName());
System.out.println(stopWatch.getLastTaskInfo());
// 任务总的耗时 如果你想获取到每个任务详情(包括它的任务名、耗时等等)可使用
System.out.println("所有任务总耗时:" + sw.getTotalTimeMillis());
System.out.println("任务总数:" + sw.getTaskCount());
System.out.println("所有任务详情:" + sw.getTaskInfo());
}
Hutool工具
Hutool是一个小而全的Java工具类库,通过静态方法封装,降低相关API的学习成本,提高工作效率,使Java拥有函数式语言般的优雅。Hutool是对文件、流、加密解密、转码、正则、线程、XML等JDK方法进行封装,组成各种Util工具类。
在项目的pom.xml的dependencies中加入以下内容:
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>5.5.2</version>
</dependency>
禁用 netflix-eureka 客户端
- Spring Cloud Eureka主要负责实现微服务架构中的服务治理功能
- SpringCloudGateway作为SpringCloud生态系中的网关,为微服务架构提供统一的路由管理,并且根据http请求进行相应的匹配、断言、过滤
eureka.client.enabled=false
事务处理
public class TransactionStatusUtils {
@Autowired
PlatformTransactionManager dataSourceTransactionManager;
public TransactionStatus getTransactionStatus(DataSourceType dbType){
DbContextHolder.setDbType(dbType);
DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition();
transactionDefinition.setPropagationBehavior(DefaultTransactionDefinition.PROPAGATION_REQUIRES_NEW);
TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
return transactionStatus;
}
public void commit(TransactionStatus transactionStatus) {
dataSourceTransactionManager.commit(transactionStatus);
}
public void rollback(TransactionStatus transactionStatus) {
dataSourceTransactionManager.rollback(transactionStatus);
}
}
MyBatisPlus 获取实体表名或字段名
MyBatis Plus 通过实体的原理是通过TableId,TableField,TableName等注解或属性名称/类名称转下划线实现的
//表名
SqlHelper.table(User.class).getTableName();
//主键
SqlHelper.table(User.class).getKeyColumn();
//获取userName的数据库字段
WrapperKit.getTableFieldInfoByProperty(User.class, "userName").getColumn();
import com.baomidou.mybatisplus.entity.TableFieldInfo;
import com.baomidou.mybatisplus.entity.TableInfo;
import com.baomidou.mybatisplus.toolkit.TableInfoHelper;
import java.util.List;
public class WrapperKit {
/**
* 获取主键的实体属性名
*
* @param cls
* @return
*/
public static String getTableIdProperty(Class<?> cls) {
return TableInfoHelper.getTableInfo(cls).getKeyProperty();
}
public static List<TableFieldInfo> getTableFieldInfo(Class<?> cls) {
TableInfo tableInfo = TableInfoHelper.getTableInfo(cls);
return tableInfo.getFieldList();
}
public static TableFieldInfo getTableFieldInfoByProperty(Class<?> cls, String property) {
final List<TableFieldInfo> tableFieldInfoList = getTableFieldInfo(cls);
for (TableFieldInfo tableFieldInfo : tableFieldInfoList) {
if (tableFieldInfo.getProperty().equals(property)) {
return tableFieldInfo;
}
}
if (!getTableIdProperty(cls).equals(property)) {
System.out.println(cls.toString() + " 属性" + property + "不存在,或属性没有配置@TableField");
} else {
System.out.println(property + "主键不支持,因为@TableId不会加入到fieldList");
}
return null;
}
public static <T> void appendDynamicEqual(Wrapper<T> wrapper, Object model, String... attrs) {
TableInfo tableInfo = TableInfoHelper.getTableInfo(model.getClass());
List<TableFieldInfo> fieldList = tableInfo.getFieldList();
Map<String, TableFieldInfo> collect = fieldList.stream()
.collect(Collectors.toMap(TableFieldInfo::getProperty, account -> account));
for (String attr : attrs) {
String[] attrArray = attr.split(":");
// 最后一个就是真实的
String reallyAttrName = attrArray[attrArray.length - 1];
Object attrValue = ReflectionKit.getMethodValue(model, reallyAttrName);
if (attrValue == null) {
continue;
}
if (String.class.equals(attrValue.getClass()) && StrKit.isEmpty((String) attrValue)) {
continue;
}
TableFieldInfo tableFieldInfo = collect.get(reallyAttrName);
if (attrArray.length > 1) {
switch (attrArray[0]) {
case "in":
wrapper.in(tableFieldInfo.getColumn(), attrValue.toString());
break;
case "like":
wrapper.like(tableFieldInfo.getColumn(), attrValue.toString());
break;
case "notlike":
wrapper.notLike(tableFieldInfo.getColumn(), attrValue.toString());
break;
default:
wrapper.eq(tableFieldInfo.getColumn(), attrValue);
break;
}
} else {
wrapper.eq(tableFieldInfo.getColumn(), attrValue);
}
}
}
}
更新不了空字符串或NULL问题
@TableField(strategy = FieldStrategy.IGNORED)
The valid characters are defined in RFC 7230 and RFC 3986
在yml中配置
server:
tomcat:
relaxedQueryChars: <,>, [,],^,`,{,|,} #参数内容
relaxedPathChars: <,>, [,],^,`,{,|,} #参数名称
更多推荐
所有评论(0)