MybatisPlus特点
- MyBatis-Plus是MyBatis的强大增强工具。它为MyBatis提供了许多有效的操作。你可以从MyBatis无缝切换到MyBatis-Plus。
- MyBatis-Plus可以自动注入基本的SQL片段;
- MyBatis-Plus有许多有用的插件(例如代码生成器,自动分页,性能分析等);
依赖
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>latest-version</version>
</dependency>
MybatisPlus的特性
**无侵入:**只做增强不做改变,引入它不会对现有工程产生影响;
**损耗小:**启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作;
**强大的 CRUD 操作:**内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求;
**支持 Lambda 形式调用:**通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错;
**支持主键自动生成:**支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题;
**支持 ActiveRecord 模式:**支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作;
**支持自定义全局通用操作:**支持全局通用方法注入( Write once, use anywhere );
**内置代码生成器:**采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用;
**内置分页插件:**基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询;
**分页插件支持多种数据库:**支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库;
**内置性能分析插件:**可输出 SQL 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询;
**内置全局拦截插件:**提供全表删除 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
数据库支持
框架结构
mybatisplus是在mybatis的curd基础上及逆行增强,所以需要将mybatis中的SqlSessionFactory替换为调整 SqlSessionFactory 为 MyBatis-Plus 的 SqlSessionFactory
看一下xml中的配置—>>
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
</bean>
所以在0xml中声明sqlSessionFactory实际为mybatisPlus中的;
查看源码–>>
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
MybatisSqlSessionFactoryBean factory = new MybatisSqlSessionFactoryBean();
factory.setDataSource(dataSource);
factory.setVfs(SpringBootVFS.class);
.........................省略......................
return factory.getObject();
}
我们获取一下sqlSessionFactory的全限定名,
package com.codem;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
//@MapperScan("com.codem.mapper")
public class MybatiplusApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(MybatiplusApplication.class, args); System.out.println(context.getBean("sqlSessionFactory").getClass());
}
}
全限定名—>
class org.apache.ibatis.session.defaults.DefaultSqlSessionFactory
当dataSource作为参数传入MybatisPlus的SqlSessionFactory时,会做一系列处理-----即增强了之前的SqlSessionFactory;
整个案例感受一下
表还是之前的经典表emp与dept
创建一个springboot项目
父依赖
<parent> <groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
mybatisplus启动器
<!--mybatisplus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.1</version>
</dependency>
mysql数据库配置
spring:
datasource:
# 使用druid链接池
type: com.alibaba.druid.pool.DruidDataSource
# 数据库要素
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://127.0.0.1:3306/gavin?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
username: gavin
password: 955945
mybatis配置
mybatis:
type-aliases-package: com.gavin.pojo
mapper-locations: classpath:com/gavin/mapper/*.xml #按照习惯来吧
实体类
package com.codem.pojo;
import java.io.Serializable;
import java.util.Date;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.ibatis.annotations.Mapper;
/**
* emp
* @author
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Emp implements Serializable {
private Integer empno;
private String ename;
private String job;
private Integer mgr;
private Date hiredate;
private Integer sal;
private Integer comm;
private Integer deptno;
private static final long serialVersionUID = 1L;
}
测试类
package codem;
import com.codem.MybatiplusApplication;
import com.codem.mapper.EmpMapper;
import com.codem.pojo.Emp;
import org.junit.jupiter.api.Test;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest(classes = MybatiplusApplication.class)
//@MapperScan("com.codem.mapper")//扫描mapper所在的包,或者在每一个mapper上夹注解@Mapper
public class MybatiplusApplicationTests {
@Autowired
private EmpMapper empMapper;
@Test
public void contextLoads() {
List<Emp> emps = empMapper.selectAll();
emps.forEach(System.out::println);
}
}
mapper接口
package com.codem.mapper;
import com.codem.pojo.Emp;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface EmpMapper {
List<Emp> selectAll();
}
结果—>>
Emp(empno=7369, ename=SMITH, job=CLERK, mgr=7902, hiredate=Wed Dec 17 00:00:00 CST 1980, sal=800, comm=null, deptno=20)
Emp(empno=7499, ename=ALLEN, job=SALESMAN, mgr=7698, hiredate=Fri Feb 20 00:00:00 CST 1981, sal=1600, comm=300, deptno=30)
Emp(empno=7521, ename=WARD, job=SALESMAN, mgr=7698, hiredate=Sun Feb 22 00:00:00 CST 1981, sal=1250, comm=500, deptno=30)
看起来跟之前springbootmybatis没有什么太大的区别,但是这只是基础,
MyBatis-Plus 提供了大量的个性化配置来满足不同复杂度的工程
Mybatisplus为我们提供了两个接口已简化我们对curd的编写
BaseMapper extends Mapper
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.baomidou.mybatisplus.core.mapper;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import org.apache.ibatis.annotations.Param;
public interface BaseMapper<T> extends Mapper<T> {
int insert(T entity);//插入
int deleteById(Serializable id);//删除
int deleteById(T entity);
int deleteByMap(@Param("cm") Map<String, Object> columnMap);
int delete(@Param("ew") Wrapper<T> queryWrapper);
int deleteBatchIds(@Param("coll") Collection<?> idList);//批量删除
int updateById(@Param("et") T entity);//更新
int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);
T selectById(Serializable id);//查找
List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);//批量查找
List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);
default T selectOne(@Param("ew") Wrapper<T> queryWrapper) {
List<T> ts = this.selectList(queryWrapper);
if (CollectionUtils.isNotEmpty(ts)) {
if (ts.size() != 1) {
throw ExceptionUtils.mpe("One record is expected, but the query result is multiple records", new Object[0]);
} else {
return ts.get(0);
}
} else {
return null;
}
}
default boolean exists(Wrapper<T> queryWrapper) {
Long count = this.selectCount(queryWrapper);
return null != count && count > 0L;
}
Long selectCount(@Param("ew") Wrapper<T> queryWrapper);
List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);
List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper);
List<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper);
<P extends IPage<T>> P selectPage(P page, @Param("ew") Wrapper<T> queryWrapper);
<P extends IPage<Map<String, Object>>> P selectMapsPage(P page, @Param("ew") Wrapper<T> queryWrapper);
}
还有另一个接口—>>
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.baomidou.mybatisplus.extension.service;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.QueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.UpdateChainWrapper;
import com.baomidou.mybatisplus.extension.kotlin.KtQueryChainWrapper;
import com.baomidou.mybatisplus.extension.kotlin.KtUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.toolkit.ChainWrappers;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import java.io.Serializable;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.springframework.transaction.annotation.Transactional;
public interface IService<T> {
int DEFAULT_BATCH_SIZE = 1000;
default boolean save(T entity) {//节点存储
return SqlHelper.retBool(this.getBaseMapper().insert(entity));
}
@Transactional(
rollbackFor = {Exception.class}//回滚
)
default boolean saveBatch(Collection<T> entityList) {
return this.saveBatch(entityList, 1000);//存储,默认1000
}
boolean saveBatch(Collection<T> entityList, int batchSize);//节点存储自定义容量
@Transactional(
rollbackFor = {Exception.class}
)
default boolean saveOrUpdateBatch(Collection<T> entityList) {
return this.saveOrUpdateBatch(entityList, 1000);
}
boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);
default boolean removeById(Serializable id) {
return SqlHelper.retBool(this.getBaseMapper().deleteById(id));
}
default boolean removeById(Serializable id, boolean useFill) {
throw new UnsupportedOperationException("不支持的方法!");
}
default boolean removeById(T entity) {
return SqlHelper.retBool(this.getBaseMapper().deleteById(entity));
}
default boolean removeByMap(Map<String, Object> columnMap) {
Assert.notEmpty(columnMap, "error: columnMap must not be empty", new Object[0]);
return SqlHelper.retBool(this.getBaseMapper().deleteByMap(columnMap));
}
default boolean remove(Wrapper<T> queryWrapper) {
return SqlHelper.retBool(this.getBaseMapper().delete(queryWrapper));
}
default boolean removeByIds(Collection<?> list) {
return CollectionUtils.isEmpty(list) ? false : SqlHelper.retBool(this.getBaseMapper().deleteBatchIds(list));
}
@Transactional(
rollbackFor = {Exception.class}
)
default boolean removeByIds(Collection<?> list, boolean useFill) {
if (CollectionUtils.isEmpty(list)) {
return false;
} else {
return useFill ? this.removeBatchByIds(list, true) : SqlHelper.retBool(this.getBaseMapper().deleteBatchIds(list));
}
}
@Transactional(
rollbackFor = {Exception.class}
)
default boolean removeBatchByIds(Collection<?> list) {
return this.removeBatchByIds(list, 1000);
}
@Transactional(
rollbackFor = {Exception.class}
)
default boolean removeBatchByIds(Collection<?> list, boolean useFill) {
return this.removeBatchByIds(list, 1000, useFill);
}
default boolean removeBatchByIds(Collection<?> list, int batchSize) {
throw new UnsupportedOperationException("不支持的方法!");
}
default boolean removeBatchByIds(Collection<?> list, int batchSize, boolean useFill) {
throw new UnsupportedOperationException("不支持的方法!");
}
default boolean updateById(T entity) {
return SqlHelper.retBool(this.getBaseMapper().updateById(entity));
}
default boolean update(Wrapper<T> updateWrapper) {
return this.update((Object)null, updateWrapper);
}
default boolean update(T entity, Wrapper<T> updateWrapper) {
return SqlHelper.retBool(this.getBaseMapper().update(entity, updateWrapper));
}
@Transactional(
rollbackFor = {Exception.class}
)
default boolean updateBatchById(Collection<T> entityList) {
return this.updateBatchById(entityList, 1000);
}
boolean updateBatchById(Collection<T> entityList, int batchSize);
boolean saveOrUpdate(T entity);
default T getById(Serializable id) {
return this.getBaseMapper().selectById(id);
}
default List<T> listByIds(Collection<? extends Serializable> idList) {
return this.getBaseMapper().selectBatchIds(idList);
}
default List<T> listByMap(Map<String, Object> columnMap) {
return this.getBaseMapper().selectByMap(columnMap);
}
default T getOne(Wrapper<T> queryWrapper) {
return this.getOne(queryWrapper, true);
}
T getOne(Wrapper<T> queryWrapper, boolean throwEx);
Map<String, Object> getMap(Wrapper<T> queryWrapper);
<V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);
default long count() {
return this.count(Wrappers.emptyWrapper());
}
default long count(Wrapper<T> queryWrapper) {
return SqlHelper.retCount(this.getBaseMapper().selectCount(queryWrapper));
}
default List<T> list(Wrapper<T> queryWrapper) {
return this.getBaseMapper().selectList(queryWrapper);
}
default List<T> list() {
return this.list(Wrappers.emptyWrapper());
}
default <E extends IPage<T>> E page(E page, Wrapper<T> queryWrapper) {
return this.getBaseMapper().selectPage(page, queryWrapper);
}
default <E extends IPage<T>> E page(E page) {
return this.page(page, Wrappers.emptyWrapper());
}
default List<Map<String, Object>> listMaps(Wrapper<T> queryWrapper) {
return this.getBaseMapper().selectMaps(queryWrapper);
}
default List<Map<String, Object>> listMaps() {
return this.listMaps(Wrappers.emptyWrapper());
}
default List<Object> listObjs() {
return this.listObjs(Function.identity());
}
default <V> List<V> listObjs(Function<? super Object, V> mapper) {
return this.listObjs(Wrappers.emptyWrapper(), mapper);
}
default List<Object> listObjs(Wrapper<T> queryWrapper) {
return this.listObjs(queryWrapper, Function.identity());
}
default <V> List<V> listObjs(Wrapper<T> queryWrapper, Function<? super Object, V> mapper) {
return (List)this.getBaseMapper().selectObjs(queryWrapper).stream().filter(Objects::nonNull).map(mapper).collect(Collectors.toList());
}
default <E extends IPage<Map<String, Object>>> E pageMaps(E page, Wrapper<T> queryWrapper) {
return this.getBaseMapper().selectMapsPage(page, queryWrapper);
}
default <E extends IPage<Map<String, Object>>> E pageMaps(E page) {
return this.pageMaps(page, Wrappers.emptyWrapper());
}
BaseMapper<T> getBaseMapper();
Class<T> getEntityClass();
default QueryChainWrapper<T> query() {
return ChainWrappers.queryChain(this.getBaseMapper());
}
default LambdaQueryChainWrapper<T> lambdaQuery() {
return ChainWrappers.lambdaQueryChain(this.getBaseMapper());
}
default KtQueryChainWrapper<T> ktQuery() {
return ChainWrappers.ktQueryChain(this.getBaseMapper(), this.getEntityClass());
}
default KtUpdateChainWrapper<T> ktUpdate() {
return ChainWrappers.ktUpdateChain(this.getBaseMapper(), this.getEntityClass());
}
default UpdateChainWrapper<T> update() {
return ChainWrappers.updateChain(this.getBaseMapper());
}
default LambdaUpdateChainWrapper<T> lambdaUpdate() {
return ChainWrappers.lambdaUpdateChain(this.getBaseMapper());
}
default boolean saveOrUpdate(T entity, Wrapper<T> updateWrapper) {
return this.update(entity, updateWrapper) || this.saveOrUpdate(entity);
}
}
在这个接口中也提供了一些方法,用于简化手写的curd以及批量提交/回滚
所以在自动给注入时我们service,或者mapper可以注入
接口实现类—ServiceImpl
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.baomidou.mybatisplus.extension.service.impl;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.Assert;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
import com.baomidou.mybatisplus.core.toolkit.ReflectionKit;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.IService;
import com.baomidou.mybatisplus.extension.toolkit.SqlHelper;
import java.io.Serializable;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import java.util.function.Function;
import org.apache.ibatis.binding.MapperMethod.ParamMap;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.session.SqlSession;
import org.mybatis.spring.SqlSessionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
public class ServiceImpl<M extends BaseMapper<T>, T> implements IService<T> {
protected Log log = LogFactory.getLog(this.getClass());
@Autowired
protected M baseMapper;
protected Class<T> entityClass = this.currentModelClass();
protected Class<M> mapperClass = this.currentMapperClass();
public ServiceImpl() {
}
public M getBaseMapper() {
return this.baseMapper;
}
public Class<T> getEntityClass() {
return this.entityClass;
}
/** @deprecated */
@Deprecated
protected boolean retBool(Integer result) {
return SqlHelper.retBool(result);
}
protected Class<M> currentMapperClass() {
return ReflectionKit.getSuperClassGenericType(this.getClass(), ServiceImpl.class, 0);
}
protected Class<T> currentModelClass() {
return ReflectionKit.getSuperClassGenericType(this.getClass(), ServiceImpl.class, 1);
}
/** @deprecated */
@Deprecated
protected SqlSession sqlSessionBatch() {
return SqlHelper.sqlSessionBatch(this.entityClass);
}
/** @deprecated */
@Deprecated
protected void closeSqlSession(SqlSession sqlSession) {
SqlSessionUtils.closeSqlSession(sqlSession, GlobalConfigUtils.currentSessionFactory(this.entityClass));
}
/** @deprecated */
@Deprecated
protected String sqlStatement(SqlMethod sqlMethod) {
return SqlHelper.table(this.entityClass).getSqlStatement(sqlMethod.getMethod());
}
@Transactional(
rollbackFor = {Exception.class}
)
public boolean saveBatch(Collection<T> entityList, int batchSize) {
String sqlStatement = this.getSqlStatement(SqlMethod.INSERT_ONE);
return this.executeBatch(entityList, batchSize, (sqlSession, entity) -> {
sqlSession.insert(sqlStatement, entity);
});
}
protected String getSqlStatement(SqlMethod sqlMethod) {
return SqlHelper.getSqlStatement(this.mapperClass, sqlMethod);
}
@Transactional(
rollbackFor = {Exception.class}
)
public boolean saveOrUpdate(T entity) {
if (null == entity) {
return false;
} else {
TableInfo tableInfo = TableInfoHelper.getTableInfo(this.entityClass);
Assert.notNull(tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!", new Object[0]);
String keyProperty = tableInfo.getKeyProperty();
Assert.notEmpty(keyProperty, "error: can not execute. because can not find column for id from entity!", new Object[0]);
Object idVal = tableInfo.getPropertyValue(entity, tableInfo.getKeyProperty());
return !StringUtils.checkValNull(idVal) && !Objects.isNull(this.getById((Serializable)idVal)) ? this.updateById(entity) : this.save(entity);
}
}
@Transactional(
rollbackFor = {Exception.class}
)
public boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize) {
TableInfo tableInfo = TableInfoHelper.getTableInfo(this.entityClass);
Assert.notNull(tableInfo, "error: can not execute. because can not find cache of TableInfo for entity!", new Object[0]);
String keyProperty = tableInfo.getKeyProperty();
Assert.notEmpty(keyProperty, "error: can not execute. because can not find column for id from entity!", new Object[0]);
return SqlHelper.saveOrUpdateBatch(this.entityClass, this.mapperClass, this.log, entityList, batchSize, (sqlSession, entity) -> {
Object idVal = tableInfo.getPropertyValue(entity, keyProperty);
return StringUtils.checkValNull(idVal) || CollectionUtils.isEmpty(sqlSession.selectList(this.getSqlStatement(SqlMethod.SELECT_BY_ID), entity));
}, (sqlSession, entity) -> {
ParamMap<T> param = new ParamMap();
param.put("et", entity);
sqlSession.update(this.getSqlStatement(SqlMethod.UPDATE_BY_ID), param);
});
}
@Transactional(
rollbackFor = {Exception.class}
)
public boolean updateBatchById(Collection<T> entityList, int batchSize) {
String sqlStatement = this.getSqlStatement(SqlMethod.UPDATE_BY_ID);
return this.executeBatch(entityList, batchSize, (sqlSession, entity) -> {
ParamMap<T> param = new ParamMap();
param.put("et", entity);
sqlSession.update(sqlStatement, param);
});
}
public T getOne(Wrapper<T> queryWrapper, boolean throwEx) {
return throwEx ? this.baseMapper.selectOne(queryWrapper) : SqlHelper.getObject(this.log, this.baseMapper.selectList(queryWrapper));
}
public Map<String, Object> getMap(Wrapper<T> queryWrapper) {
return (Map)SqlHelper.getObject(this.log, this.baseMapper.selectMaps(queryWrapper));
}
public <V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper) {
return SqlHelper.getObject(this.log, this.listObjs(queryWrapper, mapper));
}
/** @deprecated */
@Deprecated
protected boolean executeBatch(Consumer<SqlSession> consumer) {
return SqlHelper.executeBatch(this.entityClass, this.log, consumer);
}
protected <E> boolean executeBatch(Collection<E> list, int batchSize, BiConsumer<SqlSession, E> consumer) {
return SqlHelper.executeBatch(this.entityClass, this.log, list, batchSize, consumer);
}
protected <E> boolean executeBatch(Collection<E> list, BiConsumer<SqlSession, E> consumer) {
return this.executeBatch(list, 1000, consumer);
}
public boolean removeById(Serializable id) {
TableInfo tableInfo = TableInfoHelper.getTableInfo(this.getEntityClass());
return tableInfo.isWithLogicDelete() && tableInfo.isWithUpdateFill() ? this.removeById(id, true) : SqlHelper.retBool(this.getBaseMapper().deleteById(id));
}
@Transactional(
rollbackFor = {Exception.class}
)
public boolean removeByIds(Collection<?> list) {
if (CollectionUtils.isEmpty(list)) {
return false;
} else {
TableInfo tableInfo = TableInfoHelper.getTableInfo(this.getEntityClass());
return tableInfo.isWithLogicDelete() && tableInfo.isWithUpdateFill() ? this.removeBatchByIds(list, true) : SqlHelper.retBool(this.getBaseMapper().deleteBatchIds(list));
}
}
public boolean removeById(Serializable id, boolean useFill) {
TableInfo tableInfo = TableInfoHelper.getTableInfo(this.entityClass);
if (useFill && tableInfo.isWithLogicDelete() && !this.entityClass.isAssignableFrom(id.getClass())) {
T instance = tableInfo.newInstance();
tableInfo.setPropertyValue(instance, tableInfo.getKeyProperty(), new Object[]{id});
return this.removeById(instance);
} else {
return SqlHelper.retBool(this.getBaseMapper().deleteById(id));
}
}
@Transactional(
rollbackFor = {Exception.class}
)
public boolean removeBatchByIds(Collection<?> list, int batchSize) {
TableInfo tableInfo = TableInfoHelper.getTableInfo(this.entityClass);
return this.removeBatchByIds(list, batchSize, tableInfo.isWithLogicDelete() && tableInfo.isWithUpdateFill());
}
@Transactional(
rollbackFor = {Exception.class}
)
public boolean removeBatchByIds(Collection<?> list, int batchSize, boolean useFill) {
String sqlStatement = this.getSqlStatement(SqlMethod.DELETE_BY_ID);
TableInfo tableInfo = TableInfoHelper.getTableInfo(this.entityClass);
return this.executeBatch(list, batchSize, (sqlSession, e) -> {
if (useFill && tableInfo.isWithLogicDelete()) {
if (this.entityClass.isAssignableFrom(e.getClass())) {
sqlSession.update(sqlStatement, e);
} else {
T instance = tableInfo.newInstance();
tableInfo.setPropertyValue(instance, tableInfo.getKeyProperty(), new Object[]{e});
sqlSession.update(sqlStatement, instance);
}
} else {
sqlSession.update(sqlStatement, e);
}
});
}
}
通过实现类来实现接口,最终完成功能;
当有mybatis_plus前缀时会自动去检查下面的配置,比如映射文件地址—>“classpath*:/mapper/**/*.xml”
mybatis与mybatis-plus基本配置对比
mybatis:
type-aliases-package: com.codem.pojo
mapper-locations: classpath:com/codem/mapper/*.xml #按照习惯来吧
# mybatisplus在mybatis基础上进行强化,但并不是改变mybatis
mybatis-plus:
type-aliases-package: com.codem.pojo
mapper-locations: classpath:com/codem/mapper/*.xml #默认值 classpath*:/mapper/**/*.xml
mybatisPlus实现curd代码—>>
目录结构
mapper接口----->>
package com.codem.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.codem.pojo.Emp;
public interface EmpMapper extends BaseMapper<Emp> {
}
service接口
package com.codem.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.codem.pojo.Emp;
import org.apache.ibatis.annotations.Mapper;
public interface EmpService extends IService<Emp> {
}
service接口实现类
package com.codem.service.imp;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.codem.mapper.EmpMapper;
import com.codem.pojo.Emp;
import com.codem.service.EmpService;
import org.springframework.stereotype.Service;
@Service
public class empServiceImpl extends ServiceImpl<EmpMapper, Emp> implements EmpService {
}
配置注解@SpringBootTest(classes = MybatiplusApplication.class),因为测试类也需要启动springboot环境;
所以在测试类之前还需要在springboot启动类上做一些配置–
package com.codem;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
@MapperScan("com.codem.mapper")//扫描包而不是单个注解mapper
public class MybatiplusApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(MybatiplusApplication.class, args);
String[] beanDefinitionNames = context.getBeanDefinitionNames();
for (String definitionName : beanDefinitionNames) {
System.out.println(definitionName);
}
System.out.println("---------------------");
System.out.println(context.getBean("sqlSessionFactory").getClass());
}
}
测试类---->>
package codem;
import com.codem.MybatiplusApplication;
import com.codem.mapper.EmpMapper;
import com.codem.pojo.Emp;
import com.codem.service.EmpService;
import org.junit.jupiter.api.Test;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import java.util.List;
@SpringBootTest(classes = MybatiplusApplication.class)
//@MapperScan("com.codem.mapper")
public class MybatiplusApplicationTests {
/* @Autowired
private EmpMapper empMapper;*/
@Autowired
EmpService empService;
@Test
public void contextLoads() {
List<Emp> emps = empService.list();
// List<Emp> emps = empMapper.selectAll();
emps.forEach(System.out::println);
}
}
MyBatis-Plus 配置参数详解
MybatisPlus中的注解
mybatisplus注解所在的包
mybatis-plus-annotation
常用的注解
@TableName
表注解
//指定对应的表名, 指定映射 resultMap,可以理解为类构造对应mapper中的resultMap
//autoResultMap---自动构建resultMap
@TableName(value = "emp" ,resultMap ="empconstructor",autoResultMap = true)
public class Emp implements Serializable {
@TableId
主键注解
//主键字段名, 主键--自增
@TableId(value = "empno",type = IdType.AUTO)
private Integer empno;
//IdType.INPUT----insert前自行set主键值
@TableField
属性/字段注解(非主键)
//属性名与字段名不一致时可以加注解使得其匹配,插入的策略---非空,还有update,where等
@TableField(value = "ename",insertStrategy = FieldStrategy.NOT_NULL)
private String ename;
@OrderBy
排序,作用域字段/属性/注解上
//指定排列顺序优先级低于 wrapper 条件查询
@OrderBy(asc = false)
//主键字段名,设置主键--自增
@TableId(value = "empno",type = IdType.AUTO)
private Integer empno;
其他注解的使用API
代码自动生成
代码生成器(3.4.1+版本)
引入代码自动生成的依赖(非最新版本)---->>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.4.1</version>
</dependency>
依赖分析
MyBatis-Plus 支持 Velocity(默认)、Freemarker、Beetl,可以采用自定义模板引擎。
<!-- 模板引擎-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
注意!如果您选择了非默认引擎,需要在 AutoGenerator 中 设置模板引擎。
官方教程
案例—>>
代码生成器代码
package com;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class CodeGenerator {
/**
* <p>
* 读取控制台内容
* </p>
*/
public static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
StringBuilder help = new StringBuilder();
//设置生成代码的上一级目录
help.append("请输入" + tip + ":");
System.out.println(help.toString());
if (scanner.hasNext()) {
String ipt = scanner.next();
if (StringUtils.isNotBlank(ipt)) {
return ipt;
}
}
throw new MybatisPlusException("请输入正确的" + tip + "!");
}
public static void main(String[] args) {
// 代码生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
//设置代码生成的位置,默认在根目录下
gc.setOutputDir(projectPath + "/demo/src/main/java");
gc.setAuthor("codeM");
gc.setOpen(false);
// gc.setSwagger2(true); 实体属性 Swagger2 注解
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/gavin?useUnicode=true&useSSL=false&characterEncoding=utf8");
// dsc.setSchemaName("public");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("gavin");
dsc.setPassword("955945");
mpg.setDataSource(dsc);
// 包配置D:\Program Data\idea2019Data\mybatiplus\demo
PackageConfig pc = new PackageConfig();
pc.setModuleName(scanner("模块"));
//设置java下的顶级目录
pc.setParent("com");
mpg.setPackageInfo(pc);
// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};
// 如果模板引擎是 freemarker
String templatePath = "/templates/mapper.xml.ftl";
// 如果模板引擎是 velocity
// String templatePath = "/templates/mapper.xml.vm";
// 自定义输出配置
List<FileOutConfig> focList = new ArrayList<>();
// 自定义配置会被优先输出
focList.add(new FileOutConfig(templatePath) {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输出文件名 , 如果你 Entity 设置了前后缀、此处注意 xml 的名称会跟着发生变化!!
return projectPath + "/src/main/resources/mapper/" + pc.getModuleName()
+ "/" + tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
/*
cfg.setFileCreate(new IFileCreate() {
@Override
public boolean isCreate(ConfigBuilder configBuilder, FileType fileType, String filePath) {
// 判断自定义文件夹是否需要创建
checkDir("调用默认方法创建的目录,自定义目录用");
if (fileType == FileType.MAPPER) {
// 已经生成 mapper 文件判断存在,不想重新生成返回 false
return !new File(filePath).exists();
}
// 允许生成模板文件
return true;
}
});
*/
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
// 配置模板
TemplateConfig templateConfig = new TemplateConfig();
// 配置自定义输出模板
//指定自定义模板路径,注意不要带上.ftl/.vm, 会根据使用的模板引擎自动识别
// templateConfig.setEntity("templates/entity2.java");
// templateConfig.setService();
// templateConfig.setController();
templateConfig.setXml(null);
mpg.setTemplate(templateConfig);
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
// 继承的父类
// strategy.setSuperEntityClass("你自己的父类实体,没有就不用设置!");
strategy.setEntityLombokModel(true);
strategy.setRestControllerStyle(true);
// 公共父类
// strategy.setSuperControllerClass("你自己的父类控制器,没有就不用设置!");
// 写于父类中的公共字段
strategy.setSuperEntityColumns("id");
strategy.setInclude(scanner("表名,多个英文逗号分割").split(","));
strategy.setControllerMappingHyphenStyle(true);
strategy.setTablePrefix(pc.getModuleName() + "_");
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.execute();
}
}
目录结构
基本按照上面那个模板来就好,
这个模板的思路是---->>
- 首先获取指定的文件夹(scanner)
- 然后获取配置内容,
- 链接数据库
- 定义模板
mybatis支持的模板引擎—
如果使用Thymeleaf,则需要自定义模板,
这就需要继承
com.baomidou.mybatisplus.generator.engine.AbstractTemplateEngine
自定义代码模板
//指定自定义模板路径, 位置:/resources/templates/entity2.java.ftl(或者是.vm)
//注意不要带上.ftl(或者是.vm), 会根据使用的模板引擎自动识别
TemplateConfig templateConfig = new TemplateConfig()
.setEntity("templates/entity2.java");
AutoGenerator mpg = new AutoGenerator();
//配置自定义模板
mpg.setTemplate(templateConfig);
//自定义属性注入
InjectionConfig injectionConfig = new InjectionConfig() {
//自定义属性注入:abc
//在.ftl(或者是.vm)模板中,通过${cfg.abc}获取属性
@Override
public void initMap() {
Map<String, Object> map = new HashMap<>();
map.put("abc", this.getConfig().getGlobalConfig().getAuthor() + "-mp");
this.setMap(map);
}
};
AutoGenerator mpg = new AutoGenerator();
//配置自定义属性注入
mpg.setCfg(injectionConfig);
entity2.java.ftl
自定义属性注入abc=${cfg.abc}
entity2.java.vm
自定义属性注入abc=$!{cfg.abc}
#字段其他信息查询注入
代码生成器(3.5.1+版本)
文件位置配置–>>
location.properties
jdbc_url=jdbc:mysql://127.0.0.1:3306/gavin?useSSL=false&useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
jdbc_username=gavin
jdbc_password=955945
location_path=zzy\\src\\main/\\java
mapper_path=zzy\\src\\main\\resources\\mapper
代码快速自动生成
package com;
import com.baomidou.mybatisplus.generator.FastAutoGenerator;
import com.baomidou.mybatisplus.generator.config.OutputFile;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import org.apache.ibatis.io.Resources;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collections;
import java.util.Properties;
/**
* @author Gavin
*/
public class FastAutoGeneratorTest {
public static void main(String[] args) throws IOException {
InputStream FTT = Resources.getResourceAsStream("config/location.properties");
Properties properties= new Properties();
properties.load(FTT);
String url = properties.getProperty("jdbc_url");
String username = properties.getProperty("jdbc_username");
String password = properties.getProperty("jdbc_password");
String location_path = properties.getProperty("location_path");
String mapper_path = properties.getProperty("mapper_path");
FastAutoGenerator.create(url, username,password)
.globalConfig(builder -> {
builder.author("codeM") // 设置作者
.enableSwagger() // 开启 swagger 模式
.fileOverride() // 覆盖已生成文件
.outputDir(location_path); // 指定输出目录
})
//D:\Program Data\idea2019Data\mybatiplus\zzy\src\main\java\com
.packageConfig(builder -> {
builder.parent("com") // 设置父包名
.moduleName("gavin") // 设置父包模块名/ /zzy/src/main/resources/mapper
.pathInfo(Collections.singletonMap(OutputFile.mapperXml, mapper_path)); // 设置mapperXml生成路径
})
.strategyConfig(builder -> {
builder.addInclude("dept") // 设置表名----要生成实体
.addTablePrefix("t_", "c_"); // 设置过滤表前缀
})
.templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
.execute();
}
}
记录遇到的问题---->>路径问题
如图所示,
解决方法—把 / 改成 (要转义)
目录结构