1、SpringBoot+老杜MyBatis

一、简单回顾一下MyBatis

二、快速入门

​三、简易插入删除更改

四、查询

①、按其中一个字段查询

②、按所有字段进行查询

​五、详解MyBatis核心配置(复习)

六、结合Web及SpringMVC

2、MyBatis小技巧

一、#{}与${}及模糊查询

二、别名机制与mapper标签

三、插入使用生成的主键值

3、MyBatis参数处理

一、单个简单类型参数

二、Map参数

①、插入信息

②、查询单个汽车信息

③、返回多个Map

④、返回Map,map>

三、实体类参数

四、多参数(@Param)

五、resultMap结果映射

①、使用resultMap进行结果映射(常用)

②、开启驼峰命名规范自动映射

六、获取总记录条数

4、动态SQL(注:使用了驼峰命名规范)

一、if标签

二、where标签

三、trim标签

四、set标签

五、choose where otherwise

六、foreach标签

①批量删除

②批量添加

七、sql、include标签

5、高级映射及延迟加载

一、多对一

二、多对一延迟加载

三、一对多

四、一对多延迟加载

6、MyBatis缓存机制

一、一级缓存

二、二级缓存

7、MyBatis使用PageHelper

一、limit分页

二、PageHelper插件


1、SpringBoot+老杜MyBatis

一、简单回顾一下MyBatis

  • 核心对象包括以下三个:

    • SqlSessionFactoryBulider

    • SqlSessionFactory

    • SqlSession

    • SqlSessionFactoryBuilder --> SqlSessionFactory --> SqlSession

  • 关于MyBatis的事务管理机制(两种)

    • <transactionManager type="JDBC"/> JDBC表示事务管理器

    • <transactionManager type="MANAGED"/> MANAGED表示事务事务管理器

JDBC事务管理器: MyBatis框架自己管理事务,自己采用原生的JDBC代码去管理事务: conn.setAutoCommit(false); 开启事务。 ....业务处理... conn.commit(); 手动提交事务 使用JDBC事务管理器的话,底层创建的事务管理器对象:JdbcTransaction对象。

如果你编写的代码是下面的代码: SqlSession sqlSession = sqlSessionFactory.openSession(true); 表示没有开启事务。因为这种方式压根不会执行:conn.setAutoCommit(false); 在JDBC事务中,没有执行conn.setAutoCommit(false);那么autoCommit就是true。 如果autoCommit是true,就表示没有开启事务。只要执行任意一条DML语句就提交一次。

MANAGED事务管理器: MyBatis不再负责事务的管理了。事务管理交给其它容器来负责。例如:spring。 我不管事务了,你来负责吧。对于我们当前的单纯的只有mybatis的情况下,如果配置为:MANAGED 那么事务这块是没人管的。没有人管理事务表示事务压根没有开启。没有人管理事务就是没有事务。

  • JDBC中的事务: 如果你没有在JDBC代码中执行:conn.setAutoCommit(false);的话,默认的autoCommit是true。

  • 重点: 以后注意了,只要你的autoCommit是true,就表示没有开启事务。 只有你的autoCommit是false的时候,就表示开启了事务。

在SpringBoot+MyBatis项目中就不用写事务相关的东西了,但是用到业务层Service就需要了

二、快速入门

第一步:引入依赖

<!--junit测试依赖-->
<dependency><groupId>junit</groupId><artifactId>junit</artifactId><scope>test</scope>
</dependency>
<!--lombok依赖,是为了简化实体类的编写代码量-->
<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId>
</dependency>

第二步:编写yml配置文件(此处我将properties后缀改成了yml)

其中包含连接数据库以及MyBatis的核心配置信息(但在SpringBoot框架中无需用MyBatis原核心配置文件)

spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/powernodeusername: rootpassword: rootmybatis:mapper-locations: classpath:mapper/*.xml#目的是为了省略resultType里的代码量type-aliases-package: com.chf.pojoconfiguration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

第三步:构建实体类(在pojo包下),与表中字段一一对应

第四步:创建接口,用来写方法

第五步:每一个实体类对应一个mapper映射文件,在resources的mapper包下写映射文件(SQL语句)

其实这里的Sql语句是有问题的,查询到控制台的有问题,这里做个伏笔后面会知道为什么。

第六步:先测试自己是否成功连接到了数据库,不然你不管怎么测试方法都不知道你为什么爆红

测试成功,开心开心(这里控制台输出的null是我埋下的伏笔,下面会讲)

三、简易插入删除更改

@Mapper
public interface CarMapper {/*** 插入汽车* @return* @param car*/int insert(Car car);/*** 按id删除车辆信息* @param id* @return*/int delete(Long id);/*** 更新车辆信息* @param car* @return*/int update(Car car);
}
<!--namespace和里面标签的id两者都是为了动态代理而需要的-->
<mapper namespace="com.chf.mapper.CarMapper"><!--#{}对应的是pojo层实体类的属性名"abcDe"对应的"getAbcDe"的"abcDe"(驼峰命名规范)想简单点,对应属性名就行,复杂可能会乱ovo--><insert id="insert">insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})</insert><!--如果占位符只有一个,其实可以随便写里面的内容但不能不写,但最好见名知意,这次只是测试--><delete id="delete">delete from t_car where id = #{dasdad}</delete><update id="update">update t_car setcar_num=#{carNum},brand=#{brand},guide_price=#{guidePrice},produce_time=#{produceTime},car_type=#{carType}where id=#{id}</update>
</mapper>
@SpringBootTest
public class Mybatis001IntroduceApplicationTests {@Autowiredprivate CarMapper carMapper;@Testvoid testInsert(){Car car = new Car(null,"111","奔驰",30.00,"2022-10-2","新能源");int count = carMapper.insert(car);System.out.println((count == 1 ? "插入成功" : "插入失败"));}@Testvoid testDelete(){int count = carMapper.delete(4L);System.out.println((count == 1 ? "删除成功" : "删除失败"));}@Testvoid testUpdate(){Car car = new Car(6L,"1111","奔驰",30.00,"2022-10-2","新能源");int count = carMapper.update(car);System.out.println((count == 1 ? "更新成功" : "更新失败"));}
}

