学习mybaits-plus,首先要明确 MyBatis-Plus(简称 MP)是一个 MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
也就是说已经mybatis的配置都没有发生改变,都可以使用,这是在这个基础上做了增强。
官网地址:https://mp.baomidou.com/guide/quick-start.html#
目录
1.搭建mybatis-plus环境
1.1原来mybatis环境
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--spring扫描注解,排除@Controller注解-->
<context:component-scan base-package="com.hww" use-default-filters="false">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:exclude-filter>
</context:component-scan>
<!--加载properties文件-->
<context:property-placeholder location="classpath:config.properties"></context:property-placeholder>
<!--数据源
有三种方式:
1. 从JNDI获得DataSource,JndiObjectFactoryBean
2. 使用DriverManagerDataSource获得DataSource,DriverManagerDataSource建立连接是只要有连接就新建一个connection,根本没有连接池的作用
3. 从第三方连接池中获取DataSource
Apache的DBCP
C3P0
ali的 com.alibaba.druid.pool.DruidDataSource
-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${db.driver}"></property>
<property name="url" value="${db.url}"></property>
<property name="username" value="${db.username}"></property>
<property name="password" value="${db.password}"></property>
</bean>
<!--spring 事务管理器-->
<bean id="transaction" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--spring 事务管理器,开启注解-->
<tx:annotation-driven transaction-manager="transaction"></tx:annotation-driven>
<!--sqlSession
这里面整合了原先的mybatis配置,可以将原来在mybatis中的配置,都配置在sqlSessionFactoryBean中
-->
<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<property name="mapperLocations" value="classpath:com/hww/dao/*.xml"></property>
</bean>
<!--mapper扫描器
2.1如果Mapper.xml与Mapper.class在同一个包下且同名,spring 中MapperScannerConfigurer 扫描Mapper.class的同时会自动扫描同名的Mapper.xml并装配到Mapper.class。
2.2如果Mapper.xml与Mapper.class不在同一个包下或者不同名,就必须使用配置mapperLocations指定mapper.xml的位置。(如idea中 maven 默认不打包java文件夹下的xml文件,未在pom.xml中配置resource的情况下)
此时spring是通过识别mapper.xml中的
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.hww.dao"></property>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"></property>
</bean>
</beans>
1.2 mybaits和mybatis-plus中的区别
那么mybaits和mybatis-plus中的区别在哪里呢。这需要将mybaits中的SqlSessionFactoryBean替换成MybatisSqlSessionFactoryBean
<bean id="sqlSessionFactoryBean" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<property name="configuration" ref="mybatisConfiguration"></property>
</bean>
1.3 来个完整mybatis-plus配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--spring扫描注解,排除@Controller注解-->
<context:component-scan base-package="com.hww" use-default-filters="false">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:exclude-filter>
</context:component-scan>
<!--加载properties文件-->
<context:property-placeholder location="classpath:config.properties"></context:property-placeholder>
<!--数据源
有三种方式:
1. 从JNDI获得DataSource,JndiObjectFactoryBean
2. 使用DriverManagerDataSource获得DataSource,DriverManagerDataSource建立连接是只要有连接就新建一个connection,根本没有连接池的作用
3. 从第三方连接池中获取DataSource
Apache的DBCP
C3P0
ali的 com.alibaba.druid.pool.DruidDataSource
-->
<!-- <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${driver}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${username}"></property>
<property name="password" value="${password}"></property>
</bean>-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${db.driver}"></property>
<property name="url" value="${db.url}"></property>
<property name="username" value="${db.username}"></property>
<property name="password" value="${db.password}"></property>
</bean>
<!--spring 事务管理器-->
<bean id="transaction" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--spring 事务管理器,开启注解-->
<tx:annotation-driven transaction-manager="transaction"></tx:annotation-driven>
<!--sqlSession
这里面整合了原先的mybatis配置,可以将原来在mybatis中的配置,都配置在sqlSesion中
-->
<bean id="sqlSessionFactoryBean" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
</bean>
<!--mapper扫描器
2.1如果Mapper.xml与Mapper.class在同一个包下且同名,spring 中MapperScannerConfigurer 扫描Mapper.class的同时会自动扫描同名的Mapper.xml并装配到Mapper.class。
2.2如果Mapper.xml与Mapper.class不在同一个包下或者不同名,就必须使用配置mapperLocations指定mapper.xml的位置。(如idea中 maven 默认不打包java文件夹下的xml文件,未在pom.xml中配置resource的情况下)
此时spring是通过识别mapper.xml中的
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.hww.dao"></property>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"></property>
</bean>
</beans>
2. mybatis-plus的使用
在mybaits中,我们需要定义mapper接口以及方法,同时还需要和mapper接口相同的sql映射文件。
而在mybatis-plus中,可以直接让mapper接口继承BaseMapper<T>类,此时就不需要编写sql映射文件,也不需要定义方法,就能够完成简单的数据库curd
- mybatis-plus配置,见上1.3
- mapper接口(实现BaseMapper)
package com.hww.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.mapper.Mapper;
import com.hww.bean.Client;
import org.apache.ibatis.annotations.Select;
import java.util.List;
import java.util.Map;
public interface ClientDao extends BaseMapper<Client> {
}
以下是 BaseMapper<T>类,定义了一些curd的操作方法。
// // 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 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 deleteByMap(@Param("cm") Map<String, Object> columnMap); int delete(@Param("ew") Wrapper<T> wrapper); int deleteBatchIds(@Param("coll") Collection<? extends Serializable> 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); T selectOne(@Param("ew") Wrapper<T> queryWrapper); Integer 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); <E extends IPage<T>> E selectPage(E page, @Param("ew") Wrapper<T> queryWrapper); <E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param("ew") Wrapper<T> queryWrapper); }
- bean(注意注解@TableName,@TableId)
这里需要增加一个知识点,此时此时我们看到,这里配置了两个注解@TableName,@TableId
这就是mybatis-plus中提供的一些注解。虽然利用mybatis-plus会简化一些curd,但是这些简化还需要配合在实体bean的注解。
具体的详解见注解章节。
package com.hww.bean;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
@TableName("t_ecif_client")
public class Client {
@TableId
private Integer clientId;
private String clientName;
private String clientNameEn;
public Integer getClientId() {
return clientId;
}
public void setClientId(Integer clientId) {
this.clientId = clientId;
}
public String getClientName() {
return clientName;
}
public void setClientName(String clientName) {
this.clientName = clientName;
}
public String getClientNameEn() {
return clientNameEn;
}
public void setClientNameEn(String clientNameEn) {
this.clientNameEn = clientNameEn;
}
@Override
public String toString() {
return "Client{" +
"clientId='" + clientId + '\'' +
", clientName='" + clientName + '\'' +
", clientNameEn='" + clientNameEn + '\'' +
'}';
}
}
此时我们只需要一个mybatis-plus配置,mapper接口,实体bean,其他的和mybatis一样。就可以测试了
- test
package com.test;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.hww.bean.Client;
import com.hww.dao.ClientDao;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import java.util.List;
public class MyTest {
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
@Test
public void test(){
ClientDao clientDao = (ClientDao)context.getBean("clientDao");
List<Client> clients = clientDao.selectList(null);
System.out.println(clients);
}
@Test
public void test2(){
ClientDao clientDao = (ClientDao)context.getBean("clientDao");
Client client = new Client();
client.setClientId(3);
client.setClientName("ccc");
client.setClientNameEn("");
//if判断为空,则不更新。
// int update = clientDao.updateById(client);
//if判断为空,则不更新。
UpdateWrapper<Client> wrapper = new UpdateWrapper<Client>();
wrapper.eq("client_id",3);
int update1 = clientDao.update(client, wrapper);
}
}
总结:
这里比mybatis好的地方就是不需要配置sql映射文件也可以操作curd。当然可能不是只有这么一点好的操作。
3. 注解
-
@TableName 表名注解,用来加到实体bean的类上,表示这个实体类对应的表名称以及一些属性
-
@TableId 主键注解,用来加到实体bean的主键字段上,用来映射数据库表的主键
-
@TableField 字段注解(非主键),用来加到实体bean的其他属性上,用来映射数据库表中的表字段
-
@KeySequence 序列主键策略
oracle
目前常用的就是这些。
当实体类中没有配置@TableId 时,当使用一些xxxbyId的方法时是会报错的(eg:updateById,deleteById),因为mybatis找不到主键是哪个。
当实体类的名称和数据库表名称不相同时,如果没有用@TableName指定表名称是,因为mybatis找不到表
同理,如果数据库表字段和和实体bean中字段不相同时,如果不同 @TableField指定也是会报错的。(当然标准的下划线形式和标准驼峰不配置@TableField也没问题,但是这就需要必须严格按照标准,应为在mybatis-plus中,mapUnderscoreToCamelCase配置默认是true的。
4. mybaits-plus配置
-
sqlSessionFactory配置,配置数据源等常用配置,本部分配置包含了大部分用户的常用配置,其中一部分为 MyBatis 原生所支持的配置
- configuration配置
mybatis和spring整合时发现,除了mybatis-config.xml中的settings配置不能够整合在spring.xml中之外,其他的所有的配置均可以移到spring.xml中配置。
那么mybatis-plus在这个基础上,也可以将settings中的配置在spring中进行配置,利用 configuration配置,可以实现将mybatis-config.xml的配置移植到spring中。
本部分(Configuration)的配置大都为 MyBatis 原生支持的配置,这意味着您可以通过 MyBatis XML 配置文件的形式进行配置。
这里要注意configuration配置和configLocation(这里指的mybatis-config.xml)配置不能够同时配置。见图。这两个配置是不能够一起配置的。如果一起配置会报错。
- globalConfig配置
除了configuration配置配置外,又提供了globalConfig配置。配置一些全局的配置等
- dbConfig配置
MyBatis-Plus 全局策略中的 DB 策略配置
<bean id="sqlSessionFactory" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="configuration" ref="configuration"/> <!-- 非必须 -->
<property name="globalConfig" ref="globalConfig"/> <!-- 非必须 -->
......
</bean>
<bean id="configuration" class="com.baomidou.mybatisplus.core.MybatisConfiguration">
......
</bean>
<bean id="globalConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig">
<property name="dbConfig" ref="dbConfig"/> <!-- 非必须 -->
......
</bean>
<bean id="dbConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig.DbConfig">
......
</bean>
完整的配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!--spring扫描注解,排除@Controller注解-->
<context:component-scan base-package="com.hww" use-default-filters="false">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"></context:exclude-filter>
</context:component-scan>
<!--加载properties文件-->
<context:property-placeholder location="classpath:config.properties"></context:property-placeholder>
<!--数据源
有三种方式:
1. 从JNDI获得DataSource,JndiObjectFactoryBean
2. 使用DriverManagerDataSource获得DataSource,DriverManagerDataSource建立连接是只要有连接就新建一个connection,根本没有连接池的作用
3. 从第三方连接池中获取DataSource
Apache的DBCP
C3P0
ali的 com.alibaba.druid.pool.DruidDataSource
-->
<!-- <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${driver}"></property>
<property name="url" value="${url}"></property>
<property name="username" value="${username}"></property>
<property name="password" value="${password}"></property>
</bean>-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${db.driver}"></property>
<property name="url" value="${db.url}"></property>
<property name="username" value="${db.username}"></property>
<property name="password" value="${db.password}"></property>
</bean>
<!--spring 事务管理器-->
<bean id="transaction" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--spring 事务管理器,开启注解-->
<tx:annotation-driven transaction-manager="transaction"></tx:annotation-driven>
<!--sqlSession
这里面整合了原先的mybatis配置,可以将原来在mybatis中的配置,都配置在sqlSesion中
-->
<bean id="sqlSessionFactoryBean" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<property name="configuration" ref="mybatisConfiguration"></property>
<property name="globalConfig" ref="globalConfig"></property>
</bean>
<!--mapper扫描器
2.1如果Mapper.xml与Mapper.class在同一个包下且同名,spring 中MapperScannerConfigurer 扫描Mapper.class的同时会自动扫描同名的Mapper.xml并装配到Mapper.class。
2.2如果Mapper.xml与Mapper.class不在同一个包下或者不同名,就必须使用配置mapperLocations指定mapper.xml的位置。(如idea中 maven 默认不打包java文件夹下的xml文件,未在pom.xml中配置resource的情况下)
此时spring是通过识别mapper.xml中的
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.hww.dao"></property>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactoryBean"></property>
</bean>
<!--Configuration配置-->
<bean id="mybatisConfiguration" class="com.baomidou.mybatisplus.core.MybatisConfiguration">
<!--日志-->
<property name="logImpl" value="org.apache.ibatis.logging.log4j.Log4jImpl"></property>
<!--标准驼峰转换-->
<property name="mapUnderscoreToCamelCase" value="true"></property>
</bean>
<!--globalConfig全局配置-->
<bean id="globalConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig">
<!--banner,mybatis启动时logo-->
<property name="banner" value="false"></property>
<property name="dbConfig" ref="dbConfig"></property>
</bean>
<!--db策略-->
<bean id="dbConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig">
<!--sql数据库schema名称-->
<property name="schema" value="test"></property>
</bean>
</beans>
总结配置:
mybatis-plus有四个基本配置,分别是以下四种,注意层级依赖。
1. SqlSessionFactory
1.1 configuration
1.2 globalConfig
1.2.1dbConfig
配置好配置后,将SqlSessionFactory注入到扫描器中,MapperScannerConfigurer。这样就实现了mybatis-plus,同时和spring做了整合。
5 插件及扩展
MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
- Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed
- ParameterHandler (getParameterObject, setParameters)
- ResultSetHandler (handleResultSets, handleOutputParameters)
- StatementHandler (prepare, parameterize, batch, update, query)
5.1 分页插件
PaginationInterceptor
<bean id="sqlSessionFactoryBean" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configuration" ref="mybatisConfiguration"></property>
<property name="globalConfig" ref="globalConfig"></property>
<property name="plugins">
<array>
<bean class="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor"></bean>
</array>
</property>
</bean>
使用时,一般需要指定每页条数+当前页
@Test
public void test3(){
Page<Client> page = new Page();
page.setSize(10);//每页数
page.setCurrent(2);//第几页
ClientDao clientDao = (ClientDao)context.getBean("clientDao");
//这里返回的并不是记录数,而是一个page对象。可以从page对象中获取
Page pageRs = clientDao.selectPage(page, null);
//获取记录数
List<Client> records = pageRs.getRecords();
System.out.println("======================");
System.out.println(records);
System.out.println("当前页:"+pageRs.getCurrent());
System.out.println("每页条数:"+pageRs.getSize());
System.out.println("总记录数"+pageRs.getTotal());
System.out.println("总页码:"+page.getPages());
System.out.println("是否有上一页:"+page.hasPrevious());
System.out.println("是否有下一页:"+page.hasNext());
}
5.2 乐观锁插件
当要更新一条记录的时候,希望这条记录没有被别人更新
乐观锁实现方式:
取出记录时,获取当前version 更新时,带上这个version 执行更新时, set version = newVersion where version = oldVersion 如果version不对,就更新失败
插件
<property name="plugins">
<array>
<bean class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"></bean>
</array>
</property>
测试
/**
* 乐观锁插件
*/
@Test
public void test4(){
ClientDao clientDao = (ClientDao)context.getBean("clientDao");
Client client = new Client();
client.setClientId(3);
client.setClientName("李四");
client.setClientNameEn("lisi1111223333");
client.setVersion(2);
int i = clientDao.updateById(client);
System.out.println(i);
}
对应的实体bean中需要加@Version注解
public class Client {
@TableId
private Integer clientId;
private String clientName;
private String clientNameEn;
@Version
private Integer version;
原理说明:
当加上version注解后,每一次更新,version都会加1。
也就是每次更新都会自动加上 update client set id= Id,...version=version+1 where id=id and version=version;
也就是说每次更新version都会自动加1,并且增加条件version=version,判断取出的version和数据库中的version是否相同,如果相同,就更更新成功。如果不相同,则本次不更新。
比如说 A和B都更新同 一条数据,当A和B去查询时,此时version=0,B先更新,此时version=1。当A更新的时候发现此时的version=1,此时A的更新就失效。只能等下次A在去数据库中获取数据,拿到version,再去做更新。
但是如果update的时候,不带这个version,此时就能够更新。
ClientDao clientDao = (ClientDao)context.getBean("clientDao"); Client client = new Client(); client.setClientId(3); client.setClientName("李四"); client.setClientNameEn("lisi1111223333"); int i = clientDao.updateById(client); System.out.println(i);
5.3 SQL执行分析插件,避免出现全表更新和删除
配置
<property name="plugins">
<array>
<bean class="com.baomidou.mybatisplus.extension.plugins.SqlExplainInterceptor">
<property name="sqlParserList">
<list>
<bean class="com.baomidou.mybatisplus.extension.parsers.BlockAttackSqlParser"></bean>
</list>
</property>
</bean>
</array>
</property>
测试
当操作全表的时候,不论是update还是delete都会报错
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:
### Error updating database. Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Prohibition of table update operation
### Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: Prohibition of table update operation
/**
* sql分析插件
*/
@Test
public void test5(){
ClientDao clientDao = (ClientDao)context.getBean("clientDao");
Client client = new Client();
client.setClientName("李四");
//全表更更新
int i = clientDao.update(client,null);
System.out.println(i);
}
5.4 非法sql插件
配置
<property name="plugins">
<array>
<bean class="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor"></bean>
<bean class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"></bean>
<bean class="com.baomidou.mybatisplus.extension.plugins.SqlExplainInterceptor">
<property name="sqlParserList">
<list>
<bean class="com.baomidou.mybatisplus.extension.parsers.BlockAttackSqlParser"></bean>
</list>
</property>
</bean>
<bean class="com.baomidou.mybatisplus.extension.plugins.IllegalSQLInterceptor"></bean>
</array>
</property>
测试
/**
* 非法sql
*/
@Test
public void test6(){
ClientDao clientDao = (ClientDao)context.getBean("clientDao");
QueryWrapper<Client> clientQueryWrapper = new QueryWrapper<Client>();
clientQueryWrapper.or();
clientDao.selectList(clientQueryWrapper);
}
org.mybatis.spring.MyBatisSystemException: nested exception is org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: 非法SQL,必须要有where条件
### The error may exist in com/hww/dao/ClientDao.java (best guess)
### The error may involve com.hww.dao.ClientDao.selectList
### The error occurred while executing a query
### Cause: com.baomidou.mybatisplus.core.exceptions.MybatisPlusException: 非法SQL,必须要有where条件
5.5 公共字段填充(这个还是很实用的)
-
实现元对象处理器接口:com.baomidou.mybatisplus.core.handlers.MetaObjectHandler
-
注解填充字段
@TableField(.. fill = FieldFill.INSERT)
生成器策略部分也可以配置!metaobject:元对象,是mybatis提供的一个用于更加方便,更加优雅的访问对象的属性,给对象的属性设置值的一个对象,还会用于包装对象,支持Object,Map,Collection等对象进行包装。本质上metaobject是给对象的属性设置值,最终还是要通过Reflect获取到属性的对应方法的invoker,最终执行。
场景,例如一些技术字段,创建时间create_datetime和更新时间mod_datetime,在数据创建时或插入create_datetime和mod_datetime字段,在数据更新是只更更新mod_datetime字段
自定义MetaObjectHandler类型,实现插入时reate_datetime和mod_datetime都填充当前日期,更新是只更新mod_datetime字段
package com.hww.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import java.util.Date;
public class MyMetaObjectHandler implements MetaObjectHandler {
public void insertFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "createDatetime", Date.class, new Date()); // 起始版本 3.3.0(推荐使用)
this.strictInsertFill(metaObject, "modDatetime", Date.class, new Date()); // 起始版本 3.3.0(推荐使用)
}
public void updateFill(MetaObject metaObject) {
this.strictInsertFill(metaObject, "modDatetime", Date.class, new Date()); // 起始版本 3.3.0(推荐使用)
}
}
实体bean,@TableField 制动fill填充
@TableField(value = "create_datetime",fill = FieldFill.INSERT)
private Date createDatetime;
@TableField(value = "mod_datetime",fill = FieldFill.INSERT_UPDATE)
private Date modDatetime;
测试
/**
* 公共字段填充 插入
*/
@Test
public void test7(){
ClientDao clientDao = (ClientDao)context.getBean("clientDao");
Client client = new Client();
client.setClientName("西门吹雪");
client.setClientNameEn("xi");
client.setClientId(19);
int insert = clientDao.insert(client);
System.out.println(insert);
}
/**
* 公共字段填充 更新
*/
@Test
public void test8(){
ClientDao clientDao = (ClientDao)context.getBean("clientDao");
Client client = new Client();
client.setClientName("西门吹雪");
client.setClientNameEn("xi0000");
client.setClientId(19);
int updateById = clientDao.updateById(client);
System.out.println(updateById);
}
5.6 自定义sql的实现方法
自定义sql可以有两种方式。
第一:通过xml方式(就是mybatis的方式)
步骤:
1. 自定义一个sql 的xml文件。
<?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="com.hww.dao.ClientDao">
<select id="selectAll" resultType="com.hww.bean.Client">
select * from t_ecif_client
</select>
</mapper>
2. 自定义一个方法
package com.hww.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.mapper.Mapper;
import com.hww.bean.Client;
import com.hww.mapper.MyMapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
import java.util.Map;
public interface ClientDao extends MyMapper<Client> {
public List<Client> selectAll();
}
3. 将xml文件配置到spring中
<bean id="sqlSessionFactoryBean" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="mapperLocations">
<array>
<value>classpath*:sql/clientMapper.xml</value>
</array>
</property>
</bean>
4. 测试
/**
* 自动以sql,xml形式
*/
@Test
public void test10(){
ClientDao clientDao = (ClientDao)context.getBean("clientDao");
List<Client> clients = clientDao.selectAll();
System.out.println(clients);
}
分析,可以对每一个实体自定义方法,但是每个方法是针对一个具体的Mapper的。不能像BaseMapper中提供的方法,所有的Mapper都可以使用。
那么怎么实现这个方式呢。就是通过sql注入器的方式。
第二:通过Sql注入器方式
全局配置 sqlInjector
用于注入 ISqlInjector
接口的子类,实现自定义方法注入。也就是说我们可以将配置在xml中的文件使用注入的方式注入到全局中,就不需要再编写sql语句。
人话解释:就是自定一个sql,可以将这个sql注入到全局变量中,使用时可以直接使用。就和mybatis-plus中BaseMapper提供的方法一样。可以直接进行使用
其实mybatis-plus中BaseMapper提供的方法就是这中方式实现的。
//
// 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 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 deleteByMap(@Param("cm") Map<String, Object> columnMap);
int delete(@Param("ew") Wrapper<T> wrapper);
int deleteBatchIds(@Param("coll") Collection<? extends Serializable> 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);
T selectOne(@Param("ew") Wrapper<T> queryWrapper);
Integer 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);
<E extends IPage<T>> E selectPage(E page, @Param("ew") Wrapper<T> queryWrapper);
<E extends IPage<Map<String, Object>>> E selectMapsPage(E page, @Param("ew") Wrapper<T> queryWrapper);
}
实现步骤
1. 需要枚举类。BaseMapper里的方式就是通过枚举类的形式,当然不是非得用枚举。也可以用其他的方式。
需要事先定义好自定义的 方法名称,方法描述,需要的基本sql语句
package com.hww.injector;
public enum MySqlMethod {
/**
* 删除全部
*/
DELETE_ALL("deleteAll", "根据 entity 条件删除记录", "<script>\nDELETE FROM %s %s\n</script>");
private final String method;
private final String desc;
private final String sql;
MySqlMethod(String method, String desc, String sql) {
this.method = method;
this.desc = desc;
this.sql = sql;
}
public String getMethod() {
return method;
}
public String getDesc() {
return desc;
}
public String getSql() {
return sql;
}
}
2. 定义个类,继承AbstractMethod方法。这方法中,将sql语句完善
package com.hww.injector;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.metadata.TableInfo;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
public class DeleteAll extends AbstractMethod {
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo) {
String sql;
MySqlMethod mySqlMethod = MySqlMethod.DELETE_ALL;
if (tableInfo.isLogicDelete()) {
sql = String.format(mySqlMethod.getSql(), tableInfo.getTableName(), tableInfo,
sqlWhereEntityWrapper(true,tableInfo));
} else {
mySqlMethod = MySqlMethod.DELETE_ALL;
sql = String.format(mySqlMethod.getSql(), tableInfo.getTableName(),
sqlWhereEntityWrapper(true,tableInfo));
}
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);
return addUpdateMappedStatement(mapperClass, modelClass, mySqlMethod.getMethod(), sqlSource);
}
}
3. 定义sql注入器,继承AbstractSqlInject,将
自定义自己的通用方法可以实现接口 ISqlInjector
也可以继承抽象类 AbstractSqlInjector
注入通用方法 SQL 语句
然后继承 BaseMapper
添加自定义方法,全局配置 sqlInjector
注入 MP 会自动将类所有方法注入到 mybatis
容器中。
package com.hww.injector;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.AbstractSqlInjector;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public class MyInjector extends AbstractSqlInjector {
public List<AbstractMethod> getMethodList(Class<?> mapperClass) {
return Stream.of(new DeleteAll()).collect(Collectors.toList());
}
}
4. 配置到全局配置中
<!--globalConfig全局配置-->
<bean id="globalConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig">
<property name="sqlInjector" ref="myinject"></property>
</bean>
<bean id="myinject" class="com.hww.injector.MyInjector"></bean>
5. 继承BaseMapper,添加自定义的方法
package com.hww.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.mapper.Mapper;
public interface MyMapper<T> extends BaseMapper<T> {
Integer deleteAll();
}
6. Mapper接口此时就继承MyMapper,就可以引用自定义的sql和BaseMapper中的sql了
package com.hww.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.mapper.Mapper;
import com.hww.bean.Client;
import com.hww.mapper.MyMapper;
import org.apache.ibatis.annotations.Select;
import java.util.List;
import java.util.Map;
public interface ClientDao extends MyMapper<Client> {
}
7. 进行测试
/**
* 自定义sql测试
*/
@Test
public void test9(){
ClientDao clientDao = (ClientDao)context.getBean("clientDao");
clientDao.deleteAll();
}
分析,此时这个自定义sql就是全局的。每个mapper接口都可以使用了