



<dependency>
<!--Spring Boot整合Mybatis-Plus的依赖-->
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<!--Mybatis-Plus的依赖-->
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.4.0</version>
</dependency>

CREATE DATABASE mp CHARACTER SET utf8;
USE mp;
DROP TABLE IF EXISTS tb_user;
CREATE TABLE USER(
id BIGINT(20) NOT NULL COMMENT '主键ID',
NAME VARCHAR(30) NULL DEFAULT NULL COMMENT '姓名',
--null代表可以为null,默认是可以为null的,所以可以不写,即与not null(不可以为null)相反的
age INT(11) NULL DEFAULT NULL COMMENT '年龄',
email VARCHAR(50) NULL DEFAULT NULL COMMENT '邮箱',
PRIMARY KEY (id)
);
INSERT INTO USER (id, NAME, age, email) VALUES
(1, 'Jone', 18, 'test1@baomidou.com'),
(2, 'Jack', 20, 'test2@baomidou.com'),
(3, 'Tom', 28, 'test3@baomidou.com'),
(4, 'Sandy', 21, 'test4@baomidou.com'),
(5, 'Billie', 24, 'test5@baomidou.com');
<dependencies>
<!-- mybatis-plus插件依赖 -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus</artifactId>
<version>3.1.1</version>
</dependency>
<!--Mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.11</version>
</dependency>
<!--简化bean代码的⼯具包-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
</dependency>
<dependency>
<!--@Test注解的操作-->
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<!--操作日志的-->
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.6.4</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<!--maven插件,一般用来指定项目的JDK编译版本
maven好像默认使用1.5版本进行编译,这里指定1.8-->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>

log4j.rootLogger=DEBUG,A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=[%t] [%c]-[%p] %m%n
<?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>
<properties resource="jdbc.properties"></properties>
<!--environments: 运⾏环境-->
<environments default="development">
<environment id="development">
<!--当前的事务事务管理器是JDBC-->
<transactionManager type="JDBC"></transactionManager>
<!--数据源信息 POOLED:使⽤mybatis的连接池-->
<dataSource type="POOLED">
<!--虽然,会操作默认驱动,但配置不能少,可能以后可以不写,或者版本的操作-->
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--引⼊映射配置⽂件-->
<mappers>
<package name="com.lagou.mapper"></package>
</mappers>
</configuration>
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///mp
jdbc.username=root
jdbc.password=123456
#自己的数据库信息
package com.lagou.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
*
*/
@Data // 生成的方法中包括了getter setter @toString
@NoArgsConstructor //生成无参构造
@AllArgsConstructor //生成全参构造
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}

package com.lagou.mapper;
import com.lagou.pojo.User;
import java.util.List;
/**
*
*/
public interface UserMapper {
//查询所有
List<User> findAll();
}
<?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.lagou.mapper.UserMapper">
<!-- 查询所有 通过接口找到这个进行操作-->
<select id="findAll" resultType="com.lagou.pojo.User">
select * from user
</select>
</mapper>
package com.lagou.test;
import com.lagou.mapper.UserMapper;
import com.lagou.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
/**
*
*/
public class MPTest {
@Test
public void test1() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> all = mapper.findAll();
for (User user : all) {
System.out.println(user);
}
}
}
User(id=1, name=Jone, age=18, email=test1@baomidou.com)
User(id=2, name=Jack, age=20, email=test2@baomidou.com)
User(id=3, name=Tom, age=28, email=test3@baomidou.com)
User(id=4, name=Sandy, age=21, email=test4@baomidou.com)
User(id=5, name=Billie, age=24, email=test5@baomidou.com)
package com.lagou.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lagou.pojo.User;
import java.util.List;
/**
*
*/
public interface UserMapper extends BaseMapper<User> {
List<User> findAll();
}
@Test
public void test2() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-config.xml");
//这⾥使⽤的是MP中的MybatisSqlSessionFactoryBuilder,才会使得操作泛型时,进行初始化操作
//否则对应的方法是找不到的,即找不到模板
//这里的模板也可以说是对应的MappedStatement类信息(可以理解为,生成一个xml操作),后面会说明
SqlSessionFactory sqlSessionFactory = new MybatisSqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// 可以调⽤BaseMapper中定义的⽅法
List<User> all = mapper.selectList(null);
for (User user : all) {
System.out.println(user);
}
}
User(id=1, name=Jone, age=18, email=test1@baomidou.com)
User(id=2, name=Jack, age=20, email=test2@baomidou.com)
User(id=3, name=Tom, age=28, email=test3@baomidou.com)
User(id=4, name=Sandy, age=21, email=test4@baomidou.com)
User(id=5, name=Billie, age=24, email=test5@baomidou.com)
package com.lagou.pojo;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
*
*/
@Data // 包括了getter setter @toString
@NoArgsConstructor
@AllArgsConstructor
@TableName("user") //使得操作数据库表名的地方使用这个名称,是什么就是什么,即USer,就是USer
public class Userr {
private Long id;
private String name;
private Integer age;
private String email;
}
//发现,没有报错了
//那么有个疑问,在原来的时候,类是User,但表名在日志里面却是user
//而我们操作普通的mybatis时,是对应的表名(不会操作大小写,是什么就是什么)
//很明显mybatis-plus,将对应的在数据库的位置上的表名都进行小写了,那么真的一定都会小写吗
//那么在这之前,首先要知道,在数据库里面,是不区分大小写的,也就是说,当我们操作User表时
//对应的数据库位置是user,但在执行时,是不区分大小写的
//这些只要使得你是对应的名称即可,所以这就是为什么普通的mybatis可以区分大小写的原因
//但是虽然数据库不会区分大小写,可mybatis-plus在对应的位置上,却不一定是对应的小写
//实际上mybatis-plus会对数据库位置的名称进行改变,如果是User,那么就是user,这的确是小写了
//那么若是如下的情况呢
/*
若是USer,UserAge,UserageAAge,uSer,User
针对这五种情况,经过测试(看日志)
对应的表名分别是:u_ser,user_age,userage_a_age,u_ser,user
也就是说,除了首字母前面没有_外,其他的大写都会在前面加上_并变成小写,这样的操作简称为小写操作或者特殊小写(驼峰的操作)
所以通常mybatis-plus是默认来操作驼峰的,后面会说明
至此,我们操作UserageAAge,先执行一次,然后将数据库的表名修改成userage_a_age再次执行
那么会发现,第一次报错,第二次成功,至此结论正确,那么对应的@TableName("user")也是这样的吗
答:不是,他是直接的替换,而不会出现上面的操作,比如说@TableName("USer")中的USer
在数据库对应的位置中,也就是USer,而不是u_ser
那么还有一个疑问,如果不操作泛型呢,一般情况下,我们执行操作工厂时,因为MybatisSqlSessionFactoryBuilder工厂
会找当前项目所有的继承BaseMapper类型的接口
基本必须是接口,因为并没有类的操作,模板基本只是操作了语句和对应的信息在MappedStatement类里面
所以会找泛型,从而进行操作,而不操作泛型时,默认是Object,也就是说,表名是object
我测试了一下,若没有泛型,在初始化时就会报错
当然,有泛型,却不是对应的MybatisSqlSessionFactoryBuilder工厂,也会初始化报错,是同一个报错
因为都使得没有操作或者说创建模板
也就是说,必须要有泛型和对应的MybatisSqlSessionFactoryBuilder工厂
否则方法不让执行(方法那里报错了,使得没有找到,因为报错,使得后续不执行,自然也找不到对应的方法模板)
最后一个疑问,如果初始化报错了,那么自己写的findAll()方法可以执行吗,答:可以
因为初始化报错,只是使得对应的方法不能操作而已,但我们自己写的与初始化无关,还是在的,即findAll()方法可以执行
*/