四、查询

①、按其中一个字段查询

通过控制台你会仔细的发现:除了id和brand其他皆为null。

原因就在于:属性名与表名不一致造成的,所以我们应该编写Sql语句就可以完成查询

②、按所有字段进行查询

这也是我在快速入门那里留下的伏笔,其实那个select也要进行修改

五、详解MyBatis核心配置(复习)

这里就当复习了,因为是看的老杜讲解的,更加细致。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration><!--default表示默认使用的环境--><environments default="development"><!--其中的一个环境 连接的数据库是powernode一般一个数据库会对应一个SqlSessionFactory对象一个环境environment会对应一个SqlSessionFactory对象--><environment id="development"><!--MyBatis事务管理器接口Transaction有两个实现类如果type="JDBC"那么底层会实例化JdbcTransaction对象如果type="MANAGED"那么底层会实例化ManagedTransaction对象--><transactionManager type="JDBC" /><!--datasource配置:1、dataSource被称为数据源2、dataSource为程序提供Connection对象3、数据源实际上是一套规范,JDK中有这套规范:javax.sql.DataSource4、type有三种值可选其一:POOLED:使用MyBatis自己实现的数据库连接池UNPOOLED:不适用MyBatis的数据库连接池,每一次请求过来创建新的Connection对象JNDI:集成其它第三方的数据库连接池,这是一套规范,大部分Web容器都实现了此规范--><dataSource type="POOLED"><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/powernode"/><property name="username" value="root"/><property name="password" value="root"/></dataSource></environment><!--MyBatis另外一个环境,也就是连接的数据库是另一个数据库MyBatis--><environment id="mybatisDB"><transactionManager type="JDBC" /><dataSource type="POOLED"><property name="driver" value="com.mysql.cj.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mybatis"/><property name="username" value="root"/><property name="password" value="root"/></dataSource></environment>
</environments> <!--通过此标签找到映射文件,实际在SpringBoot中的yml配置文件中变成:mybatis:mapper-locations--><mappers><package name="com.chf.mapper" /></mappers>
</configuration>
@SpringBootTest
public class ConfigurationTest{@Testvoid testEnvironment() throws Exception{//获取SqlSessionFactory对象(采用默认方式获取)SqlSessionFactoryBuilder ssf = new SqlSessionFactoryBuilder();//采用这种方式获取的就是默认的环境SqlSessionFactory sqlSessionFactory = ssf.build(Resources.getResourceAsStream("MyBatisConfig.xml"));//这种方式通过id获取的是指定的环境SqlSessionFactory sqlSessionFactory = ssf.build(Resources.getResourceAsStream("MyBatisConfig.xml"),"mybatisDB");}
}

六、结合Web及SpringMVC

这里老杜的是使用MVC架构模式,然后优化使用了动态代理写了两个工具类

但我是基于SpringBoot框架的基础上去复习老杜的MyBatis,所以会使用到SpringMVC去实现老杜的课程

一个项目从前往后写才知道具体需要实现的功能是什么(老杜教的)

修改成功和失败的页面就不截图展示了

项目目录如下以及超简易页面以及数据库表结构

 ①根据表结构去编写实体类做到与表中字段一一对应

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Account {private Long id;private String actno;private Double balance;
}

②根据网页所知功能需求是银行转账,在Mapper接口编写方法

@Mapper
public interface AccountMapper {/*** 根据账号查询账户信息* @param actno* @return*/Account selectByActno(String actno);/*** 更新账户信息* @param account* @return*/int updateByActno(Account account);
}

③根据Mapper接口的方法在映射文件中写Sql语句

④根据Mapper接口所需方法在业务层中实现

public interface AccountService {/*** 根据账号查询账户信息* @param actno* @return*/Account selectByActno(String actno);/*** 更新账户信息* @param account* @return*/int updateByActno(Account account);/*** 账户转账业务。* @param fromActno 转出账号* @param toActno 转入账号* @param money 转账金额*/void transfer(String fromActno, String toActno, double money) throws MoneyNotEnoughException, TransferException;
}
@Service
public class AccountServiceImpl implements AccountService {@Autowiredprivate AccountMapper accountMapper;@Overridepublic Account selectByActno(String actno) {Account account = accountMapper.selectByActno(actno);return account;}@Overridepublic int updateByActno(Account account) {int count = accountMapper.updateByActno(account);return count;}@Override@Transactionalpublic void transfer(String fromActno,String toActno,double money)throws MoneyNotEnoughException,TransferException {Account fromAct = selectByActno(fromActno);if(fromAct.getBalance() < money) throw new MoneyNotEnoughException("对不起,余额不足");Account toAct = selectByActno(toActno);fromAct.setBalance(fromAct.getBalance() - money);toAct.setBalance(toAct.getBalance() + money);int count = updateByActno(fromAct);count += updateByActno(toAct);if(count != 2) throw new TransferException("转账异常,未知原因");}
}

⑤根据想抛出的异常去编写异常类

public class MoneyNotEnoughException extends Exception{public MoneyNotEnoughException(){}public MoneyNotEnoughException(String msg){super(msg);}
}
public class TransferException extends Exception{public TransferException(){}public TransferException(String msg){super(msg);}
}

⑥数据层和业务层方法实现后在表示层编写

@Controller
public class AccountController {@Autowiredprivate AccountService accountService;@PostMapping("/bank")public String transfer(String fromActno, String toActno, double money) {double m = Double.parseDouble(String.valueOf(money));try {accountService.transfer(fromActno,toActno,m);return "redirect:/success.html";} catch (MoneyNotEnoughException | TransferException e) {e.printStackTrace();return "redirect:/error.html";}}
}

2、MyBatis小技巧

一、#{}与${}及模糊查询

这里就不多写了,详细可以看我博客另外一条文章:花了几天整理了学完的Mybatis框架(内含源码及面试题)_慢慢ovo的博客-CSDN博客

放一点笔记出来品品

<mapper namespace="com.chf.mapper.CarMapper"><!--这里是不能使用#{} 否则会以字符串形式放在Sql语句当中 只能使用${}#{}是防止Sql注入风险的 是以值的方式放到Sql语句当中如果需要的Sql语句的关键字(固定值)放到Sql语句当中只能使用${}--><select id="selectAllByAscOrDesc" resultType="Car">selectid,car_num carNum,brand,guide_price guidePrice,produce_time produceTime,car_type carTypefromt_carorder byproduce_time ${ascOrDesc}</select><select id="selectByCarType" resultType="Car">selectid,car_num carNum,brand,guide_price guidePrice,produce_time produceTime,car_type carTypefromt_carwherecar_type=#{carType}</select><!--delete from t_car where id in (?,?)--><delete id="deleteBatch">delete from t_carwhere id in (${ids})</delete><select id="selectByBrandLike" resultType="Car">selectid,car_num carNum,brand,guide_price guidePrice,produce_time produceTime,car_type carTypefromt_carwhere<!--brand like '%${brand}%'--><!--brand like concat('%',#{brand},'%')-->brand like "%"#{brand}"%"</select>
</mapper>

二、别名机制与mapper标签

mybatis:#基于SpringBoot的mapper标签mapper-locations: classpath:mapper/*.xml#基于SpringBoot的别名机制用于配合xml中的resultTypetype-aliases-package: com.chf.pojoconfiguration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
<!--MyBatis核心配置文件中的--><typeAliases><!--别名自己指定的--><typeAlias type="com.chf.pojo.Car" alias="aaa"/><typeAlias type="com.chf.pojo.Log" alias="bbb"/><!--采用默认的别名机制--><typeAlias type="com.chf.pojo.Car"/><typeAlias type="com.chf.pojo.Log"/><!--包下所有的类自动起别名。使用简名作为别名。--><package name="com.chf.pojo"/></typeAliases><!--所有别名不区分大小写。namespace不能使用别名机制。--><mapper resource="CarMapper.xml"/>       <!--要求类的根路径下必须有:CarMapper.xml--><mapper url="file:///d:/CarMapper.xml"/> <!--要求在d:/下有CarMapper.xml文件--><mapper class="全限定接口名,带有包名"/><!--mapper标签的属性可以有三个:resource:这种方式是从类的根路径下开始查找资源。采用这种方式的话,你的配置文件需要放到类路径当中才行。url: 这种方式是一种绝对路径的方式,这种方式不要求配置文件必须放到类路径当中,哪里都行,只要提供一个绝对路径就行。这种方式使用极少,因为移植性太差。class: 这个位置提供的是mapper接口的全限定接口名,必须带有包名的。思考:mapper标签的作用是指定SqlMapper.xml文件的路径,指定接口名有什么用呢?<mapper class="com.chf.mapper.CarMapper"/>如果你class指定是:com.chf.mapper.CarMapper那么mybatis框架会自动去com/chf/mapper目录下查找CarMapper.xml文件。注意:也就是说:如果你采用这种方式,那么你必须保证CarMapper.xml文件和CarMapper接口必须在同一个目录下。并且名字一致。CarMapper接口-> CarMapper.xmlLogMapper接口-> LogMapper.xml....-->

三、插入使用生成的主键值

@Mapper
public interface CarMapper{/*** 插入车辆信息并且使用生成的主键值* @param car* @return*/int insertCarUseGeneratedKeys(Car car);
}

3、MyBatis参数处理

一、单个简单类型参数

简单类型包括:

  • 七种数据类型(除了boolean)以及他们的包装类

  • String

  • java.util.Date

