mybatis-plus
前言
熟练使用mybatis 等orm框架,mybatis-plus是对mybatis的进一步拓展,
mybatis能够使用的,mybatis-plus也可以使用,而且可以提供了更加方便简洁的开发方式提高开发效率
一、spring整合mybatis-plus
1.配置
mvn坐标
在ssm环境下导入坐标
<!-- mp依赖
mybatisPlus 会自动的维护Mybatis 以及MyBatis-spring相关的依赖
-->
<!--普通ssm只需导入该坐标-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>2.3</version>
</dependency>
spring核心配置文件
<!-- 数据源 -->
<context:property-placeholder location="classpath:db.properties"/>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="${jdbc.driver}"></property>
<property name="jdbcUrl" value="${jdbc.url}"></property>
<property name="user" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!-- 事务管理器 -->
<bean id="dataSourceTransactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 基于注解的事务管理 -->
<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
<!-- 配置SqlSessionFactoryBean
Mybatis提供的: org.mybatis.spring.SqlSessionFactoryBean
MP提供的:com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean
-->
<bean id="sqlSessionFactoryBean" class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean">
<!-- 数据源 -->
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<!-- 别名处理 -->
<property name="typeAliasesPackage" value="com.atguigu.mp.beans"></property>
<!-- 注入全局MP策略配置 -->
<property name="globalConfig" ref="globalConfiguration"></property>
</bean>
<!-- 定义MybatisPlus的全局策略配置-->
<bean id ="globalConfiguration" class="com.baomidou.mybatisplus.entity.GlobalConfiguration">
<!-- 在2.3版本以后,dbColumnUnderline 默认值就是true -->
<property name="dbColumnUnderline" value="true"></property> <!--将下划线和字母大写设置-->
<!-- 全局的主键策略 -->
<property name="idType" value="0"></property>
<!-- 全局的表前缀策略配置 -->
<property name="tablePrefix" value="tbl_"></property>
</bean>
<!--
配置mybatis 扫描mapper接口的路径
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.atguigu.mp.mapper"></property>
</bean>
数据库信息(db.properties)
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mp_plus
jdbc.username=root
jdbc.password=123456
实体类
@TableName(value="empty")
public class Employee {
/*
* @TableId:
* value: 指定表中的主键列的列名, 如果实体属性名与列名一致,可以省略不指定.
* type: 指定主键策略.
*/
@TableId(value="id" , type =IdType.AUTO) //主键
private Integer id ; // int
@TableField(value = "last_name") //非主键
private String lastName;
private String email ;
private Integer gender;
private Integer age ;
@TableField(exist=false) 表示数据库没有说该属性对于的字段
private Double salary ;
getter...
setter...
}
mapper接口
/**
* Mapper接口
*
* 基于Mybatis: 在Mapper接口中编写CRUD相关的方法 提供Mapper接口所对应的SQL映射文件 以及 方法对应的SQL语句.
*
* 基于MP: 让XxxMapper接口继承 BaseMapper接口即可.
* BaseMapper<T> : 泛型指定的就是当前Mapper接口所操作的实体类类型
*
*/
public interface EmployeeMapper extends BaseMapper<Employee> {
无需写sql映射文件,当然也可以添加映射文件
// Integer insertEmployee(Employee employee );
// <insert useGeneratedKeys="true" keyProperty="id" > SQL...</insert>
}
2.使用
测试
public class TestMP {
private ApplicationContext ioc =
new ClassPathXmlApplicationContext("applicationContext.xml");
private EmployeeMapper employeeMapper =
ioc.getBean("employeeMapper",EmployeeMapper.class);
}
查询
List<Employee> employees = employeeMapper.selectList(null); 设置条件为null
System.out.println("查询全部"+employees);
Employee employee = employeeMapper.selectById(2);
System.out.println("查询单个"+employee);
ArrayList<Integer> employees1 = new ArrayList<Integer>();
employees1.add(1);
employees1.add(2);
List<Employee> employees2 = employeeMapper.selectBatchIds("批量查询"+employees1);
employees2.forEach(user->{
System.out.println(user);
});
Integer integer = employeeMapper.selectCount(null);设置条件为null
System.out.println("查询记录数"+integer);
// new Page<Employee>(1, 2) 设置查看第一页,显示两条数据
List<Employee> emps =employeeMapper.selectPage(new Page<Employee>(1, 2),null)条件为null
System.out.println("分页查询"+emps);
// 通过多个列进行查询 id + lastName
// Employee employee = new Employee();
// //employee.setId(7);
// employee.setLastName("小泽老师");
// employee.setGender(0);
//
// Employee result = employeeMapper.selectOne(employee);
// 通过Map封装条件查询
// Map<String,Object> columnMap = new HashMap<>();
// columnMap.put("last_name", "Tom");
// columnMap.put("gender", 1);
//
// List<Employee> emps = employeeMapper.selectByMap(columnMap);
List<Employee> emps = employeeMapper.selectList(
new EntityWrapper<Employee>()
.eq("gender", 0)
.like("last_name", "老师")
//.or() // SQL: (gender = ? AND last_name LIKE ? OR email LIKE ?)
.orNew() // SQL: (gender = ? AND last_name LIKE ?) OR (email LIKE ?)
.like("email", "a")
);
List<Employee> emps = employeeMapper.selectList(
new EntityWrapper<Employee>()
.eq("gender", 0)
.orderBy("age") 默认升序
//.orderDesc(Arrays.asList(new String [] {"age"}))根据age降序
.last("desc limit 1,3") //这种属于sql拼接,不安全
);
携带条件查询
分页查询带条件
List<Employee> emps =employeeMapper.selectPage(new Page<Employee>(1, 2),
new EntityWrapper<Employee>()
.between("age", 18, 50) 注意:指定的是数据库字段表示类属性
.eq("gender", 1)
.eq("last_name", "Tom") 他们都是and的关系
);
EntityWrapper是构造条件的类
public class EntityWrapper<T> extends Wrapper<T>
EntityWrapper继承了Wrapper
还有一个Condition也继承了 Wrapper<T>
删除
List<Employee> employees = employeeMapper.selectList(null);
带条件
employeeMapper.delete(
new EntityWrapper<Employee>()
.eq("last_name", "Tom")
.eq("age", 22)
);
批量删除
employeeMapper.deleteBatchIds(new ArrayList<>());
删除的id
employeeMapper.deleteById(1)
修改
Employee employee = new Employee();
employee.setLastName("老师");
employee.setEmail("cls@sina.com");
employee.setGender(0);
employeeMapper.update(employee,
new EntityWrapper<Employee>()
.eq("last_name", "Tom")
.eq("age", 44)
);
根据条件筛选数据进行修改
不携带条件的方法,我们需要给他的实体类设置id才能够·有效的操作数据库数据
如 employee.setId(1);
employeeMapper.updateById(employee);
employeeMapper.updateAllColumnById(employee);
插入
Employee employee = new Employee();
employee.setLastName("MP");
employee.setEmail("mp@atguigu.com");
//employee.setGender(1);
//employee.setAge(22);
employee.setSalary(20000.0);
//插入到数据库
// insert方法在插入时, 会根据实体类的每个属性进行非空判断,只有非空的属性对应的字段才会出现到SQL语句中
Integer result = employeeMapper.insert(employee);
//insertAllColumn方法在插入时, 不管属性是否非空, 属性所对应的字段都会出现到SQL语句中.
Integer result = employeeMapper.insertAllColumn(employee);
插入成功后,可以直接在该实体类里获取插入数据的id值
Condition
List<Employee > emps = employeeMapper.selectPage(
new Page<Employee>(1,2),
Condition.create()
.between("age", 18, 50)
.eq("gender", "1")
.eq("last_name", "Tom")
);
和EntityWrapper的使用是一样的,只不过创建的方式不一样
Condition的是Condition.create()
EntityWrapper是new
但这只是低版本的,高版本的plus不支持EntityWrapper
Model
实体类继承Model<T> ,当然mapper也好需要继承BaseMapper<Employee>
继承Model,还需要重写一个方法
protected Serializable pkVal() {
return this.id;
}
此时的实体类就有许多的单表操作数据的方法了
model测试
public class TestMP {
private ApplicationContext ioc =
new ClassPathXmlApplicationContext("applicationContext.xml");
private EmployeeMapper employeeMapper =
ioc.getBean("employeeMapper",EmployeeMapper.class);
// AR 查询全部
@Test
public void select(){
Employee employee=new Employee();
List<Employee> employees = employee.selectAll();
System.out.println(employees);
}
}
//查询单个
@Test
public void select1(){
Employee employee=new Employee();
employee.setId(1);
Employee employee1 = employee.selectById();
//或者,使用这种方式也可以
Employee employee2 = employee.selectById(1);
System.out.println(employee1);
System.out.println(employee2);
//删除和该两种种方式类似,
}
//条件查询
//注意,column是数据库字段而非实体类属性名
@Test
public void select2(){
Employee employee=new Employee();
List<Employee> age = employee.selectList(new EntityWrapper().eq("age", 11));
System.out.println(age);
}
//查询总记录数,携带条件
@Test
public void select3(){
Employee employee=new Employee();
int age = employee.selectCount(new EntityWrapper().between("age", 10, 60));
System.out.println(age);
}
//page,分页,可携带条件
@Test
public void select4(){
Employee employee=new Employee();
Page<Employee> employeePage = employee.selectPage(new Page<Employee>(1, 2), null);
System.out.println(employeePage);
System.out.println(employeePage.getRecords());
}
3.代码生成器
坐标
<!--模板引擎 -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.2</version>
</dependency>
还有其他众多模板可参考官网
代码生成
@Test
public void test() {
//全局配置
GlobalConfig globalConfig = new GlobalConfig();
globalConfig.setAuthor("gww") //作者
.setActiveRecord(true) //开启AR模式
.setOutputDir("E:/projects/mybatis-plus/demo1/mybatisplus_demo3/src/main/java") //生成路径
.setFileOverride(true) //文件覆盖
.setIdType(IdType.AUTO) //主键策略
.setServiceName("%sServer") //生成的server接口首字母是否为I
.setBaseResultMap(true)
.setBaseColumnList(true);
//数据源配置
DataSourceConfig dataSourceConfig = new DataSourceConfig();
dataSourceConfig.setDbType(DbType.MYSQL)
.setPassword("123456")
.setDriverName("com.mysql.jdbc.Driver")
.setUsername("root")
.setUrl("jdbc:mysql://localhost:3306/mp_plus");
//策略配置
StrategyConfig strategyConfig = new StrategyConfig();
strategyConfig.setCapitalMode(true) //全局大写
.setDbColumnUnderline(true) //指定表名字段是否下划线
.setNaming(NamingStrategy.underline_to_camel) //数据库映射到实体的命名策略
.setInclude("empty"); //生成的表
// .setTablePrefix("tab_");
//包名策略配置
PackageConfig packageConfig = new PackageConfig();
packageConfig.setParent("com.sz")
.setMapper("mapper")
.setService("service")
.setController("controller")
.setEntity("beans")
.setXml("mapper");
//整合配置!
AutoGenerator autoGenerator = new AutoGenerator();
autoGenerator.setPackageInfo(packageConfig)
.setStrategy(strategyConfig)
.setGlobalConfig(globalConfig)
.setDataSource(dataSourceConfig);
autoGenerator.execute(); //执行生成
}
执行该段代码会自动生成一些文件和类
注意,生成的mapper映射文件的二级缓存记得关闭,否则很可能报错
可生成mapper,service,impl,controller
其余一些配置可参考官网
代码生成器生成的mapper接口继承了BaseMapper<T>接口
实体类继承了Model<T>抽象类
3.注册插件
分页插件
在 spring配置文件中的sqlSessionFactoryBean下注册
<bean id="sqlSessionFactoryBean" class="com.baomidou.mybatisplus.spring.MybatisSqlSessionFactoryBean">
<!-- 数据源 -->
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:mybatis-config.xml"></property>
<!-- 别名处理 -->
<property name="typeAliasesPackage" value="com.atguigu.mp.beans"></property>
<!-- 注入全局MP策略配置 -->
<property name="globalConfig" ref="globalConfiguration"></property>
<property name="plugins">
<list>
<!--注册分页插件-->
<bean class="com.baomidou.mybatisplus.plugins.PaginationInterceptor"></bean>
</list>
</property>
</bean>
mybati-plus已经可以使用分页了,为什么还需要分页插件呢
之前不使用分页插件的话,会直接在内存中分页,会查询出大量的数据,消耗内存
当进行修改操作后,可能会导致数据会不准确
使用了分页插件后,分页的写法和之前一样,只不过不是在内存中分页,而是查询的数据库
执行分析插件
作用:当有全表的数据操作是,会阻止该行为,不会报错
依然在bean为sqlSessionFactoryBean的里面plugins里面添加,添加在list里面,和分页插件同个位置
<!--执行分析插件-->
<bean class="com.baomidou.mybatisplus.plugins.SqlExplainInterceptor">
<property name="stopProceed" value="true"></property>
</bean>
性能分析插件
依然添加到plugins插件处
<!--性能分析插件-->
<bean class="com.baomidou.mybatisplus.plugins.PerformanceInterceptor">
<property name="maxTime" value="2000"></property><!--sql执行的最大超时时间-->
<property name="format" value="true"></property><!--输出的sql语句格式化-->
</bean>
乐观锁插件
需要在表中添加一个字段 version。
并且在实体类中添加一个属性 version,并且使用@Version注解。
添加该字段的原因是,当我们的实体类修改数据库时,会将我设置的version值和数据库的值进行比对,如果相同,则操作成功。
如果不同,那么语句依然可以执行,但是改变的记录为0,不会报错
4.自定义全局配置
使用
在mapper的接口类里写自定义的方法,但是可以不需要再映射文件里写相应的sql
如
public interface EmptyMapper extends BaseMapper<Empty> {
List<Empty> seleon();
}
没有写对应的映射文件
此时,需要定义一个类区继承AutoSqlInjector类
public class MyJector extends AutoSqlInjector{
@Override
public void inject(Configuration configuration, MapperBuilderAssistant builderAssistant, Class<?> mapperClass, Class<?>
modelClass, TableInfo table) { //table表信息
String sql="select * from "+table.getTableName(); 拼接成一个sql
String method="seleon"; //在mapper接口下的方法的方法名
SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, mapperClass);
MappedStatement mappedStatement = this.addSelectMappedStatement(mapperClass, method, sqlSource, Empty.class, table);
}
}
<!--将全局配置加入到spring,然后再加入全局策略配置-->
<bean class="com.sz.jector.MyJector" id="myJector"></bean>
<!-- MybatisPlus的全局策略配置-->
<!--加入到该全局配置策略里--> 在spring核心配置文件里
<property name="sqlInjector" ref="myJector"></property>
注意(驼峰下划线配置)
使用代码生成器生成的实体类有可能驼峰命名会无效,需要在配置一下
之前在spring配置文件配置的
<property name="dbColumnUnderline" value="true"></property>
可能无效
需要另外配置一下
在mybatis-config.xml,也就是mybatis的配置文件中
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 全局配置 -->
<settings>
<!--是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典Java 属性名 aColumn 的类似映射。 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
</configuration>
5.逻辑删除
改动
实体类添加属性
@TableLogic //逻辑删除字段的注解
private Integer logIc;
数据库也需要添加该字段
在spring配置文件中添加一个bean
<!--逻辑删除-->
<bean class="com.baomidou.mybatisplus.mapper.LogicSqlInjector" id="logicSqlInjector"></bean>
然后在 <!-- MybatisPlus的全局策略配置-->里做以下修改
注意:此时,之前做的自定义全局配置的 <!--<property name="sqlInjector" ref="myJector"></property>--> 需要注释
原因是,他们都是都一个属性,是不能同时存在两个的
然后添加以下内容:
<!--使用了逻辑删除,就需要把myJector注释了 ,然后还需要配置字段的值对应的状态-->
<property name="sqlInjector" ref="logicSqlInjector"></property>
<property name="logicDeleteValue" value="-1"></property> <!-- 删除的状态为-1-->
<property name="logicNotDeleteValue" value="1"></property> <!--没有删除的状态为1-->
此时的删除,并不是真正意义上的删除,而是将该字段设置为了-1,当查询时,是查询不到该数据的,但是该数据还一直保存在数据库里
6.公共字段填充
步骤
新建一个类
MyMetaObjectHandler 继承 MetaObjectHandler抽象类
覆写两个方法
public class MyMetaObjectHandler extends MetaObjectHandler{
//插入数据时填充
public void insertFill(MetaObject metaObject) {
Object log_ic = getFieldValByName("name", metaObject); 指的是实体类的属性
if (log_ic==null){
System.out.println("填充数据");
setFieldValByName("name", "gww", metaObject); 当插入数据时,name属性为空时,就用值为gww的数据填充到执行的sql里,插入数据库
}
}
//修改数据时填充
public void updateFill(MetaObject metaObject) {
Object log_ic = getFieldValByName("name", metaObject);
if (log_ic==null){
System.out.println("填充数据"); 如上,雷同,就是在修该数据的时候
setFieldValByName("name", "gww", metaObject);
}
}
}
然后将新建的类加入到spring里管理<!--数据字段填充-->
<bean class="com.sz.handle.MyMetaObjectHandler" id="myMetaObjectHandler"></bean>
然后将该对象放到 MybatisPlus的全局策略配置里
<!--数据字段填充注入-->
<property name="metaObjectHandler" ref="myMetaObjectHandler"></property>
对应的实体类属性也要做一些修改
@TableField(fill = FieldFill.INSERT_UPDATE) //添加填充属性,表示在插入和修改时无数据就填充设置好的值
private String name;