<properties>
<spring.version>5.1.6.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<!--springMVC坐标,含有控制器,如DispatcherServlet-->
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<!--Spring自带的使用连接池的-->
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<!--Spring整合@Test,使得可以使用注解指定配置类或者配置文件-->
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
</dependencies>

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/mp?serverTimezone=GMT%2B8&useSSL=false
jdbc.username=root
jdbc.password=123456
<?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
http://www.springframework.org/schema/context/spring-context.xsd">
<!--引⼊properties-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--dataSource-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!--这⾥使⽤MP提供的sqlSessionFactory,完成spring与mp的整合-->
<bean id="sqlSessionFactory"
class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--扫描mapper接⼝,使用的依然是mybatis原⽣的扫描器
因为对应的接口还是需要一样的扫描的,且我们只改变了工厂-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.lagou.mapper"/>
</bean>
</beans>
package com.lagou.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
*
*/
@Data // 包括了getter setter @toString
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
package com.lagou.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lagou.pojo.User;
import java.util.List;
/**
*
*/
public interface UserMapper extends BaseMapper<User> {
List<User> findAll();
}
package com.lagou.test;
import com.lagou.mapper.UserMapper;
import com.lagou.pojo.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.List;
/**
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:applicationContext.xml")
public class TestSpringMp {
@Autowired
private UserMapper userMapper;
@Test
public void test(){
List<User> all = userMapper.selectList(null);
System.out.println(all);
//这里需要提一下,如果说,我们到表是操作了上面的特殊小写,那么表到类,就是会反过来的,所以这里要注意
//当然这是mybatis-plus的原因,而mybatis本身并不会这样
//但并不是完全一样,只是只有去除了下划线而已,因为字段不区分大小写
//所以对应的方法或者变量需要操作去掉下划线的字段
}
//findAll()现在还不能操作,因为我们没有他的配置文件,所以我们只能使用BaseMapper接口的方法
}
<dependencies>
<dependency>
<!--spring boot的基本依赖,虽然其他的依赖基本有他,所以可以不写-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<exclusions>
<exclusion>
<!--该标签一般是排除的意思(即不使用他的依赖,那么在maven中看不到了)
使得我们导入的日志文件可以起作用,而不是使用他的,即他会覆盖我们的日志依赖(只是部分主要功能,比如后面的slf4j-log4j12依赖),但不是没有使用他(优先原则)
即还是导入的
因为虽然我们导入了,但是部分主要功能被覆盖了,使得不会出现对应的打印信息
而他的,通常不会打印出对应的信息,比如后面的sql语句的信息,所以这里就进行排除了,他通常用来打印启动项目信息
我们导入的下面的日志依赖(slf4j-log4j12)基本不会有操作该信息,所以各有优点吧
注意如果有log4j.properties文件(名称必须一致),他们两个都会使用,否则没有的话,我们的slf4j-log4j12不操作,而spring-boot-starter-logging操作他自带的,所以实际上spring-boot-starter-logging也是操作日志的,否则什么日志都没有,可以删除slf4j-log4j12依赖,然后排除pring-boot-starter-logging就知道了,而slf4j-log4j12不操作也是如此
-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<!--使得可以进行测试spring boot,这个@SpringBootTest需要这个依赖-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--简化代码的⼯具包-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--mybatis-plus的springboot⽀持,类似于mybatis一样的
好像也是对应的去迎合spring boot,而不是spring boot自带的我们-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.1</version>
<!--通常说,不同的版本可能代码的显示不同,或者方法不同
比如对应的结果中where后面用()括起来,比如是3.3.2版本就会括起来
且对应的方法可能参数不一样,比如map的分页,3.3.2是纯IPage类(不加泛型的,即参数不能加泛型)
而3.1.1是IPage(子类是Page),有泛型,即参数可以加泛型,也可以不加,这里考虑了泛型的赋值,百度了解即可
3.1.1版本不会使用()括起来
但是实际上可能相同的版本显示也是不同
因为他并不是不变的(就相当于你写博客时,难道不会修改吗,所以对应的mybatis-plus的官方可能也是会修改的)
简称为维护,注意即可-->
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--操作日志-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!--maven插件-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

log4j.rootLogger=DEBUG,A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=[%t] [%c]-[%p] %m%n
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
#密码记得要正确,不要在数后面加上#,否则#当成数,而不是注释,通常说明是不要在=后面写
package com.lagou.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
*
*/
@Data // 包括了getter setter @toString
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
package com.lagou.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lagou.pojo.User;
import java.util.List;
/**
*
*/
public interface UserMapper extends BaseMapper<User> {
List<User> findAll();
}
package com.lagou;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.lagou")
public class LagouMpSpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(LagouMpSpringbootApplication.class, args);
}
}
package com.lagou;
import com.lagou.mapper.UserMapper;
import com.lagou.pojo.User;
import org.junit.jupiter.api.Test;
//该导入,会使得没有@Test也可以执行方法
//即有对应的执行按钮,只是什么都不操作,其他的作用具体到88章博客里面查看
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
class LagouMpSpringbootApplicationTests {
@Autowired
private UserMapper userMapper;
//若出现爆红,不需要管,这是idea认为没有注入,而出现的爆红
//但实际上运行时,是注入的
@Test
void testSelect() {
List<User> all = userMapper.selectList(null);
System.out.println(all);
//查询表,指定变量字段
}
//对应的findAll()方法,在这里也执行不了,虽然看不到报错(spring boot使得的)
}
log4j.rootLogger=info,A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout
log4j.appender.A1.layout.ConversionPattern=[%t] [%c]-[%p] %m%n
log4j.logger.com.lagou.mapper=TRACE

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);
IPage<T> selectPage(IPage<T> page, @Param("ew") Wrapper<T> queryWrapper);
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param("ew") Wrapper<T> queryWrapper);
}
//插⼊⼀条记录
//entity:实体对象,T一般我们都会指定,否则操作不了,因为即初始化不了
int insert(T entity);
/*
测试添加
*/
@Test
public void testInsert(){
User user = new User();
//user.setId(9l); 因为long类型,需要加上l
user.setName("哈哈哈");
user.setAge(18);
user.setEmail("zimu@jajaja");
//返回值自然也是影响行数
int insert = userMapper.insert(user); //整合spring自动提交(springboot一般包括spring的,即整合springboot基本也是一样的)
System.out.println(insert); //1
System.out.println("id值为:"+user.getId());
//1562065619489136641(其中一个,一般与时间戳有关,所以你的和我的基本是不一样的,除非能改变时间戳,所以通常这里与UUID有关)
//该值是整型的(会根据自身的值来决定类型,这是特殊的,所以这里会默认加上l,因为超过了int类型,使得认为是long类型)
//而不是字符串,也就是说,如果对应的id是String类型,那么就会报错
//且也要满足长度的原因,所以Integer也不行
//所以这里就使用Long类型了(该值认为自己是long类型,且Long默认是null,所以可以会操作生成的值进行赋值)
//否则会变成0的,所以通常在不考虑自增的前提下,这里也基本只能是Long类型
//注意:数据库的字段需要可以存放他这样的,否则不行,即会报错
//且如果不设置的字段是基本类型,那么一般默认是0,那么返回的也是0,因为不是null,即一般也会加上该字段,所以是返回0,即通常是null导致不加的
//而不加,那么就操作生成了,或者后面的不加上字段(比如查询)
//注意:对应的数据库的主键并没有操作自增,且因为大小写忽略,所以字段是不能出现同一个的
//即Id和id不可以同时存在,在创建表时就会报错,而我之所以会说明,是为了后面的解释
//我们可以看到,我们这里并没有编写主键,为什么可以运行呢:
/*
实际上,当我们没有设置id值,那么他会操作类的变量为id(忽略大小写)
然后自动的根据时间戳的值来加上(给)对应的id(赋值),在生成之前
甚至可能会检查是否有对应的id存在,但一般不会,所以可能也有冲突,但基本不会出现
在生成后,那么会放入对应的sql语句中,并且对应sql语句字段
都是类操作的变量(初始化时就决定了),然后对应的数据放在对应的位置
且变量放入对应位置时都是对应的小写操作,即Name就是name,且NAme,也是n_ame,会加上"_",即也是特别处理
但是这里的id是特例,他是什么就是什么,即ID也就是ID,前提是,没有设置策略给其他(没有覆盖)
那么id就会操作特殊小写了,如单独的给其他变量设置自增策略,那么ID也就是i_d,即也操作了特殊小写
那么如何看到sql语句呢
实际上只需要修改变量名,使得报错即可,那么就可以看到了,或者修改了log4j.properties文件,前面的修改
那么有个疑问,如果设置了id呢,那么就是使用id的
那么如果,没有id呢,且不设置对应的变量值,那么会说明,没有设置的不能操作自动的添加id值
会提示没有id,因为主键不能是null,且必须添加
如果没有id,且都设置了值,那么就是操作对应的设置值的sql语句,这时就要看数据库了
这就是对应的id细节
所以说,对应的id生成是针对于id这个数来操作的,即该策略针对于id这个数
或者说,只要你没有设置id的值,那么我帮你生成
那么为什么会设置id值呢,我们也可以说明在执行之前生成,这是一个解释,实际上也是如此
那么是如何得到返回的id值的,可以理解为执行两个sql语句,先添加,然后获取
具体的方式,我们可以参照62章博客的内容即可,更具体一点,可以理解为第二个sql操作的是
select last_insert_id()语句,该函数可以获得最后一次添加的内容的主键
由于自增一般有范围限制,可以手动添加过大的数,然后再使得自增就知道了
可能会回来,也有可能会增加上限(刷新即可,大概是显示的问题,还没有转换)
所以在一定的数量下,我们一般不会使用这个
而是使用语句进行查看或者使用自己生成的
当然通常并不会出现这种情况,因为是显示,所以也是会使用的自增的
所以那么是操作这个吗,答:不是
因为last_insert_id()函数虽然可以获得最后一次添加的内容的主键
但是自己添加的不算,也就是直接添加的不算
只能是因为自动添加的最后一个,所以也通常操作自增
自增也使得该字段必须是主键,所以整体来说是获得最后一次添加内容的主键
而这里虽然是主键,但不是自增,所以操作不了,那么既然也是操作sql语句
那么是否也是通过直接查询自动生成的id并得到(除非你已经设置过了,若设置过了自然是返回自己设置的)
还是直接将生成的赋值呢
大概率是直接将生成的赋值,因为这样是最方便的,虽然可能对应的数据库,但通过有风险
万一数据库在添加后的一瞬间被修改了,那么结果是不一致的,当然,这是正常情况,因为我们只需要返回添加的id
而不是最终稳定的结果,所以是没有影响的,所以实际上基本也就是操作一个sql语句
*/
//最后注意:后面的操作的默认的id(如顺序)中,基本都是忽略大小写的,注意即可
//或者说只要是操作id的,那么基本都是忽略大小写,这里也称为id数(id数:表示该id忽略大小写)
}

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.baomidou.mybatisplus.annotation;
public enum IdType {
//数据库ID⾃增
AUTO(0),
//该类型为未设置主键类型
NONE(1),
//⽤户输⼊ID,该类型可以通过⾃⼰注册⾃动填充插件进⾏填充
INPUT(2),
//以下3种类型、只有当插⼊对象ID 为空,才⾃动填充
//全局唯⼀ID (idWorker),这个是默认的策略
ID_WORKER(3),
//全局唯⼀ID (UUID)
UUID(4),
//字符串全局唯⼀ID (idWorker 的字符串表示)
ID_WORKER_STR(5);
private final int key;
private IdType(int key) {
this.key = key;
}
public int getKey() {
return this.key;
}
}
package com.lagou.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
*
*/
@Data // 包括了getter setter @toString
@NoArgsConstructor
@AllArgsConstructor
public class User {
//使得初始化时,操作自增的策略,即AUTO(0),而不是默认的全局唯一ID策略,即ID_WORKER(3)
@TableId(type = IdType.AUTO)
private Long id;
private String name;
private Integer age;
private String email;
//只要我们设置了策略,那么默认的策略就变成了该策略,无论是哪个变量设置,即对方也变成了该设置的对应
//当然默认的是默认操作id数
//这里需要我们指定给谁设置
}
/*
比如
INSERT INTO USER (id, NAME, age, email) VALUES (1, 'Jone', 18, 'test1@baomidou.com')
变成了
INSERT INTO USER (NAME, age, email) VALUES ('Jone', 18, 'test1@baomidou.com')
id不参与了,当然,他并不是只操作id(不与前面的生成策略一样),所以其他字段也可以加上该注解
只是该注解只会使得第一个操作的注解起作用,其他的注解并不会起作用,所以其他字段就算加上了
也会参与字段,所以相当于没有该注解
最后注意:当没有设置值时,默认null的值,不会加上字段
因为这里默认是null,若默认不是null,那么会加上,如将age修改成int类型,那么就会加上,因为默认是0,而不是null
除了策略的作用,如前面的全局唯一ID策略,操作的id,我们实际上可以认为
默认给类的id数加上全局唯一ID策略的,除非没有id数,或者除非有其他策略,那么就不会加上
因为可以通过判断是否是null,而操作是否加上,这里可以参照62章博客即可
*/
package com.lagou.pojo;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
*
*/
@Data // 包括了getter setter @toString
@NoArgsConstructor
@AllArgsConstructor
public class User {
@TableId(type = IdType.AUTO)
private Long id;
@TableField(select = false) //表示查询时,不返回该字段的值,即不在对应位置加上该字段变量,true则加上
//所以导致不会返回
//注意是查询,即操作查询时,不加上对应的该字段,但是其他的操作如添加会加上
//相当于部分的@TableField(exist = false) ,即这里只操作查,增删改不操作,即增删改会加上该字段
//而查询不会加上该字段
private String name;
private Integer age;
@TableField(value = "eMail") //解决字段名不一致问题
private String mail;
//在sql语句中是这样的:eMail AS mail
//也就是说,eMail是什么就是什么(没有特使的操作,如小写操作)
//且变量变成了别名
@TableField(exist = false)
//表示该字段,在数据库表中不存在,实际上是不加上对应的字段,所以这样说明,默认为true,而设置为false时
//自然不会在初始化时,在对应的位置加上
//而设置为true时,由于数据库没有该字段,那么当然是报错的(数据库没有该字段)
//他作用与所有的操作,即增删改查
private String address;
}
/*
现在我们将关键信息放在这里,以后基本需要参照这些信息
类名对应表名,按照特殊小写(或者说小写操作)触发
变量对应字段,按照特殊小写(或者说小写操作)触发
其他的设置名称,或者操作对应策略的字段(比如全局唯一ID策略操作的变量)
那么是什么就是什么(如USer,就是USer,而不是u_ser)
没有设置值或者操作了对应策略(比如自增策略),那么默认不加上字段
除了操作的字段(比如全局唯一ID策略操作的变量)
特殊小写:NAme,就是n_ame,User,就是user,UsEr,就是us_er
在mybatis-plus里面,上面的解释,基本贯彻所有
*/
//根据 ID 修改
//entity:实体对象
int updateById(@Param("et") T entity);
/*
测试根据id进行修改
*/
@Test
public void testUpdateById(){
User user = new User();
user.setId(6l); //主键,这里是where后面的条件
user.setAge(30); //更新的字段,这里是set后面的条件
//上面针对于更新(update)
//将id为6的age修改成30
int i = userMapper.updateById(user);
System.out.println(i);
//符合不设置,不加字段
}
//注意:这里有个细节,我们是怎么确定,对应的where后面的字段的,答:根据顺序来
//顺序是:策略设置者-id数-null
//后面大多数都是这个顺序,除了特别的会使得报错外(如AR的添加和更新并存的insertOrUpdate方法)
//即我们给那个字段操作了策略,那么后面的就是那个字段,比如这里就是id
//那么同样的,如果给name设置该策略(该策略是自增策略,所以只会第一个生效),那么后面的就是name
//如果都没有设置,那么默认为id变量,没有id变量,默认是null字段,而默认是null时,那么自然的,是必定失败的
//因为我们虽然设置了值,但是获得值时,一般需要对应的get方法或者赋值,具体看对应的61章博客介绍的#{}
//而没有null这个变量,因为不能创建,自然报错
//根据条件,更新记录
//entity:实体对象 (set 条件值,可以为 null,前提是另外一个参数操作了set,否则会报错,语法错误)
//因为sql语句中"update 表名"后面基本需要指定set才可
//即UpdateWrapper类可以为null,但QueryWrapper类不可以为null
//updateWrapper:实体对象封装操作类,可以为 null,那么就相当于代表所有的进行修改
//⾥⾯的 entity(T,也是实体对象,只是统称为entity)
//通常⽤于⽣成 where 语句或者set语句
int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper);
/*
测试根据条件进行修改
*/
@Test
public void testUpdate(){
User user = new User();
//更新的字段
user.setAge(35);
//更新的条件
//QueryWrapper是Wrapper的子类,基本只能操作where后面的条件
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper.eq("name","解决");
//eq代表操作"=",即这里相当于name="解决"的条件
int i = userMapper.update(user,objectQueryWrapper);
System.out.println(i);
//将name为"解决"的age修改成35
}
//即where后面的是name,set后面的是age,其中name是什么就是什么,当然age是操作特殊小写的
//最后注意:set后面不会操作id,即自动的不加上id这个数,所以这里设置id的值没有用
//或者说,因为我们指定了条件,所以覆盖了id条件
/*
测试根据条件进行修改
*/
@Test
public void testUpdate2(){
//换一个对象,UpdateWrapper是Wrapper的子类
//该对象不止可以操作where后面的条件,也可以操作set后面的条件
UpdateWrapper<User> objectUpdateWrapper = new UpdateWrapper<>();
objectUpdateWrapper.eq("id","6").set("age",40);
int i = userMapper.update(null,objectUpdateWrapper);
System.out.println(i);
//将id为6的age修改成40,即这里的eq变成了where后面的条件,后面的set方法设置set后面的条件
//注意:这里的id可以写在set后面,不会不加上
}
//根据 ID 删除
//id:主键ID
int deleteById(Serializable id);
/*
根据id进行删除
*/
@Test
public void testDeleteById(){
int i = userMapper.deleteById(6l);
System.out.println(i);
}
//注意:在有设置策略时,默认设置的那个策略为where后面的条件操作,否则默认为类里面的id变量为对应的条件
//其中6l就是对应id的值,只要符合方法参数和数据库类型基本就可以
//根据 columnMap 条件,删除记录
//columnMap:表字段的 map 对象
int deleteByMap(@Param("cm") Map<String, Object> columnMap);
/*
根据columnMap进行删除
*/
@Test
public void testDeleteByMap(){
HashMap<String, Object> map = new HashMap<>();
map.put("name","哈哈哈");
map.put("age",35);
//name在前,age在后,按照顺序(ASCII大小,大的在前),后面或者前面默认也是如此,注意即可
//一般操作map的基本都是如此,因为map自动的排序了,所以根据map从头到尾获取,自然是先操作name
//将map集合当成条件(where后面的)来进行删除,而不是使用类的变量,当然,多个条件自然也是and连接的关系
int i = userMapper.deleteByMap(map);
System.out.println(i);
}
//那么有个疑问,如果值是null呢,那么加上IS NULL,如name IS NULL
//前面或者后面也是如此(下面或者上面的介绍也是)
//这是操作map集合的作用
//其他的如eq,gt,set,等等,结果就是null(字符串),而没有操作IS NULL,即以name为例,就是
//name = null,name >null,set name = null这三种情况
//若是类的设置为null,那么不会加上字段
//实际上无论你设置什么,结果是以字符串的形式出发的,只是类和map会进行检查而已
//根据 Wrapper条件,删除记录
//entity:(实体类,或者T泛型的解释)
//wrapper:实体对象封装操作类(可以为 null,那么相当于没有条件,即也就是相当于删除所有)
int delete(@Param("ew") Wrapper<T> wrapper);
/*
根据wrapper进行删除
*/
@Test
public void testDelete(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
//操作where后面的条件
objectQueryWrapper.eq("name","33").eq("age",18);
// User user = new User();
// user.setName("33");
// user.setAge(18);
// QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>(user);
// //这样也可,相当于objectQueryWrapper.eq("name","33").eq("age",18);
//执行类似的eq方法或者gt方法,通常会返回调用者objectQueryWrapper,使得可以继续操作
// //只是对应的字段会操作特殊小写,因为是类
int i = userMapper.delete(objectQueryWrapper);
System.out.println(i);
}
//我们可以发现,为什么明明是调用方法,为什么会使得对应的MappedStatement类信息发生了改变(字段变化)
//可能是方法也会在执行之前,修改MappedStatement类信息吧
//删除(根据ID 批量删除)
//idList:主键ID列表(不能为 null 以及 empty)
//而之所以不能是empty或者null,那是因为in后面括号里基本识别不了,或者在执行前就操作了异常
int deleteBatchIds(@Param("coll") Collection<? extends Serializable> idList);
/*
根据deleteBatchIds进行删除
*/
@Test
public void testdeleteBatchIds(){
int i = userMapper.deleteBatchIds(Arrays.asList(17,18));
System.out.println(i);
//删除17和18这两个数据,该deleteBatchIds一般操作的是in来进行删除,如in(17,18)
//对应操作的where后面的字段,也是根据前面说的:策略设置者-id数-null
}
//id:主键ID
T selectById(Serializable id);
/*
根据id进行查询
*/
@Test
public void testSelectById(){
User user = userMapper.selectById(15);
System.out.println(user);
//对应操作的where后面的字段,也是根据前面说的:策略设置者-id-null
}
//idList:主键ID列表 (不能为 null 以及 empty)
List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);
/*
根据id进行批量查询
*/
@Test
public void testSelectBatchIds(){
List<User> users = userMapper.selectBatchIds(Arrays.asList(4, 5));
System.out.println(users);
//也是操作in
}
//根据 columnMap 条件,查询记录
//columnMap:表字段的 map 对象
List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);
/*
根据map集合进行查询
*/
@Test
public void testSelectByMap(){
HashMap<String, Object> map = new HashMap<>();
map.put("name","Jack");
List<User> users = userMapper.selectByMap(map);
System.out.println(users);
//也是将map集合看成条件,是什么就是什么
}
//根据 条件,查询⼀条记录
//entity(实体类,或者T泛型的解释)
//queryWrapper:实体对象封装操作类(可以为 null)
T selectOne(@Param("ew") Wrapper<T> queryWrapper);
/*
测试SelectOne
*/
@Test
public void testSelectOne(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper.eq("name","Jack");
//根据条件查询一条记录并返回,如果超过一条记录,会报错,大概是判断查询的结果,进行操作异常造成的
User user = userMapper.selectOne(objectQueryWrapper);
System.out.println(user);
}
//根据 Wrapper条件,查询总记录数
//queryWrapper:实体对象封装操作类(可以为 null)
Integer selectCount(@Param("ew") Wrapper<T> queryWrapper);
/*
根据 Wrapper条件,查询总记录数
*/
@Test
public void testSelectCount(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper.gt("age","18"); //操作大于18的数据
//gt代表操作>,即这里相当于age>18的条件
//对应的方法只要不是set,那么基本都是操作where后面的操作
//返回总条数
int user = userMapper.selectCount(objectQueryWrapper);
System.out.println(user);
//相当于操作了count(1),所以可以查询所有记录,具体的可以到36章博客进行查看
}
//根据 queryWrapper 条件,查询全部记录
//queryWrapper:实体对象封装操作类(可以为 null)
List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);
/*
根据 Wrapper条件,查询满足条件的所有记录
*/
@Test
public void testSelectList(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper.gt("age","18"); //操作大于18的数据
//返回满足条件的所有记录
List<User> user = userMapper.selectList(objectQueryWrapper);
System.out.println(user);
}
//根据 queryWrapper 条件,查询全部记录,并放入map集合中
//queryWrapper:实体对象封装操作类(可以为 null)
List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper);
/*
根据Wrapper条件,查询满足条件的所有记录,并将根据条件的查询的结果放入map集合中
*/
@Test
public void testSelectMaps(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper.gt("age","18"); //操作大于18的数据
//将所有的满足条件的所有记录,并放入map集合中
//而不是类中(前面操作的都是类,所以就没有进行数码,这里有点特别,就说明一下)
List<Map<String, Object>> user = userMapper.selectMaps(objectQueryWrapper);
for(Map m : user) {
System.out.println(m);
}
//注意:查询的结果根据对应的ASCII进行排序,一般越大,越在前,如
/*
{mail=test3@baomidou.com, id=3, age=28}
{mail=test4@baomidou.com, id=4, age=21}
{mail=test5@baomidou.com, id=5, age=24}
m的最大的,然后是i,最后是a,可以试着将m修改成b,那么就是如下:
{id=3, bail=test3@baomidou.com, age=28}
{id=4, bail=test4@baomidou.com, age=21}
{id=5, bail=test5@baomidou.com, age=24}
这时就是i>b>a了,发现的确如此
*/
}
//根据 queryWrapper 条件,查询全部记录,并根据顺序:操作策略者-id数-查询条件的第一个,来进行返回数据
//queryWrapper:实体对象封装操作类(可以为 null)
List<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper);
/*
根据Wrapper条件,将满足条件的所有记录中,根据顺序:操作策略者-id数-查询条件的第一个,来进行返回数据
*/
@Test
public void testSelectObjs(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper.gt("age","18"); //操作大于18的数据
//将满足条件的所有记录中,根据顺序:
//"操作策略者(也就是设置策略者)-id数-查询条件的第一个",来进行返回数据
List<Object> list = userMapper.selectObjs(objectQueryWrapper);
for(Object m : list) {
System.out.println(m);
}
//即上面的m就是查询的所有id的值,当成一个集合,根据顺序,这里也就是id
}
//page:分⻚查询条件
//queryWrapper:实体对象封装操作类(可以为 null)
//实际上queryWrapper条件一般是操作where后面的,当然,有些他的子类,是可以操作set
//如QueryWrapper是Wrapper的子类,基本只能操作where后面的条件
//但UpdateWrapper虽然也是Wrapper的子类,该对象不止可以操作where后面的条件,也可以操作set后面的条件
IPage<T> selectPage(IPage<T> page, @Param("ew") Wrapper<T> queryWrapper);
package com.lagou.config;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
*
*/
@Configuration
public class MybatisPlusConfig {
/*
分页插件
*/
@Bean
public PaginationInterceptor paginationInterceptor(){
return new PaginationInterceptor();
}
}
//该分页插件,使得数据合理化,即对应的总条数或者总页数使得计算
//并计算得出当前页数据,否则一般会使得当前页数的那个方法,是总数据,即不会计算
//在测试时,可以试着将@Configuration注释掉,再次测试即可知道了
//在mybatis-plus里面一般需要这个,他的底层需要这个操作分页的计算
//若不操作注入的方式,那么需要给mybatis添加插件来使得计算
//即使用<plugins>标签,后面会说明,或者在62章博客里查看
//而之所以可以操作注入,是因为对应的PaginationInterceptor类是mybatis-plus里面的
//整合了spring boot,并且有对应的分页操作,他可以操作类和配置
//而mybatis的PageHelper分页,一般只能是配置才可计算,就算整合了spring也差不多也要配置,而不是类
//当然我们也可以自己操作插件,使得,可以操作配置或者类,后面插件部分会进行说明
/*
分页查询
*/
@Test
public void testSelectPage(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper.gt("age","18"); //操作大于18的数据
//参数1:当前页,参数2,每页显示条数
Page<User> page = new Page(1,2); //Page是对应的mybatis-plus里面的
//这里Page<User>可以不指定泛型,直接的Page也可,反正是赋值,只看对象
//但指定了泛型,需要是对应的类型,否则报错
//记住泛型只在编译期起作用,运行期不区分什么类型了,相当于Object了
//单纯的不指定,可以赋值指定的,因为默认结合,而不能是已经指定不同的了,所以可以这样写
IPage<User> iPage = userMapper.selectPage(page, objectQueryWrapper);
System.out.println("总条数:" + iPage.getTotal());
System.out.println("总页数:" + iPage.getPages());
System.out.println("当前页的分页数据:" + iPage.getRecords());
}
//page:分⻚查询条件
//queryWrapper:实体对象封装操作类(可以为 null)
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param("ew") Wrapper<T> queryWrapper);
/*
分页查询,只是数据是用map集合保存,而不是类
*/
@Test
public void testSelectMapsPage(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper.gt("age","18"); //操作大于18的数据
//参数1:当前页,参数2,每页显示条数
Page<User> page = new Page(1,2);
IPage<Map<String, Object>> mapIPage = userMapper.selectMapsPage(page, objectQueryWrapper);
System.out.println("总条数:" + mapIPage.getTotal());
System.out.println("总页数:" + mapIPage.getPages());
System.out.println("当前页的分页数据:" + mapIPage.getRecords()); //显示的是map集合的数据
//也按照"操作策略者(也就是设置策略者)-id数-查询条件的第一个",来进行返回数据
}

/*
在测试类LagouMpSpringbootApplicationTests中,找到如下:
根据id进行查询
@Test
public void testSelectById(){
断点到这里,并不是他执行了,而是准备执行(需要你点击下一步来执行)
不要以为对应的初始化是该方法的执行,注意即可
在这里打上断点 User user = userMapper.selectById(15);
System.out.println(user);
//对应where后面的也是:指定策略者-id-null
}
使用ctrl+n,找到ISqlInjector,并找到他的实现类AbstractSqlInjector,如下:
public void inspectInject(MapperBuilderAssistant builderAssistant, Class<?> mapperClass) {
在这里打上断点 Class<?> modelClass = this.extractModelClass(mapperClass);
if (modelClass != null) {
String className = mapperClass.toString();
Set<String> mapperRegistryCache =
GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());
if (!mapperRegistryCache.contains(className)) {
List<AbstractMethod> methodList = this.getMethodList();
if (CollectionUtils.isNotEmpty(methodList)) {
TableInfo tableInfo = TableInfoHelper.initTableInfo(builderAssistant,
modelClass);
methodList.forEach((m) -> {
m.inject(builderAssistant, mapperClass, modelClass, tableInfo);
});
} else {
logger.debug(mapperClass.toString() + ", No effective injection method was
found.");
}
mapperRegistryCache.add(className);
}
}
}
断点打完后,执行testSelectById(debug运行),会发现,首先跳转到
Class<?> modelClass = this.extractModelClass(mapperClass);
我们下一步,看看modelClass,发现,就是我们的实体类对象class信息
继续下一步
String className = mapperClass.toString();
返回我们接口的地址信息和其他信息,如他是一个接口
继续下一步:
Set<String> mapperRegistryCache =
GlobalConfigUtils.getMapperRegistryCache(builderAssistant.getConfiguration());
一般是看看是否有对应的mapperClass.toString();结果值,通常情况下,是操作缓存的
List<AbstractMethod> methodList = this.getMethodList();
拿到方法集合,是对应的BaseMapper接口的所有方法(是不包含他的子类的)
一般有17个,所以methodList的size也是17,具体方法调用的类是DefaultSqlInjector
但是该顺序,却不是对应的BaseMapper接口的方法顺序,这里要注意,因为接口方法的顺序,并不完整
找到这里
methodList.forEach((m) -> {
m.inject(builderAssistant, mapperClass, modelClass, tableInfo);
});
该m代表循环上面的17个操作
进入inject(先手动在里面打断点进入),否则一般进入不了:
public void inject(MapperBuilderAssistant builderAssistant, Class<?>
mapperClass,
Class<?> modelClass, TableInfo tableInfo) {
this.configuration = builderAssistant.getConfiguration();
this.builderAssistant = builderAssistant;
this.languageDriver = this.configuration.getDefaultScriptingLanguageInstance();
this.injectMappedStatement(mapperClass, modelClass, tableInfo);
}
先手动点击 this.injectMappedStatement(mapperClass, modelClass, tableInfo);
参数分别是:接口地址信息和其他信息,实体类对象class信息,关于表的信息
进入public abstract MappedStatement injectMappedStatement(
Class<?> mapperClass, Class<?> modelClass, TableInfo tableInfo);
我们看他谁操作了他,发现,是对应的方法名称的类操作了他
如图:
*/

