MyBatisPlus
文章目录
简介
MyBatis-Plus (opens new window)(简称 MP)是一个 MyBatis (opens new window)的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
MyBatis和MyBatiPlus之间的关系:
MyBatiPlus的愿景是MyBatis 最好的搭档,就像 魂斗罗 中的 1P、2P,基友搭配,效率翻倍。
特性
- 无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
- 损耗小:启动即会自动注入基本 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 操作智能分析阻断,也可自定义拦截规则,预防误操作
支持数据库
任何能使用 MyBatis进行 CRUD, 并且支持标准 SQL 的数据库,具体支持情况如下,如果不在下列表查看分页部分教程 PR 您的支持。
入门案例
通过MyBatisPlus实现查询所有学生的信息。
1、新建t_student表并插入一些数据
sql语句
/*
SQLyog Enterprise v12.14 (64 bit)
MySQL - 5.7.38-log
*********************************************************************
*/
/*!40101 SET NAMES utf8 */;
CREATE TABLE `t_student` (
`sid` INT (11),
`s_name` VARCHAR (150),
`sage` INT (11),
`sgender` CHAR (3),
`hone` CHAR (33)
);
INSERT INTO `t_student` (`sid`, `s_name`, `sage`, `sgender`, `hone`) VALUES('1','永康','23','1','13245678954');
INSERT INTO `t_student` (`sid`, `s_name`, `sage`, `sgender`, `hone`) VALUES('2','王华','19','0','13724760962');
INSERT INTO `t_student` (`sid`, `s_name`, `sage`, `sgender`, `hone`) VALUES('3','张三','40','1','13524567096');
2、导依赖
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!-- mybatis-plus-->
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.5.1</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.37</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.5</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
3、导入spring整合myBatis项目需要的配置文件
导入applicationContext.xml、jdbc_config.properties、log4j.properties、mybatis_config.xml
4、根据Mybatisplus修改上述配置文件
修改applicationContext.xml配置文件:
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:jdbc_config.properties"/>
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.userName}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- <bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">-->
<!-- <property name="configLocation" value="classpath:mybatis_config.xml"/>-->
<!-- <property name="dataSource" ref="druidDataSource"/>-->
<!-- </bean>-->
<!-- 改成-->
<bean id="mybatisSqlSessionFactoryBean" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis_config.xml"/>
<property name="dataSource" ref="druidDataSource"/>
</bean>
</beans>
5、编写实体类
package com.lwg.domain;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
/**
* @author lwg
* @title
* @create 2022/11/22 23:35
*/
@TableName("t_student")
public class Student {
private Integer sid;
@TableField("s_name")
private String sname;
private Integer sage;
private String sgender;
private String hone;
public Student() {
}
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}
public Integer getSage() {
return sage;
}
public void setSage(Integer sage) {
this.sage = sage;
}
public String getSgender() {
return sgender;
}
public void setSgender(String sgender) {
this.sgender = sgender;
}
public String getHone() {
return hone;
}
public void setHone(String hone) {
this.hone = hone;
}
public String getSname() {
return sname;
}
public void setSname(String sname) {
this.sname = sname;
}
@Override
public String toString() {
return "Student{" +
"sid=" + sid +
", sname='" + sname + '\'' +
", sage=" + sage +
", sgender='" + sgender + '\'' +
", hone='" + hone + '\'' +
'}';
}
}
注:
这里我们使用了两个注解分别是@TableName(“t_student”)、@TableField(“s_name”)。
@TableName注解:当数据库中的表名和实体类的类名不一致时(实体类名首字母小写就是数据库应该对应的表名),就需要指定数据库的表名,@TableName注解指定数据库中表名,属性值为数据库表名。
@TableField注解:当实体类中的属性名与数据库中的字段名不一致时,就需要指定数据库字段名,使用@TableField注解指定,属性值为数据库字段名。
6、编写dao层
MyBatisPlus实现了基本的CRUD,不需要我们再编写方法实现了,只需要dao接口继承BaseMapper即可。
package com.lwg.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lwg.domain.Student;
/**
* @author lwg
* @title
* @create 2022/11/22 23:39
*/
public interface StudentDao extends BaseMapper<Student> {
}
BaseMapper接口要传dao接口对应的实体类,底层会通过反射获取实体类的信息,然后通过实体类信息分析出表的信息,从而动态生成CRU相关的SQL语句。
7、编写测试类,直接调用selectList()方法。
package com.lwg.dao;
import com.lwg.domain.Student;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
/**
* @author lwg
* @title
* @create 2022/11/22 23:46
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class StudentDaoTest {
@Autowired
private StudentDao studentDao;
@Test
public void setletList(){
List<Student> studentList = studentDao.selectList(null);
System.out.println("studentList = " + studentList);
}
}
运行结果:
由入门案例可以看出mybatisplus的强大,不需要我们再编写xml,编写对应的sql语句。
Mapper的CRUD接口
入门案例中我们不用编写任何的mapper方法和编写任何的sql语句就可以实现查询功能,那是因为MyBatisplus的BaseMapper定义了基本的CRUD相关的方法。
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 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);
}
- 通用的CRUD封装BaseMapper接口,为MyBatisplus启动时扫描实体类关系映射为MyBatis内部对象注入容器。
- 泛型T为任意实体对象
- Serializable为任意类型主键,MyBatisplus不推荐使用复合主键约定,每一张表都应该有唯一的主键id
添加功能
BaseMapper接口方法:
int insert(T entity);
类型 | 参数名 | 描述 |
---|---|---|
T | entity | 实体类对象 |
// 添加功能
@Test
public void insetStudentTesdt(){
Student student = new Student();
student.setSage(22);
student.setSgender("1");
student.setHone("11382476096");
student.setSname("王维");
int insert = studentDao.insert(student);
System.out.println("insert = " + insert);
}
总结:
- 传入的实体类对象为Null的属性,不会拼接到该字段到SQL语句
- 默认指定某个属性为主键,不会开启主键回填,可通过@TableId注解设置某个属性为主键。
@TableId注解:设置某个属性为主键列,其type属性设置主键的增长的类型,是枚举类型,分别有:
- IdType.AUTO:自动增长
- IdType.NONE:不设置增长类型
- IdType.INPUT:人工设置id
- IdType.ASSIGN_ID:雪花算法
- IdType.ASSIGN_UUID:UUID
更新功能
BaseMapper接口方法:
//通过id更新,传入实体对象
int updateById(@Param("et") T entity);
//根据条件更新,传入实体对象、条件类对象
int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);
类型 | 参数名 | 描述 |
---|---|---|
T | entity | 实体类 |
Wrapper | updateWrapper | 实体对象封装类(可以为Nul,生成where条件语句) |
通过id更新:
// 通过Id更新
@Test
public void updateStudentByIdTest(){
Student student = new Student();
student.setSid(5);
student.setSage(55);
student.setSgender("1");
student.setHone("13414987977");
student.setSname("卢比奥");
int i = studentDao.updateById(student);
System.out.println("i = " + i);
}
运行结果:
总结:
- 更新要设置id值,如果不设置id值会更新全表
- 如果不设置的值,也就是为Null的属性不会拼接sql语句,不会更新对应的字段
删除功能
BaseMapper接口方法:
//通过id值删除单个
int deleteById(Serializable id);
//根据实体类对象条件删除
int deleteById(T entity);
//根据columnMap条件删除
int deleteByMap(@Param("cm") Map<String, Object> columnMap);
//根据条件删除
int delete(@Param("ew") Wrapper<T> queryWrapper);
//根据id值进行批量删除
int deleteBatchIds(@Param("coll") Collection<?> idList);
类型 | 参数名 | 描述 |
---|---|---|
Serializable | id | 主键ID |
T | entity | 实体类对象 |
Map<String, Object> | columnMap | 表字段Map对象 |
Wrapper | queryWrapper | 实体对象封装操作对象(可以为null,where条件) |
Collection | idList | id列表(不能为null或者为entity) |
通过id删除单个
// 通过id删除单个
@Test
public void deleteStudentByIdTest(){
// 通过直接传入id删除
int i = studentDao.deleteById(4);
System.out.println("i = " + i);
// 通过传入实体类对象,实体类对象设置id删除
Student student = new Student();
student.setSid(6);
int i2 = studentDao.deleteById(student);
System.out.println("i = " + i2);
}
运行结果:
通过多条件删除,将条件封装在map集合里
// 通过多条件删除,将条件封装在map集合中
@Test
public void deleteStudentByMapTest(){
Map<String,Object> conditionMap=new HashMap<>();
conditionMap.put("s_name","王华");
conditionMap.put("sage",19);
int i = studentDao.deleteByMap(conditionMap);
System.out.println("i = " + i);
}
运行结果:
总结:Map集合中的key是字段名,值就是字段值,拼接的条件是and
批量删除,难过传入Id列表
// 批量删除,传入Id列表
@Test
public void deleteIdListTest(){
// 传入id列表批量删除
List<Integer> idList=new ArrayList<>();
idList.add(1);
idList.add(2);
int i = studentDao.deleteBatchIds(idList);
System.out.println("i = " + i);
// 传入实体类对象列表批量删除
List<Student> studentList=new ArrayList<>();
Student student1 = new Student();
student1.setSid(3);
Student student2 = new Student();
student2.setSid(4);
Student student3 = new Student();
student3.setSid(5);
studentList.add(student1);
studentList.add(student2);
studentList.add(student3);
int i1 = studentDao.deleteBatchIds(studentList);
System.out.println("i1 = " + i1);
}
运行结果:
总结:批量删除可传入id集合,也可以传入实体类集合。
查询功能
BaseMapper接口方法
//通过id查询查询一条记录
T selectById(Serializable id);
//通过id批量查询
List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);
//根据条件查询,条件封装在map集合
List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);
//通过条件查询单个
T selectOne(@Param("ew") Wrapper<T> queryWrapper);
//根据Wrapper条件查询总记录数
Long selectCount(@Param("ew") Wrapper<T> queryWrapper);
//根据Wrapper条件查询多个记录
List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);
//根据Wrapper条件查询多条记录
List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper);
//根据Wrapper条件查询多条记录,只返回第一个字段的值
List<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper);
//根据Wrapper条件,查询多条记录,并分页
<P extends IPage<T>> P selectPage(P page, @Param("ew") Wrapper<T> que/根据Wrapper条件,查询多条记录,并分页ryWrapper);
///根据Wrapper条件,查询多条记录,并分页
<P extends IPage<Map<String, Object>>> P selectMapsPage(P page, @Param("ew") Wrapper<T> queryWrapper);
通过id查询单条记录
// 通过id查询单个记录
@Test
public void selectStudentByIdTest(){
Student student = studentDao.selectById(1);
System.out.println("student = " + student);
}
运行结果:
通过多个条件组合查询多个记录
/ 通过多个条件查询学生记录
@Test
public void selectByMapTest(){
Map<String,Object> conditionMap=new HashMap<>();
conditionMap.put("s_name","张三");
conditionMap.put("sage",32);
List<Student> studentList = studentDao.selectByMap(conditionMap);
System.out.println("studentList = " + studentList);
}
运行结果:
总结:
- 多条件查询,将条件封装在map集合中,key是字段名,值是条件值。
- 条件拼接是and
查询所有记录
// 查询所有
@Test
public void selectAllTest(){
//传入null就是查全部的记录
List<Student> studentList = studentDao.selectList(null);
System.out.println("studentList = " + studentList);
}
运行结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wmK0gtXQ-1669600205876)(C:\Users\35173\AppData\Roaming\Typora\typora-user-images\image-20221123231450467.png)]
总结:传入null就是查询所有记录。
通过多个id查询多条记录
//通过多个id查询多条记录
@Test
public void selectByIdsTest(){
List<Integer> ids=new ArrayList<>();
ids.add(1);
ids.add(2);
ids.add(3);
List<Student> studentList = studentDao.selectBatchIds(ids);
System.out.println("studentList = " + studentList);
}
运行结果:
查询结果集中的第一列数据
// 查询结果集中的第一列数据
@Test
public void selectObjTest(){
List<Object> objects = studentDao.selectObjs(null);
System.out.println("objects = " + objects);
}
运行结果:
查询所有记录的记录数
// 查询学生总记录数
@Test
public void selectCount(){
Long count = studentDao.selectCount(null);
System.out.println("count = " + count);
}
运行结果:
注:
selectOne()方法必须传入条件,否则会报错,而且传入的条件得到的结果必须只有一条记录,否则也会报错。
条件构造器
在一些复杂的查询中需要用到一些复杂的条件,而MyBatisPlus只是实现基本的CRUD操作。但是其他条件没有实现,比如or、like、in等,然后MyBatisPlus为了不需要我们编写sql语句,就引入了Wrapper(条件构造器),使用Wrapper就可以像编写sql语句一样构造条件。
Wrapper本质是一个对象,以方法的形式提供sql语句中关键字,我们调用对象调用方法拼接sql语句。
Wrapper的核心类有:QueryWrapper/UpdateWrapper
- AbstractWrapper:组装where/group by/having/order by/limit
- QueryWrapper:继承AbstractWrapper,可以拼接所有条件,还可以组装字段列表select字段列表
- UpdateWrapper:继承AbstractWrapper,可以拼接所有条件,还可以组装字段 列表set=字段1=值1,。。。。
我们创建QueryWrapper对象,使用该对象中提供的数据库操作的方法拼接条件,QueryWrapper对象最终是拼接好的sql片段,将片段拼接在sql语句。
使用条件构造器拼接条件写sql语句顺序一样的。
sql语句:
查询:
SELECT 字段名
FROM 表名
WHERE 条件
GROUP BY 分组字段
HAVING 分组条件
ORDER BY 排序字段
LIMIT 限定
插入:
INSERT INTO 表名(字段名列表) VALUES (字段值列表)
更新:
UPDATE 表名 SET 字段1=值1,字段2=值2 ... WHERE 条件
删除:
DELETE FROM 表名 WHERE 条件
总结:
- 插入sql语句不带条件,查询、更新和删除都可带条件,所以只有查询、更新、删除有Wrapper。
- 查询使用QueryWrapper拼接条件
- 更新使用UpdateWrapper拼接条件
- 删除可使用QueryWrapper、UpdateWrapper任一拼接sql
AbstractWrapper
allEq
allEq(Map<R, V> params)
allEq(Map<R, V> params, boolean null2IsNull)
allEq(boolean condition, Map<R, V> params, boolean null2IsNull)
全部eq(或个别等于isNull)
参数类型 | 参数名称 | 描述 |
---|---|---|
(Map<R, V> | params | 条件集合,key为字段名,value为字段值 |
boolean | null2IsNull | 为true则在map的value为null时调用 isNull 方法,为false时则忽略value为null的 |
@Test
public void allEqTest(){
QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
Map<String,Object> map=new HashMap<>();
map.put("sage",32);
map.put("sgender",0);
studentQueryWrapper.allEq(map);
List<Student> studentList = studentDao.selectList(studentQueryWrapper);
System.out.println("studentList = " + studentList);
}
运行结果:
@Test
public void allEqTest(){
QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
Map<String,Object> map=new HashMap<>();
map.put("sage",32);
map.put("sgender",0);
map.put("s_name",null);
//为true就会拼接s_name is Null
studentQueryWrapper.allEq(map,true);
List<Student> studentList = studentDao.selectList(studentQueryWrapper);
System.out.println("studentList = " + studentList);
}
allEq(BiPredicate<R, V> filter, Map<R, V> params)
allEq(BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull)
allEq(boolean condition, BiPredicate<R, V> filter, Map<R, V> params, boolean null2IsNull)
eq
eq(R column, Object val)
eq(boolean condition, R column, Object val)
- 等于 =
- 例:
eq("name", "老王")
—>name = '老王'
@Test
public void eqTest(){
QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
studentQueryWrapper.eq("s_name","李四");
studentQueryWrapper.eq("sage",44);
List<Student> studentList = studentDao.selectList(studentQueryWrapper);
System.out.println("studentList = " + studentList);
}
总结:多个eq,条件会用and连接。
ne
ne(R column, Object val)
ne(boolean condition, R column, Object val)
- 不等于 <>
- 例:
ne("name", "老王")
—>name <> '老王'
@Test
public void neTest(){
QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
studentQueryWrapper.ne("s_name","李四");
List<Student> studentList = studentDao.selectList(studentQueryWrapper);
System.out.println("studentList = " + studentList);
}
运行结果:
gt
gt(R column, Object val)
gt(boolean condition, R column, Object val)
- 大于 >
- 例:
gt("age", 18)
—>age > 18
@Test
public void gtTest(){
QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
studentQueryWrapper.gt("sage",5);
List<Student> studentList = studentDao.selectList(studentQueryWrapper);
System.out.println("studentList = " + studentList);
}
运行结果:
ge
ge(R column, Object val)
ge(boolean condition, R column, Object val)
- 大于等于 >=
- 例:
ge("age", 18)
—>age >= 18
@Test
public void geTest(){
QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
studentQueryWrapper.ge("sage",6);
List<Student> studentList = studentDao.selectList(studentQueryWrapper);
}
运行结果:
lt
lt(R column, Object val)
lt(boolean condition, R column, Object val)
- 小于 <
- 例:
lt("age", 18)
—>age < 18
@Test
public void ltTest(){
QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
studentQueryWrapper.lt("sage",30);
List<Student> studentList = studentDao.selectList(studentQueryWrapper);
System.out.println("studentList = " + studentList);
}
运行结果:
le
le(R column, Object val)
le(boolean condition, R column, Object val)
- 小于等于 <=
- 例:
le("age", 18)
—>age <= 18
@Test
public void leTest(){
QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
studentQueryWrapper.le("sage",44);
List<Student> studentList = studentDao.selectList(studentQueryWrapper);
System.out.println(studentList);
}
运行结果:
between
between(R column, Object val1, Object val2)
between(boolean condition, R column, Object val1, Object val2)
- BETWEEN 值1 AND 值2
- 例:
between("age", 18, 30)
—>age between 18 and 30
@Test
public void betweenTest(){
QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
studentQueryWrapper.between("sage",32,44);
List<Student> studentList = studentDao.selectList(studentQueryWrapper);
System.out.println("studentList = " + studentList);
}
运行结果:
notBetween
notBetween(R column, Object val1, Object val2)
notBetween(boolean condition, R column, Object val1, Object val2)
- NOT BETWEEN 值1 AND 值2
- 例:
notBetween("age", 18, 30)
—>age not between 18 and 30
@Test
public void noBetweenTest(){
QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
studentQueryWrapper.notBetween("sage",32,44);
List<Student> studentList = studentDao.selectList(studentQueryWrapper);
System.out.println("studentList = " + studentList);
}
运行结果:
like
like(R column, Object val)
like(boolean condition, R column, Object val)
- LIKE ‘%值%’
- 例:
like("name", "王")
—>name like '%王%'
@Test
public void likeTest(){
QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
studentQueryWrapper.like("s_name","王");
List<Student> studentList = studentDao.selectList(studentQueryWrapper);
System.out.println("studentList = " + studentList);
}
运行结果:
notLike
notLike(R column, Object val)
notLike(boolean condition, R column, Object val)
- NOT LIKE ‘%值%’
- 例:
notLike("name", "王")
—>name not like '%王%'
@Test
public void noLikeTest(){
QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
studentQueryWrapper.notLike("s_name","王");
List<Student> studentList = studentDao.selectList(studentQueryWrapper);
System.out.println("studentList = " + studentList);
}
运行结果:
likeLeft
likeLeft(R column, Object val)
likeLeft(boolean condition, R column, Object val)
- LIKE ‘%值’
- 例:
likeLeft("name", "王")
—>name like '%王'
@Test
public void likeLeftTest(){
QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
studentQueryWrapper.likeLeft("s_name","王");
List<Student> studentList = studentDao.selectList(studentQueryWrapper);
System.out.println("studentList = " + studentList);
}
运行结果:
ikeRight
likeRight(R column, Object val)
likeRight(boolean condition, R column, Object val)
- LIKE ‘值%’
- 例:
likeRight("name", "王")
—>name like '王%'
@Test
public void likeRighTest(){
QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
studentQueryWrapper.likeRight("s_name","王");
List<Student> studentList = studentDao.selectList(studentQueryWrapper);
System.out.println("studentList = " + studentList);
}
运行结果:
isNull
isNull(R column)
isNull(boolean condition, R column)
- 字段 IS NULL
- 例:
isNull("name")
—>name is null
@Test
public void isNullTest(){
QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
studentQueryWrapper.isNull("s_name");
List<Student> studentList = studentDao.selectList(studentQueryWrapper);
System.out.println("studentList = " + studentList);
}
运行结果:
isNotNull
isNotNull(R column)
isNotNull(boolean condition, R column)
- 字段 IS NOT NULL
- 例:
isNotNull("name")
—>name is not null
@Test
public void isNotNullTest(){
QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
studentQueryWrapper.isNotNull("s_name");
List<Student> studentList = studentDao.selectList(studentQueryWrapper);
System.out.println("studentList = " + studentList);
}
运行结果:
in
in(R column, Collection<?> value)
in(boolean condition, R column, Collection<?> value)
- 字段 IN (value.get(0), value.get(1), …)
- 例:
in("age",{1,2,3})
—>age in (1,2,3)
@Test
public void inTest(){
QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
studentQueryWrapper.in("sage",22,32,44);
List<Student> studentList = studentDao.selectList(studentQueryWrapper);
System.out.println("studentList = " + studentList);
}
运行结果:
notIn
notIn(R column, Collection<?> value)
notIn(boolean condition, R column, Collection<?> value)
- 字段 NOT IN (value.get(0), value.get(1), …)
- 例:
notIn("age",{1,2,3})
—>age not in (1,2,3)
@Test
public void noInTest(){
QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
List<Integer> ids= new ArrayList<>();
//可以封装好集合
/* ids.add(22);
ids.add(32);
ids.add(42);
studentQueryWrapper.notIn("sage",ids);*/
//也可以直接传进多个值
studentQueryWrapper.notIn("sage",22,32,33);
List<Student> studentList = studentDao.selectList(studentQueryWrapper);
System.out.println("studentList = " + studentList);
}
inSql
inSql(R column, String inValue)
inSql(boolean condition, R column, String inValue)
- 字段 IN ( sql语句 )
- 例:
inSql("age", "1,2,3,4,5,6")
—>age in (1,2,3,4,5,6)
- 例:
inSql("id", "select id from table where id < 3")
—>id in (select id from table where id < 3)
多表查询
notInSql
notInSql(R column, String inValue)
notInSql(boolean condition, R column, String inValue)
- 字段 NOT IN ( sql语句 )
- 例:
notInSql("age", "1,2,3,4,5,6")
—>age not in (1,2,3,4,5,6)
- 例:
notInSql("id", "select id from table where id < 3")
—>id not in (select id from table where id < 3)
groupBy
groupBy(R... columns)
groupBy(boolean condition, R... columns)
- 分组:GROUP BY 字段, …
- 例:
groupBy("id", "name")
—>group by id,name
@Test
public void groupByTest(){
QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
studentQueryWrapper.groupBy("sage","s_name");
List<Student> studentList = studentDao.selectList(studentQueryWrapper);
System.out.println("studentList = " + studentList);
}
运行结果:
orderByAsc
orderByAsc(R... columns)
orderByAsc(boolean condition, R... columns)
- 排序:ORDER BY 字段, … ASC
- 例:
orderByAsc("id", "name")
—>order by id ASC,name ASC
按某字段升序
@Test
public void orderByAscTest(){
QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
studentQueryWrapper.orderByAsc("sage");
List<Student> studentList = studentDao.selectList(studentQueryWrapper);
System.out.println("studentList = " + studentList);
}
运行结果:
orderByDesc
orderByDesc(R... columns)
orderByDesc(boolean condition, R... columns)
- 排序:ORDER BY 字段, … DESC
- 例:
orderByDesc("id", "name")
—>order by id DESC,name DESC
按某字段降序
@Test
public void orderByDescTest(){
QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
studentQueryWrapper.orderByDesc("sage");
List<Student> studentList = studentDao.selectList(studentQueryWrapper);
System.out.println("studentList = " + studentList);
}
运行结果:
orderBy
orderBy(boolean condition, boolean isAsc, R... columns)
- 排序:ORDER BY 字段, …
- 例:
orderBy(true, true, "id", "name")
—>order by id ASC,name ASC
@Test
public void orderByTest(){
QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
studentQueryWrapper.orderBy(true,false,"sage");
List<Student> studentList = studentDao.selectList(studentQueryWrapper);
System.out.println("studentList = " + studentList);
}
运行结果:
having
having(String sqlHaving, Object... params)
having(boolean condition, String sqlHaving, Object... params)
- HAVING ( sql语句 )
- 例:
having("sum(age) > 10")
—>having sum(age) > 10
- 例:
having("sum(age) > {0}", 11)
—>having sum(age) > 11
func
func(Consumer<Children> consumer)
func(boolean condition, Consumer<Children> consumer)
- func 方法(主要方便在出现if…else下调用不同方法能不断链)
- 例:
func(i -> if(true) {i.eq("id", 1)} else {i.ne("id", 1)})
or
or()
or(boolean condition)
-
拼接 OR
注意事项:
主动调用
or
表示紧接着下一个方法不是用and
连接!(不调用or
则默认为使用and
连接) -
例:
eq("id",1).or().eq("name","老王")
—>id = 1 or name = '老王'
or(Consumer<Param> consumer)
or(boolean condition, Consumer<Param> consumer)
- OR 嵌套
- 例:
or(i -> i.eq("name", "李白").ne("status", "活着"))
—>or (name = '李白' and status <> '活着')
and
and(Consumer<Param> consumer)
and(boolean condition, Consumer<Param> consumer)
- AND 嵌套
- 例:
and(i -> i.eq("name", "李白").ne("status", "活着"))
—>and (name = '李白' and status <> '活着')
nested
nested(Consumer<Param> consumer)
nested(boolean condition, Consumer<Param> consumer)
- 正常嵌套 不带 AND 或者 OR
- 例:
nested(i -> i.eq("name", "李白").ne("status", "活着"))
—>(name = '李白' and status <> '活着')
apply
apply(String applySql, Object... params)
apply(boolean condition, String applySql, Object... params)
-
拼接 sql
注意事项:
该方法可用于数据库函数 动态入参的
params
对应前面applySql
内部的{index}
部分.这样是不会有sql注入风险的,反之会有! -
例:
apply("id = 1")
—>id = 1
-
例:
apply("date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
—>date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
-
例:
apply("date_format(dateColumn,'%Y-%m-%d') = {0}", "2008-08-08")
—>date_format(dateColumn,'%Y-%m-%d') = '2008-08-08'")
last
last(String lastSql)
last(boolean condition, String lastSql)
-
无视优化规则直接拼接到 sql 的最后
注意事项:
只能调用一次,多次调用以最后一次为准 有sql注入的风险,请谨慎使用
-
例:
last("limit 1")
exists
exists(String existsSql)
exists(boolean condition, String existsSql)
- 拼接 EXISTS ( sql语句 )
- 例:
exists("select id from table where age = 1")
—>exists (select id from table where age = 1)
notExists
notExists(String notExistsSql)
notExists(boolean condition, String notExistsSql)
- 拼接 NOT EXISTS ( sql语句 )
- 例:
notExists("select id from table where age = 1")
—>not exists (select id from table where age = 1)
QueryWrapper
继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件
及 LambdaQueryWrapper, 可以通过 new QueryWrapper().lambda() 方法获取
select
select(String... sqlSelect)
select(Predicate<TableFieldInfo> predicate)
select(Class<T> entityClass, Predicate<TableFieldInfo> predicate)
-
设置查询字段
说明:
以上方法分为两类.
第二类方法为:过滤查询字段(主键除外),入参不包含 class 的调用前需要wrapper
内的entity
属性有值! 这两类方法重复调用以最后一次为准 -
例:
select("id", "name", "age")
-
例:
select(i -> i.getProperty().startsWith("test"))
@Test
public void selectTest(){
QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
studentQueryWrapper.select("s_name");
List<Student> studentList = studentDao.selectList(studentQueryWrapper);
System.out.println(studentList);
}
运行结果:
UpdateWrapper
继承自 AbstractWrapper ,自身的内部属性 entity 也用于生成 where 条件
及 LambdaUpdateWrapper, 可以通过 new UpdateWrapper().lambda() 方法获取!
set
set(String column, Object val)
set(boolean condition, String column, Object val)
- SQL SET 字段
- 例:
set("name", "老李头")
- 例:
set("name", "")
—>数据库字段值变为空字符串 - 例:
set("name", null)
—>数据库字段值变为null
@Test
public void setTest(){
UpdateWrapper<Student> studentUpdateWrapper = new UpdateWrapper<>();
studentUpdateWrapper.set("s_name","和尚");
studentUpdateWrapper.eq("sid",1);
Student student = new Student();
student.setSid(1);
int update = studentDao.update(student, studentUpdateWrapper);
System.out.println("update = " + update);
}
运行结果:
setSql
setSql(String sql)
- 设置 SET 部分 SQL
- 例:
setSql("name = '老李头'")
@Test
public void setSqlTest(){
UpdateWrapper<Student> studentUpdateWrapper = new UpdateWrapper<>();
studentUpdateWrapper.setSql("s_name='孙亮' where sid=2");
int update = studentDao.update(null, studentUpdateWrapper);
System.out.println("update = " + update);
}
运行结果:
lambda
- 获取
LambdaWrapper
- 在
QueryWrapper
中是获取LambdaQueryWrapper
- 在
UpdateWrapper
中是获取LambdaUpdateWrapper
根据条件构造器删除功能
BaseMapper接口方法
int delete(@Param("ew") Wrapper<T> queryWrapper);
@Test
public void deleteTest(){
QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
studentQueryWrapper.eq("sage",76);
int delete = studentDao.delete(studentQueryWrapper);
System.out.println("delete = " + delete);
}
运行结果:
根据条件构造器进行更新操作
BaseMapper接口方法:
int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);
@Test
public void updateTest(){
UpdateWrapper<Student> studentUpdateWrapper = new UpdateWrapper<>();
studentUpdateWrapper.in("sid",2,3,4);
Student student = new Student();
student.setSname("更新");
int update = studentDao.update(student, studentUpdateWrapper);
System.out.println("update = " + update);
}
运行结果
分页查询
分页查询客户端需要传入页码、每页显示多少条数据。
mysql的sql语句
SELECT 字段列表 FROM 表名
LIMIT (页码-1)是*每页条数,每页条数
oracle的sql语句
mybatisplus中sql语句动态使用实体类生成的,然后分页是使用插件来实现适应不同的数据库的分页操作。
使用分页步骤
1、配置分页插件
<bean id="mybatisSqlSessionFactoryBean" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis_config.xml"/>
<property name="dataSource" ref="druidDataSource"/>
<property name="plugins">
<array>
<!-- 配置分页插件:拦截对象-->
<bean class="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor">
<!-- 指明数据库类型-->
<property name="dbType" value="MYSQL"/>
</bean>
</array>
</property>
</bean>
注意:分页插件需要mybatis-plus 3.3.1版本才可以使用
2、调用分页查询
BaseMapper接口方法:
<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);
- 创建Page对象,对象传入页码、每页显示数量
- 创建Wrapper对象,构造条件
@Test
public void pageSelectTest(){
// 创建page对象,传入页码、每页条数
IPage<Student> page = new Page<>(1,2);
IPage<Student> studentPage = studentDao.selectPage(page, null);
// studentPage封装着分页数据、页数、总记录数
System.out.println("studentPage = " + studentPage);
//获得分页数据
List<Student> data = studentPage.getRecords();
System.out.println("分页数据:" + data);
//获得每页显示条数
long size = studentPage.getSize();
System.out.println("每页显示条数:" + size);
//获得当前页码
long current = studentPage.getCurrent();
System.out.println("当前页码:" + current);
//获得总页数
long pages = studentPage.getPages();
System.out.println("总页数:" + pages);
//获得总记录数
long total = studentPage.getTotal();
System.out.println("总记录数:" + total);
}
运行结果:
MyBatisPlus常用注解
@TableName
- 描述:表名注解,标识实体类对应的表
- 使用位置:实体类
@TableName("sys_user")
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
常用属性:
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
value | String | 否 | "" | 表名 |
schema | String | 否 | "" | schema |
keepGlobalPrefix | boolean | 否 | false | 是否保持使用全局的 tablePrefix 的值(当全局 tablePrefix 生效时) |
resultMap | String | 否 | "" | xml 中 resultMap 的 id(用于满足特定类型的实体类对象绑定) |
autoResultMap | boolean | 否 | false | 是否自动构建 resultMap 并使用(如果设置 resultMap 则不会进行 resultMap 的自动构建与注入) |
excludeProperty | String[] | 否 | {} | 需要排除的属性名 @since 3.3.1 |
@TableId
- 描述:主键注解
- 使用位置:实体类主键字段
@TableName("sys_user")
public class User {
@TableId
private Long id;
private String name;
private Integer age;
private String email;
}
常用属性:
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
value | String | 否 | "" | 主键字段名 |
type | Enum | 否 | IdType.NONE | 指定主键类型 |
IdType可选值
值 | 描述 |
---|---|
AUTO | 数据库 ID 自增 |
NONE | 无状态,该类型为未设置主键类型(注解里等于跟随全局,全局里约等于 INPUT) |
INPUT | insert 前自行 set 主键值 |
ASSIGN_ID | 分配 ID(主键类型为 Number(Long 和 Integer)或 String)(since 3.3.0),使用接口IdentifierGenerator 的方法nextId (默认实现类为DefaultIdentifierGenerator 雪花算法) |
ASSIGN_UUID | 分配 UUID,主键类型为 String(since 3.3.0),使用接口IdentifierGenerator 的方法nextUUID (默认 default 方法) |
分布式全局唯一 ID 长整型类型(please use ASSIGN_ID ) | |
32 位 UUID 字符串(please use ASSIGN_UUID ) | |
分布式全局唯一 ID 字符串类型(please use ASSIGN_ID ) |
@TableField
- 描述:字段注解(非主键)
@TableName("sys_user")
public class User {
@TableId
private Long id;
@TableField("nickname")
private String name;
private Integer age;
private String email;
}
属性 | 类型 | 必须指定 | 默认值 | 描述 |
---|---|---|---|---|
value | String | 否 | "" | 数据库字段名 |
exist | boolean | 否 | true | 是否为数据库表字段 |
condition | String | 否 | "" | 字段 where 实体查询比较条件,有值设置则按设置的值为准,没有则为默认全局的 %s=#{%s} ,参考
(opens new window) |
update | String | 否 | "" | 字段 update set 部分注入,例如:当在version字段上注解update="%s+1" 表示更新时会 set version=version+1 (该属性优先级高于 el 属性) |
insertStrategy | Enum | 否 | FieldStrategy.DEFAULT | 举例:NOT_NULL insert into table_a(<if test="columnProperty != null">column</if>) values (<if test="columnProperty != null">#{columnProperty}</if>) |
updateStrategy | Enum | 否 | FieldStrategy.DEFAULT | 举例:IGNORED update table_a set column=#{columnProperty} |
whereStrategy | Enum | 否 | FieldStrategy.DEFAULT | 举例:NOT_EMPTY where <if test="columnProperty != null and columnProperty!=''">column=#{columnProperty}</if> |
fill | Enum | 否 | FieldFill.DEFAULT | 字段自动填充策略 |
select | boolean | 否 | true | 是否进行 select 查询 |
keepGlobalFormat | boolean | 否 | false | 是否保持使用全局的 format 进行处理 |
jdbcType | JdbcType | 否 | JdbcType.UNDEFINED | JDBC 类型 (该默认值不代表会按照该值生效) |
typeHandler | Class<? extends TypeHandler> | 否 | UnknownTypeHandler.class | 类型处理器 (该默认值不代表会按照该值生效) |
numericScale | String | 否 | "" | 指定小数点后保留的位数 |
FieldStrategy
值 | 描述 |
---|---|
IGNORED | 忽略判断 |
NOT_NULL | 非 NULL 判断 |
NOT_EMPTY | 非空判断(只对字符串类型字段,其他类型字段依然为非 NULL 判断) |
DEFAULT | 追随全局配置 |
NEVER | 不加入SQL |
FieldFill
值 | 描述 |
---|---|
DEFAULT | 默认不处理 |
INSERT | 插入时填充字段 |
UPDATE | 更新时填充字段 |
INSERT_UPDATE | 插入和更新时填充字段 |
MyBatisplus基本配置
MyBatisplus在使用过程中的配置选项,本部分配置包含了一部分用户的常用配置,其中一部分为MyBatis原生所支持的配置,一部分为MyBatisPlus的配置。
详情配置参考官方文档
案例:配置全局配置,设置表前缀,主键自增。
tablePrefix 表名前缀
- 类型:
String
- 默认值:
null
idType 全局默认主键类型
- 类型:
com.baomidou.mybatisplus.annotation.IdType
- 默认值:
ASSIGN_ID
<bean id="mybatisSqlSessionFactoryBean" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis_config.xml"/>
<property name="dataSource" ref="druidDataSource"/>
<property name="plugins">
<array>
<!-- 配置分页插件:拦截对象-->
<bean class="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor">
<!-- 指明数据库类型-->
<property name="dbType" value="MYSQL"/>
</bean>
</array>
</property>
<property name="globalConfig" ref="globalConfig"/>
</bean>
<bean id="globalConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig">
<property name="dbConfig">
<bean id="dbConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig">
<property name="tablePrefix" value="t_"/>
<property name="idType" value="AUTO"/>
</bean>
</property>
</bean>
ActiveRecord模式
ActiveRecord模式是一种领域模型模式,特点是一个模型类对应数据库中的一个表,而模型类中的一个实例对应数据库表中的一行记录。
MyBatisPlus支持AR模式,支持ActiveRecord形调用,实体类只需继承Model类即可进行CRUD操作。
总结:模型类对应数据库表中的一个表,操作我们的模型类可以实现强大的增删查改的功能。
AR模式和Mapper模式对比
原有的MP的Mapper模式:
- 创建与数据库对应的实体类
- 创建实体类对应的mapper接口并继承BaseMapper接口
- 从spring容器中获取Mapper接口实例化对象完成数据库操作
MP的AR模式:
- 创建数据库中对应表的实体类,并继承Model类
- 在实体类覆写pkval方法
- 创建Mapper接口并继承BaseMapper接口
- 创建Spring对象,spring容器 对Mapper层实例化扫描
- 创建实体类对象,直接调用实体类对象从Model中继承的数据库方法进行操作
总结:MP的AR模式底层仍然是使用Mapper层操作数据库,不过由我们主动调用Mapper对象变成了通过实体类对象来调用Mapper完成数据库操作。
AR模式的特点
AR模式相对于MP模式操作数据库,不用再调用Mapper对象,然后再将实体类传入Mapper层操作数据库,直接调用实体类即可操作。
AR模式代码实现
1、编写Student类实体,继承Model类,并重写pkVal方法
package com.lwg.domain;
import com.baomidou.mybatisplus.extension.activerecord.Model;
import java.io.Serializable;
/**
* @author lwg
* @title
* @create 2022/11/25 22:08
*/
public class Student extends Model<Student> {
private Integer sid;
private String s_name;
private Integer sage;
private String sgender;
private String hone;
public Student() {
}
public Integer getSid() {
return sid;
}
public void setSid(Integer sid) {
this.sid = sid;
}
public String getS_name() {
return s_name;
}
public void setS_name(String s_name) {
this.s_name = s_name;
}
public Integer getSage() {
return sage;
}
public void setSage(Integer sage) {
this.sage = sage;
}
public String getSgender() {
return sgender;
}
public void setSgender(String sgender) {
this.sgender = sgender;
}
public String getHone() {
return hone;
}
public void setHone(String hone) {
this.hone = hone;
}
//重写pkVal方法
@Override
protected Serializable pkVal() {
return sid;
}
}
2、编写 StudentMapper接口并继承BaseMapper接口
package com.lwg.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lwg.domain.Student;
/**
* @author lwg
* @title
* @create 2022/11/25 22:12
*/
public interface StudentMapper extends BaseMapper<Student> {
}
注:必须要有对应的原始Mapper并继承BaseMapper才可以使用AR模式
3、编写测试类,调用对应的方法。
调用实体类,但是调用的其实是Model类的方法。
Model类:
//插入
public boolean insert();
//插入或更新,传入id就是更新,没传入id就是插入
public boolean insertOrUpdate();
//根据Id删除
public boolean deleteById(Serializable id);
//根据主键删除
public boolean deleteById();
//根据条件删除,根据实体封装操作类拼接的条件删除
public boolean delete(Wrapper<T> queryWrapper);
//根据id更新
public boolean updateById();
//根据条件更新,根据实体封装操作类的条件更新
public boolean update(Wrapper<T> updateWrapper);
//查询所有
public List<T> selectAll() ;
//根据id查询
public T selectById(Serializable id);
//根据主键查询
public T selectById();
//根据条件查询,根据实体封装类的拼接的条件查询
public List<T> selectList(Wrapper<T> queryWrapper);
//根据条件查询单个对象
public T selectOne(Wrapper<T> queryWrapper);
//根据条件查询记录数
public Integer selectCount(Wrapper<T> queryWrapper);
//分页查询
public <E extends IPage<T>> E selectPage(E page, Wrapper<T> queryWrapper);
package com.lwg.dao;
import com.lwg.domain.Student;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* @author lwg
* @title
* @create 2022/11/25 22:24
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class StudentDaoTest {
@Test
public void insertTest(){
Student student = new Student();
student.setSName("任一凡");
student.setSage(12);
student.setSgender("0");
student.setHone("12345678901");
boolean insert = student.insert();
System.out.println("insert = " + insert);
}
@Test
public void updateTest(){
Student student = new Student();
student.setSid(11);
student.setSName("吴某");
student.setSage(43);
student.setSgender("1");
student.setHone("13245687909");
boolean b = student.insertOrUpdate();
System.out.println("b = " + b);
}
@Test
public void deleteTest(){
Student student = new Student();
student.setSid(11);
boolean b = student.deleteById();
System.out.println("b = " + b);
}
}
代码生成器
MyBatisPlus提供的AutoGenerator代码生成器,可以帮助我们通过数据库表自动创建实体类、dao层、service层、controller层。
使用步骤:创建一个代码生成类,调用该类的对象方法,并将对应的数据库表作为参数传递进入以及一些生成代码的其他要求也传递给 对象,让对象生成基础的开发代码。
实现步骤
1、引入依赖
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-generator -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.2.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>2.0.3</version>
<type>pom</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.8.0-beta0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.8.0-beta0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.velocity/velocity-engine-core -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
</dependency>
2、代码生成器相关代码
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import org.junit.Test;
/**
* @author lwg
* @title
* @create 2022/11/25 22:59
*/
public class codeGenerator {
@Test
public void testenerator(){
// -----创建代码生成对象--
AutoGenerator mpg = new AutoGenerator();
// --配置全局属性--
GlobalConfig gc = new GlobalConfig();
// 动态获取当前项目路径
String projectPath = System.getProperty("user.dir");
// 设置代码自动生成输出目录
gc.setOutputDir(projectPath + "/src/main/java");
// 需要修改AR模式,修改后,代码生成器会自动将生成的实体类自动继承Model类,并且重写pkVal方法
gc.setActiveRecord(true);
// 设置作者声明
gc.setAuthor("lwg");
// 是否自动生成手动映射代码,即是否自动生成mappr.xml文件.默认是false
gc.setBaseResultMap(true);
// 设置是否重名覆盖
gc.setFileOverride(false);
// 设置是否开启二级缓存
gc.setEnableCache(true);
// 设置Id增长类型
gc.setIdType(IdType.AUTO);
gc.setBaseColumnList(false);
gc.setOpen(false);
// gc.setSwagger2(true); 实体属性 Swagger2 注解
mpg.setGlobalConfig(gc);
// ---配置数据源---
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/mybatisplus?useUnicode=true&useSSL=false&characterEncoding=utf8");
// dsc.setSchemaName("public");
dsc.setDriverName("com.mysql.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("密码");
mpg.setDataSource(dsc);
// ---包配置---
PackageConfig pc = new PackageConfig();
pc.setParent("com.lwg")
.setMapper("dao")
.setService("service")
.setController("controller")
.setEntity("domain")
.setXml("dao");
mpg.setPackageInfo(pc);
// ---策略配置---
StrategyConfig strategy = new StrategyConfig();
//设置全局大写命名
strategy.setCapitalMode(true) //配置数据库映射到实体类命名的策略
.setNaming(NamingStrategy.underline_to_camel)
.setTablePrefix("t_") //设置表前缀
.setInclude("t_student"); //需要生成的表
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
// 执行生成代码
mpg.execute();
}
}
3、执行代码。
帮我们创建了对应的controller层、service层、dao层、domain层。
而且service层的接口继承了IService接口,而IService接口实现了简单的CRUD。
IService代码:
//插入一条记录
boolean save(T entity);
//批量插入,entityList表示对象集合
boolean saveBatch(Collection<T> entityList);
//批量插入,entityList表示对象集合,batchSize:插入的数量
boolean saveBatch(Collection<T> entityList, int batchSize);
//批量插入或更新
boolean saveOrUpdateBatch(Collection<T> entityList);
//批量插入或更新
boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);
//根据id删除
boolean removeById(Serializable id);
//根据Map条件删除
boolean removeByMap(Map<String, Object> columnMap);
//根据Wrapper条件删除
boolean remove(Wrapper<T> queryWrapper);
//批量删除,按id列表删除
boolean removeByIds(Collection<? extends Serializable> idList) ;
//通过id进行更新
boolean updateById(T entity);
//根据Wrapper进行更新
boolean update(Wrapper<T> updateWrapper);
//根据entity和Wrapper进行更新
boolean update(T entity, Wrapper<T> updateWrapper);
//批量更新,根据id
boolean updateBatchById(Collection<T> entityList);
//批量更新,根据Id
boolean updateBatchById(Collection<T> entityList, int batchSize);
//插入或更新,有传入id且id在数据库中存在就更新,否则就是插入
boolean saveOrUpdate(T entity);
//通过id查询
T getById(Serializable id);
//批量查询,通过id查询
List<T> listByIds(Collection<? extends Serializable> idList);
//根据map条件进行查询
List<T> listByMap(Map<String, Object> columnMap);
//根据Wrapper查询单个
T getOne(Wrapper<T> queryWrapper);
//根据Wrapper查询单个
T getOne(Wrapper<T> queryWrapper, boolean throwEx);
//根据Wrapper查询,结果集返回Map集合
Map<String, Object> getMap(Wrapper<T> queryWrapper);
//根据queryWrapper查询第一列
<V> V getObj(Wrapper<T> queryWrapper, Function<? super Object, V> mapper);
//......
SSMP整合学生的增删改查
分页显示学生的信息,对学生进行添加、修改、删除、批量删除。
1、导入项目需要的依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.37</version>
</dependency>
<!-- mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- spring整合mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.5</version>
</dependency>
<!-- mybatis-plus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.5.1</version>
</dependency>
<!-- 代码生成器-->
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-generator -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.2.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>2.0.3</version>
<type>pom</type>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.8.0-beta0</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.8.0-beta0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.velocity/velocity-engine-core -->
<dependency>
<groupId>org.apache.velocity</groupId>
<artifactId>velocity-engine-core</artifactId>
<version>2.3</version>
</dependency>
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.28</version>
</dependency>
<!-- 数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.9</version>
</dependency>
<!-- spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<!-- 事务-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<!-- aop驱动-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.6</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.10.RELEASE</version>
</dependency>
<!-- springmvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
</dependency>
<!-- springmvc返回json-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.13.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.3</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.24</version>
</dependency>
</dependencies>
2、编写代码生成器类,运行自动生成对应的controller、service、dao、domain
@Test
public void generatorCodeTest(){
// -----创建代码生成对象--
AutoGenerator mpg = new AutoGenerator();
// --配置全局属性--
GlobalConfig gc = new GlobalConfig();
// 动态获取当前项目路径
String projectPath = System.getProperty("user.dir");
// 设置代码自动生成输出目录
gc.setOutputDir(projectPath + "/src/main/java");
// 需要修改AR模式,修改后,代码生成器会自动将生成的实体类自动继承Model类,并且重写pkVal方法
gc.setActiveRecord(true);
// 设置作者声明
gc.setAuthor("lwg");
// 是否自动生成手动映射代码,即是否自动生成mappr.xml文件.默认是false
gc.setBaseResultMap(true);
// 设置是否重名覆盖
gc.setFileOverride(false);
// 设置是否开启二级缓存
// gc.setEnableCache(true);
// 设置Id增长类型
gc.setIdType(IdType.AUTO);
gc.setBaseColumnList(false);
gc.setOpen(false);
// gc.setSwagger2(true); 实体属性 Swagger2 注解
mpg.setGlobalConfig(gc);
// ---配置数据源---
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/mybatisplus?useUnicode=true&useSSL=false&characterEncoding=utf8");
// dsc.setSchemaName("public");
dsc.setDriverName("com.mysql.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("20010107wdsr");
mpg.setDataSource(dsc);
// ---包配置---
PackageConfig pc = new PackageConfig();
pc.setParent("com.lwg")
.setMapper("dao")
.setService("service")
.setController("controller")
.setEntity("domain")
.setXml("dao");
mpg.setPackageInfo(pc);
// ---策略配置---
StrategyConfig strategy = new StrategyConfig();
//设置全局大写命名
strategy.setCapitalMode(true) //配置数据库映射到实体类命名的策略
.setNaming(NamingStrategy.underline_to_camel)
.setTablePrefix("t_") //设置表前缀
.setInclude("t_student"); //需要生成的表
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
// 执行生成代码
mpg.execute();
}
3、编写配置文件
编写mybatis核心配置文件
mybatis_config.xml
<?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是核心配置的根标签,表示配置的意思
-->
<configuration>
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
</configuration>
编写log4j配置文件,因为上面使用到了Log4j
log4j.rootLogger=DEBUG,stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
编写jdbcconfig
jdbc_config.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis_task?useUnicode=true&characterEncoding=UTF-8
jdbc.userName=root
jdbc.password=xxxxxxxx
编写spring核心配置文件
applicationContext.xml
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.lwg.service"/>
<bean id="mybatisSqlSessionFactoryBean" class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="configLocation" value="classpath:mybatis_config.xml"/>
<!-- 开启分页插件-->
<property name="plugins">
<array>
<bean class="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor">
<property name="dbType" value="MYSQL"/>
</bean>
</array>
</property>
<!-- 配置全局配置-->
<!-- <property name="globalConfig" ref="globalConfig"/>-->
</bean>
<context:property-placeholder location="classpath:jdbc_config.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.userName}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- <bean id="globalConfig" class="com.baomidou.mybatisplus.core.config.GlobalConfig">
<property name="dbConfig" >
<bean class="com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig">
<property name="idType" value="AUTO"/>
<property name="tablePrefix" value="t_"/>
</bean>
</property>
</bean>-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.lwg.dao"/>
<property name="beanName" value="mybatisSqlSessionFactoryBean"/>
</bean>
</beans>
编写springmvc核心配置文件
<?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:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.lwg.controller"/>
<mvc:annotation-driven/>
<mvc:default-servlet-handler/>
<!-- 视图解析器-->
<!-- <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">-->
<!-- <property name="prefix"-->
<!-- </bean>-->
</beans>
编写web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<filter>
<filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<init-param>
<param-name>forceRequestEncoding</param-name>
<param-value>true</param-value>
</init-param>
<init-param>
<param-name>forceResponseEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
4、编写controller层,使用AR模式
为了返回前端数据一致,我们新建一下实体类用来封装数据,进行前后端数据交互。
R
package com.lwg.domain;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Objects;
/**
* @author lwg
* @title
* @create 2022/11/26 17:22
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class R {
private Boolean success;
private Object data;
private String msg;
}
controller层
StudentController类
package com.lwg.controller;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.lwg.domain.R;
import com.lwg.domain.Student;
import org.springframework.web.bind.annotation.*;
import org.springframework.stereotype.Controller;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* <p>
* 前端控制器
* </p>
*
* @author lwg
* @since 2022-11-26
*/
@Controller
@RequestMapping("/student")
public class StudentController {
@GetMapping("data/{page}/{size}")
@ResponseBody
public R pagingData(@PathVariable Integer page,@PathVariable Integer size){
Student student = new Student();
IPage<Student> studentIPage = new Page<>(page,size);
QueryWrapper<Student> studentQueryWrapper = new QueryWrapper<>();
IPage<Student> page1 = student.selectPage(studentIPage, null);
List<Student> data = page1.getRecords();
System.out.println("data = " + data);
long pages = page1.getPages();
Map<String,Object> dataMap=new HashMap<>();
dataMap.put("data",data);
dataMap.put("pages",pages);
R result = R.builder().data(dataMap).success(true).msg(null).build();
return result;
}
@PostMapping
@ResponseBody
// 添加学生
public R addStudent(Student student){
boolean insert = student.insert();
R r = new R();
r.setData(null);
if(insert){
r.setMsg("添加成功");
}else {
r.setMsg("添加失败");
}
r.setSuccess(insert);
return r;
}
@PutMapping
@ResponseBody
public R updataStudent(Student student){
System.out.println("student = " + student);
boolean update = student.updateById();
R r = new R();
r.setData(null);
r.setSuccess(update);
if (update) {
r.setMsg("修改成功");
}else {
r.setMsg("修改失败");
}
return r;
}
@DeleteMapping("/{id}")
@ResponseBody
public R removeStudent(@PathVariable Integer id){
Student student = new Student();
student.setSid(id);
boolean delete = student.deleteById();
R r = new R();
r.setData(null);
r.setSuccess(delete);
if (delete) {
r.setMsg("删除成功");
}else {
r.setMsg("删除失败");
}
return r;
}
@GetMapping("/{id}")
@ResponseBody
public R getStudentById(@PathVariable Integer id){
Student student = new Student();
student.setSid(id);
Student data = student.selectById();
return R.builder().data(data).msg(null).success(true).build();
}
}
5、编写前端,实现增删改。
使用到了bootstrap框架以及其他插件。
主要代码:
<!doctype html>
<html lang="zh">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
<link rel="icon" href="static/images/favicons/favicon-32x32.png" sizes="32x32" type="image/png">
<link rel="icon" href="static/images/favicons/favicon-16x16.png" sizes="16x16" type="image/png">
<meta name="author" content="ajiho">
<link rel="stylesheet" href="lib/bootstrap-icons/font/bootstrap-icons.css">
<link rel="stylesheet" href="static/css/bootstrap.min.css">
<link rel="stylesheet" href="static/css/bootstrap-admin.min.css">
<title>学生管理</title>
<script src="src/jquery-1.10.1.js"></script>
<script>
$(function () {
let pageNum=1
let pageSize=1;
dataStu();
function dataStu() {
$.ajax({
url:"/ssmp/student/data/"+pageNum+"/10",
type:"GET",
success:function (data) {
if (data.success){
$("ul[class='pagination']").empty();
$("ul[class='pagination']").append(`
<li class="page-item ">
<a class="page-link" href="javascript:" aria-label="Previous">
上一页
</a>
</li>
<li class="page-item ">
<a class="page-link" href="javascript:" aria-label="Next">
下一页
</a>
</li>
`)
let firstChild = $("ul[class='pagination']").children()[0];
let pages = data.data.pages;
pageSize=pages
if (pages>=5){
if(pages-pageNum>=5){
for (var i=0;i<5;i++){
if (i==4){
$(firstChild).after(`
<li class="page-item active"><a class="page-link" href="javascript:">${5-i+Number(pageNum)-1}</a></li>
`)
continue;
}
$(firstChild).after(`
<li class="page-item "><a class="page-link" href="javascript:">${5-i+Number(pageNum)-1}</a></li>
`)
}
} else {
for(var j=0;j<5;j++){
if ((Number(pageNum) - (5 - (pages - Number(pageNum))) + 1 + (4 - j)) == Number(pageNum)) {
$(firstChild).after(`
<li class="page-item active"><a class="page-link" href="javascript:">${Number(pageNum)-(5-(pages-Number(pageNum)))+1+(4-j)}</a></li>
`)
continue;
}
$(firstChild).after(`
<li class="page-item"><a class="page-link" href="javascript:">${Number(pageNum)-(5-(pages-Number(pageNum)))+1+(4-j)}</a></li>
`)
}
}
}else {
for (var i=pages;i>0;i--){
if (i==Number(pageNum)){
$(firstChild).after(`
<li class="page-item active"><a class="page-link" href="javascript:">${Number(pageNum)}</a></li>
`)
continue;
}
$(firstChild).after(`
<li class="page-item "><a class="page-link" href="javascript:">${i}</a></li>
`)
}
}
// 拼接数据
let stuList = $("table[class='table align-middle table-hover']");
let tableBoby = stuList.children("tbody")[0];
$(tableBoby).empty()
let stu = data.data.data;
stu.forEach(function (value,index) {
let sid = value.sid;
let sName = value.sName;
let sage = value.sage;
let sgender = value.sgender;
if (sgender == '0') {
sgender='男'
}else {
sgender='女'
}
let hone = value.hone;
$(tableBoby).append(`
<tr>
<td>
<input class="form-check-input" aria-label="checkbox" type="checkbox" name="id[]"
value="1">
<input type="hidden" id="sid" name="sid" value="${sid}">
</td>
<td>${index+1}</td>
<td>${sName}</td>
<td>${sage}</td>
<td>${sgender}</td>
<td>${hone}</td>
<td><span class="badge rounded-pill bg-success">正常</span></td>
<td>
<div class="d-flex align-items-center">
<a class="btn btn-sm btn-primary ms-2 upata" href="javascript:" title="修改"><i
class="bi bi-pencil"></i></a>
<a class="btn btn-sm btn-primary ms-2 delete" href="javascript:" title="删除"><i
class="bi bi-trash"></i></a>
</div>
</td>
</tr>
`)
})
}
}
})
}
$("ul[class='pagination']").delegate("li","click",function (){
console.log(this)
let a=$(this).children('a')[0]
console.log(a);
let attr = $(a).attr('aria-label');
console.log(attr);
if (attr!=undefined){
if (attr=='Previous'){
if (pageNum==1){
pageNum=1;
}else {
pageNum=Number(pageNum)-1;
dataStu();
}
}else {
if (Number(pageNum)==pageSize){
pageNum=pageSize
}else {
pageNum=Number(pageNum)+1
dataStu();
}
}
}else {
console.log($(a))
console.log($(a).text());
pageNum=$(a).text();
dataStu();
}
})
var stuModal = new bootstrap.Modal(document.getElementById('stu_modal'))
function openModal(){
stuModal.show();
}
function closeModal(){
stuModal.hide();
}
$('#add_stu').click(function () {
openModal();
console.log("进来")
let children = $("#stu_form").find("input[type=text]").val('');
})
//添加用户提交到服务器并返回结果并显示
$('#add_stu_determine').click(function () {
let sName = $("#stu_name").val();
let sage = $("#stu_age").val();
let hone = $("#stu_hone").val();
let gender = $("#gender_man").prop("checked");
let sgender='1';
if (gender){
sgender="0";
}
closeModal();
$.ajax({
url:"/ssmp/student",
type:"POST",
data:{
sName:sName,
sage:sage,
hone:hone,
sgender:sgender
},
dataType:"json",
success:function (data) {
var msgToast = document.getElementById('msg_toast');
var toast = new bootstrap.Toast(msgToast)
$('.toast-body').text(data.msg)
toast.show()
dataStu();
}
})
})
// 更新静态框
var updataModal = new bootstrap.Modal(document.getElementById('stu_updata_modal'))
function openUpdataModal(){
updataModal.show();
}
function closeUpdataModal(){
updataModal.hide();
}
// 通过id查询学生信息并填充到表单中去
$("tbody").delegate("a[class='btn btn-sm btn-primary ms-2 upata']","click",function () {
let tdList = $(this).parents('tr');
let hidCode = tdList.find('input[type="hidden"]');
let hid = hidCode.val();
$.ajax({
url:"/ssmp/student/"+hid,
type:"GET",
success:function (data) {
openUpdataModal();
$('#stu_updata_sid').val(data.data.sid);
$('#stu_updata_name').val(data.data.sName);
$('#stu_updata_age').val(data.data.sage);
$('#stu_updata_hone').val(data.data.hone);
if (data.data.sgender=='0'){
$('#gender_updata_man').prop("checked",true);
$('#gender_updata_wom').prop("checked",false);
}else {
$('#gender_updata_man').prop("checked",false);
$('#gender_updata_wom').prop("checked",true);
}
}
})
})
// 修改提交到服务器并返回结果
$("#updata_stu_determine").click(function () {
let sid = $('#stu_updata_sid').val();
let sName = $("#stu_updata_name").val();
let sage = $("#stu_updata_age").val();
let hone = $("#stu_updata_hone").val();
let gender = $("#gender_updata_man").prop("checked");
let sgender='1';
if (gender){
sgender="0";
}
closeUpdataModal();
$.ajax({
url:"http://localhost:8087/ssmp/student",
data:{
_method:"put",
sid:sid,
sName:sName,
sage:sage,
hone:hone,
sgender:sgender
},
type:"POST",
dataType: "json",
success:function (data) {
var msgToast = document.getElementById('msg_toast');
var toast = new bootstrap.Toast(msgToast)
$('.toast-body').text(data.msg)
toast.show()
dataStu();
}
})
})
var deleteModal = new bootstrap.Modal(document.getElementById('stu_detele_modal'))
function openDeleteModal(){
deleteModal.show();
}
function closeDeleteModal(){
deleteModal.hide();
}
//删除提交到数据库
$("tbody").delegate("a[class='btn btn-sm btn-primary ms-2 delete']","click",function () {
openDeleteModal();
let tdList = $(this).parents('tr');
let hidCode = tdList.find('input[type="hidden"]');
let hid = hidCode.val();
$("#stu_delete_sid").val(hid);
})
$("#delete_stu_determine").click(function () {
let sid = $("#stu_delete_sid").val();
closeDeleteModal();
$.ajax({
url:"/ssmp/student/"+sid,
data:{
_method:"delete",
},
type:"POST",
success:function (data) {
var msgToast = document.getElementById('msg_toast');
var toast = new bootstrap.Toast(msgToast)
$('.toast-body').text(data.msg)
toast.show()
dataStu();
}
})
})
})
</script>
</head>
<body>
<!--头部导航-->
<ul class="bsa-header">
<!-- 移动端侧边栏toggler(不可删除) -->
<li>
<div class="bsa-nav-item bsa-mobile-sidebar-toggler-item">
<i class="bi bi-list"></i>
</div>
</li>
<!-- 空白占位符(可以让.bsa-header-item左右分布) -->
<li class="bsa-spacer"></li>
</ul>
<!--侧边栏-->
<div class="bsa-sidebar">
<div class="bsa-sidebar-header">
<img src="static/images/logo-icon.png" class="bsa-logo-icon" alt="logo-icon">
<div class="bsa-logo-text">学生管理系统</div>
</div>
<div class="bsa-sidebar-body">
<ul class="bsa-menu">
<li>
<a href="index.html" class="active">
<i class="bi bi-person"></i>学生管理
</a>
</li>
</ul>
</div>
</div>
<!--内容区域-->
<div class="bsa-main-wrapper">
<div class="bsa-main-content">
<!-- 内容都应该被包裹在此-->
<div class="container-fluid">
<div class="bsa-main-content">
<!-- 内容都应该被包裹在此-->
<div class="card border-0 shadow-sm">
<!-- 数据展示-->
<div class="card-body p-4">
<!-- 功能区-->
<div class="row row-cols-auto g-3">
<div class="col">
<a class="btn btn-outline-primary" id="add_stu" href="javascript:" role="button" ><i class="bi bi-plus"></i>新增</a>
</div>
<div class="col">
<a class="btn btn-outline-danger" href="javascript:" role="button"><i class="bi bi-x"></i>删除</a>
</div>
<div class="col-4"></div>
<div class="col">
<input class="form-control mb-3" type="text" placeholder="请输入用户名" aria-label="default input example" autocomplete="off">
</div>
<div class="col">
<a class="btn btn-light" href="javascript:" role="button"><i class="bi bi-search"></i></a>
</div>
</div>
<hr>
<!-- 新增窗口-->
</button>
<div class="modal fade" id="stu_modal" tabindex="-1" aria-hidden="true" data-bs-backdrop="static" style="display: none;">
<div class="modal-dialog modal-lg modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">添加学生</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="stu_form" action="https://v5.bootcss.com/docs/components/modal/" method="post" class="was-validated" >
<div class="row" style="margin: 10px 0px;" >
<label class="col-md-2" for="stu_name">姓名:</label>
<div class="col">
<input id="stu_name" name="sName" class="form-control" type="text" placeholder="请输入学生姓名" autocomplete="off" required >
<div class="invalid-feedback">姓名不能为空</div>
</div>
</div>
<div class="row" style="margin: 10px 0px;">
<label class="col-2" for="stu_age">年龄:</label>
<div class="col">
<input id="stu_age" name="sage" class="form-control col" type="text" placeholder="请输入学生年龄" aria-label="default input example" required autocomplete="off">
<div class="invalid-feedback">年龄不能为空</div>
</div>
</div>
<div class="row" style="margin: 10px 0px;">
<label class="col-2" >性别:</label>
<div class="col">
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="sgender" id="gender_man" checked="checked" value="0" autocomplete="off">
<label class="form-check-label" for="gender_man">男</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="sgender" id="gender_wom" value="1" autocomplete="off">
<label class="form-check-label" for="gender_wom">女</label>
</div>
</div>
</div>
<div class="row" style="margin: 10px 0px;">
<label class="col-2" for="stu_hone">电话:</label>
<div class="col">
<input id="stu_hone" name="hone" class="form-control col" type="text" placeholder="请输入学生电话" aria-label="default input example" required autocomplete="off">
<div class="invalid-feedback">电话不能为空</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
取消
</button>
<button type="button" id="add_stu_determine" class="btn btn-primary">添加</button>
</div>
</div>
</div>
</div>
<!-- 更新窗口-->
<div class="modal fade" id="stu_updata_modal" tabindex="-1" aria-hidden="true" data-bs-backdrop="static" style="display: none;">
<div class="modal-dialog modal-lg modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">修改学生</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<form id="stu_updata_form" action="https://v5.bootcss.com/docs/components/modal/" method="post" class="was-validated" >
<div class="row" style="margin: 10px 0px;" >
<label class="col-md-2" for="stu_updata_name">姓名:</label>
<div class="col">
<input type="hidden" name="sid" id="stu_updata_sid"/>
<input id="stu_updata_name" name="sName" class="form-control" type="text" placeholder="请输入学生姓名" autocomplete="off" required >
<div class="invalid-feedback">姓名不能为空</div>
</div>
</div>
<div class="row" style="margin: 10px 0px;">
<label class="col-2" for="stu_updata_age">年龄:</label>
<div class="col">
<input id="stu_updata_age" name="sage" class="form-control col" type="text" placeholder="请输入学生年龄" aria-label="default input example" required autocomplete="off">
<div class="invalid-feedback">年龄不能为空</div>
</div>
</div>
<div class="row" style="margin: 10px 0px;">
<label class="col-2" >性别:</label>
<div class="col">
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="sgender" id="gender_updata_man" checked="checked" value="0" autocomplete="off">
<label class="form-check-label" for="gender_updata_man">男</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="sgender" id="gender_updata_wom" value="1" autocomplete="off">
<label class="form-check-label" for="gender_updata_wom">女</label>
</div>
</div>
</div>
<div class="row" style="margin: 10px 0px;">
<label class="col-2" for="stu_updata_hone">电话:</label>
<div class="col">
<input id="stu_updata_hone" name="hone" class="form-control col" type="text" placeholder="请输入学生电话" aria-label="default input example" required autocomplete="off">
<div class="invalid-feedback">电话不能为空</div>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
取消
</button>
<button type="button" id="updata_stu_determine" class="btn btn-primary">修改</button>
</div>
</div>
</div>
</div>
<!-- 删除确认窗口-->
<div class="modal fade" id="stu_detele_modal" tabindex="-1" aria-hidden="true" data-bs-backdrop="static" style="display: none;">
<div class="modal-dialog modal-lg modal-dialog-centered modal-dialog-scrollable">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">删除学生</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<input type="hidden" id="stu_delete_sid" />
确定删除该学生?
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
取消
</button>
<button type="button" id="delete_stu_determine" class="btn btn-danger">删除</button>
</div>
</div>
</div>
</div>
<!-- 数据区-->
<div class="table-responsive">
<table class="table align-middle table-hover">
<thead>
<tr>
<th>
<input class="form-check-input check-all" aria-label="checkbox" type="checkbox">
</th>
<th>编号</th>
<th>姓名</th>
<th>年龄</th>
<th>性别</th>
<th>手机号</th>
<th>状态</th>
<th>操作</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
<!-- 分页控件-->
<nav aria-label="Page navigation" style="margin: 0px auto;display: table;">
<ul class="pagination">
<li class="page-item ">
<a class="page-link" href="javascript:" aria-label="Previous">
上一页
</a>
</li>
<li class="page-item ">
<a class="page-link" href="javascript:" aria-label="Next">
下一页
</a>
</li>
</ul>
</nav>
</div>
</div>
</div>
</div>
</div>
</div>
<!--操作提示消息-->
<div class="position-fixed bottom-0 end-0 p-3" style="z-index: 11">
<div id="msg_toast" class="toast fade hide" role="alert" aria-live="assertive" aria-atomic="true">
<div class="toast-header">
<strong class="me-auto">提示消息</strong>
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<div class="toast-body">
</div>
</div>
</div>
<!--调色板-->
<div class="bsa-switcher">
<div class="bsa-switcher-toggler-btn">
<div class="bsa-switcher-toggler-icon">
<i class="bi bi-gear"></i>
</div>
</div>
<div class="bsa-switcher-header">
<h5 class="bsa-switcher-header-title">自定义主题</h5>
<button type="button" class="btn-close text-reset bsa-switcher-toggler-btn"></button>
</div>
<div class="bsa-switcher-body">
<div class="row row-cols-1 g-4">
<div class="col">
<h6>头部颜色</h6>
<hr>
<div class="bsa-headercolor-wrap">
<div class="row row-cols-auto g-3">
<div class="col">
<div class="headercolor0"></div>
</div>
<div class="col">
<div class="headercolor1"></div>
</div>
<div class="col">
<div class="headercolor2"></div>
</div>
<div class="col">
<div class="headercolor3"></div>
</div>
<div class="col">
<div class="headercolor4"></div>
</div>
<div class="col">
<div class="headercolor5"></div>
</div>
<div class="col">
<div class="headercolor6"></div>
</div>
<div class="col">
<div class="headercolor7"></div>
</div>
</div>
</div>
</div>
<div class="col">
<h6>侧边栏颜色</h6>
<hr>
<div class="bsa-sidebarcolor-wrap">
<div class="row row-cols-auto g-3">
<div class="col">
<div class="sidebarcolor0"></div>
</div>
<div class="col">
<div class="sidebarcolor1"></div>
</div>
<div class="col">
<div class="sidebarcolor2"></div>
</div>
<div class="col">
<div class="sidebarcolor3"></div>
</div>
<div class="col">
<div class="sidebarcolor4"></div>
</div>
<div class="col">
<div class="sidebarcolor5"></div>
</div>
<div class="col">
<div class="sidebarcolor6"></div>
</div>
<div class="col">
<div class="sidebarcolor7"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="static/js/bootstrap.bundle.min.js"></script>
<script src="static/js/bootstrap-admin.min.js"></script>
<script src="lib/chart.js/dist/chart.min.js"></script>
<script>
new Chart(document.getElementById('chart-bar').getContext('2d'), {
type: 'bar',
data: {
labels: ['2022/7/11', '2022/7/12', '2022/7/13', '2022/7/14', '2022/7/15', '2022/7/16'],
datasets: [{
label: 'pv值',
data: [12, 19, 3, 5, 2, 3],
backgroundColor: [
'rgba(52,143,80, 0.6)',
'rgba(52,143,80, 0.6)',
'rgba(52,143,80, 0.6)',
'rgba(52,143,80, 0.6)',
'rgba(52,143,80, 0.6)',
'rgba(52,143,80, 0.6)'
],
borderColor: [
'rgba(52,143,80, 0.6)',
'rgba(52,143,80, 0.6)',
'rgba(52,143,80, 0.6)',
'rgba(52,143,80, 0.6)',
'rgba(52,143,80, 0.6)',
'rgba(52,143,80, 0.6)'
],
borderWidth: 1
}]
},
options: {
scales: {
y: {
beginAtZero: true
}
}
}
});
</script>
</body>
</html>
6、运行
运行结果:
点击新增弹出表单填写:
填写完成后点击添加弹出添加提示消息:
点击修改弹出表单,表单里的内容为此条信息的详情:
点击提交修改后,提交数据并显示提示信息:
点击删除后,会弹出确定框:
点击删除后显示提示信息: