MyBaits-Plus 3.X
文章目錄
一.簡述
1. 簡介
MyBatis-Plus(簡稱 MP)是一個 MyBatis 的增強工具,在 MyBatis 的基礎上只做增強不做改變,爲簡化開發、提高效率而生。
2. 特性
- 無侵入:只做增強不做改變,引入它不會對現有工程產生影響,如絲般順滑
- 損耗小:啓動即會自動注入基本 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 語句以及其執行時間,建議開發測試時啓用該功能,能快速揪出慢查詢
- 內置全局攔截插件:提供全表 delete 、 update 操作智能分析阻斷,也可自定義攔截規則,預防誤操作#支持數據庫
二.快速啓動
1. Spring Boot
- 添加依賴
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.2.0</version>
</dependency>
- 配置:啓動類添加
MapperScan
註解
@SpringBootApplication
//啓動類添加此註解
@MapperScan("com.baomidou.mybatisplus.samples.quickstart.mapper")
public class Application {
public static void main(String[] args) {
SpringApplication.run(QuickStartApplication.class, args);
}
}
- 測試
@RunWith(SpringRunner.class)
@SpringBootTest
public class SampleTest {
@Resource
private UserMapper userMapper;
@Test
public void testSelect() {
System.out.println(("----- selectAll method test ------"));
List<User> userList = userMapper.selectList(null);
Assert.assertEquals(5, userList.size());
userList.forEach(System.out::println);
}
}
2. Spring MVC
- 添加依賴
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.2.0</version>
</dependency>
- 配置
spring
配置文件中配置MapperScan
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.baomidou.mybatisplus.samples.quickstart.mapper"/>
</bean>
mybatis
配置文件中,修改SqlSessionFactory
爲MyBatis-Plus
的SqlSessionFactory
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
</bean>
- 測試
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {"classpath*:spring.xml"})
public class SampleTest {
@Autowired
private UserMapper userMapper;
@Test
public void testSelect() {
System.out.println(("----- selectAll method test ------"));
List<User> userList = userMapper.selectList(null);
Assert.assertEquals(5, userList.size());
userList.forEach(System.out::println);
}
@Test
public void testSequence() {
User u = new User();
u.setName("Tomcat");
u.setAge(18);
u.setEmail("[email protected]");
userMapper.insert(u);
Long id1 = u.getId();
u = new User();
u.setName("Tomcat2");
userMapper.insert(u);
Assert.assertEquals("id should increase 1", id1 + 1, u.getId().longValue());
}
@Test
public void testCustomizedSql() {
System.out.println("maxAge=" + userMapper.selectMaxAge());
}
@Test
public void testPagination() {
Page<User> page = new Page<>(1, 3);
userMapper.selectPage(page, null);
Assert.assertTrue("total should not be 0", page.getTotal() != 0);
for (User u : page.getRecords()) {
System.out.println(u);
}
Assert.assertEquals("pagination should be 3 per page", 3, page.getRecords().size());
}
}
三.核心功能
1. 代碼生成器
官網鏈接:代碼生成器
- 作用:基於模板生成代碼
- 支持模板類型
- Velocity(默認)
- Freemarker
- Beetl
- 自定義模板
2. CRUD接口
官網鏈接:CRUD接口
//1. 繼承BaseMapper,泛型中傳入這個表的實體類
//2. 在其他地方通過@Autowire注入即可
public interface UserMapper extends BaseMapper<User> {}
//1. 繼承IService,泛型中傳入這個表的實體類
//2. 在其他地方通過@Autowire注入即可
public interface UserServcie extends IService<User> {}
- 參數:
參數 | 作用 |
---|---|
T entity | 保存的時候用 |
IPage page | 分頁(需要配置分頁插件) |
wrapper(條件構造器) | 用於生成sql的where條件 |
Collection entityList | 批量保存或者更新 |
Collection<? extends Serializable> idList | 批量刪除 |
Map<String, Object> | 查詢的時候用 |
3. 條件構造器
官網鏈接: 條件構造器
- 作用:用於生成sql的where條件
- api
查詢方式 | 說明 |
---|---|
setSqlSelect | 設置 SELECT 查詢字段 |
where | WHERE 語句,拼接 + WHERE 條件 |
and | AND 語句,拼接 + AND 字段=值 |
andNew | AND 語句,拼接 + AND (字段=值) |
or | OR 語句,拼接 + OR 字段=值 |
orNew | OR 語句,拼接 + OR (字段=值 |
eq | 等於= |
allEq | 基於 map 內容等於= |
ne | 不等於<> |
gt | 大於> |
ge | 大於等於>= |
lt | 小於< |
le | 小於等於<= |
like | 模糊查詢 LIKE |
notLike | 模糊查詢 NOT LIKE |
in | IN 查詢 |
notIn | NOT IN 查詢 |
isNull | NULL 值查詢 |
isNotNull | IS NOT NULL |
groupBy | 分組 GROUP BY |
having | HAVING 關鍵詞 |
orderBy | 排序 ORDER BY |
orderAsc | ASC 排序 ORDER BY |
orderDesc | DESC 排序 ORDER BY |
exists | EXISTS 條件語句 |
notExists | NOT EXISTS 條件語句 |
between | BETWEEN 條件語句 |
notBetween | NOT BETWEEN 條件語句 |
addFilter | 自由拼接 SQL |
last | 拼接在最後,例如:last(“LIMIT 1”) |
- 參數
參數 | 作用 |
---|---|
boolean condition | 表示該條件是否加入最後生成的sql中 |
R column | 表示數據庫字段(String類型) |
T entity | 查詢的時候把entity需要查詢的字段set好,然後傳入,相當於where and and。。。 |
Collection entityList | 批量保存或者更新 |
Collection<? extends Serializable> idList | 批量刪除 |
Map<String, Object> | 和實體類作用差不多 |
String sql | 用來拼接sql |
- 注意:
- 不支持
rpc調用
將Wrapper
進行傳輸(wrapper很重,非要用可以寫dto) - 傳入的map或者list爲null,則不加入最後sql中
- sql拼接有兩種:
apply
和last
,其中last
有sql注入風險
- 不支持
- 分類
- AbstractWrapper :
QueryWrapper
和UpdateWrapper
的父類 - QueryWrapper :比
AbstractWrapper
多一個select(String... sql)
方法 - UpdateWrapper :比
AbstractWrapper
多一個set(String column , Object val)
方法
- AbstractWrapper :
- 使用wrapper自定義sql
4. 分頁插件
官網鏈接: 分頁插件
- 配置
- spring mvc
<!-- spring xml 方式 -->
<plugins>
<plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor">
<property name="sqlParser" ref="自定義解析類、可以沒有" />
<property name="dialectClazz" value="自定義方言類、可以沒有" />
</plugin>
</plugins>
- spring boot
//Spring boot方式
@EnableTransactionManagement
@Configuration
@MapperScan("com.baomidou.cloud.service.*.mapper*")
public class MybatisPlusConfig {
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// 設置請求的頁面大於最大頁後操作, true調回到首頁,false 繼續請求 默認false
// paginationInterceptor.setOverflow(false);
// 設置最大單頁限制數量,默認 500 條,-1 不受限制
// paginationInterceptor.setLimit(500);
return paginationInterceptor;
}
}
- 使用
- 測試類
@Resource
private UserMapper userMapper;
@Test
public void queryUserForPage(){
IPage<User> userPage = new Page<>(2, 2);//參數一是當前頁,參數二是每頁個數
userPage = userMapper.selectPage(userPage, null);
List<User> list = userPage.getRecords();
for(User user : list){
System.out.println(user);
}
}
- 向前端返回json
//----------------------包裝類,用來保存分頁需要的數據------------------------
@Data
public class UserVo {
private Integer current;
private Integer size;
private Long total;
private List<User> userList;
}
//--------------------------------Controller返回---------------------------------
@GetMapping("queryUser")
@ResponseBody
public UserVo queryList(Integer current, Integer size) {
/**
* 這些代碼應該寫在service層
*/
UserVo userVo = new UserVo();
IPage<User> page = new Page<>(current, size);
userMapper.selectPage(page, null);
userVo.setCurrent(current);
userVo.setSize(size);
userVo.setTotal(page.getTotal());
userVo.setUserList(page.getRecords());
return userVo;
}
5. Sequence主鍵
官網鏈接: Sequence主鍵
- 作用:唯一主鍵,保證全局不重複
- 步驟
- 實體類添加兩個註解:
@KeySequence
和@TableId(value = "id", type = IdType.INPUT)
- 注:
oracle
這裏是input
,mysql
是auto
- 注:
@Data
@KeySequence("SEQ_USER")
public class User {
@TableId(value = "id", type = IdType.INPUT)
private Long id;
private String name;
private Integer age;
private String email;
}
- 添加配置
@Configuration
public class MybatisPlusConfig {
/**
* sequence主鍵,需要配置一個主鍵生成器
* 配合實體類註解 {@link KeySequence} + {@link TableId} type=INPUT
* @return
*/
@Bean
public H2KeyGenerator h2KeyGenerator(){
return new H2KeyGenerator();
}
}
四.擴展功能
1. 邏輯刪除
官網鏈接: 邏輯刪除
-
效果:
- 刪除時:
update user set deleted=1 where id =1 and deleted=0
- 查找時:
select * from user where deleted=0
- 刪除時:
-
添加配置:如果與默認配置相同,則不需要配置
mybatis-plus:
global-config:
db-config:
logic-delete-value: 1 # 邏輯已刪除值(默認爲 1)
logic-not-delete-value: 0 # 邏輯未刪除值(默認爲 0)
- 實體類添加字段配置
@TableLogicprivate
Integer deleted;
2. 自動填充
官網鏈接: 自動填充
- 實體類添加註解
/**
* 創建時間
*/
@TableField(fill=FieldFill.INSERT)
private Date gmtCreate;
/**
* 修改時間
*/
@TableField(fill=FieldFill.INSERT_UPDATE)
private Date gmtModified;
- 定義處理類
public class MybatisObjectHandler extends MetaObjectHandler{
@Override
public void insertFill(MetaObject metaObject) {
//新增時填充的字段
setFieldValByName("gmtCreate", new Date(), metaObject);
setFieldValByName("gmtModified", new Date(), metaObject);
}
@Override
public void updateFill(MetaObject metaObject) {
//更新時 需要填充字段
setFieldValByName("gmtModified", new Date(), metaObject);
}
}
- 添加全局配置
<!----------------------------------------------xml配置------------------------------------------------->
<bean id="globalConfig" class="com.baomidou.mybatisplus.entity.GlobalConfiguration">
<!--
AUTO->`0`("數據庫ID自增")QW
INPUT->`1`(用戶輸入ID")
ID_WORKER->`2`("全局唯一ID")
UUID->`3`("全局唯一ID")
-->
<property name="idType" value="2" />
<property name="metaObjectHandler" ref="mybatisObjectHandler"></property>
</bean>
<bean id="mybatisObjectHandler" class="cn.lqdev.learning.mybatisplus.samples.config.MybatisObjectHandler"/>
//------------------------------------------------srpingboot配置------------------------------------------------
public MybatisSqlSessionFactoryBean sqlSessionFactory(@Qualifier("primaryDataSource") DataSource dataSource) throws IOException {
MybatisSqlSessionFactoryBean mybatisPlus = new MybatisSqlSessionFactoryBean();
//加載數據源
mybatisPlus.setDataSource(dataSource);
//全局配置
GlobalConfig globalConfig = new GlobalConfig();
//配置填充器
globalConfig.setMetaObjectHandler(new MetaObjectHandlerConfig());
mybatisPlus.setGlobalConfig(globalConfig);
return mybatisPlus;
}
3. sql注入器
官網鏈接:sql注入器
- 創建自定義sql方法類
MyInsertAll
,繼承AbstractMethod
public class MyInsertAll extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
String sql = "insert into %s %s values %s";
StringBuilder fieldSql = new StringBuilder();
fieldSql.append(tableInfo.getKeyColumn()).append(",");
StringBuilder valueSql = new StringBuilder();
valueSql.append("#{").append(tableInfo.getKeyProperty()).append("},");
tableInfo.getFieldList().forEach(x->{
fieldSql.append(x.getColumn()).append(",");
valueSql.append("#{").append(x.getProperty()).append("},");
});
fieldSql.delete(fieldSql.length()-1, fieldSql.length());
fieldSql.insert(0, "(");
fieldSql.append(")");
valueSql.insert(0, "(");
valueSql.delete(valueSql.length()-1, valueSql.length());
valueSql.append(")");
SqlSource sqlSource = languageDriver.createSqlSource(configuration, String.format(sql, tableInfo.getTableName(), fieldSql.toString(), valueSql.toString()), modelClass);
return this.addInsertMappedStatement(mapperClass, modelClass, "myInsertAll", sqlSource, new NoKeyGenerator(), null, null);
}
}
- 創建sql注入類,繼承
MyLogicSqlInjector
,並繼承DefaultSqlInjector
, 複寫getMethodList
方法;並配置
public class MyLogicSqlInjector extends DefaultSqlInjector {
/**
* 如果只需增加方法,保留MP自帶方法
* 可以super.getMethodList() 再add
* @return
*/
@Override
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
List<AbstractMethod> methodList = super.getMethodList(mapperClass);
methodList.add(new MyInsertAll());
return methodList;
}
}
- 編寫自定義BaseMapper接口
MyBaseMapper<T>
繼承BaseMapper<T>
,添加自定義sql方法
public interface MyBaseMapper<T> extends BaseMapper<T> {
/**
* 自定義通用方法
*/
int myInsertAll(T entity);
}
- 業務層mapper繼承自定義baseMapper
MyBaseMapper
public interface UserMapper extends MyBaseMapper<User> { }
- 使用
@RunWith(SpringRunner.class)
@SpringBootTest
public class DeluxeTest {
@Resource
private UserMapper mapper;
@Test
public void myInsertAll(){
long id =1008888L;
User u = new User().setEmail("[email protected]").setVersion(1).setDeleted(0).setId(id);
mapper.myInsertAll(u);
User user = mapper.selectById(id);
Assert.assertNotNull(user);
Assert.assertNotNull(user.getCreateTime());
}
}
4. 動態數據源
官網鏈接:動態數據源
- 添加依賴
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>${version}</version>
</dependency>
- 添加配置
spring:
datasource:
dynamic:
datasource:
master:
username: sa
password: ""
url: jdbc:h2:mem:test
driver-class-name: org.h2.Driver
schema: db/schema.sql
slave_1:
username: sa
password: ""
url: jdbc:h2:mem:test
driver-class-name: org.h2.Driver
slave_2:
username: sa
password: ""
url: jdbc:h2:mem:test
driver-class-name: org.h2.Driver
slave_3:
username: sa
password: ""
url: jdbc:h2:mem:test
driver-class-name: org.h2.Driver
- serviceImpl或者mapper接口的方法上添加依賴
@DS
@Service
public class UserServiceImpl implements UserService {
@Resource
private UserMapper userMapper;
//不配置,就是默認數據源
@Override
public void addUser(User user) {
userMapper.addUser(user.getName(), user.getAge());
}
@DS("slave_1")
@Override
public List selectUsersFromDs() {
return userMapper.selectUsers();
}
@DS("slave")
@Override
public List selectUserFromDsGroup() {
return userMapper.selectUsers();
}
}
5. ActiveRecord 模式
實體類
繼承Model類
即可使用CRUD接口
/**
* 通用CURD示例
* @author oKong
*
*/
@RunWith(SpringRunner.class)
//SpringBootTest 是springboot 用於測試的註解,可指定啓動類或者測試環境等,這裏直接默認。
@SpringBootTest
@Slf4j
public class GeneralTest {
@Autowired
IUserService userService;
@Test
public void testInsert() {
User user = new User();
user.setCode("001");
user.setName("okong-insert");
//默認的插入策略爲:FieldStrategy.NOT_NULL,即:判斷 null
//對應在mapper.xml時寫法爲:<if test="field!=null">
//這個可以修改的,設置字段的@TableField(strategy=FieldStrategy.NOT_EMPTY)
//所以這個時候,爲null的字段是不會更新的,也可以開啓性能插件,查看sql語句就可以知道
userService.insert(user);
//新增所有字段,
userService.insertAllColumn(user);
log.info("新增結束");
}
@Test
public void testUpdate() {
User user = new User();
user.setCode("101");
user.setName("oKong-insert");
//這就是ActiveRecord的功能
user.insert();
//也可以直接 userService.insert(user);
//更新
User updUser = new User();
updUser.setId(user.getId());
updUser.setName("okong-upd");
updUser.updateById();
log.info("更新結束");
}
@Test
public void testDelete() {
User user = new User();
user.setCode("101");
user.setName("oKong-delete");
user.insert();
//刪除
user.deleteById();
log.info("刪除結束");
}
@Test
public void testSelect() {
User user = new User();
user.setCode("201");
user.setName("oKong-selecdt");
user.insert();
log.info("查詢:{}",user.selectById());
}
}
6. 樂觀鎖
官網鏈接:樂觀鎖
當要更新一條記錄的時候,希望這條記錄沒有被別人更新
樂觀鎖實現方式:
- 取出記錄時,獲取當前version
- 更新時,帶上這個version
- 執行更新時, set version = newVersion where version = oldVersion
- 如果version不對,就更新失敗
- 添加配置
<!--------------------------------xml方式配置--------------------------------->
<bean class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"/>
//--------------------------------springboot配置-----------------------------------
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
- 註解實體類中代表version字段(表中也要有)
@Version
private Integer version;