/*
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo
tableInfo) {
在这里打上断点 SqlMethod sqlMethod = SqlMethod.LOGIC_SELECT_BY_ID;
SqlSource sqlSource = new RawSqlSource(this.configuration, String.format(sqlMethod.getSql(),
this.sqlSelectColumns(tableInfo, false), tableInfo.getTableName(), tableInfo.getKeyColumn(),
tableInfo.getKeyProperty(), tableInfo.getLogicDeleteSql(true, false)), Object.class);
return this.addSelectMappedStatement(mapperClass, sqlMethod.getMethod(), sqlSource,
modelClass, tableInfo);
}
打上断点后,进行点击下一个断点跳转即可(而不是下一步)
我们可以先看看LOGIC_SELECT_BY_ID的值,发现,有个模板,然后后面的代码
就是通过表信息进行补全模板,表信息可以看成是对应的类和接口地址操作成的
然后放入对应的MappedStatement类信息中(后面的return后面的代码的操作),使得可以操作数据库
所以说,若根据结果,模板也可以说是MappedStatement类信息,虽然是对应的语句
为了验证,我们直接看看sqlSource的值,继续下一步,可以发现对应的值(sql部分)
就是一个完整的sql了(且对应的字段值,在等待我们输入)
即User user = userMapper.selectById(15);中的15就是我们输入的值
且包含了其他的信息
使得普通的mybatis操作的xml放入MappedStatement类信息中得到的类似的信息
也就是说sqlSource他相当于sql语句的信息和其他信息,就如jdbc里面的连接一样
而返回的就是运行后的结果,类似于代码平台,准备调用
至此,大致解析完毕,的确是得到了对应的信息放入MappedStatement类信息中
使得我们不需要编写对应的xml了
至此,当这些初始化完毕后,到下一个断点,直到在
根据id进行查询
@Test
public void testSelectById(){
到这里了,之前打的断点 User user = userMapper.selectById(15);
System.out.println(user);
//对应where后面的也是:指定策略者-id-null
}
那么他的执行,也就是操作了数据库语句的执行,至此,得到了数据
那么最后有个疑问,初始化是否在在对应的工程那里进行了,答:是的,工厂那么就是上面的执行流程
所以前面也说是初始化的操作
*/
<?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>
<plugins>
<plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor">
</plugin>
</plugins>
</configuration>
#加载全局配置文件
mybatis-plus.config-location = classpath:mybatis-config.xml
#那么运行Spring Boot时,会帮我们进行加载,并读取配置,从而操作,相当于下面的配置
#因为这里Spring Boot与Mybatis-plus是整合的,所以可以操作得到,相当于下面的配置
<bean id="sqlSessionFactory"
class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!--继续操作对应的配置,进行读取操作-->
/*
自定义findById方法
*/
public User findById(Integer id);
<?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.lagou.mapper.UserMapper">
<select id="findById" resultType="com.lagou.pojo.User">
select * from user where id = #{id}
</select>
</mapper>
#加载映射配置文件
mybatis-plus.mapper-locations = classpath*:mapper/*.xml
#classpath*也会扫描其他的classes目录(比如其他的jar包),而classpath只扫描当前的classes目录
<bean id="sqlSessionFactory"
class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="mapperLocations" value="classpath*:mapper/*.xml"/>
</bean>
#使得扫描接口,进而扫描配置
mybatis-plus.mapper-locations = classpath*:com.lagou.mapper
#对应的单独的mybatis,也可以是这样,测试过了,首先优先看对应的mapper是否是类,是类,则操作该类,否则操作该包
/*
测试自定义的findById方法
*/
@Test
public void findById(){
User byId = userMapper.findById(1);
System.out.println(byId);
//注意:他操作的是自己写好的语句,不是我们放入对应的位置的类变量操作
//所以基本不会操作类的信息(如特殊小写)
}
# 起别名
mybatis-plus.type-aliases-package = com.lagou.pojo
<bean id="sqlSessionFactory"
class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="typeAliasesPackage" value="com.baomidou.mybatisplus.samples.quickstart.entity"/>
</bean>
<select id="findById" resultType="User">
<!--直接写User,即可,而不用写对应的地址,忽略大小写,即uSer也可-->
select * from user where id = #{id}
</select>
#关闭自动驼峰映射,该参数不能和mybatis-plus.config-location同时存在
mybatis-plus.configuration.map-underscore-to-camel-case=false
#也操作了类到表,不只是表到类
#所以设置为false时,那么对应的就不会特殊小写了,即aGe,就是aGe,是什么就是什么了,而不是a_ge了
#那么既然不能和mybatis-plus.config-location同时存在,也就使得不能操作全局配置文件了
#那么为什么不能共存呢,因为该配置是操作mybatis-plus.config-location里面的
#即在mybatis-plus.config-location里面,我们操作spring时,是写在里面的,所以看起来是共存了
#但这里的共存,很明显是可以写两个的,所以如果对应的mybatis-plus.config-location配置里也有该配置
#即mybatis-plus.configuration.map-underscore-to-camel-case配置
#那么就可能会覆盖或者冲突,所以为了不出现这样的操作,则直接报异常即可
#如果我们需要对应的mybatis-plus.config-location配置
#我们在里面写mybatis-plus.configuration.map-underscore-to-camel-case配置即可,而不是直接的配置
<settings>
<setting name="mapUnderscoreToCamelCase" value="false"/>
</settings>
<!--这里操作全局配置的,且根据顺序,要在<plugins>标签之前
若在mybatis-plus中,则操作表到类的同时,也操作了类到表-->
mybatis-plus.configuration.cache-enabled=false
#注意:配置后,默认使得类(基本是所有的类,但要注意是操作的泛型类)里面的id数(id数:表示该id忽略大小写)
#是设置了@TableId(type = IdType.AUTO)配置
mybatis-plus.global-config.db-config.id-type=auto
<bean id="sqlSessionFactory"
class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--下面的是主要部分,一般只能操作mybatis-plus,因为对应的包就是他里面的
且只能操作mybatis-plus工厂,因为对应的mybatis没有下面的操作,甚至没有globalConfig变量-->
<property name="globalConfig">
<bean class="com.baomidou.mybatisplus.core.config.GlobalConfig">
<property name="dbConfig">
<bean class="com.baomidou.mybatisplus.core.config.GlobalConfig$DbConfig">
<property name="idType" value="AUTO"/>
</bean>
</property>
</bean>
</property>
</bean>
#给表名加上前缀"tb_"
mybatis-plus.global-config.db-config.table-prefix=tb_
<bean id="sqlSessionFactory"
class="com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<!--下面的是主要部分,一般只能操作mybatis-plus,因为对应的包就是他里面的
且只能操作mybatis-plus工厂,因为对应的mybatis没有下面的操作,甚至没有globalConfig变量-->
<property name="globalConfig">
<bean 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="tb_"/>
</bean>
</property>
</bean>
</property>
</bean>

