官网:MyBatis-Plus
依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
配置
配置 MapperScan 注解
@SpringBootApplication
@MapperScan("com.mybatisplus.mapper")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
配置application.yml
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
configuration:
map-underscore-to-camel-case: true
auto-mapping-behavior: full
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
# 逻辑删除配置
global-config:
db-config:
logic-delete-value: 1
logic-not-delete-value: 0
其他参数参照:基本配置
使用
实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName(value = "user")//和实体类名不一致
public class User {
//标记该属性为主键。value:属性名和列名不一样
@TableId(value = "id")
private Integer id;
@TableField(value = "name")
private String name;
private Integer age;
private String email;
}
注解
@TableName
表名注解,标识实体类对应的表,用在实体类上。
属性 | 类型 | 必须指定 | 默认值 | 描述 |
value | String | 否 | "" | 表名 |
schema | String | 否 | "" | schema |
keepGlobalPrefix | boolean | 否 | false | 是否保持使用全局的 tablePrefix 的值(当全局 tablePrefix 生效时) |
resultMap | String | 否 | "" | xml 中 resultMap 的 id(用于满足特定类型的实体类对象绑定) |
autoResultMap | boolean | 否 | false | 是否自动构建 resultMap 并使用(如果设置 resultMap 则不会进行 resultMap 的自动构建与注入) |
excludeProperty | String[] | 否 | {} | 需要排除的属性名 @since 3.3.1 |
@TableId
实体类主键字段
属性 | 类型 | 必须指定 | 默认值 | 描述 |
value | String | 否 | "" | 主键字段名 |
type | Enum | 否 | IdType.NONE | 指定主键类型 |
IdType
值 | 描述 |
AUTO | 数据库 ID 自增 |
NONE | 无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT) |
INPUT | insert 前自行 set 主键值 |
ASSIGN_ID | 分配 ID(主键类型为 Number(Long 和 Integer)或 String)(since 3.3.0),使用接口IdentifierGenerator的方法nextId(默认实现类为DefaultIdentifierGenerator雪花算法) |
ASSIGN_UUID | 分配 UUID,主键类型为 String(since 3.3.0),使用接口IdentifierGenerator的方法nextUUID(默认 default 方法) |
@TableField
字段注解(非主键)
属性 | 类型 | 必须指定 | 默认值 | 描述 |
value | String | 否 | "" | 数据库字段名 |
exist | boolean | 否 | true | 是否为数据库表字段 |
condition | String | 否 | "" | 字段 where 实体查询比较条件,有值设置则按设置的值为准,没有则为默认全局的 %s=#{%s} |
update | String | 否 | "" | 字段 update set 部分注入,例如:当在version字段上注解update="%s+1" 表示更新时会 set version=version+1 (该属性优先级高于 el 属性) |
insertStrategy | Enum | 否 | FieldStrategy.DEFAULT | 举例:NOT_NULL insert into table_a(<if test="columnProperty != null">column</if>) values (<if test="columnProperty != null">#{columnProperty}</if>) |
updateStrategy | Enum | 否 | FieldStrategy.DEFAULT | 举例:IGNORED update table_a set column=#{columnProperty} |
whereStrategy | Enum | 否 | FieldStrategy.DEFAULT | 举例:NOT_EMPTY where <if test="columnProperty != null and columnProperty!=''">column=#{columnProperty}</if> |
fill | Enum | 否 | FieldFill.DEFAULT | 字段自动填充策略 |
select | boolean | 否 | true | 是否进行 select 查询 |
keepGlobalFormat | boolean | 否 | false | 是否保持使用全局的 format 进行处理 |
jdbcType | JdbcType | 否 | JdbcType.UNDEFINED | JDBC 类型 (该默认值不代表会按照该值生效) |
typeHandler | Class<? extends TypeHandler> | 否 | UnknownTypeHandler.class | 类型处理器 (该默认值不代表会按照该值生效) |
numericScale | String | 否 | "" | 指定小数点后保留的位数 |
FieldStrategy
值 | 描述 |
IGNORED | 忽略判断 |
NOT_NULL | 非 NULL 判断 |
NOT_EMPTY | 非空判断(只对字符串类型字段,其他类型字段依然为非 NULL 判断) |
DEFAULT | 追随全局配置 |
NEVER | 不加入SQL |
FieldFill
值 | 描述 |
DEFAULT | 默认不处理 |
INSERT | 插入时填充字段 |
UPDATE | 更新时填充字段 |
INSERT_UPDATE | 插入和更新时填充字段 |
@Version
乐观锁注解、标记 @Version 在字段上
@EnumValue
普通枚举类注解(注解在枚举字段上)
@TableLogic
表字段逻辑处理注解(逻辑删除)
属性 | 类型 | 必须指定 | 默认值 | 描述 |
value | String | 否 | "" | 逻辑未删除值 |
delval | String | 否 | "" | 逻辑删除值 |
@KeySequence
序列主键策略 oracle
属性 | 类型 | 必须指定 | 默认值 | 描述 |
value | String | 否 | "" | 序列名 |
dbType | Enum | 否 | DbType.OTHER | 数据库类型,未配置默认使用注入 IKeyGenerator 实现,多个实现必须指定 |
@InterceptorIgnore
value 值为 1 | yes | on 视为忽略,例如 @InterceptorIgnore(tenantLine = "1")
value 值为 0 | false | off | 空值不变 视为正常执行。
@OrderBy
内置 SQL 默认指定排序,优先级低于 wrapper 条件查询
属性 | 类型 | 必须指定 | 默认值 | 描述 |
isDesc | boolean | 否 | true | 是否倒序查询 |
sort | short | 否 | Short.MAX_VALUE | 数字越小越靠前 |
Service CRUD 接口
参考官网接口,官网:CRUD 接口
save
boolean save(T entity);// 插入一条记录(选择字段,策略插入)
boolean saveBatch(Collection<T> entityList);// 插入(批量)
boolean saveBatch(Collection<T> entityList, int batchSize);// 插入(批量)
saveOrUpdate
boolean saveOrUpdate(T entity);// TableId 注解属性值存在则更新记录,否插入一条记录
boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper);// 根据updateWrapper尝试更新,否继续执行saveOrUpdate(T)方法
boolean saveOrUpdateBatch(Collection<T> entityList);// 批量修改插入
boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);// 批量修改插入
remove
boolean remove(Wrapper<T> queryWrapper);// 根据 queryWrapper 设置的条件,删除记录
boolean removeById(Serializable id);// 根据 ID 删除
boolean removeByMap(Map<String, Object> columnMap);// 根据 columnMap 条件,删除记录
boolean removeByIds(Collection<? extends Serializable> idList);// 删除(根据ID 批量删除)
update
boolean update(Wrapper<T> updateWrapper);// 根据 UpdateWrapper 条件,更新记录 需要设置sqlset
boolean update(T updateEntity, Wrapper<T> whereWrapper);// 根据 whereWrapper 条件,更新记录
boolean updateById(T entity);// 根据 ID 选择修改
boolean updateBatchById(Collection<T> entityList);// 根据ID 批量更新
boolean updateBatchById(Collection<T> entityList, int batchSize);// 根据ID 批量更新
get
T getById(Serializable id);// 根据 ID 查询
T getOne(Wrapper<T> queryWrapper);// 根据 Wrapper,查询一条记录。结果集,如果是多个会抛出异常,随机取一条加上限制条件 wrapper.last("LIMIT 1")
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);// 根据 Wrapper,查询一条记录
list
List<T> list();// 查询所有
List<T> list(Wrapper<T> queryWrapper);// 查询列表
Collection<T> listByIds(Collection<? extends Serializable> idList);// 查询(根据ID 批量查询)
Collection<T> listByMap(Map<String, Object> columnMap);// 查询(根据 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);// 查询全部记录
List<Object> listObjs(Wrapper<T> queryWrapper);// 根据 Wrapper 条件,查询全部记录
<V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);// 根据 Wrapper 条件,查询全部记录
page
IPage<T> page(IPage<T> page);// 无条件分页查询
IPage<T> page(IPage<T> page, Wrapper<T> queryWrapper);// 条件分页查询
IPage<Map<String, Object>> pageMaps(IPage<T> page);// 无条件分页查询
IPage<Map<String, Object>> pageMaps(IPage<T> page, Wrapper<T> queryWrapper);// 条件分页查询
count
int count();// 查询总记录数
int count(Wrapper<T> queryWrapper);// 根据 Wrapper 条件,查询总记录数
long count();// 查询总记录数(自3.4.3.2开始,返回值修改为long
long count(Wrapper<T> queryWrapper);// 根据 Wrapper 条件,查询总记录数
1、service接口-继承Iservice
public interface IUserService extends IService<User> {
}
2、接口实现类
@Service
public class IUserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
}
Mapper CRUD 接口
insert
int insert(T entity);// 插入一条记录
delete
int delete(@Param(Constants.WRAPPER) Wrapper<T> wrapper);// 根据 entity 条件,删除记录
int deleteBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);// 删除(根据ID 批量删除)
int deleteById(Serializable id);// 根据 ID 删除
int deleteByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);// 根据 columnMap 条件,删除记录
update
int update(@Param(Constants.ENTITY) T updateEntity, @Param(Constants.WRAPPER) Wrapper<T> whereWrapper);// 根据 whereWrapper 条件,更新记录
int updateById(@Param(Constants.ENTITY) T entity);// 根据 ID 修改
select
T selectById(Serializable id);// 根据 ID 查询
T selectOne(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);// 根据 entity 条件,查询一条记录
List<T> selectBatchIds(@Param(Constants.COLLECTION) Collection<? extends Serializable> idList);// 查询(根据ID 批量查询)
List<T> selectList(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);// 根据 entity 条件,查询全部记录
List<T> selectByMap(@Param(Constants.COLUMN_MAP) Map<String, Object> columnMap);// 查询(根据 columnMap 条件)
List<Map<String, Object>> selectMaps(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);// 根据 Wrapper 条件,查询全部记录
List<Object> selectObjs(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);// 根据 Wrapper 条件,查询全部记录。注意: 只返回第一个字段的值
IPage<T> selectPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);// 根据 entity 条件,查询全部记录(并翻页)
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param(Constants.WRAPPER) Wrapper<T> queryWrapper);// 根据 Wrapper 条件,查询全部记录(并翻页)
Integer selectCount(@Param(Constants.WRAPPER) Wrapper<T> queryWrapper);// 根据 Wrapper 条件,查询总记录数
参考
MyBatis-Plus 之selectMaps、selectObjs、selectCount、selectOne的使用 - Python技术站
MyBatis-Plus 之selectMaps、selectObjs、selectCount、selectOne的使用_java_脚本之家
//BaseMapper接口中提供了单表的所有操作,连表不行,需要自己实现
@Repository
public interface UserMapper extends BaseMapper<User> {
}
扩展使用
QueryWrapper与LambdaQueryWrapper
使用condition组装条件
boolean condition:用于指定当前这个条件是否有效;如果为 true,则使用当前条件;如果为 false,则忽略当前条件。
参考
QueryWrapper 是的列名匹配使用的是数据库中的字段名
LambdaQueryWrapper 的列名匹配使用的是“Lambda的语法,偏向于对象”,不用写死字段名
方法 | 简介 | 使用范围 | 普通 Wrapper示例 | Lambda Wrapper示例 | 对应sql |
allEq | 通过一个 Map 来设置多个字段的相等条件 | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper | queryWrapper.allEq(Map.of("name", "老王", "age", null)); |
lambdaQueryWrapper.allEq(Map.of( "name", "老王", "age", null));
| WHERE name = '老王' AND age IS NULL |
eq | 相等 | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper | queryWrapper.eq("name", "老王"); | lambdaQueryWrapper.eq(User::getName, "老王"); | WHERE name = '老王' |
ne | 不相等 | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper |
queryWrapper.ne("name", "老王");
| lambdaQueryWrapper.ne(User::getName, "老王"); | WHERE name <> '老王' |
gt | 大于 > | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper |
queryWrapper.gt("age", 18);
| lambdaQueryWrapper.gt(User::getAge, 18); | WHERE age > 18 |
ge | 大于等于 >= | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper | queryWrapper.ge("age", 18); | lambdaQueryWrapper.ge(User::getAge, 18); | WHERE age >= 18 |
lt | 小于 < | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper | queryWrapper.lt("age", 18); | lambdaQueryWrapper.lt(User::getAge, 18); | WHERE age < 18 |
le | 小于等于 <= | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper | queryWrapper.le("age", 18); | lambdaQueryWrapper.le(User::getAge, 18); | WHERE age <= 18 |
between | 区间查询 | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper | queryWrapper.between("age", 18, 30); | lambdaQueryWrapper.between(User::getAge, 18, 30); | WHERE age BETWEEN 18 AND 30 |
notBetween | 不在区间范围 | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper | queryWrapper.notBetween("age", 18, 30); | lambdaQueryWrapper.notBetween(User::getAge, 18, 30); | WHERE age NOT BETWEEN 18 AND 30 |
like | 模糊匹配(左模糊 likeRight 、右模糊likeLeft) | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper | queryWrapper.like("name", "王"); | lambdaQueryWrapper.like(User::getName, "王"); | WHERE name LIKE '%王%' |
notLike | 不包含 | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper | queryWrapper.notLike("name", "王"); | lambdaQueryWrapper.notLike(User::getName, "王"); | WHERE name NOT LIKE '%王%' |
isNull | 空 | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper | queryWrapper.isNull("name"); | lambdaQueryWrapper.isNull(User::getName); | WHERE name IS NULL |
in | 多个条件等于 | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper | queryWrapper.in("age", Arrays.asList(1, 2, 3)); | lambdaQueryWrapper.in(User::getAge, Arrays.asList(1, 2, 3)); | WHERE age IN (1, 2, 3) |
notIn | 多个条件不等于 | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper | queryWrapper.notIn("age", Arrays.asList(1, 2, 3)); | lambdaQueryWrapper.notIn(User::getAge, Arrays.asList(1, 2, 3)); | WHERE age NOT IN (1, 2, 3) |
inSql | in查询,可以添加sql | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper | queryWrapper.inSql("age", "1,2,3,4,5,6"); | lambdaQueryWrapper.inSql(User::getAge, "1,2,3,4,5,6"); | WHERE age IN (1, 2, 3, 4, 5, 6) |
notInSql | 不属于,可添加sql | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper | queryWrapper.notInSql("age", "1,2,3,4,5,6"); queryWrapper.notInSql("id", "select id from other_table where id < 3"); | lambdaQueryWrapper.notInSql(User::getAge, "1,2,3,4,5,6"); lambdaQueryWrapper.notInSql(User::getId, "select id from other_table where id < 3"); | WHERE age NOT IN (1, 2, 3, 4, 5, 6) WHERE id NOT IN (select id from other_table where id < 3) |
eqSql | 字段等于sql结果(版本3.5.6) | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper | queryWrapper.eqSql("id", "select MAX(id) from table"); | lambdaQueryWrapper.eqSql(User::getId, "select MAX(id) from table"); | WHERE id = (select MAX(id) from table) |
groupBy | 分组 | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper | queryWrapper.groupBy("id", "name"); | lambdaQueryWrapper.groupBy(User::getId,User::getName); | group by id,name |
orderByAsc | 排序-升序 | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper | queryWrapper.orderByAsc("id", "name"); | lambdaQueryWrapper.orderByAsc(User::getId,User::getName); | order by id asc, name asc |
orderByDesc | 排序-降序 | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper | queryWrapper.orderByDesc("id", "name"); | lambdaQueryWrapper.orderByDesc(User::getId,User::getName); | order by id desc, name desc |
orderBy | 排序 | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper | queryWrapper.orderBy(true, true, "id", "name"); | lambdaQueryWrapper.orderBy(true, true, User::getId, User::getName); | order by id asc, name asc |
having | 对分组后的数据进行条件筛选 | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper |
queryWrapper.groupBy("age").having("sum(age) > 10");
|
lambdaQueryWrapper.groupBy(User::getAge).having("sum(age) > {0}", 10);
| GROUP BY age HAVING sum(age) > 10 |
func | 构建查询条件 | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper | queryWrapper.func(i -> { if (true) { i.eq("id", 1); } else { i.ne("id", 1); } }); | lambdaQueryWrapper.func(i -> { if (true) { i.eq(User::getId, 1); } else { i.ne(User::getId, 1); } }); | 如果条件为 true SELECT * FROM user WHERE id = 1 如果条件为 false SELECT * FROM user WHERE id != 1 |
or | 或 | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper |
queryWrapper.eq("id", 1).or().eq("name", "老王");
嵌套 queryWrapper.or(i -> i.and(j -> j.eq("name", "李白").eq("status", "alive")) .or(j -> j.eq("name", "杜甫").eq("status", "alive"))); |
lambdaQueryWrapper.eq(User::getId, 1).or().eq(User::getName, "老王");
嵌套 lambdaQueryWrapper.or(i -> i.and(j -> j.eq(User::getName, "李白").eq(User::getStatus, "alive")) .or(j -> j.eq(User::getName, "杜甫").eq(User::getStatus, "alive"))); |
WHERE id = 1 OR name = '老王'
嵌套 WHERE (name = '李白' AND status = 'alive') OR (name = '杜甫' AND status = 'alive') |
and | 且 | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper | queryWrapper.and(i -> i.and(j -> j.eq("name", "李白").eq("status", "alive")) .and(j -> j.eq("name", "杜甫").eq("status", "alive"))); | lambdaQueryWrapper.and(i -> i.and(j -> j.eq(User::getName, "李白").eq(User::getStatus, "alive")) .and(j -> j.eq(User::getName, "杜甫").eq(User::getStatus, "alive"))); | WHERE (name = '李白' AND status <> '活着') |
nested | 查询条件块 | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper |
queryWrapper.nested(i -> i.eq("name", "李白").ne("status", "活着"));
|
lambdaQueryWrapper.nested(i -> i.eq(User::getName, "李白").ne(User::getStatus, "活着"));
| WHERE (name = '李白' AND status <> '活着') |
apply | 自定义查询条件 | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper |
queryWrapper.apply("id = 1");
queryWrapper.apply("date_format(dateColumn, '%Y-%m-%d') = {0}", "2008-08-08"); |
lambdaQueryWrapper.apply("date_format(dateColumn, '%Y-%m-%d') = '2008-08-08'");
| WHERE id = 1 WHERE date_format(dateColumn, '%Y-%m-%d') = '2008-08-08' |
last | 查询语句最后添加sql片段 | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper | queryWrapper.last("limit 1"); | lambdaQueryWrapper.last("limit 1"); | LIMIT 1 |
exists | 添加子查询 | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper |
queryWrapper.exists("select id from table where age = 1");
|
lambdaQueryWrapper.exists("select id from table where age = 1");
| WHERE EXISTS (select id from table where age = 1) |
notExists | 与exists相反 | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper | queryWrapper.notExists("select id from table where age = 1"); | lambdaQueryWrapper.notExists("select id from table where age = 1"); | WHERE NOT EXISTS (select id from table where age = 1) |
select | 查询字段 | QueryWrapper、LambdaQueryWrapper | queryWrapper.select("id", "name", "age"); |
lambdaQueryWrapper.select(User::getId, User::getName, User::getAge);
|
SELECT id, name, age FROM user
|
set | 更新语句,set字段 | UpdateWrapper、LambdaUpdateWrapper | updateWrapper.set("name", "老李头"); | lambdaUpdateWrapper.set(User::getName, "老李头"); | UPDATE user SET name = '老李头' |
setSql | 更新语句,sql语句 | QueryWrapper、LambdaQueryWrapper、UpdateWrapper、LambdaUpdateWrapper | setSql("name = '老李头'") setSql("dateColumn={0}", LocalDate.now()) setSql("type={0,javaType=int,jdbcType=NUMERIC,typeHandler=xxx.xxx.MyTypeHandler}", "待处理字符串"); | ||
setIncrBy | 数值增加指定数值(版本3.5.6) | UpdateWrapper、LambdaUpdateWrapper |
updateWrapper.setIncrBy(Product::getNum, 1);
| lambdaUpdateWrapper.setIncrBy(Product::getNum, 1); | UPDATE product SET num = num + 1 |
setDecrBy | 数值减少指定值(版本>= 3.5.6) | UpdateWrapper、LambdaUpdateWrapper |
updateWrapper.setDecrBy("num", 1);
|
lambdaUpdateWrapper.setDecrBy(Product::getNum, 1);
| UPDATE product SET num = num - 1 |
lambda | Lambda表达式 | QueryWrapper、UpdateWrapper | QueryWrapper调用lambda返回LambdaQueryWrapper。 UpdateWrapper调用lambda返回LambdaUpdateWrapper。 |
使用limit
// 查询单条
Article article = articleService.lambdaQuery().last("limit 1").one();
// 查询多条
List<Teacher> list = teacherService.lambdaQuery().last("limit 5").list();
去重
new QueryWrapper<T>().select("DISTINCT user_id");
自动填充功能
注解填充字段 @TableField(.. fill = FieldFill.INSERT)
public class User {
@TableField(fill = FieldFill.INSERT)
private Date createTime;
@TableField(fill = FieldFill.INSERT_UPDATE)
private Date updateTime;
....
}
@Slf4j
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
@Override
public void insertFill(MetaObject metaObject) {
this.setFieldValByName("createTime", new Date(), metaObject);
this.setFieldValByName("updateTime", new Date(), metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);
}
}
根据指定字段更新或插入
自带的saveOrUpdate()方法,默认情况下根据主键是否存在进行更新或插入操作
public boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper) {
return this.update(entity, updateWrapper) || this.saveOrUpdate(entity);
}
使用
// 创建UpdateWrapper指定查询条件
UpdateWrapper<User> updateWrapper = Wrappers.query();
updateWrapper .eq("name", 123);
// 调用saveOrUpdate传入实体对象和UpdateWrapper对象
userMapper.saveOrUpdate(user, updateWrapper );
参考:
https://blog.csdn.net/u012288582/article/details/135091100
雪花算法
mybatis-plus默认雪花算法生成分布式唯一id
在分布式场景下,如果多台机器上的服务都指定相同的datacenterId和workerId,在高并发请求下,会出现Id重复的风险。
如下:
https://blog.csdn.net/wagnteng/article/details/117064242
现象:
出现重复的部署架构是单服务集群的部署方式
分析:
单机跑,压到1000的并发都没有出现过id重复,这个说明单机情况下不存在id重复问题,说明只有集群的情况下才会出现。
雪花算法的核心能影响到id生成的几个因素:1.服务器时间;2.workId(机器 ID 部分);3.datacenterId(数据标识 ID 部分)。
在服务器时间一样的情况下,有可能是由于两台机的虚拟机名称一样
解决:
mybatis-plus框架会根据应用所在服务器IP地址来生成datacenterId和workerId。
# 设置随机
mybatis-plus.global-config.worker-id: ${random.int(1,31)}
mybatis-plus.global-config.datacenter-id: ${random.int(1,31)}
其他解决方式:
https://blog.csdn.net/w1014074794/article/details/125607205
引入idworker框架,通过zookeeper管理snowflake算法中workerId和dataCenterId的生成,保证其唯一性,避免出现id重复的情况。
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<dependency>
<groupId>com.imadcn.framework</groupId>
<artifactId>idworker</artifactId>
<version>1.5.0</version>
</dependency>
添加zookeeper配置
mybatis-plus.zookeeper.serverLists=127.0.0.1:2181
指定mybatis-plus的id生成器
@Configuration
public class IdAutoConfig {
@Value("${mybatis-plus.zookeeper.serverLists}")
private String zkServerLists;
@Bean
public IdentifierGenerator idGenerator() {
return new ImadcnIdentifierGenerator(zkServerLists);
}
}
数据库全局配置策略
https://blog.csdn.net/BKKBjams/article/details/125538459
查询策略whereStrategy,更新策略updateStrategy,和添加策略insertStrategy
IGNORED:忽略判断,所有字段都进行更新、插入和查询;
NOT_NULL(默认):只更新和插入非NULL值,也不在查询时考虑空值字段;
NOT_EMPTY:只更新和插入非NULL值且非空字符串,也不在查询时考虑空值字段;
NEVER:永远不进行更新、插入和查询;
DEFAULT:默认策略;
分页查询
参考
mybatis-plus分页查询三种方法_mybatisplus分页查询_李长渊哦的博客-CSDN博客
MybatisPlus自定义对象查询和分页方法_mybatis-plus mapper 自定义方法 page对象_LinMain_copy的博客-CSDN博客
配置类
@Configuration
public class MybatisPlusConfig {
/**
* 分页插件。如果你不配置,分页插件将不生效
*/
@Bean
public MybatisPlusInterceptor paginationInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
// 指定数据库方言为 MYSQL
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
// 旧版
@Configuration
public class MybatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false
paginationInterceptor.setOverflow(false);
// 设置最大单页限制数量,默认 500 条,-1 不受限制
paginationInterceptor.setLimit(500);
// 开启 count 的 join 优化,只针对部分 left join
paginationInterceptor.setCountSqlParser(new JsqlParserCountOptimize(true));
return paginationInterceptor;
}
}
// 新版
@Configuration
public class MybatisPlusConfig {
/**
* (3.4以上版本使用)新的分页插件,一缓和二缓遵循mybatis的规则,
* 需要设置 MybatisConfiguration#useDeprecatedExecutor = false
* 避免缓存出现问题(该属性会在旧插件移除后一同移除)
*/
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
//DbType.MYSQL改为自己需要连接的数据库类型
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
调用通用方法-selectPage
QueryWrapper<User> wrapper = new QueryWrapper<>();
wrapper.isNotNull("account");
// 创建分页对象(1表示第一页;4表示每页大小为4)
Page<User> page = new Page<>(1, 10);
Page<User> result = userMapper.selectPage(page, wrapper);
for(Usern user : result.getRecords()) {
System.out.println(user);
}
自定义分页查询
如果返回类型是 IPage 则入参的 IPage 不能为null
如果想临时不分页,可以在初始化IPage时size参数传 <0 的值
service
public IPage<PlanVO> getMaintenancePlanList(PlanPageDTO PlanDTO) {
IPage<PlanVO> page = new Page<>(PlanDTO.getPageNo(), PlanDTO.getPageSize());
IPage<PlanVO> result = planMapper.queryPlanList(page, PlanVO);
return result;
}
mapper
传递参数 Page即自动分页,必须放在第一位
IPage<PlanVO> queryPlanList(@Param("page") IPage<PlanVO> page,@Param("PlanDTO") PlanPageDTO PlanDTO);
xml
<select id="queryPlanList" resultType="com.entity.vo.PlanVO">
select
p.id as id,
p.plan_code as planCode,
p.plan_name as planName
from plan p
order by p.id desc
</select>
IPage与Page
IPage
是Mybatis-Plus中的分页结果集接口,提供了一系列的分页查询方法。该接口主要用于返回分页后的数据结果。用于封装分页查询的结果,它在查询执行后由框架或库填充并返回。
使用@Param注解来传递分页参数。
分页查询通过调用BaseMapper的selectPage方法,返回的是一个包含查询结果的列表。
Page:
是IPage接口的默认实现类,实现了IPage接口中的方法。在进行分页查询时定义分页查询的参数,通常会创建一个Page对象,并设置相关的分页参数。在MyBatis-Plus 2.0版本之后,推荐使用Page接口进行分页查询。
通过方法的参数直接传递分页参数,更加简洁方便。
分页查询通过调用BaseMapper的selectPage方法,返回的是一个IPage对象,包含查询结果以及分页信息
feign调用返回IPage对象时报错
错误Cannot construct instance of com.baomidou.mybatisplus.core.metadata.IPage
解决:
方案一
在feign调用时将返回IPage改为返回page
方案二
将分页信息序列化 加入相应的配置类
https://blog.csdn.net/qq_35705176/article/details/128101924
处理json数据
对象
@Data
@Accessors(chain = true)
@TableName(autoResultMap = true)
public class User {
/**
* 必须开启映射注解
* @TableName(autoResultMap = true)
* 以下两种类型处理器,二选一 也可以同时存在,
* 选择对应的 JSON 处理器也必须存在对应 JSON 解析依赖包
*/
@TableField(typeHandler = JacksonTypeHandler.class)
// @TableField(typeHandler = FastjsonTypeHandler.class)
private OtherInfo otherInfo;
}
集合
自定义处理器,继承 FastjsonTypeHandler ,重载parse方法
public class JSONTypeHandler extends FastjsonTypeHandler {
private final Class<? extends Object> type;
public JSONTypeHandler(Class<?> type) {
super(type);
this.type = type;
}
@Override
protected List parse(String json) {
return JSON.parseArray(json, type);
// return JSON.parseObject(json, type);
}
@Override
protected String toJson(Object obj) {
return super.toJson(obj);
}
}
实体类
@TableName(autoResultMap = true)
public class User {
@TableField(typeHandler = JSONTypeHandler.class)
private List<Address> address;
}
在对应的xml中新增typeHandler、javaType
<mapper namespace="com.mapper.UserMapper">
<resultMap id="BaseResultMap" type="com.entiry.User">
<result property="address" column="address" typeHandler="com.handler.JSONTypeHandler" javaType="com.entiry.Address"/>
</resultMap>
</mapper>
下划线驼峰转换
参考
在mybatis-plus中,默认开启了下滑线-驼峰转换
可以通过配置文件修改
mybatis-plus.configuration.map-underscore-to-camel-case=true
批量新增
参考
1、【MyBatis-Plus】之批量插入_mybatisplus批量insert_王廷云的博客的博客-CSDN博客
2、MyBatis-plus 批量新增方法性能测试及优化学习_mybatisplus savebatch_找了一圈尾巴的博客-CSDN博客3、mybatis以及mybatisplus批量插入问题_mybatisplus 批量插入_又 欠的博客-CSDN博客
IService-saveBatch
自定义批量新增
pom
<!-- mybatis plus 与 springboot 整合的依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.0</version>
</dependency>
<!-- mybatis plus extension 包含了 mybatis plus core -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-extension</artifactId>
<version>3.4.0</version>
</dependency>
自定义SQL注入类
public class CustomSqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
// 获取父类SQL注入方法列表
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
// 将批量插入方法添加进去
methodList.add(new InsertBatchSomeColumn());
return methodList;
}
}
mybatis-plus配置类
@Configuration
public class MybatisPlusConfig {
@Bean
public CustomSqlInjector customSqlInjector() {
return new CustomSqlInjector();
}
}
扩展BaseMapper
/**
* 添加批量插入接口
*/
public interface CustomMapper<T> extends BaseMapper<T> {
/**
* 批量插入
* @param entityList 实体列表
* @return 影响行数
*/
Integer insertBatchSomeColumn(Collection<T> entityList);
}
批量更新
参考
Mybatis-plus通过其他字段批量更新或新增_mybatisplus批量更新指定的字段_帅宇Yeah~的博客-CSDN博客mybatisplus 根据非主键字段批量更新内容_mybatis plus 非主键更新_白衣渡江-吕子明的博客-CSDN博客1、Mybatis-plus通过其他字段批量更新或新增_mybatisplus批量更新指定的字段_帅宇Yeah~的博客-CSDN博客
根据id更新
IService-updateBatchById、saveOrUpdateBatch
自定义更新
扩展BaseServiceImpl
public boolean updateBatchByQueryWrapper(final Collection<T> entityList, final Function<T, QueryWrapper> function) {
entityList.forEach(BaseEntity::preUpdate);
final int batchSize = 1000;
final String sqlStatement = this.sqlStatement(SqlMethod.UPDATE);
try (final SqlSession batchSqlSession = this.sqlSessionBatch()) {
int i = 0;
for (final T entity : entityList) {
final Map<String, Object> map = new HashMap<String, Object>(1);
map.put("ew", function.apply(entity));
map.put("et", entity);
batchSqlSession.update(sqlStatement, (Object)map);
if (i >= 1 && i % batchSize == 0) {
batchSqlSession.flushStatements();
}
++i;
}
batchSqlSession.flushStatements();
}
return true;
}
使用
Function<User, QueryWrapper> function = user -> {
QueryWrapper<SpatialStructureNodeTag> queryWrapper = new QueryWrapper();
queryWrapper.lambda().eq(User::getUserId, user.getUserId());
return queryWrapper;
};
BaseServiceImpl.updateBatchByQueryWrapper(userList, function);
逻辑删除
参考
MyBatis Plus 逻辑删除_mybatisplus逻辑删除_我有一只肥螳螂的博客-CSDN博客
Mybatis Plus 3.x 注入逻辑删除 LogicSqlInjector 报错_mybatis-plus 3.5 没有 logicsqlinjector 了_拄杖忙学轻声码的博客-CSDN博客
3.x需要在配置类新增
@Bean
public ISqlInjector sqlInjector() {
return new LogicSqlInjector();
}
实体类
@ApiModelProperty("是否删除标识 0:未删除 1:删除")
@TableLogic(value = "0",delval = "1")
private int isDel;
表字段为数据库关键字
@TableField(value="`group`")
private String group;
不能更新空值字段
参考
Mybatis-Plus不能更新对象字段为空值问题解决_mybatisplus空值不更新_lgily-1225的博客-CSDN博客Mybatis-Plus更新对象时字段更新为空值的问题 - 知乎
Mybatis-Plus中字段的更新策略是通过FieldStrategy属性控制的。
在实体字段上,如果不通过@TableField注解指定字段的更新策略,字段默认的更新策略是FieldStrategy.DEFAULT,即跟随全局策略。而Mybatis-Plus的全局配置中,字段的默认更新策略是FieldStrategy.NOT_NULL,即进行空值判断,不对NULL值数据进行处理。
设置字段级别的更新策略
@TableField(updateStrategy = FieldStrategy.IGNORED)
private String email;
设置全局更新策略
mybatis-plus.global-config.db-config.update-strategy=ignored
代码生成器
旧版
参考
依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.2</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.2.0</version>
</dependency>
<!-- 模板引擎 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.0</version>
</dependency>
配置代码
public class Code {
public static void main(String[] args) {
//需要构建一个 代码自动生成器 对象
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
//配置策略
//1、全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "\\mybatis-plus\\src\\main\\java");
gc.setAuthor("xxxx");
gc.setOpen(false);
gc.setFileOverride(false); //是否覆盖
gc.setServiceName("%sService"); //去Service的I前缀
gc.setIdType(IdType.ID_WORKER);
gc.setDateType(DateType.ONLY_DATE);
gc.setSwagger2(true);
mpg.setGlobalConfig(gc);
//2、设置数据源
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://111.11.11.11:111/aaa?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("root");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
//3、包的配置
PackageConfig pc = new PackageConfig();
// pc.setModuleName("mybatis-plus");
pc.setParent("com.test");
pc.setEntity("pojo");
pc.setMapper("mapper");
pc.setService("service");
pc.setController("controller");
mpg.setPackageInfo(pc);
//4、策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setInclude("plan"); //设置要映射的表名
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
strategy.setEntityLombokModel(true); //自动lombok
strategy.setLogicDeleteFieldName("del_flag");
//自动填充配置
TableFill createTime = new TableFill("create_date", FieldFill.INSERT);
TableFill updateTime = new TableFill("update_date", FieldFill.UPDATE);
ArrayList<TableFill> tableFills = new ArrayList<>();
tableFills.add(createTime);
tableFills.add(updateTime);
strategy.setTableFillList(tableFills);
//乐观锁
// strategy.setVersionFieldName("version");
// strategy.setRestControllerStyle(true);
// strategy.setControllerMappingHyphenStyle(true); //localhost:8080/hello_id_2
mpg.setStrategy(strategy);
mpg.execute(); //执行代码构造器
}
}
新版
<!--mybatis-plus依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
<!--MP代码生成器依赖-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.5.1</version>
</dependency>
<!--模板引擎依赖-->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
代码
public class CodeGeneration {
/**
* 根据表名生成相应结构代码
*
* @param tableName 表名
*/
public static void Generation(String serviceName, String parent, String dataURL, String username, String password, String author, String... tableName) {
FastAutoGenerator.create(dataURL, username, password)
.globalConfig(builder -> {
builder.author(author) // 设置作者
.enableSwagger()//启用swagger
.outputDir(serviceName + "\\src\\main\\java");//指定输出目录
})
.packageConfig(builder -> {
builder.entity("entities.entity")//实体类包名
.parent("com.xxx." + parent)//设置父包名
.controller("controller")//控制层包名
.mapper("dao")//mapper层包名
.service("service")//service层包名
.serviceImpl("service.impl")//service实现类包名
//自定义mapper.xml文件输出目录
.pathInfo(Collections.singletonMap(OutputFile.mapperXml, serviceName + "\\src\\main\\resources\\mapper"));
})
.strategyConfig(builder -> {
builder.addInclude(tableName) //设置要生成的表名
.addTablePrefix("xxx_")//设置表前缀过滤
.entityBuilder()
.enableLombok()// 开启lombok模型
.enableChainModel() // 开启链式模型
.naming(NamingStrategy.underline_to_camel)//数据表映射实体命名策略:默认下划线转驼峰underline_to_camel
.columnNaming(NamingStrategy.underline_to_camel)//表字段映射实体属性命名规则:默认null,不指定按照naming执行
.superClass("com.xx.xxx.xxx.BaseEntity")//添加父类
.addSuperEntityColumns("create_time","update_time") //添加父类公共字段
.idType(IdType.AUTO)//添加全局主键类型
.formatFileName("%s")//格式化实体名称,%s取消首字母I,
.mapperBuilder() // 开启生成mapper
.enableMapperAnnotation()//开启mapper注解
// .enableBaseResultMap()//启用xml文件中的BaseResultMap 生成
// .enableBaseColumnList()//启用xml文件中的BaseColumnList
.formatMapperFileName("%sMapper")//格式化Dao类名称
.formatXmlFileName("%sMapper")//格式化xml文件名称
.serviceBuilder() // 开启生成service
.formatServiceFileName("%sService")//格式化 service 接口文件名称
.formatServiceImplFileName("%sServiceImpl")//格式化 service 接口文件名称
.controllerBuilder() //开启controller生成
.enableRestStyle();
}).execute();
}
public static void main(String[] args) {
Generation("xxx-xxx-xxx",
"com.xxx.xxx.xxxx",
"jdbc:mysql://xxxx:xxx/xxxxx?useUnicode=true&characterEncoding=UTF-8&serverTimezone=GMT%2B8&useSSL=false",
"xxxx",
"xxxx",
"xxx",
"xxxxx1","xxxxx2");
}
}
参考
多数据源配置
依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.5.0</version>
</dependency>
配置文件
spring:
# 配置数据源信息
datasource:
dynamic:
# 设置默认的数据源或者数据源组,默认值即为master
primary: master
# 严格匹配数据源,默认false.true未匹配到指定数据源时抛异常,false使用默认数据源
strict: false
datasource:
master:
url: jdbc:mysql://localhost:3306/mybatis_plus?characterEncoding=utf-8&useSSL=false
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: 123456
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: 123456
使用
DS注解既可以写在类上,也可以写在方法上,方法上的优先级高于类。
如果没有指定DS的属性值或者没有使用DS注解,就自动走master主库。
@DS("slave_1")//指定所操作的数据源
@Service
public class ProductServiceImpl extends ServiceImpl<ProductMapper, Product>
implements ProductService {
}
参考:https://baijiahao.baidu.com/s?id=1754519454163819416&wfr=spider&for=pc
联表查询
依赖,注意:需要 Mybatis-plus version >= 3.4.0
<!-- 引入mybatis-plus联表查询相关依赖项 -->
<!-- MVNW pom格式 -->
<dependency>
<groupId>com.github.yulichang</groupId>
<artifactId>mybatis-plus-join</artifactId>
<version>1.2.4</version>
</dependency>
2、使用
mapper继承MPJBaseMapper (必选)
service继承MPJBaseService (可选)
serviceImpl继承MPJBaseServiceImpl (可选)
- 方法
MPJBaseMapper继承BaseMapper,在原有的方法基础上又添加了以下方法:
selectJoinOne 连表查询一条记录对象
selectJoinList 连表查询返回命中记录对象集合
selectJoinPage 连表分页查询对象集合
selectJoinMap 连表查询一条记录返回Map
selectJoinMaps 连表查询返回命中记录Map集合
selectJoinMapsPage 连表分页查询返回Map集合
MPJBaseService 继承了IService,同样添加以上方法
MPJBaseServiceImpl 继承了ServiceImpl,同样添加了以上方法
- 查询核心类
MPJLambdaWrapper和MPJQueryWrapper
方法:
selectAll() // 可以查全部参数;
select() // 查询字段,一个括号内仅能查一个实体,如需要查询多个表的字段,将有多个select();
selectAs() // 相当于取别名,为了数据表内字段名称和结果集实体名称一致;
leftJoin() //联结多个表,将有多个leftJoin(),方法3个参数:联入表实体,联入表关系字段,原表关系字段;参数说明
第一个参数: 联入表实体,参与连表的实体类class
第二个参数: 连表的ON字段,这个属性必须是第一个参数实体类的属性
第三个参数: 参与连表的ON的另一个实体类属性
默认主表别名是t,其他的表别名以先后调用的顺序使用t1,t2,t3…
示例1
// 产品类
public class Product
// 厂商类
public class Factory
// 产品关联厂商类
public class ProductVO
@Repository
public interface ProductMapper extends MPJBaseMapper<Product> {
}
// 联表查询
List<ProductVO> productList = productMapper.selectJoinList(ProductVO.class,
new MPJLambdaWrapper<Product>()
.select(Product::getCode, Product::getName)
.selectAs(Factory::getCode, ProductVO::getFactoryCode)
.selectAs(Factory::getName, ProductVO::getFactoryName)
.leftJoin(Factory.class, Factory::getCode, Product::getFactoryCode)
);
-- 对应sql
SELECT
t.code,
t.name,
t1.factoryCode,
t1.factoryName
FROM
product t
LEFT JOIN factory t1 ON t1.code = t.factoryCode
示例2
// 用户类
public class User
// 用户联系方式类
public class UserAddress
// 产品关联厂商类
public class Area
@Repository
public interface UserMapper extends MPJBaseMapper<User> {
}
// 联表查询
List<UserVO> userList = userMapper.selectJoinList(UserVO.class,
new MPJLambdaWrapper<User>()
.selectAll(User.class)
.select(UserAddress::getTel)
.selectAs(UserAddress::getAddress, UserVO::getUserAddress)
.select(Area::getProvince, Area::getCity)
.leftJoin(UserAddress.class, UserAddress::getUserId, User::getId)
.leftJoin(Area.class, Area::getId, UserAddress::getAreaId)
.eq(User::getId, 1)
.like(UserAddress::getTel, "131")
.gt(User::getId, 5)
);
-- 对应sql
SELECT
t.id,
t.name,
t.sex,
t1.tel,
t1.address AS userAddress,
t2.province,
t2.city
FROM
user t
LEFT JOIN user_address t1 ON t1.user_id = t.id
LEFT JOIN area t2 ON t2.id = t1.area_id
WHERE (
t.id = ?
AND t1.tel LIKE ?
AND t.id > ?)
参考:
https://blog.csdn.net/ZGL_cyy/article/details/117199228
https://blog.csdn.net/weixin_70506521/article/details/130722631