  • java.sql.Date

@Mapper
public interface StudentMapper {/*** 当接口的方法的参数只有一个,并且参数的数据类型都是简单类型* 根据id、name、birth、sex查询*/List<Student> selectById(Long id);List<Student> selectByName(String name);List<Student> selectByBirth(Date birth);List<Student> selectBySex(Character sex);
}

parameterType属性的作用: 告诉MyBatis框架这个方法的参数类型是什么类型 MyBatis框架自身带有类型自动推断机制,所以大部分情况下parameterType属性都是可以省略不写的

二、Map参数

①、插入信息

@Mapper
public interface StudentMapper{/*** 保存学生信息,通过Map参数,以下是单个参数,但是参数的类型不是简单类型,是Map集合* @param map* @return*/int insertStudentByMap(Map<String,Object> map);
}
<mapper namespace="com.chf.mapper.StudentMapper"><insert id="insertStudentByMap" parameterType="map">insert intot_studentvalues(null,#{姓名},#{年龄},#{身高},#{生日},#{性别})</insert>
</mapper>

②、查询单个汽车信息

@Mapper
public interface CarMapper{/*** 根据id获取汽车信息,将汽车信息放到Map集合中* @param id* @return*/Map<String,Object> selectByIdRetMap(Long id);
}
<mapper namespace="com.chf.mapper.CarMapper"><select id="selectByIdRetMap" resultType="map">selectid,car_num carNum,brand,guide_price guidePrice,produce_time produceTime,car_type carTypefromt_carwhereid = #{id}</select>
</mapper>

③、返回多个Map

查询结果大于等于1条数据,则可以返回一个存储Map集合的List集合,List<Map>等同于List<Car>

@Mapper
public interface CarMapper{/*** 查询所有的Car信息返回一个放Map集合的List集合* @return*/List<Map<String,Object>> selectAllRetListMap();
}
<mapper namespace="com.chf.mapper.CarMapper"><!--注意:这个resultType是map不是list--><select id="selectAllRetListMap" resultType="map">selectid,car_num carNum,brand,guide_price guidePrice,produce_time produceTime,car_type carTypefromt_car</select>
</mapper>

④、返回Map<String,Map>

通过Car的id做Key,以后取出对应的Map集合时更方便

@Mapper
public interface CarMapper{/*** 查询所有的Car返回一个大Map结合* Map集合的key是每条记录的主键值* Map集合的value的每条记录* @return*/@MapKey("id")Map<Long,Map<String,Object>> selectAllRetMap();
}
<mapper namespace="com.chf.mapper.CarMapper"><select id="selectAllRetMap" resultType="map">selectid,car_num carNum,brand,guide_price guidePrice,produce_time produceTime,car_type carTypefromt_car</select>
</mapper>

{

1={carType=燃油车, carNum=1001, guidePrice=10.00, produceTime=2022-10-1, id=1, brand=宝马520Li},

2={carType=新能源, carNum=1002, guidePrice=55.00, produceTime=2022-10-2, id=2, brand=奔驰E300L},

6={carType=新能源, carNum=1111, guidePrice=30.00, produceTime=2022-10-3, id=6, brand=奔驰},

7={carType=新能源, carNum=111, guidePrice=30.00, produceTime=2022-10-2, id=7, brand=奔驰},

10={carType=新能源, carNum=111, guidePrice=30.00, produceTime=2022-10-2, id=10, brand=奔驰},

11={carType=新能源, carNum=111, guidePrice=30.00, produceTime=2022-10-8, id=11, brand=奔驰}

}

三、实体类参数

@Mapper
public interface StudentMapper{/*** 保存学生信息,通过POJO参数,Student是单个参数,但不是简单类型* @param student* @return*/int insertStudentByPOJO(Student student);
}
<mapper namespace="com.chf.mapper.StudentMapper"><insert id="insertStudentByPOJO">insert intot_studentvalues(null,#{name},#{age},#{height},#{birth},#{sex})</insert>
</mapper>

四、多参数(@Param)

不需要使用arg0、arg1、param1、param2等等,直接使用@Param注解增强可读性

需求:根据name和age查询学生信息

@Mapper
public interface StudentMapper{/*** 这是多参数查询* 根据name和sex查询Student信息* 如果是多个参数的话,MyBatis框架底层的做法如下:*     MyBatis框架会自动创建一个Map集合并且Map集合是以这种方式存储参数的*          map.put("arg0",name);/map.put("param1",name);*          map.put("arg1",sex);/map.put("param2",sex);*          * 使用Param注解指定Sql语句中的#{}命名* @param name* @param sex* @return*/List<Student> selectByNameAndSex(@Param("nnn") String name,@Param("sss") Character sex);
}

五、resultMap结果映射

①、使用resultMap进行结果映射(常用)

查询结果的列名和java对象的属性名对应不上的做法?

  • 第一种方式:as 给列起别名 as可以省略不写,我们前面的做法就是如此

  • 第二种方式:使用resultMap进行结果映射

  • 第三种方式:是否开启驼峰命名自动映射(设置settings)

在一对标签中resultType和resultMap两者只能有一个 当查询要返回对象,

而且属性和字段不一致的时候用resultMap。 

@Mapper
public interface CarMapper{/*** 查询所有的Car信息,使用resultMap标签进行结果映射* @return*/List<Car> selectAllByResultMap();
}
<mapper namespace="com.chf.mapper.CarMapper"><!--1、专门定义一个结果映射,在这个结果映射当中指定数据库表的字段名和Java类的属性名的对应关系2、type属性:用来指定POJO类的类名3、id属性:指定resultMap的唯一标识,这个id将来要在select标签中使用--><resultMap id="carResultMap" type="Car"><!--如果数据库表中有主键,一般都是有主键,要不然不符合数据库设计第一范式--><!--如果有主键,建议这里配置一个id标签,这样的配置可以让MyBatis提高效率--><id property="id" column="id" /><!--property后面填写的是POJO类的属性名column后面填写数据库表的字段名--><result property="carNum" column="car_num" /><result property="guidePrice" column="guide_price" /><result property="produceTime" column="produce_time" /><result property="carType" column="car_type" /></resultMap><!--select标签中的resultMap属性用来指定使用哪个结果映射,resultMap后面的值是resultMap的id--><select id="selectAllByResultMap" resultMap="carResultMap">selectid,car_num,brand,guide_price,produce_time,car_typefromt_car</select>
</mapper>

②、开启驼峰命名规范自动映射

使用这种方式的前提是:属性名遵循Java的命名规范,数据库表的列名遵循SQL的命名规范。

Java命名规范:首字母小写,后面每个单词首字母大写,遵循驼峰命名方式。

SQL命名规范:全部小写,单词之间采用下划线分割。

mybatis:  mapper-locations: classpath:mapper/*.xml  type-aliases-package: com.chf.pojo  configuration:    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl    #开启驼峰自动映射    map-underscore-to-camel-case: true
@Mapper
public interface CarMapper{List<Car> selectAllByResultMapTwo();
}
<mapper namespace="com.chf.mapper.CarMapper"><select id="selectAllByResultMap" resultType="Car">selectid,car_num,brand,guide_price,produce_time,car_typefromt_car</select>
</mapper>

六、获取总记录条数

@Mapper
public interface CarMapper{/*** 获取Car的总记录条数* @return*/Long selectTotal();
}
<mapper namespace="com.chf.mapper.CarMapper"><select id="selectTotal" resultType="long">selectcount(*)fromt_car</select>
</mapper>

4、动态SQL(注:使用了驼峰命名规范)

什么是动态SQL?

  • SQL的内容是变化的, 可以根据条件获取到不同的SQL语句 主要是where部分发生变化。 动态SQL的实现, 使用的是MyBatis提供的标签

为什么使用动态SQL

  • 使用动态SQL可以解决某些功能的使用 例如使用条件查询某个商品 输入价格,地区等等进行筛选,如果使用静态SQL可能会查询出来的是一个空内容 但使用动态SQL可以很好的解决这种问题

动态SQL的标签:

一、if标签

@Mapper
public interface CarMapper {/*** 多条件查询* @param brand 品牌* @param guidePrice 指导价* @param carType 汽车类型* @return*/List<Car> selectByMultiCondition(@Param("brand") String brand,@Param("guidePrice") Double guidePrice,@Param("carType") String carType);
}
<mapper namespace="com.chf.mapper.CarMapper"><!--1、if标签中的test属性是必须的2、if标签中test属性的值是false或者是true3、如果为true,则if标签中的sql语句就会拼接。反之就不会拼接4、test属性中可以使用的是:当使用了@Param注解,那么test中要出现的是@Param注解指定的参数名。当没有使用@Param注解,那么test中要出现的是:param1 param2 param3 ...当使用了POJO,那么test中出现的是POJO类的属性名5、在MyBatis的动态SQL中,不能使用&&,使用的是and6、标签内与#{}内写的都是POJO属性名,其余是SQL的字段名7、注意:这里我在yml文件配置了MyBatis的自动驼峰命名规范,所以不用使用as重新命名8、这个1 = 1是防止后面出现空传值导致SQL语句出现错误--><select id="selectByMultiCondition" resultType="Car">selectid,car_num,brand,guide_price,produce_time,car_typefromt_carwhere1 = 1<if test="brand != null and brand != ''">and brand like "%"#{brand}"%"</if><if test="guidePrice != null and guidePrice != ''">and guide_price >= #{guidePrice}</if><if test="carType != null and carType != ''">and car_type like "%"#{carType}"%"</if></select>
</mapper>

二、where标签

where标签的作用:让where子句更加动态智能。

  • 所有条件都有空时,where标签保证不会生成where子句。

  • 自动去除某些条件前面多余的and或or

@Mapper
public interface CarMapper {/*** 使用where标签,让where子句更加的智能* @param brand* @param guidePrice* @param carType* @return*/List<Car> selectByMultiConditionWithWhere(@Param("brand") String brand,@Param("guidePrice") Double guidePrice,@Param("carType") String carType);
}
<mapper namespace="com.chf.mapper.CarMapper"><!--where标签是专门负责where子句动态生成的这里将1 = 1去掉了并且第一个if标签语句还有"and"关键字 但丝毫不影响SQL查询语句但要注意:不能在语句后面加"and"比如:and brand like "%"#{brand}"%" and--><select id="selectByMultiConditionWithWhere" resultType="Car">selectid,car_num,brand,guide_price,produce_time,car_typefromt_car<where><if test="brand != null and brand != ''">and brand like "%"#{brand}"%"</if><if test="guidePrice != null and guidePrice != ''">and guide_price >= #{guidePrice}</if><if test="carType != null and carType != ''">and car_type like "%"#{carType}"%"</if></where></select>
</mapper>

三、trim标签

@Mapper
public interface CarMapper {/*** 使用trim标签* @param brand* @param guidePrice* @param carType* @return*/List<Car> selectByMultiConditionWithTrim(@Param("brand") String brand,@Param("guidePrice") Double guidePrice,@Param("carType") String carType);
}
<mapper namespace="com.chf.mapper.CarMapper"><!--prefix:加前缀suffix:加后缀prefixOverrides:删除前缀suffixOverrides:删除后缀以下表示在trim标签所有内容的前面添加where,后缀的and或者or去掉--><select id="selectByMultiConditionWithTrim" resultType="Car">selectid,car_num,brand,guide_price,produce_time,car_typefromt_car<trim prefix="where" suffixOverrides="and|or"><if test="brand != null and brand != ''">brand like "%"#{brand}"%" and</if><if test="guidePrice != null and guidePrice != ''">guide_price >= #{guidePrice} and</if><if test="carType != null and carType != ''">car_type like "%"#{carType}"%"</if></trim></select>
</mapper>

四、set标签

主要使用在update语句当中,用于生成set关键字,同时去掉最后多余的","

比如我们只更新提交的不为空的字段,如果提交的数据是空或者"",那么这个字段我们将不更新。

@Mapper
public interface CarMapper {/*** 使用set标签进行更新* @param car* @return*/int updateBySet(Car car);
}
<mapper namespace="com.chf.mapper.CarMapper"><update id="updateBySet">updatet_car<set><if test="carNum != null and carNum != ''">car_num = #{carNum},</if><if test="brand != null and brand != ''">brand = #{brand},</if><if test="guidePrice != null and guidePrice != ''">guide_price = #{guidePrice},</if><if test="produceTime != null and produceTime != ''">produce_time = #{produceTime},</if><if test="carType != null and carType != ''">car_type = #{carType}</if></set>whereid = #{id}</update>
</mapper>

五、choose where otherwise

这三个标签是在一起使用的

Mapper映射语法格式:

<choose><when></when><when></when><otherwise></otherwise>
</choose>

等同于Code语法格式:

if(){}else if(){}else if(){}else{}
@Mapper
public interface CarMapper {/*** 使用choose when otherwise标签* @param brand* @param guidePrice* @param carType* @return*/List<Car> selectByChoose(@Param("brand") String brand,@Param("guidePrice") Double guidePrice,@Param("carType") String carType);
}
<mapper namespace="com.chf.mapper.CarMapper"><select id="selectByChoose" resultType="Car">selectid,car_num,brand,guide_price,produce_time,car_typefromt_car<where><choose><when test="brand != null and brand != ''">brand like "%"#{brand}"%"</when><when test="guidePrice != null and guidePrice != ''">guide_price >= #{guidePrice}</when><otherwise>car_type like "%"#{carType}"%"</otherwise></choose></where></select>
</mapper>

六、foreach标签

①批量删除

@Mapper
public interface CarMapper {/*** 根据id批量删除 foreach* @param ids* @return*/int deleteByIds(@Param("ids") Long[] ids);
}
<mapper namespace="com.chf.mapper.CarMapper"><!--foreach标签的属性:collection:指定数组或者集合item:代表数组或集合中的元素separator:循环之间的分隔符open:在标签先添加的符号close:在标签后添加的符号--><update id="deleteByIds">delete fromt_carwhereid in<foreach collection="ids" item="aaa" separator="," open="(" close=")">#{aaa}</foreach></update>
</mapper>

②批量添加

@Mapper
public interface CarMapper {/*** 批量插入,一次插入多条Car信息* @param cars* @return*/int insertBatch(@Param("cars") List<Car> cars);
}
<mapper namespace="com.chf.mapper.CarMapper"><insert id="insertBatch">insert intot_carvalues<foreach collection="cars" item="car" separator=",">(null,#{car.carNum},#{car.brand},#{car.guidePrice},#{car.produceTime},#{car.carType})</foreach></insert>
</mapper>

七、sql、include标签

sql标签用来声明sql片段

include标签用来将声明的sql片段包含到某个sql语句当中

作用:代码复用、易维护

在我跟着老杜学的MyBatis中。他提过一句查询语句最好不要使用星号,因为这会使MySQL索引失效从而导致查询性能下降。所以我上面的笔记没有使用到星号,都是用具体字段进行查询。

<mapper namespace="com.chf.mapper.CarMapper"><!--声明一个SQL片段--><sql id="carColumnNameSql">id,car_num,brand,guide_price,produce_time,car_type</sql><!--将声明的SQL片段包含进来--><select id="selectAll" resultType="Car">select<include refid="carColumnNameSql" />fromt_car</select>
</mapper>

5、高级映射及延迟加载

一、多对一

多种方式,常见的包括三种:

  • 第一种方式:一条SQL语句,级联属性映射

  • 第二种方式:一条SQL语句,association

  • 第三种方式(常用):两条SQL语句,分步查询。 优点:可复用、支持懒加载

表的结构如下:

两个实体类如下:

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Student {  //Student是多的一方private Integer sid;private String sname;private Class clazz;  //clazz是一的一方
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Class { //教室类private Integer cid;private String cname;
}

第一种方式:级联属性映射

@Mapper
public interface StudentMapper {/*** 根据id获取学生信息,同时获取学生关联的班级信息* @param id 学生的id* @return 学生对象,但是学生对象当中含有班级对象*/Student selectById(Integer id);
}
<mapper namespace="com.chf.mapper.StudentMapper"><!--记住:前者是属性名,后者是字段名 前面了解过 这里再复习一下--><!--多对一映射的第一种方式:一条SQL语句,级联属性映射--><resultMap id="studentResultMap" type="Student"><id property="sid" column="sid" /><result property="sname" column="sname" /><result property="clazz.cid" column="cid" /><result property="clazz.cname" column="cname" /></resultMap><select id="selectById" resultMap="studentResultMap">selects.sid,s.sname,c.cid,c.cnamefromt_stu sleft joint_class cons.cid = c.cidwheres.sid = #{sid}</select>
</mapper>

第二种方式:association

@Mapper
public interface StudentMapper {/*** 一条SQL语句,association* @param id* @return*/Student selectByIdAssociation(Integer id);
}
<mapper namespace="com.chf.mapper.StudentMapper"><!--association翻译为关联,一个Student对象关联一个Class对象property:提供要映射的POJO的参数名javaType:用来指定要映射的java类型--><resultMap id="studentResultMapAssociation" type="Student"><id property="sid" column="sid" /><result property="sname" column="sname" /><association property="clazz" javaType="Class"><id property="cid" column="cid" /><result property="cname" column="cname" /></association></resultMap><select id="selectByIdAssociation" resultMap="studentResultMapAssociation">selects.sid,s.sname,c.cid,c.cnamefromt_stu sleft joint_class cons.cid = c.cidwheres.sid = #{sid}</select>
</mapper>

第三种方式:分步查询

@Mapper
public interface StudentMapper {/*** 分步查询第一步:先根据学生的sid查询学生的信息* @param id* @return*/Student selectByIdStep1(Integer id);
}
@Mapper
public interface ClassMapper {/*** 分步查询第二步:根据cid获取班级信息* @param id* @return*/Class selectByIdStep2(Integer id);
}
<mapper namespace="com.chf.mapper.StudentMapper"><!--分步查询的有点:第一:复用性增强。可以重复利用(大步分成小步,每一小步更加可以重新利用)第二:可以充分利用他们的延迟加载/懒加载机制--><!--两条SQL语句,完成多对一的多步查询--><!--这里是第一步:根据学生的id查询学生的所有信息,这些信息当中含有班级id(cid)--><resultMap id="studentResultMapByStep" type="Student"><id property="sid" column="sid" /><result property="sname" column="sname" /><association property="clazz"select="com.chf.mapper.ClassMapper.selectByIdStep2"column="cid"/></resultMap><select id="selectByIdStep1" resultMap="studentResultMapByStep">selectsid,sname,cidfromt_stuwheresid = #{sid}</select>
</mapper>
<mapper namespace="com.chf.mapper.ClassMapper"><!--分步查询第二步:根据cid获取班级信息--><select id="selectByIdStep2" resultType="Class">selectcid,cnamefromt_classwherecid = #{cid}</select>
</mapper>

二、多对一延迟加载

实际开发中的模式:

把全局的延迟加载打开,如果某个映射文件不需要那么就在association标签里使用fetchType="eager"关闭

<mapper namespace="com.chf.mapper.StudentMapper"><!--延迟加载的核心机制:用的时候再执行查询语句,不用的时候不查询,可以提高性能。默认情况下是没有开启延迟加载的,需要手动设置开启。开启延迟加载的方法:association标签中添加fetchType="lazy"但是这里只是开启默认的延迟加载,仅局限于此Mapper映射文件,需要在核心配置文件里设置如果开启了全局延迟加载,但又不想在某个映射文件中开启,那么就需要在association标签设置fetchType="eager"--><resultMap id="studentResultMapByStep" type="Student"><id property="sid" column="sid" /><result property="sname" column="sname" /><association property="clazz"select="com.chf.mapper.ClassMapper.selectByIdStep2"column="cid"fetchType="lazy"/></resultMap><select id="selectByIdStep1" resultMap="studentResultMapByStep">selectsid,sname,cidfromt_stuwheresid = #{sid}</select>
</mapper>
mybatis:mapper-locations: classpath:mapper/*.xmltype-aliases-package: com.chf.pojoconfiguration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl#实际开发中,大部分都是需要使用延迟加载的#延迟加载的全局开关,默认值false为不开启lazy-loading-enabled: true

三、一对多

一对多的实现,通常是在一的一方中有List集合属性。

在Class(教室)类中添加List<Student> studentList属性。

一对多的实现通常包括两种实现方式:

  • 第一种方式:collection

  • 第二种方式:分步查询

两个实体类如下:

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Student {  private Integer sid;private String sname;private Class clazz;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Class { //教室类private Integer cid;private String cname;private List<Student> studentList;
}

第一种方式:collection

<mapper namespace="com.chf.mapper.ClassMapper"><resultMap id="classResultMap" type="Class"><id property="cid" column="cid" /><result property="cname" column="cname" /><!--一对多,这里是collection,collection是集合的意思--><!--ofType属性用来指定结合当中的元素类型即集合中的泛型--><collection property="studentList" ofType="Student"><id property="sid" column="sid" /><result property="sname" column="sname" /></collection></resultMap><select id="selectByCollection" resultMap="classResultMap">selectc.cid,c.cname,s.sid,s.snamefromt_class cleft joint_stu sonc.cid = s.cidwherec.cid = #{cid}</select>
</mapper>

注意:控制台输出的clazz=null是没有问题的

第二种方式:分步查询

@Mapper
public interface ClassMapper {/*** 分步查询第一步:根据班级编号获取班级信息* @param cid* @return*/Class selectByStep1(Integer cid);
}
@Mapper
public interface StudentMapper {/*** 根据班级编号查询学生信息* @param cid* @return*/List<Student> selectByStep2(Integer cid);
}
<mapper namespace="com.chf.mapper.ClassMapper"><!--分步查询第一句:根据班级的cid查询班级信息--><resultMap id="classResultMapStep" type="Class"><id property="cid" column="cid" /><result property="cname" column="cname" /><association property="studentList" column="cid"select="com.chf.mapper.StudentMapper.selectByStep2"/></resultMap><select id="selectByStep1" resultMap="classResultMapStep">selectcid,cnamefromt_classwherecid = #{cid}</select>
</mapper>
<mapper namespace="com.chf.mapper.StudentMapper"><!--分步查询第二步:根据传过来的班级编号查询学生信息--><select id="selectByStep2" resultType="Student">selectsid,snamefromt_stuwherecid = #{cid}</select>
</mapper>

四、一对多延迟加载

与上面的多对一延迟加载相同,可以回去重新看一下。

6、MyBatis缓存机制

缓存:cache

缓存的作用:通过减少IO的方式来提高程序的执行效率。

MyBatis的缓存:将select语句的查询结果放到缓存(内存)当中,下一次还是这条select语句的话,直接从缓存中取,不需要查询数据库。一方面是减少了IO,另一方面不再执行繁琐的查找算法,效率大大提升。

MyBatis缓存包括:

  • 一级缓存:将查询到的数据存储到SqlSession中。

  • 二级缓存:将查询到的数据存储到SqlSessionFactory中。

  • 其他集成第三方的缓存:比如EhCache【Java语言开发的】、Memcache【c语言开发的】等。

缓存只针对于DQL语句,也就是说缓存机制只对应select语句

一、一级缓存

一级缓存是默认开启的,不需要做任何配置(后半句指在纯MyBatis框架中)。

它的作用范围是在同一个SqlSession中,即在同一个SqlSession中共享。

原理:只要使用同一个SqlSession对象执行同一条SQL语句就会走缓存

@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Car {private Long id;private String carNum;private String brand;private Double guidePrice;private String produceTime;private String carType;
}
@Mapper
public interface CarMapper {/*** 根据id获取Car信息* @param id* @return*/Car selectById(Long id);
}
<mapper namespace="com.chf.mapper.CarMapper"><sql id="selectAll">id,car_num,brand,guide_price,produce_time,car_type</sql><select id="selectById" resultType="Car">select<include refid="selectAll" />fromt_carwhere id = #{id}</select>
</mapper>

我们会发现在SpringBoot结合MyBatis中没有自动开启一级缓存机制,查询相同的id使用了两次查询。但是我们在方法名上添加@Transactional注解就会发现控制台发生了变化:只执行了一次查询语句。也就是说添加了@Transactional注解就能够使用一级缓存,换言之就是同一个SqlSession。

简单回顾一下在纯MyBatis框架中如何使一级缓存失效:

只要在第一次DQL和第二次DQL之间做了两件事中的任意一件就会使一级缓存清空。

  • 1、执行了SqlSession的clearCache()方法,这是手动清空缓存

  • 2、执行了INSERT或DELETE或UPDATE语句,不管是操作哪张表都会清空缓存

二、二级缓存

二级缓存的范围是SqlSessionFactory

使用二级缓存需要具备以下几个条件:

  • 1、在核心配置文件添加cache-enabled: true(全局性地开启或关闭所以映射器配置文件已配置的任何缓存)

但这是默认开启的,所以可以不用添加

  • 2、在需要的mapper映射文件中的<mapper></mapper>里添加<cache />

  • 3、使用二级缓存的实体类对象必须是可序化的,也就是必须实现java.io.Serializable接口

  • 4、纯MyBatis中需要将SqlSession对象关闭或提交之后,一级缓存才会被写入二级缓存中,此时二级缓存才可用

<mapper namespace="com.chf.mapper.CarMapper"><cache /><sql id="selectAll">id,car_num,brand,guide_price,produce_time,car_type</sql><select id="selectById2" resultType="Car">select<include refid="selectAll" />fromt_carwhereid = #{id}</select>
</mapper>
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Car implements Serializable {private Long id;private String carNum;private String brand;private Double guidePrice;private String produceTime;private String carType;
}

二级缓存的失效:只要两次查询之间出现了增删改操作,当然这样同样使一级缓存失效

7、MyBatis使用PageHelper

这是我在之前学习MyBatis中没有学习到的东西,由于学MyBatisPlus的时候接触到感觉陌生所以这里就重新学了。

一、limit分页

回顾MySQL的limit后面两个数字:

  • 第一个数字:startIndex(起始下标,下标从0开始)

  • 第二个数字:pageSize(每页显示的记录条数)

假设已知页码pageNum,还有每页显示的记录条数pageSize,第一个数字如何动态获取?

  • startIndex = (pageSize - 1) * pageSize

@Mapper
public interface CarMapper {/*** 分页查询* @param startIndex 起始下标* @param pageSize 每页显示的记录条数* @return*/List<Car> selectByPage(@Param("startIndex") int startIndex,@Param("pageSize") int pageSize);
}
<mapper namespace="com.chf.mapper.CarMapper"><sql id="selectAllColumn">id,car_num,brand,guide_price,produce_time,car_type</sql><select id="selectByPage" resultType="Car">select<include refid="selectAllColumn" />fromt_carlimit#{startIndex},#{pageSize}</select>
</mapper>

二、PageHelper插件

使用PageHelper插件进行分页更加的快捷。

直接引入依赖即可,不需要配置核心配置文件

<dependency><groupId>com.github.pagehelper</groupId><artifactId>pagehelper-spring-boot-starter</artifactId><version>1.2.12</version>
</dependency>

这个极其重要,需要在核心启动类Application中的@SpringBootApplication注解后面添加

@SpringBootApplication(exclude = PageHelperAutoConfiguration.class)

接下来就可以进行我们的测试了。

PageInfo

{

pageNum=1, pageSize=3, size=3, startRow=0, endRow=2, total=3, pages=1,

list=[Car(id=1, carNum=1001, brand=宝马520Li, guidePrice=10.0, produceTime=2022-10-1, carType=燃油车), Car(id=2, carNum=1002, brand=奔驰E300L, guidePrice=55.0, produceTime=2022-10-2, carType=新能源), Car(id=6, carNum=1111, brand=奔驰, guidePrice=30.0, produceTime=2022-10-3, carType=燃油车)], prePage=0, nextPage=0, isFirstPage=true, isLastPage=true, hasPreviousPage=false, hasNextPage=false, navigatePages=3, navigateFirstPage=1, navigateLastPage=1, navigatepageNums=[1]

}

SpringBoot结合MyBatis 【超详细】相关推荐

  1. SpringBoot整合Mybatis超详细流程

    SpringBoot整合Mybatis超详细流程 文章目录 SpringBoot整合Mybatis超详细流程 前言 详细流程 0.引入Mybatis 1.创建数据 2.创建程序目录 3.理解后台访问流 ...

  2. MyBatis超详细介绍——SQL语句构建器类

    MyBatis超详细介绍--SQL语句构建器类 (本文作为学习笔记,了解更多请参考:MyBatis参考文档) MyBatis3提供了SQL类帮助构造SQL语句: private String sele ...

  3. IDEA如何运行SpringBoot项目(超详细截图)

    []:hello你好我是辰兮,很高兴你能来阅读,昵称是希望自己能不断精进,向着优秀程序员前行! 博客来源于项目以及编程中遇到的问题总结,偶尔会有读书分享,我会陆续更新Java前端.后台.数据库.项目案 ...

  4. 如何在idea中创建一个SpringBoot项目(超详细教学)

    如何在idea中创建一个SpringBoot项目 目录 环境准备 1.1打开IDEA,点击New新建一个项目 1.2 使用Spring Initializr方式构建Spring Boot项目 1.3 ...

  5. MyBatis超详细笔记

    文章目录 1.MyBatis入门 1.1 **简介**: 1.2 持久层 1.3 为什么需要MyBatis? 2.第一个MyBatis程序 2.1 搭建环境 2.2编写代码 2.3 测试 3. CRU ...

  6. Springboot介绍(超详细)

    目录 一.Spring和Springboot的区别及其注意事项什么? 二.忽略文件技巧,这样就可以减少很多不必要的文件生成 三.SpringBoot  Parent讲解 四.stater的介绍 五.引 ...

  7. mybatis超详细的笔记记录

    什么是 MyBatis? MyBatis 是一款优秀的持久层框架,它支持自定义 SQL.存储过程以及高级映射.MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作.MyBat ...

  8. mybatis(超详细,常用)

    1. mybatis常用标签 基本的就不说了,主要记录一下动态sql的 1.if 标签 if标签通常用于WHERE语句.UPDATE语句.INSERT语句中,通过判断参数值来决定是否使用某个查询条件. ...

  9. SpringBoot整合SpringSecurity [超详细] (一)入门案例

    文章目录 前言 1. 主流安全框架有哪些 2. Spring Security为什么越来越火 3. 为什么现在越来越多的人选择Spring Security 一.Spring Security 简介 ...

  10. SpringBoot整合Mybatis完整详细版

    原文地址:https://blog.csdn.net/iku5200/article/details/82856621

最新文章

  1. linux 下 iscsi的简单使用
  2. python文件行数统计_文件行数和代码行数统计
  3. yolo v4 python_YOLOv4: Darknet 如何于 Ubuntu 编译,及使用 Python 接口
  4. 内存颗粒和闪存颗粒的区别_NAND Flash闪存颗粒与SSD知识深度解析
  5. 什么是微型计算机的组成原理,1微型计算机基本组成原理.ppt
  6. 【14:00开播】国际顶会强力加持,算法“视”界杯巅峰之战拉开帷幕!
  7. 【STL】List基础
  8. ubuntu 搭建 php 环境
  9. Swift TouchId指纹解锁,FaceId面部解锁
  10. 哈尔滨计算机工程黑icp备,关于组织我校青年教师及研究生参加2017年中、俄、蒙、德计算机科学、计算机工程及教育技术国际夏令营活动的通知...
  11. 宏基4750网卡驱动linux,宏基4750g驱动下载-宏基4750g网卡驱动程序官方版 - 极光下载站...
  12. ES8316耳机驱动可以差分输入支持录音PEQ调节
  13. excel复制后卡死的解决办法
  14. NVIDIA Maxine 姿势识别极简教程
  15. 熊猫烧香、威金的解决办法
  16. 2019UI设计岗位最常见的面试题
  17. 谷歌浏览器如何收藏网站 谷歌浏览器收藏网站的方法步骤
  18. 我的世界服务器怎么制作头颅,我的世界怎么用指令弄自己的头颅 | 手游网游页游攻略大全...
  19. 统驭科目(Reconciliation account)
  20. 电话和互联网使用:首次移动电话数下降

热门文章

  1. 安卓7.0以后如何开启手电筒
  2. Invalid -Xlog option ‘-Xlog:gc*,gc+age=trace,safepoint:file=logs/gc.log:utctime,pid,tags:filecount=3
  3. 学java选i5还是i7_选笔记本电脑,到底CPU是要选i5还是i7
  4. ClassLoader加载类时序图及Qzon修复流程图
  5. Svchost.exe 程序占用CPU高
  6. 【Day1】一小时入门 python 基础,从安装到入门
  7. 2020年度中国“最佳女性投资人”榜单发布
  8. 桌面上的Internet explorer图标删不掉
  9. 【SRE笔记 2022.8.16 Linux命令基础01】
  10. 使用ar_receipt_api_pub.apply失误