allEq(Map<R, V> params)
allEq(Map<R, V> params, boolean null2IsNull)
allEq(boolean condition, Map<R, V> params, boolean null2IsNull)
/*
参数说明:
params:key为数据库字段名,value为字段值
null2IsNull:为true则在map的value为null时调⽤isNull的显示⽅法,为false时则忽略value为null的
不写默认为true
例1:allEq({id:1,name:"⽼王",age:null})--->id = 1 and name = '⽼王' and age is null
例2:allEq({id:1,name:"⽼王",age:null}, false)--->id = 1 and name = '⽼王'
condition:代表是否加入最后生成的sql中
*/
/*
测试条件构建器,allEq
map当条件
*/
@Test
public void testAllEq(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
//构建map
HashMap<String, Object> map = new HashMap<>();
map.put("name","Jack");
map.put("age",null);
//sql语句的where部分是:WHERE name = ? AND age IS NULL
objectQueryWrapper.allEq(map);
//加入后,相当于之前操作的eq,变成了操作map集合,这也就使得可以操作IS NULL
//使得之前我们基本只能可以使用selectByMap方法操作IS NULL,变成了都可以操作IS NULL了
//即都可以操作map了
List<User> users = userMapper.selectList(objectQueryWrapper);
System.out.println(users);
//注意:一个不进行任何操作的QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
//那么相当于null作为参数,其他可以操作null的基本也是如此(可能也不一定是该对象),注意即可
}
/*
测试条件构建器,allEq
多次添加
*/
@Test
public void testAllEq2(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
//构建map
HashMap<String, Object> map = new HashMap<>();
map.put("name","Jack");
map.put("age",null);
//多次执行
objectQueryWrapper.allEq(map);
map.put("agee",null);
objectQueryWrapper.allEq(map);
//sql语句的where部分是:
//WHERE name = ? AND age IS NULL AND name = ? AND agee IS NULL AND age IS NULL
//即全部往后添加
List<User> users = userMapper.selectList(objectQueryWrapper);
System.out.println(users);
}
/*
测试条件构建器,allEq
是否忽略null
*/
@Test
public void testAllEq3(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
//构建map
HashMap<String, Object> map = new HashMap<>();
map.put("name","Jack");
map.put("age",null);
//sql语句的where部分是:WHERE name = ?,IS NULL被忽略,true则不会
objectQueryWrapper.allEq(map,false);
List<User> users = userMapper.selectList(objectQueryWrapper);
System.out.println(users);
}
/*
测试条件构建器,allEq
是否添加
*/
@Test
public void testAllEq4(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
//构建map
HashMap<String, Object> map = new HashMap<>();
map.put("name","Jack");
map.put("age",null);
objectQueryWrapper.allEq(map);
map.put("agee",null);
objectQueryWrapper.allEq(false,map,true);
//sql语句的where部分是:WHERE name = ? AND age IS NULL
//后面的没有再加入了,注意:必须是三个参数的,两个的该参数好像没有,即allEq(false,map)没有
//所以后面我指定true,当然,false也可以
List<User> users = userMapper.selectList(objectQueryWrapper);
System.out.println(users);
}
/*
测试条件构建器,allEq
是否添加(操作都不添加)
*/
@Test
public void testAllEq5(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
//构建map
HashMap<String, Object> map = new HashMap<>();
map.put("name","Jack");
map.put("age",null);
objectQueryWrapper.allEq(false,map,true);
//由于没有条件了,那么
//sql语句是:SELECT id,age,eMail AS mail FROM user
//没有where了(这里就给出全部,当然,对应的字段是我的设置),也就是没有条件,这是自然的
List<User> users = userMapper.selectList(objectQueryWrapper);
System.out.println(users);
}
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)
/*
参数说明:
filter:过滤函数,是否允许字段传⼊⽐对条件中
例1: allEq((k,v) -> k.indexOf("a") >= 0, {id:1,name:"⽼王",age:null})--->
name = '⽼王' and age is null
例2: allEq((k,v) -> k.indexOf("a") >= 0, {id:1,name:"⽼王",age:null}, false)--->
name = '⽼王'
使得key有a这个字母,那么就添加对应条件
params 与 null2IsNull 与 condition:同上,即与前面一样
*/
/*
测试条件构建器,allEq
过滤操作
*/
@Test
public void testAllEq6(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
//构建map
HashMap<String, Object> map = new HashMap<>();
map.put("name","Jack");
map.put("age",null);
objectQueryWrapper.allEq((k,v)->!k.equals("name"),map,true);
//我们将k看成map集合的key值,过滤所有的map数据
//只要对应的返回是false,那么不会加上对应的key-value条件
//即这里就是,如果对应的key是name,那么返回false,即不加上条件
//所以这里sql语句的where部分是::WHERE age IS NULL
//没有name的对应条件了
List<User> users = userMapper.selectList(objectQueryWrapper);
System.out.println(users);
}
/*
基本比较操作
*/
@Test
public void testWrapper(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper.eq("name","哈哈")
.ge("age","20") //这个就算是20,不是"20",那么最终都会变成字符串,即是一样的
.in("name","解决","拒绝");
List<User> users = userMapper.selectList(objectQueryWrapper);
System.out.println(users);
//sql语句的where部分是:
//WHERE name = ? AND age >= ? AND name IN (?,?)
//很明显是根据调用的方法顺序来进行的操作,所以是先name,然后age,再然后就是in方法的name操作
}
/*
基本比较操作
总的合并
*/
@Test
public void testWrapper2(){
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper
.eq("name","1")
.ne("name","2")
.gt("name","3")
.ge("name","4")
.lt("name","5")
.le("name","6")
.between("name","7","8")
.notBetween("name","9","10")
.in("name","11","12")
.notIn("name","13","14");
//注意:对应的范围若使用String类型(最好使用Integer类型),那么最好不要是特殊的,比如"100"
//特殊有很多,这里只给出一个,具体看35章博客的说明
//因为在sql里面会导致失败,所以mybatis-plus会使得改变sql语句(有前提,看后面的前提)
//将select 和from中间的字段,全部删除,加上COUNT(1),与COUNT(*)一样,查询有多少记录,一般是查询所有
//因为会过滤不与他相关的(列名的操作)
//其中改变sql语句的前提是,操作分页(一般是对应的方法操作的)
//且存在分页插件(其他的插件可能也可以),否则不会改变sql语句
//即该前提没有满足,那么就算你操作了特殊的,也不会改变sql语句,所以要改变sql语句
//首先,先操作到特殊,然后满足前提,否则只要有一个没有满足,那么就不会改变
List<User> users = userMapper.selectList(objectQueryWrapper);
System.out.println(users);
//sql语句的where部分是:
/*
WHERE name = ? AND name <> ? AND name > ? AND name >= ? AND name < ?
AND name <= ? AND name BETWEEN ? AND ? AND name NOT BETWEEN ? AND ?
AND name IN (?,?) AND name NOT IN (?,?)
*/
//从这里,应该很清楚,正是因为对应的是字段,所以不能随便写
//如a,b,等等,需要是表里面有的字段,即name
//所以也并不是什么条件都可以操作,前提是字段正确,当然,map也是一样的
}
/*
模糊查询
*/
@Test
public void testWrapper3() {
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper
.like("name", "jaja")
.notLike("name", "jj")
.likeLeft("name", "aa")
.likeRight("name", "bb");
List<User> users = userMapper.selectList(objectQueryWrapper);
System.out.println(users);
//sql语句的where部分是:
//WHERE name LIKE ? AND name NOT LIKE ? AND name LIKE ? AND name LIKE ?
//值:Parameters: %jaja%(String), %jj%(String), %aa(String), bb%(String)
//那么为什么没有操作单独的值呢,的确没有,因为,eq不就可以了吗
//为什么要加一个相同操作呢,所以就没有了
//最后注意:当没有加上任何方法,时,那么自然,也就是没有where后面的操作
//也就是相当于没有条件,到表名就结束了,即 SELECT id,age,eMail AS mail FROM user (我这里是)
}
/*
排序查询
*/
@Test
public void testWrapper4() {
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper.orderBy(true,false,"name","name");
List<User> users = userMapper.selectList(objectQueryWrapper);
System.out.println(users);
//sql语句的order by部分是:
//ORDER BY name DESC , name DESC
//orderBy的参数:参数1,是否添加,参数2,是否排序,若是true,则是ASC,升序,false,则是DESC,降序
//升序(对于表来看,就是从上到下,依次提升),降序(对于表来看,就是从上到下,依次降低)
//参数3到参数n是可变长的参数,所以可以多写,注意:若这里是相同的参数,那么自然是优先考虑靠前的
//若有其他的方法,那么不会加上order by了,而是直接的相连接,若有不是排序的方法
//那么自然是再order by前面的,因为默认防止后面,也使得符合sql语法
}
/*
排序查询
总的操作
*/
@Test
public void testWrapper5() {
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper
.orderBy(true,false,"name","name")
.orderByAsc("name")
.orderByDesc("name");
List<User> users = userMapper.selectList(objectQueryWrapper);
System.out.println(users);
//sql语句的order by部分是:
//ORDER BY name DESC , name DESC , name ASC , name DESC
//当然,对应的条件(不是条件属性,他包括基本所有的属性设置,比如eq,orderByDesc等等属性设置)执行不分先后,也就是说,如果中间出现了eq,那么实际上eq也不会写在对应后面
//只是在对应的地方,所以,执行先后并没有很重要(相同的才会在对应地方后面,但是不会占用其他地方)
//可以试着将eq防止中间
/*
比如:
.orderBy(true,false,"name","name")
.eq("a",1)
.orderByAsc("name")
.eq("a",1)
.orderByDesc("name")
.eq("b",1);
来测试就知道了
那么一般是这样
WHERE a = ? AND a = ? AND b = ? ORDER BY name DESC , name DESC , name ASC , name DESC
即的确没有占位置,相同的会在后面
所以要注意:对应的先后并不是很重要,他们只会在对应的位置上进行添加,具体为什么不在后面添加
是由判断的,所以会加在对应位置,比如判断到了ORDER BY
那么eq就认为在他的前面,而不是后面,没有
那么就在where(eq会添加的,条件属性的设置通常都会添加,ORDER BY不会,他并不需要操作where,所以他不是条件属性的设置,所以不会添加)后面
*/
}
/*
逻辑查询
*/
@Test
public void testWrapper6() {
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper
.eq("name","jack").or().eq("name","jj");
List<User> users = userMapper.selectList(objectQueryWrapper);
System.out.println(users);
//sql语句的order by部分是:
//WHERE name = ? OR name = ?
//一般情况下,我们之前的操作都默认是and连接的,我们再条件之间
//加上or()那么就会使得默认的and,变成or
}
/*
逻辑查询
*/
@Test
public void testWrapper7() {
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper
.eq("name","jack").and(i->i.eq("name","j"));
List<User> users = userMapper.selectList(objectQueryWrapper);
System.out.println(users);
//sql语句的order by部分是:
//WHERE name = ? AND ( name = ? )
//手动的加上括号
}
/*
select
*/
@Test
public void testWrapper8() {
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper
.eq("name","jack").select("namee");
List<User> users = userMapper.selectList(objectQueryWrapper);
System.out.println(users);
//sql语句的order by部分是:
//SELECT name FROM user WHERE namee = ?
//加上select使得,不会默认使用操作类的所有字段来查询,而是使用select里面的字段来查询
//自然这里调用方法,会修改MappedStatement类信息的,使得改变,当然大多数的改变
//基本都是修改MappedStatement类信息,方法也是一样,实际上mybatis也是一样,如输入数据等等
//因为我们需要生成一个真正的sql,然后执行他,获得数据
//所以说方法的操作,通常都会改变MappedStatement类信息,或者说改变sql语句
//所以说,前面说的可能,一般是操作的,注意即可
}
public class User extends Model implements Serializable {
//语法,继承要在实现前面
package com.lagou;
import com.lagou.pojo.User;
import org.junit.jupiter.api.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
*
*/
@RunWith(SpringRunner.class)
@SpringBootTest
class TestAR {
/*
在AR模式下,完成主键查询
*/
@Test
public void testARSelectById(){
User user = new User();
user.setId(12l);
User user1 = (User) user.selectById();
System.out.println(user1);
//可以发现,Model不写泛型,代表操作返回值
//所以可以不写泛型,但是对应的接口BaseMapper需要,因为需要操作泛型的字段,来进行放入,即初始化
}
//那么为什么可以操作呢,我们在前面的源码分析中,可以知道,在执行@Test方法时,会进行初始化,生成语句
//那么这里也是如此,所以我们需要有对应的接口BaseMapper,并有对应的初始化
//然后就是值的放入了,我们操作的Model类,就通过我们的调用者user,来进行值的放入,所以是封装好的
//可以理解为:
//AR和MP:是先放入字段,然后值放入(值放入的不同操作而已)
//而正是只有值的放入的区别,所以也只是操作了值,所以也会操作特殊小写,字段的操作基本是没有影响的
/*
在AR模式下,完成新增数据
注意:增删改一般只要返回的影响条数不为0,那么就返回true,否则返回false
因为对应的方法中,有如下代码
return null != result && result >= 1;,这个关键代码,在增删改里面
*/
@Test
public void testARInsert(){
User user = new User();
user.setName("哈哈");
user.setAge(18);
user.setMail("建设局");
boolean user1 = user.insert();
System.out.println(user1);
}
/*
在AR模式下,完成更新操作
*/
@Test
public void testARUpdate(){
User user = new User();
user.setName("哈哈2");
user.setAge(18);
user.setMail("建设局2");
boolean user1 = user.insertOrUpdate();
//代表操作同一个时,就不在添加,而是更新,否则会添加
//一般根据id的值来进行判断,会根据如下的解释:
//首先,查询谁存在的问题的where后面的字段,根据这样的顺序:
/*
策略者(不能有多个数据,否则也报错)-id数(不能有多个数据,否则也报错)
-报错(直接判断是否为null了,而不会加上null到字段位置,这里是特殊的)
*/
//即首先查询,对应的字段是否有一个数据,若有,则更新,否则添加
//我们又可以发现,总是有id这个数存在的默认放入对应位置的字段,大概是mybatis-plus操作的吧
System.out.println(user1);
System.out.println(user.getId());
//那么既然是只操作值的放入,那么原来的id返回,自然也会在user对象里
//经过测试,发现,的确如此
}
/*
在AR模式下,完成更新操作
*/
@Test
public void testARUpdateById(){
User user = new User();
user.setId(11l);
user.setName("哈哈233");
user.setAge(18);
user.setMail("建设局23");
boolean user1 = user.updateById();
//直接根据id来进行更新,自然也需要遵循默认的顺序,该默认一般操作where后面的
System.out.println(user1);
System.out.println(user.getId());
}
/*
在AR模式下,完成删除操作
*/
@Test
public void testARDelete(){
User user = new User();
//user.setId(11l);
boolean user1 = user.deleteById(12); //这里操作手动的指定
System.out.println(user1);
System.out.println(user.getId());
}
/*
在AR模式下,完成根据条件查询
*/
@Test
public void testARFindById(){
User user = new User();
QueryWrapper<Object> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper.ge("age",20);
List user1 = user.selectList(objectQueryWrapper);
System.out.println(user1);
//也能操作查询,只是从接口变成了来而已,但都是传入值
}
}
/*
1:Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
2:ParameterHandler (getParameterObject, setParameters)
3:ResultSetHandler (handleResultSets, handleOutputParameters)
4:StatementHandler (prepare, parameterize, batch, update, query)
*/
package com.lagou;
//可以发现是mybatis自带的自定义插件操作方式
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import java.util.Properties;
/**
*
*/
@Intercepts({@Signature(
type= Executor.class,
method = "update",
args = {MappedStatement.class,Object.class})})
//拦截Executor类的update方法,并指定参数的类型MappedStatement和Object,防止重载
public class MyInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
System.out.println(1);
//拦截⽅法,具体业务逻辑编写的位置
return invocation.proceed();
}
@Override
public Object plugin(Object o) {
//该o一般是前面的总体(四个),会执行四次plugin方法
/*
org.apache.ibatis.executor.CachingExecutor@230a73f2
如果是增删改,那么这里会先执行intercept方法,然后执行plugin方法
com.baomidou.mybatisplus.core.MybatisDefaultParameterHandler@3e26482
org.apache.ibatis.executor.resultset.DefaultResultSetHandler@7c5df615
org.apache.ibatis.executor.statement.RoutingStatementHandler@2f995afc
当然,注意即可
*/
System.out.println(22);
//创建target对象的代理对象,⽬的是将当前拦截器加⼊到该对象中
return Plugin.wrap(o, this);
}
@Override
public void setProperties(Properties properties) {
System.out.println(3);
//属性设置
}
}
//⾃定义拦截器
@Bean
public MyInterceptor myInterceptor(){
return new MyInterceptor();
}
<plugins>
<plugin interceptor="com.lagou.MyInterceptor"></plugin>
</plugins>
//执行分析插件
/*
@Bean
public SqlExplainInterceptor sqlExplainInterceptor(){
SqlExplainInterceptor sqlExplainInterceptor = new SqlExplainInterceptor();
List<ISqlParser> sqlParserList = new ArrayList<>();
// 攻击 SQL 阻断解析器、加⼊解析链
sqlParserList.add(new BlockAttackSqlParser());
sqlExplainInterceptor.setSqlParserList(sqlParserList);
return sqlExplainInterceptor;
}
*/
/*
性能分析插件
*/
@Bean
public PerformanceInterceptor performanceInterceptor(){
PerformanceInterceptor performanceInterceptor = new PerformanceInterceptor();
performanceInterceptor.setMaxTime(100);
performanceInterceptor.setFormat(true);
return performanceInterceptor;
//这个对应于下面的配置,一般xml配置与类配置一起时,至于谁优先,主要看谁后覆盖,这里好像是xml优先
//即后执行(因为他并不是得到,而是已经注入了,但也不绝对)
//当然,有些情况下,可能不会允许覆盖,所以在这种情况下
//通常来说xml会优先,因为一般xml会覆盖注解,而注解不会覆盖xml,使得还是使用xml,注解的被移除
//但本质上还是覆盖问题,只是xml占优而已
}
<!-- 性能分析插件-->
<plugins>
<plugin interceptor="com.baomidou.mybatisplus.extension.plugins.PerformanceInterceptor">
<!--sql性能分析,开发环境使用,线上不推荐,maxTime指的是sql最大执行时长-->
<property name="maxTime" value="100"/>
<!--sql是否格式化,默认false-->
<property name="format" value="true"/>
</plugin>
</plugins>
/*
我这里这样
根据id进行查询
@Test
void testSelect() {
System.out.println(44);
List<User> all = userMapper.selectList(null);
System.out.println(all);
}
Time:20 ms - ID:com.lagou.mapper.UserMapper.selectList
Execute SQL:
SELECT
id,
age,
eMail AS mail
FROM
user
这是我们的显示,对应的执行时间是20ms,当然,多次的运行一般是不同的
也发现sql语句进行了格式化,我们将最大的执行时间
修改成10ms(虽然可能也会成功,如运行好执行时间更短了)
再次看看运行结果
我们可以很明显的发现,报错了,使得对应的结果没有打印
我们可以知道,对于sql的执行时间,在开发中,这个插件也就能够看看优化的sql是否是被优化还是被劣化了
没有格式化的就是如下
Execute SQL:SELECT id,age,eMail AS mail FROM user
*/
/*
测试根据条件进行修改
*/
@Test
public void testUpdate() {
User user = new User();
//更新的字段
user.setAge(35);
//更新的条件
QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
objectQueryWrapper.eq("name", "哈哈哈");
int i = userMapper.update(user, objectQueryWrapper);
System.out.println(i);
}
/*
测试根据条件进行修改
*/
@Test
public void testUpdate() {
User user = new User();
//更新的字段
user.setAge(35);
//更新的条件
// QueryWrapper<User> objectQueryWrapper = new QueryWrapper<>();
//
// objectQueryWrapper.eq("name", "哈哈哈");
int i = userMapper.update(user, null);
System.out.println(i);
}

/*
乐观锁插件
*/
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
<bean class="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor"/>
<plugins>
<plugin interceptor="com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor">
</plugin>
</plugins>
-- 添加version字段
ALTER TABLE USER
ADD COLUMN VERSION INT(10) NULL AFTER email;
-- 一般默认添加在最后面的位置,所以不需要加上AFTER email
-- 那么有没有before,添加在前,好像并没有,可能有其他方式,具体可以百度
-- 所有数据的version都设置为1,因为我们需要最开始就是1的数据
UPDATE USER SET VERSION = 1
@Version
private Integer version;
/*
测试根据id进行修改
*/
@Test
public void testUpdateById() {
User user = new User();
user.setId(6l);
user.setAge(30);
user.setName(null);
int i = userMapper.updateById(user);
System.out.println(i);
}
/*
测试根据id进行修改
*/
@Test
public void testUpdateById() {
User user = new User();
User user1 = userMapper.selectById(3);
user.setId(3l);
user.setName("哈哈哈");
user.setVersion(user1.getVersion());
int i = userMapper.updateById(user);
System.out.println(i);
System.out.println(user); //对应的version是新的值(加上1的)
}
/*
UPDATE
user
SET
name='哈哈哈',
age=0, age类型是int,所以不会省略
version=2
WHERE
id=3
AND version=1
其中 version=2 ,即表示自动的加上了1,实际上是因为由于注解@Version的存在
我们在操作值时,会默认加上AND version=对应的原值,且原来的set值变成了原值加1
所以我们没有条件,也会出现AND version=1,再次执行时,也是如此,即变成了如下:
UPDATE
user
SET
name='哈哈哈',
age=0,
version=3
WHERE
id=3
AND version=2
我们可以将@Version注解删除,看看结果
UPDATE
user
SET
name='哈哈哈',
age=0,
version=3
WHERE
id=3
继续将@Version注解加回来
UPDATE
user
SET
name='哈哈哈',
age=0,
version=4
WHERE
id=3
AND version=3
所以说,将原值进行操作的就是@Version注解的作用,以及也会加上条件
当然,需要放入对应的位置,即需要赋值才可,因为null也会导致不会加上字段
而正是因为会加上条件,使得基本不会出现操作同一个数据,即避免了大多数的拿到同一个数据问题
虽然可能存在不唯一性(基本不会),或者更新失败的问题
为了测试更新失败,我们可以在如下
@Test
public void testUpdateById() {
User user = new User();
User user1 = userMapper.selectById(3);
user.setId(3l);
user.setName("哈哈哈");
user.setVersion(user1.getVersion());
这里打上断点 int i = userMapper.updateById(user);
System.out.println(i);
}
debug执行后(只要我们不放行,那么基本就算一直在运行中,而不执行sql语句)
这时我们去数据库修改对应的version,然后放行,会发现,返回0,即没有进行操作
所以的确没有进行更新,即更新失败
*/
package com.lagou.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.lagou.pojo.User;
import java.util.List;
/**
*
*/
//通用mapper接口,以后创建其他mapper接口时,就不需要再继承BaseMapper接口了
//而是继承MyBaseMapper接口(也就是当前接口)
//只要指定了T,那么父类的T也会指定一样的,注意即可
public interface MyBaseMapper<T> extends BaseMapper<T> {
List<User> findAll();
}
//那么可以不使用MyBaseMapper吗,而是在自己的接口里写List<User> findAll();
//答:可以,只是既然是要扩展,那么自然的是我们实现的接口来扩展,而不是我们自己
package com.lagou.injector;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.AbstractSqlInjector;
import java.util.List;
/**
* 自定义sql注入器
*/
public class MySqlInjector extends AbstractSqlInjector {
@Override
public List<AbstractMethod> getMethodList() {
return null;
}
}
/*
找到 public abstract List<AbstractMethod> getMethodList();方法
看看有谁实现了他,可以发现是DefaultSqlInjector
如下:
public class DefaultSqlInjector extends AbstractSqlInjector {
public DefaultSqlInjector() {
}
public List<AbstractMethod> getMethodList() {
return (List)Stream.of(new Insert(), new Delete(), new DeleteByMap(), new DeleteById(), new
DeleteBatchByIds(), new Update(), new UpdateById(), new SelectById(), new SelectBatchByIds(),
new SelectByMap(), new SelectOne(), new SelectCount(), new SelectMaps(), new
SelectMapsPage(), new SelectObjs(), new SelectList(), new
SelectPage()).collect(Collectors.toList());
}
}
我们随便点击一个,如点击new SelectById()进去,发现包含了之前的初始化的操作
也就是说,实际上之前的初始化的操作(前面源码分析中,得到了17个数据的那个方法)
就是创建了这些对象(一般都继承AbstractMethod类,因为我们通常需要他里面的内容)
然后执行injectMappedStatement方法
所以我们也知道,要想操作自己的,我们需要也创建对象才可,且创建的对象也要有injectMappedStatement方法
*/
package com.lagou.injector;
import com.baomidou.mybatisplus.core.enums.SqlMethod;
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 FindAll extends AbstractMethod {
@Override
public MappedStatement injectMappedStatement(Class<?> mapperClass, Class<?> modelClass, TableInfo
tableInfo) {
String sql = "select * from " + tableInfo.getTableName(); //tableInfo.getTableName()表名
SqlSource sqlSource = this.languageDriver.createSqlSource(
this.configuration, sql, modelClass); //有sql以外的信息
return this.addSelectMappedStatement(
mapperClass, "findAll", sqlSource, modelClass, tableInfo);
//返回可执行的sql,相当于代码平台,直接操作数据库
}
}
package com.lagou.injector;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.AbstractSqlInjector;
import java.util.ArrayList;
import java.util.List;
/**
* 自定义sql注入器
*/
public class MySqlInjector extends AbstractSqlInjector {
@Override
public List<AbstractMethod> getMethodList() {
//扩充自定义方法
List<AbstractMethod> methodList = new ArrayList<>();
methodList.add(new FindAll());
return methodList;
//return (List) Stream.of(new FindAll()).collect(Collectors.toList());
}
}
package com.lagou.injector;
import com.baomidou.mybatisplus.core.injector.AbstractMethod;
import com.baomidou.mybatisplus.core.injector.AbstractSqlInjector;
import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector;
import com.baomidou.mybatisplus.core.injector.methods.*;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* 自定义sql注入器
*/
public class MySqlInjector extends DefaultSqlInjector {
@Override
public List<AbstractMethod> getMethodList() {
//扩充自定义方法
List<AbstractMethod> methodList = super.getMethodList();
methodList.add(new FindAll());
return methodList;
}
}
/*
自定义sql注入器
*/
@Bean
public MySqlInjector mySqlInjector(){
return new MySqlInjector();
}
//只要我们操作了这一步,那么原来的方法都会失效(即原有的BaseMapper中的方法将失效)
//因为我们这里只有findAll方法操作了
//我们可以看源码,会发现,对应的17个变成了1个
public interface UserMapper extends BaseMapper<User> {
//修改成如下
public interface UserMapper extends MyBaseMapper<User> {
//虽然public interface UserMapper extends BaseMapper<User> {
//这个也可,只需要自己写方法即可,只是,扩展,自然不是自己扩展
@Test
public void testFindAll(){
List<User> all = userMapper.findAll();
System.out.println(all);
}
//运行,发现,执行了
//那么有个问题,为什么可以执行findAll方法,在前面我们知道
//有个
/*
return this.addSelectMappedStatement(
mapperClass, "findAll", sqlSource, modelClass, tableInfo);
其中findAll就代码方法名称(必须一样,即没有大小写忽略,否则报错,即找不到方法)
只要是BaseMapper的子类(这样可以操作泛型)中
有他这个方法,那么执行该方法,就会执行,该sql语句
所以之前的17个中,才会出现语句的执行,只是这里进行了注入,所以只有该一个方法了
有注入的则使用注入的,否则使用默认的17个,当然,我们可以将那17个写上,那么也能执行
如:
return (List)Stream.of(new FindAll(),new Insert(), new Delete(), new DeleteByMap(), new
DeleteById(), new DeleteBatchByIds(), new Update(), new UpdateById(), new SelectById(),
new SelectBatchByIds(), new SelectByMap(), new SelectOne(), new SelectCount(), new
SelectMaps(), new SelectMapsPage(), new SelectObjs(), new SelectList(), new
SelectPage()).collect(Collectors.toList());
多出了new FindAll(),这样才是真正的扩展
至此,实际上FindAll类和MySqlInjector类是进行sql语句的操作,然后通过注入,使得方法与sql语句连接
至此当我们有继承了BaseMapper接口的接口时
那么该接口的方法可以与注入sql语句对应的方法进行对应,然后可以执行sql
因为我们需要BaseMapper接口来操作放入位置(特别是泛型的操作),即初始化
当然,因为继承的关系,我们只需要将MyBaseMapper的泛型写上即可,BaseMapper的泛型可以不写
那么自然,MyBaseMapper里面的都是该泛型
因为子类关系,那么我们的初始化,自然会操作对应的父类泛型,虽然并不是BaseMapper接口
只要是BaseMapper的子类即可,无论多少层级,即一路查找
所以虽然单纯的返回类可能需要强转,但集合不需要(自动的会变,因为对方并没有泛型)
因为他只是在操作时,会提示
*/
@TableField(fill = FieldFill.INSERT) //表示在插入时,进行填充
private Integer version;
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.baomidou.mybatisplus.annotation;
public enum FieldFill {
//默认不处理
DEFAULT,
//插⼊时填充字段
INSERT,
//更新时填充字段
UPDATE,
//插⼊或更新时填充字段
INSERT_UPDATE;
private FieldFill() {
}
}
//注意:现在好像,只要你有对应的注入,那么无论你是否加上对应的注解,都会执行填充,后面会说明,注意即可
package com.lagou.handler;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
/**
*
*/
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
//插入时的填充
@Override
public void insertFill(MetaObject metaObject) {
//得到传递的对象的version值
Object version = getFieldValByName("version", metaObject);
if(null == version){
//该属性为空,我们可以进行填充
setFieldValByName("version",1,metaObject);
}
System.out.println(metaObject.getOriginalObject()); //这个就是我们传递的类的信息
//且基本只能是类的信息,因为添加和修改的操作中,字段值基本是与类相关的,而不是条件
}
//更新时的填充
@Override
public void updateFill(MetaObject metaObject) {
}
}
/*
测试添加
*/
@Test
public void testInsert() {
User user = new User();
//user.setId(9l);
user.setName("哈哈哈");
user.setAge(18);
user.setMail("zimu@jajaja");
//返回值自然也是影响行数
int insert = userMapper.insert(user);
System.out.println(insert);
System.out.println("id值为:" + user.getId());
}
/*
测试添加
*/
@Test
public void testInsert() {
User user = new User();
user.setName("哈哈哈w");
user.setAge(18);
user.setMail("zimu@jajaja");
int insert = userMapper.insert(user);
System.out.println(insert);
System.out.println("id值为:" + user.getId());
}
//进行运行,我们发现,他并没有因为没有设置version的值(默认值),而不加上字段,而是已经加上了
//因为我们进行了填充,我们将对应的注解,删除再次测试,发现,也操作了方法,操作更新,也操作了方法
//所以对应的注解并不是决定作用,以注入为主了
//我们将@Component去掉,不被注入
//发现没添加字段了,这时,我们加回来,在对应的方法里,不进行操作,发现也是没有添加字段
//所以说,那个填充的在放入字段之前操作了
//即填充(操作),放入之前(判断是否为null,而决定是否添加字段),放入
//根据这样的说明,那么如果你手动的设置为null,那么就在放入之前这个阶段,使得不会添加字段
ALTER TABLE USER
ADD COLUMN deleted INT(1) NULL DEFAULT 0 COMMENT '1代表删除,0代表未删除' AFTER VERSION
@TableLogic //对该字段操作逻辑删除,至于怎么操作,看如下配置
private Integer deleted;
# 逻辑已删除值(默认为 1)
mybatis-plus.global-config.db-config.logic-delete-value=1
# 逻辑未删除值(默认为 0)
mybatis-plus.global-config.db-config.logic-not-delete-value=0
/*
根据id进行删除
*/
@Test
public void testDeleteById() {
int i = userMapper.deleteById(7);
System.out.println(i);
}
//运行后,看看打印情况,可以发现,对应的语句如下:
/*
UPDATE
user
SET
deleted=1
WHERE
id=7
AND deleted=0
明明是删除操作,却变成了更新操作
实际上更新操作和查询操作后面的where都会有deleted=0,在条件之前,类之后(如操作id)
所以也使得,只能查询或者修改deleted=0的数据
但因为插入,没有where,所以没有操作
其中
deleted=0:代表mybatis-plus.global-config.db-config.logic-not-delete-value=0
deleted=1:代表mybatis-plus.global-config.db-config.logic-delete-value=1
修改这两个值,那么对应的deleted后面的值也就会修改,而正是因为操作的是deleted,所以表字段也要deleted
自己可以进行测试
即从逻辑未删除值(默认为 0)变成逻辑已删除值(默认为 1)
通常需要指定数据库表的0为未删除,1代表已删除,就是与这里对应,使得操作的sql语句也正好对应
*/
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--mybatis-plus的springboot⽀持-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.1.1</version>
</dependency>
<!--下面两个是新的依赖-->
<dependency>
<!--有对应的StringUtils类操作,且也可以使得创建对应的AutoGenerator对象的操作-->
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-generator</artifactId>
<version>3.1.1</version>
</dependency>
<dependency>
<!--虽然明面上的代码并没有操作该依赖,但生成时,需要该依赖
没有该依赖,那么mpg.execute();这一步执行报错-->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--简化代码的⼯具包-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

package com.lagou.generator;
import com.baomidou.mybatisplus.core.exceptions.MybatisPlusException;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
*
*/
public class MysqlGenerator {
/*
读取控制台内容
*/
public static String scanner(String tip) {
Scanner scanner = new Scanner(System.in);
StringBuilder help = new StringBuilder();
help.append("请输入" + tip + ":");
System.out.println(help.toString());
if(scanner.hasNext()){
String ipt = scanner.next();
if(StringUtils.isNotEmpty(ipt)){
return ipt;
}
}
throw new MybatisPlusException("请输⼊正确的" + tip + "!");
}
/*
RUN THIS(运行这个)
*/
public static void main(String[] args) {
// 代码⽣成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java"); //代码生成位置
gc.setAuthor("lagou"); //作者是谁
gc.setOpen(false);
mpg.setGlobalConfig(gc);
// 数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl(
"jdbc:mysql://127.0.0.1:3306/mp?useUnicode=true&useSSL=false&characterEncoding=utf8");
//dsc.setSchemaName("public");
dsc.setDriverName("com.mysql.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("123456"); //修改成自己的
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setModuleName(scanner("模块名"));
pc.setParent("com.lagou.mp.generator"); //模块名的包的位置,没有会自动创建
mpg.setPackageInfo(pc);
// 自定义配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};
List<FileOutConfig> focList = new ArrayList<>();
focList.add(new FileOutConfig("/templates/mapper.xml.ftl") {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定义输⼊⽂件名称
return projectPath + "/lagou-mp-generator/src/main/resources/mapper/" +
pc.getModuleName() + "/" + tableInfo.getEntityName() + "Mapper" +
StringPool.DOT_XML;
}
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
mpg.setTemplate(new TemplateConfig().setXml(null));
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel);
strategy.setColumnNaming(NamingStrategy.underline_to_camel);
/*
strategy.setSuperEntityClass("
com.baomidou.mybatisplus.samples.generator.common.BaseEntity");
*/
strategy.setEntityLombokModel(true);
/*
strategy.setSuperControllerClass("
com.baomidou.mybatisplus.samples.generator.common.BaseController");
*/
strategy.setInclude(scanner("表名"));
strategy.setSuperEntityColumns("id");
strategy.setControllerMappingHyphenStyle(true);
strategy.setTablePrefix(pc.getModuleName() + "_");
mpg.setStrategy(strategy);
// 选择 freemarker 引擎需要指定如下加,注意 pom 依赖必须有!
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.execute(); //这个要加上,否则什么都没有执行(操作)
}
}


/*
若是public void insertt(User user);方法
那么生成时,会根据xml的namespace地址查看是否是当前接口的地址,而进行生成
若没有对应的namespace值,则没有选项,或者没有对应的生成提示了
若有,那么选择生成后,是<insert id="insertt"></insert>
所以对应的sql需要自己编写,因为他只是提供标签
具体自己测试就知道了
*/
一、什么是MQTT协议MessageQueuingTelemetryTransport:消息队列遥测传输协议。是一种基于客户端-服务端的发布/订阅模式。与HTTP一样,基于TCP/IP协议之上的通讯协议,提供有序、无损、双向连接,由IBM(蓝色巨人)发布。原理:(1)MQTT协议身份和消息格式有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。MQTT传输的消息分为:主题(Topic)和负载(payload)两部分Topic,可以理解为消息的类型,订阅者订阅(Su
TCL脚本语言简介•TCL(ToolCommandLanguage)是一种解释执行的脚本语言(ScriptingLanguage),它提供了通用的编程能力:支持变量、过程和控制结构;同时TCL还拥有一个功能强大的固有的核心命令集。TCL经常被用于快速原型开发,脚本编程,GUI和测试等方面。•实际上包含了两个部分:一个语言和一个库。首先,Tcl是一种简单的脚本语言,主要使用于发布命令给一些互交程序如文本编辑器、调试器和shell。由于TCL的解释器是用C\C++语言的过程库实现的,因此在某种意义上我们又可以把TCL看作C库,这个库中有丰富的用于扩展TCL命令的C\C++过程和函数,所以,Tcl是
开门见山|拉取镜像dockerpullelasticsearch:7.16.1|配置存放的目录#存放配置文件的文件夹mkdir-p/opt/docker/elasticsearch/node-1/config#存放数据的文件夹mkdir-p/opt/docker/elasticsearch/node-1/data#存放运行日志的文件夹mkdir-p/opt/docker/elasticsearch/node-1/log#存放IK分词插件的文件夹mkdir-p/opt/docker/elasticsearch/node-1/plugins若你使用了moba,直接右键新建即可如上图所示依次类推创建
文章目录概念索引相关操作创建索引更新副本查看索引删除索引索引的打开与关闭收缩索引索引别名查询索引别名文档相关操作新建文档查询文档更新文档删除文档映射相关操作查询文档映射创建静态映射创建索引并添加映射概念es中有三个概念要清楚,分别为索引、映射和文档(不用死记硬背,大概有个印象就可以)索引可理解为MySQL数据库;映射可理解为MySQL的表结构;文档可理解为MySQL表中的每行数据静态映射和动态映射上面已经介绍了,映射可理解为MySQL的表结构,在MySQL中,向表中插入数据是需要先创建表结构的;但在es中不必这样,可以直接插入文档,es可以根据插入的文档(数据),动态的创建映射(表结构),这就
HTTP缓存是指浏览器或者代理服务器将已经请求过的资源保存到本地,以便下次请求时能够直接从缓存中获取资源,从而减少网络请求次数,提高网页的加载速度和用户体验。缓存分为强缓存和协商缓存两种模式。一.强缓存强缓存是指浏览器直接从本地缓存中获取资源,而不需要向web服务器发出网络请求。这是因为浏览器在第一次请求资源时,服务器会在响应头中添加相关缓存的响应头,以表明该资源的缓存策略。常见的强缓存响应头如下所述:Cache-ControlCache-Control响应头是用于控制强制缓存和协商缓存的缓存策略。该响应头中的指令如下:max-age:指定该资源在本地缓存的最长有效时间,以秒为单位。例如:Ca
如何用IDEA2022创建并初始化一个SpringBoot项目?目录如何用IDEA2022创建并初始化一个SpringBoot项目?0. 环境说明1. 创建SpringBoot项目 2.编写初始化代码0. 环境说明IDEA2022.3.1JDK1.8SpringBoot1. 创建SpringBoot项目 打开IDEA,选择NewProject创建项目。 填写项目名称、项目构建方式、jdk版本,按需要修改项目文件路径等信息。 选择springboot版本以及需要的包,此处只选择了springweb。 此处需特别注意,若你使用的是jdk1
前言上一篇我们简要讲述了粒子系统是什么,如何添加,以及基本模块的介绍,以及对于曲线和颜色编辑器的讲解。从本篇开始,我们将按照模块结构讲解下去,本篇主要讲粒子系统的主模块,该模块主要是控制粒子的初始状态和全局属性的,以下是关于该模块的介绍,请大家指正。目录前言本系列提要一、粒子系统主模块1.阅读前注意事项2.参考图3.参数讲解DurationLoopingPrewarmStartDelayStartLifetimeStartSpeed3DStartSizeStartSize3DStartRotationStartRotationFlipRotationStartColorGravityModif
VMware虚拟机与本地主机进行磁盘共享前提虚拟机版本为Windows10(专业版,不是可能有问题)本地主机为家庭版或学生版(此版本会有问题,但有替代方式)最好是专业版VMware操作1.关闭防火墙,全部关闭。2.打开电脑属性3.点击共享-》高级共享-》权限4.如果没有everyone,就添加权限选择完全控制,然后应用确定。5.打开cmd输入lusrmgr.msc(只有专业版可以打开)如果不是专业版,可以跳过这一步。点击用户-》administrator密码要复杂密码,否则不行。推荐admaiN@1234类型的密码。设置完密码,点击属性,将禁用解开。6.如果虚拟机的windows不是专业版,可
查看原文>>>基于”PLUS模型+“生态系统服务多情景模拟预测实践技术应用目录第一章、理论基础与软件讲解第二章、数据获取与制备第三章、土地利用格局模拟第四章、生态系统服务评估第五章、时空变化及驱动机制分析第六章、论文撰写技巧及案例分析基于ArcGISPro、Python、USLE、INVEST模型等多技术融合的生态系统服务构建生态安全格局基于生态系统服务(InVEST模型)的人类活动、重大工程生态成效评估、论文写作等具体应用基于ArcGISPro、R、INVEST等多技术融合下生态系统服务权衡与协同动态分析实践应用 本文从数据、方法、实践三方面对生态系统服务多情景预测进行讲解。内容涵盖多
IK分词器本文分为简介、安装、使用三个角度进行讲解。简介倒排索引众所周知,ES是一个及其强大的搜索引擎,那么它为什么搜索效率极高呢,当然和他的存储方式脱离不了关系,ES采取的是倒排索引,就是反向索引;常见索引结构几乎都是通过key找value,例如Map;倒排索引的优势就是有效利用Value,将多个含有相同Value的值存储至同一位置。分词器为了配合倒排索引,分词器也就诞生了,只有合理的利用Value,才会让倒排索引更加高效,如果一整个Value不进行任何操作直接进行存储,那么Value和key毫无区别。分词器Analyzer通常会对Value进行操作:一、字符过滤,过滤掉html标签;二、分