定制设计SpringBoot结合MyBatis 【超详细】


1、SpringBoot+老杜

一、定制设计简单回顾一下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就需要了

二、快速入门

第一步:引入依赖

  1. <!--junit测试依赖-->
  2. <dependency>
  3. <groupId>junit</groupId>
  4. <artifactId>junit</artifactId>
  5. <scope>test</scope>
  6. </dependency>
  7. <!--lombok依赖,定制设计是为了简化实体类的编写代码量-->
  8. <dependency>
  9. <groupId>org.projectlombok</groupId>
  10. <artifactId>lombok</artifactId>
  11. </dependency>

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

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

  1. spring:
  2. datasource:
  3. driver-class-name: com.mysql.cj.jdbc.Driver
  4. url: jdbc:mysql://localhost:3306/powernode
  5. username: root
  6. password: root
  7. mybatis:
  8. mapper-locations: classpath:mapper/*.xml
  9. #定制设计目的是为了省略resultType定制设计里的代码量
  10. type-aliases-package: com.chf.pojo
  11. configuration:
  12. log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

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

第四步:创建接口,定制设计用来写方法  

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

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

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

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

三、定制设计简易插入删除更改

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

四、查询

①、按其中一个字段查询

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

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

②、按所有字段进行查询

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

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

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

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

六、结合Web及SpringMVC

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

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

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

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

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

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

  1. @Data
  2. @AllArgsConstructor
  3. @NoArgsConstructor
  4. @ToString
  5. public class Account {
  6. private Long id;
  7. private String actno;
  8. private Double balance;
  9. }

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

  1. @Mapper
  2. public interface AccountMapper {
  3. /**
  4. * 根据账号查询账户信息
  5. * @param actno
  6. * @return
  7. */
  8. Account selectByActno(String actno);
  9. /**
  10. * 更新账户信息
  11. * @param account
  12. * @return
  13. */
  14. int updateByActno(Account account);
  15. }

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

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

  1. public interface AccountService {
  2. /**
  3. * 根据账号查询账户信息
  4. * @param actno
  5. * @return
  6. */
  7. Account selectByActno(String actno);
  8. /**
  9. * 更新账户信息
  10. * @param account
  11. * @return
  12. */
  13. int updateByActno(Account account);
  14. /**
  15. * 账户转账业务。
  16. * @param fromActno 转出账号
  17. * @param toActno 转入账号
  18. * @param money 转账金额
  19. */
  20. void transfer(String fromActno, String toActno, double money)
  21. throws MoneyNotEnoughException, TransferException;
  22. }
  1. @Service
  2. public class AccountServiceImpl implements AccountService {
  3. @Autowired
  4. private AccountMapper accountMapper;
  5. @Override
  6. public Account selectByActno(String actno) {
  7. Account account = accountMapper.selectByActno(actno);
  8. return account;
  9. }
  10. @Override
  11. public int updateByActno(Account account) {
  12. int count = accountMapper.updateByActno(account);
  13. return count;
  14. }
  15. @Override
  16. @Transactional
  17. public void transfer(String fromActno,String toActno,double money)
  18. throws MoneyNotEnoughException,TransferException {
  19. Account fromAct = selectByActno(fromActno);
  20. if(fromAct.getBalance() < money) throw new MoneyNotEnoughException("对不起,余额不足");
  21. Account toAct = selectByActno(toActno);
  22. fromAct.setBalance(fromAct.getBalance() - money);
  23. toAct.setBalance(toAct.getBalance() + money);
  24. int count = updateByActno(fromAct);
  25. count += updateByActno(toAct);
  26. if(count != 2) throw new TransferException("转账异常,未知原因");
  27. }
  28. }

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

  1. public class MoneyNotEnoughException extends Exception{
  2. public MoneyNotEnoughException(){}
  3. public MoneyNotEnoughException(String msg){
  4. super(msg);
  5. }
  6. }
  7. public class TransferException extends Exception{
  8. public TransferException(){}
  9. public TransferException(String msg){
  10. super(msg);
  11. }
  12. }

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

  1. @Controller
  2. public class AccountController {
  3. @Autowired
  4. private AccountService accountService;
  5. @PostMapping("/bank")
  6. public String transfer(String fromActno, String toActno, double money) {
  7. double m = Double.parseDouble(String.valueOf(money));
  8. try {
  9. accountService.transfer(fromActno,toActno,m);
  10. return "redirect:/success.html";
  11. } catch (MoneyNotEnoughException | TransferException e) {
  12. e.printStackTrace();
  13. return "redirect:/error.html";
  14. }
  15. }
  16. }

2、MyBatis小技巧

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

这里就不多写了,详细可以看我博客另外一条文章:

放一点笔记出来品品

  1. <mapper namespace="com.chf.mapper.CarMapper">
  2. <!--
  3. 这里是不能使用#{} 否则会以字符串形式放在Sql语句当中 只能使用${}
  4. #{}是防止Sql注入风险的 是以值的方式放到Sql语句当中
  5. 如果需要的Sql语句的关键字(固定值)放到Sql语句当中只能使用${}
  6. -->
  7. <select id="selectAllByAscOrDesc" resultType="Car">
  8. select
  9. id,
  10. car_num carNum,
  11. brand,
  12. guide_price guidePrice,
  13. produce_time produceTime,
  14. car_type carType
  15. from
  16. t_car
  17. order by
  18. produce_time ${ascOrDesc}
  19. </select>
  20. <select id="selectByCarType" resultType="Car">
  21. select
  22. id,
  23. car_num carNum,
  24. brand,
  25. guide_price guidePrice,
  26. produce_time produceTime,
  27. car_type carType
  28. from
  29. t_car
  30. where
  31. car_type=#{carType}
  32. </select>
  33. <!--delete from t_car where id in (?,?)-->
  34. <delete id="deleteBatch">
  35. delete from t_car
  36. where id in (${ids})
  37. </delete>
  38. <select id="selectByBrandLike" resultType="Car">
  39. select
  40. id,
  41. car_num carNum,
  42. brand,
  43. guide_price guidePrice,
  44. produce_time produceTime,
  45. car_type carType
  46. from
  47. t_car
  48. where
  49. <!--brand like '%${brand}%'-->
  50. <!--brand like concat('%',#{brand},'%')-->
  51. brand like "%"#{brand}"%"
  52. </select>
  53. </mapper>

 

二、别名机制与mapper标签

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

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

  1. @Mapper
  2. public interface CarMapper{
  3. /**
  4. * 插入车辆信息并且使用生成的主键值
  5. * @param car
  6. * @return
  7. */
  8. int insertCarUseGeneratedKeys(Car car);
  9. }

3、MyBatis参数处理

一、单个简单类型参数

简单类型包括:

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

  • String

  • java.util.Date

  • java.sql.Date

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

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

二、Map参数

①、插入信息

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

②、查询单个汽车信息

  1. @Mapper
  2. public interface CarMapper{
  3. /**
  4. * 根据id获取汽车信息,将汽车信息放到Map集合中
  5. * @param id
  6. * @return
  7. */
  8. Map<String,Object> selectByIdRetMap(Long id);
  9. }
  1. <mapper namespace="com.chf.mapper.CarMapper">
  2. <select id="selectByIdRetMap" resultType="map">
  3. select
  4. id,
  5. car_num carNum,
  6. brand,
  7. guide_price guidePrice,
  8. produce_time produceTime,
  9. car_type carType
  10. from
  11. t_car
  12. where
  13. id = #{id}
  14. </select>
  15. </mapper>

③、返回多个Map

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

  1. @Mapper
  2. public interface CarMapper{
  3. /**
  4. * 查询所有的Car信息返回一个放Map集合的List集合
  5. * @return
  6. */
  7. List<Map<String,Object>> selectAllRetListMap();
  8. }
  1. <mapper namespace="com.chf.mapper.CarMapper">
  2. <!--注意:这个resultType是map不是list-->
  3. <select id="selectAllRetListMap" resultType="map">
  4. select
  5. id,
  6. car_num carNum,
  7. brand,
  8. guide_price guidePrice,
  9. produce_time produceTime,
  10. car_type carType
  11. from
  12. t_car
  13. </select>
  14. </mapper>

 ④、返回Map<String,Map>

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

  1. @Mapper
  2. public interface CarMapper{
  3. /**
  4. * 查询所有的Car返回一个大Map结合
  5. * Map集合的key是每条记录的主键值
  6. * Map集合的value的每条记录
  7. * @return
  8. */
  9. @MapKey("id")
  10. Map<Long,Map<String,Object>> selectAllRetMap();
  11. }
  1. <mapper namespace="com.chf.mapper.CarMapper">
  2. <select id="selectAllRetMap" resultType="map">
  3. select
  4. id,
  5. car_num carNum,
  6. brand,
  7. guide_price guidePrice,
  8. produce_time produceTime,
  9. car_type carType
  10. from
  11. t_car
  12. </select>
  13. </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=奔驰}

}

三、实体类参数

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

四、多参数(@Param)

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

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

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

 

五、resultMap结果映射

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

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

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

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

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

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

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

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

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

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

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

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

  1. mybatis:
  2. mapper-locations: classpath:mapper/*.xml
  3. type-aliases-package: com.chf.pojo
  4. configuration:  
  5. log-impl: org.apache.ibatis.logging.stdout.StdOutImpl    
  6. #开启驼峰自动映射  
  7. map-underscore-to-camel-case: true
  1. @Mapper
  2. public interface CarMapper{
  3. List<Car> selectAllByResultMapTwo();
  4. }
  1. <mapper namespace="com.chf.mapper.CarMapper">
  2. <select id="selectAllByResultMap" resultType="Car">
  3. select
  4. id,
  5. car_num,
  6. brand,
  7. guide_price,
  8. produce_time,
  9. car_type
  10. from
  11. t_car
  12. </select>
  13. </mapper>

六、获取总记录条数

  1. @Mapper
  2. public interface CarMapper{
  3. /**
  4. * 获取Car的总记录条数
  5. * @return
  6. */
  7. Long selectTotal();
  8. }
  1. <mapper namespace="com.chf.mapper.CarMapper">
  2. <select id="selectTotal" resultType="long">
  3. select
  4. count(*)
  5. from
  6. t_car
  7. </select>
  8. </mapper>

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

什么是动态SQL?

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

为什么使用动态SQL

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

动态SQL的标签:

一、if标签

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

二、where标签

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

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

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

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

三、trim标签

  1. @Mapper
  2. public interface CarMapper {
  3. /**
  4. * 使用trim标签
  5. * @param brand
  6. * @param guidePrice
  7. * @param carType
  8. * @return
  9. */
  10. List<Car> selectByMultiConditionWithTrim(@Param("brand") String brand,
  11. @Param("guidePrice") Double guidePrice,
  12. @Param("carType") String carType);
  13. }
  1. <mapper namespace="com.chf.mapper.CarMapper">
  2. <!--
  3. prefix:加前缀
  4. suffix:加后缀
  5. prefixOverrides:删除前缀
  6. suffixOverrides:删除后缀
  7. 以下表示在trim标签所有内容的前面添加where,后缀的and或者or去掉
  8. -->
  9. <select id="selectByMultiConditionWithTrim" resultType="Car">
  10. select
  11. id,car_num,brand,guide_price,produce_time,car_type
  12. from
  13. t_car
  14. <trim prefix="where" suffixOverrides="and|or">
  15. <if test="brand != null and brand != ''">
  16. brand like "%"#{brand}"%" and
  17. </if>
  18. <if test="guidePrice != null and guidePrice != ''">
  19. guide_price >= #{guidePrice} and
  20. </if>
  21. <if test="carType != null and carType != ''">
  22. car_type like "%"#{carType}"%"
  23. </if>
  24. </trim>
  25. </select>
  26. </mapper>

四、set标签

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

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

  1. @Mapper
  2. public interface CarMapper {
  3. /**
  4. * 使用set标签进行更新
  5. * @param car
  6. * @return
  7. */
  8. int updateBySet(Car car);
  9. }
  1. <mapper namespace="com.chf.mapper.CarMapper">
  2. <update id="updateBySet">
  3. update
  4. t_car
  5. <set>
  6. <if test="carNum != null and carNum != ''">car_num = #{carNum},</if>
  7. <if test="brand != null and brand != ''">brand = #{brand},</if>
  8. <if test="guidePrice != null and guidePrice != ''">guide_price = #{guidePrice},</if>
  9. <if test="produceTime != null and produceTime != ''">produce_time = #{produceTime},</if>
  10. <if test="carType != null and carType != ''">car_type = #{carType}</if>
  11. </set>
  12. where
  13. id = #{id}
  14. </update>
  15. </mapper>

五、choose where otherwise

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

Mapper映射语法格式:

  1. <choose>
  2. <when></when>
  3. <when></when>
  4. <otherwise></otherwise>
  5. </choose>

等同于Code语法格式:

  1. if(){
  2. }else if(){
  3. }else if(){
  4. }else{
  5. }
  1. @Mapper
  2. public interface CarMapper {
  3. /**
  4. * 使用choose when otherwise标签
  5. * @param brand
  6. * @param guidePrice
  7. * @param carType
  8. * @return
  9. */
  10. List<Car> selectByChoose(@Param("brand") String brand,
  11. @Param("guidePrice") Double guidePrice,
  12. @Param("carType") String carType);
  13. }
  1. <mapper namespace="com.chf.mapper.CarMapper">
  2. <select id="selectByChoose" resultType="Car">
  3. select
  4. id,car_num,brand,guide_price,produce_time,car_type
  5. from
  6. t_car
  7. <where>
  8. <choose>
  9. <when test="brand != null and brand != ''">
  10. brand like "%"#{brand}"%"
  11. </when>
  12. <when test="guidePrice != null and guidePrice != ''">
  13. guide_price >= #{guidePrice}
  14. </when>
  15. <otherwise>
  16. car_type like "%"#{carType}"%"
  17. </otherwise>
  18. </choose>
  19. </where>
  20. </select>
  21. </mapper>

 

六、foreach标签

①批量删除

  1. @Mapper
  2. public interface CarMapper {
  3. /**
  4. * 根据id批量删除 foreach
  5. * @param ids
  6. * @return
  7. */
  8. int deleteByIds(@Param("ids") Long[] ids);
  9. }
  1. <mapper namespace="com.chf.mapper.CarMapper">
  2. <!--
  3. foreach标签的属性:
  4. collection:指定数组或者集合
  5. item:代表数组或集合中的元素
  6. separator:循环之间的分隔符
  7. open:在标签先添加的符号
  8. close:在标签后添加的符号
  9. -->
  10. <update id="deleteByIds">
  11. delete from
  12. t_car
  13. where
  14. id in
  15. <foreach collection="ids" item="aaa" separator="," open="(" close=")">
  16. #{aaa}
  17. </foreach>
  18. </update>
  19. </mapper>

②批量添加

  1. @Mapper
  2. public interface CarMapper {
  3. /**
  4. * 批量插入,一次插入多条Car信息
  5. * @param cars
  6. * @return
  7. */
  8. int insertBatch(@Param("cars") List<Car> cars);
  9. }
  1. <mapper namespace="com.chf.mapper.CarMapper">
  2. <insert id="insertBatch">
  3. insert into
  4. t_car
  5. values
  6. <foreach collection="cars" item="car" separator=",">
  7. (null,#{car.carNum},#{car.brand},#{car.guidePrice},#{car.produceTime},#{car.carType})
  8. </foreach>
  9. </insert>
  10. </mapper>

七、sql、include标签

sql标签用来声明sql片段

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

作用:代码复用、易维护

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

  1. <mapper namespace="com.chf.mapper.CarMapper">
  2. <!--声明一个SQL片段-->
  3. <sql id="carColumnNameSql">
  4. id,
  5. car_num,
  6. brand,
  7. guide_price,
  8. produce_time,
  9. car_type
  10. </sql>
  11. <!--将声明的SQL片段包含进来-->
  12. <select id="selectAll" resultType="Car">
  13. select
  14. <include refid="carColumnNameSql" />
  15. from
  16. t_car
  17. </select>
  18. </mapper>

5、高级映射及延迟加载

一、多对一

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

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

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

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

表的结构如下:

 两个实体类如下:

  1. @Data
  2. @AllArgsConstructor
  3. @NoArgsConstructor
  4. @ToString
  5. public class Student { //Student是多的一方
  6. private Integer sid;
  7. private String sname;
  8. private Class clazz; //clazz是一的一方
  9. }
  10. @Data
  11. @AllArgsConstructor
  12. @NoArgsConstructor
  13. @ToString
  14. public class Class { //教室类
  15. private Integer cid;
  16. private String cname;
  17. }

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

  1. @Mapper
  2. public interface StudentMapper {
  3. /**
  4. * 根据id获取学生信息,同时获取学生关联的班级信息
  5. * @param id 学生的id
  6. * @return 学生对象,但是学生对象当中含有班级对象
  7. */
  8. Student selectById(Integer id);
  9. }
  1. <mapper namespace="com.chf.mapper.StudentMapper">
  2. <!--记住:前者是属性名,后者是字段名 前面了解过 这里再复习一下-->
  3. <!--多对一映射的第一种方式:一条SQL语句,级联属性映射-->
  4. <resultMap id="studentResultMap" type="Student">
  5. <id property="sid" column="sid" />
  6. <result property="sname" column="sname" />
  7. <result property="clazz.cid" column="cid" />
  8. <result property="clazz.cname" column="cname" />
  9. </resultMap>
  10. <select id="selectById" resultMap="studentResultMap">
  11. select
  12. s.sid,s.sname,c.cid,c.cname
  13. from
  14. t_stu s
  15. left join
  16. t_class c
  17. on
  18. s.cid = c.cid
  19. where
  20. s.sid = #{sid}
  21. </select>
  22. </mapper>

第二种方式:association

  1. @Mapper
  2. public interface StudentMapper {
  3. /**
  4. * 一条SQL语句,association
  5. * @param id
  6. * @return
  7. */
  8. Student selectByIdAssociation(Integer id);
  9. }
  1. <mapper namespace="com.chf.mapper.StudentMapper">
  2. <!--
  3. association翻译为关联,一个Student对象关联一个Class对象
  4. property:提供要映射的POJO的参数名
  5. javaType:用来指定要映射的java类型
  6. -->
  7. <resultMap id="studentResultMapAssociation" type="Student">
  8. <id property="sid" column="sid" />
  9. <result property="sname" column="sname" />
  10. <association property="clazz" javaType="Class">
  11. <id property="cid" column="cid" />
  12. <result property="cname" column="cname" />
  13. </association>
  14. </resultMap>
  15. <select id="selectByIdAssociation" resultMap="studentResultMapAssociation">
  16. select
  17. s.sid,s.sname,c.cid,c.cname
  18. from
  19. t_stu s
  20. left join
  21. t_class c
  22. on
  23. s.cid = c.cid
  24. where
  25. s.sid = #{sid}
  26. </select>
  27. </mapper>

 第三种方式:分步查询

  1. @Mapper
  2. public interface StudentMapper {
  3. /**
  4. * 分步查询第一步:先根据学生的sid查询学生的信息
  5. * @param id
  6. * @return
  7. */
  8. Student selectByIdStep1(Integer id);
  9. }
  10. @Mapper
  11. public interface ClassMapper {
  12. /**
  13. * 分步查询第二步:根据cid获取班级信息
  14. * @param id
  15. * @return
  16. */
  17. Class selectByIdStep2(Integer id);
  18. }
  1. <mapper namespace="com.chf.mapper.StudentMapper">
  2. <!--
  3. 分步查询的有点:
  4. 第一:复用性增强。可以重复利用(大步分成小步,每一小步更加可以重新利用)
  5. 第二:可以充分利用他们的延迟加载/懒加载机制
  6. -->
  7. <!--两条SQL语句,完成多对一的多步查询-->
  8. <!--这里是第一步:根据学生的id查询学生的所有信息,这些信息当中含有班级id(cid)-->
  9. <resultMap id="studentResultMapByStep" type="Student">
  10. <id property="sid" column="sid" />
  11. <result property="sname" column="sname" />
  12. <association property="clazz"
  13. select="com.chf.mapper.ClassMapper.selectByIdStep2"
  14. column="cid"
  15. />
  16. </resultMap>
  17. <select id="selectByIdStep1" resultMap="studentResultMapByStep">
  18. select
  19. sid,sname,cid
  20. from
  21. t_stu
  22. where
  23. sid = #{sid}
  24. </select>
  25. </mapper>
  1. <mapper namespace="com.chf.mapper.ClassMapper">
  2. <!--分步查询第二步:根据cid获取班级信息-->
  3. <select id="selectByIdStep2" resultType="Class">
  4. select
  5. cid,cname
  6. from
  7. t_class
  8. where
  9. cid = #{cid}
  10. </select>
  11. </mapper>

二、多对一延迟加载

实际开发中的模式:

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

  1. <mapper namespace="com.chf.mapper.StudentMapper">
  2. <!--
  3. 延迟加载的核心机制:用的时候再执行查询语句,不用的时候不查询,可以提高性能。
  4. 默认情况下是没有开启延迟加载的,需要手动设置开启。
  5. 开启延迟加载的方法:association标签中添加fetchType="lazy"
  6. 但是这里只是开启默认的延迟加载,仅局限于此Mapper映射文件,需要在核心配置文件里设置
  7. 如果开启了全局延迟加载,但又不想在某个映射文件中开启,那么就需要在association标签设置
  8. fetchType="eager"
  9. -->
  10. <resultMap id="studentResultMapByStep" type="Student">
  11. <id property="sid" column="sid" />
  12. <result property="sname" column="sname" />
  13. <association property="clazz"
  14. select="com.chf.mapper.ClassMapper.selectByIdStep2"
  15. column="cid"
  16. fetchType="lazy"
  17. />
  18. </resultMap>
  19. <select id="selectByIdStep1" resultMap="studentResultMapByStep">
  20. select
  21. sid,sname,cid
  22. from
  23. t_stu
  24. where
  25. sid = #{sid}
  26. </select>
  27. </mapper>
  1. mybatis:
  2. mapper-locations: classpath:mapper/*.xml
  3. type-aliases-package: com.chf.pojo
  4. configuration:
  5. log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
  6. #实际开发中,大部分都是需要使用延迟加载的
  7. #延迟加载的全局开关,默认值false为不开启
  8. lazy-loading-enabled: true

三、一对多

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

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

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

  • 第一种方式:collection

  • 第二种方式:分步查询

两个实体类如下:

  1. @Data
  2. @AllArgsConstructor
  3. @NoArgsConstructor
  4. @ToString
  5. public class Student {
  6. private Integer sid;
  7. private String sname;
  8. private Class clazz;
  9. }
  10. @Data
  11. @AllArgsConstructor
  12. @NoArgsConstructor
  13. @ToString
  14. public class Class { //教室类
  15. private Integer cid;
  16. private String cname;
  17. private List<Student> studentList;
  18. }

第一种方式:collection

  1. <mapper namespace="com.chf.mapper.ClassMapper">
  2. <resultMap id="classResultMap" type="Class">
  3. <id property="cid" column="cid" />
  4. <result property="cname" column="cname" />
  5. <!--一对多,这里是collection,collection是集合的意思-->
  6. <!--ofType属性用来指定结合当中的元素类型即集合中的泛型-->
  7. <collection property="studentList" ofType="Student">
  8. <id property="sid" column="sid" />
  9. <result property="sname" column="sname" />
  10. </collection>
  11. </resultMap>
  12. <select id="selectByCollection" resultMap="classResultMap">
  13. select
  14. c.cid,c.cname,s.sid,s.sname
  15. from
  16. t_class c
  17. left join
  18. t_stu s
  19. on
  20. c.cid = s.cid
  21. where
  22. c.cid = #{cid}
  23. </select>
  24. </mapper>

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

第二种方式:分步查询

  1. @Mapper
  2. public interface ClassMapper {
  3. /**
  4. * 分步查询第一步:根据班级编号获取班级信息
  5. * @param cid
  6. * @return
  7. */
  8. Class selectByStep1(Integer cid);
  9. }
  10. @Mapper
  11. public interface StudentMapper {
  12. /**
  13. * 根据班级编号查询学生信息
  14. * @param cid
  15. * @return
  16. */
  17. List<Student> selectByStep2(Integer cid);
  18. }
  1. <mapper namespace="com.chf.mapper.ClassMapper">
  2. <!--分步查询第一句:根据班级的cid查询班级信息-->
  3. <resultMap id="classResultMapStep" type="Class">
  4. <id property="cid" column="cid" />
  5. <result property="cname" column="cname" />
  6. <association property="studentList" column="cid"
  7. select="com.chf.mapper.StudentMapper.selectByStep2"
  8. />
  9. </resultMap>
  10. <select id="selectByStep1" resultMap="classResultMapStep">
  11. select
  12. cid,cname
  13. from
  14. t_class
  15. where
  16. cid = #{cid}
  17. </select>
  18. </mapper>
  1. <mapper namespace="com.chf.mapper.StudentMapper">
  2. <!--分步查询第二步:根据传过来的班级编号查询学生信息-->
  3. <select id="selectByStep2" resultType="Student">
  4. select
  5. sid,sname
  6. from
  7. t_stu
  8. where
  9. cid = #{cid}
  10. </select>
  11. </mapper>

四、一对多延迟加载

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

6、MyBatis缓存机制

缓存:cache

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

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

MyBatis缓存包括:

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

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

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

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

一、一级缓存

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

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

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

  1. @Data
  2. @AllArgsConstructor
  3. @NoArgsConstructor
  4. @ToString
  5. public class Car {
  6. private Long id;
  7. private String carNum;
  8. private String brand;
  9. private Double guidePrice;
  10. private String produceTime;
  11. private String carType;
  12. }
  1. @Mapper
  2. public interface CarMapper {
  3. /**
  4. * 根据id获取Car信息
  5. * @param id
  6. * @return
  7. */
  8. Car selectById(Long id);
  9. }
  1. <mapper namespace="com.chf.mapper.CarMapper">
  2. <sql id="selectAll">
  3. id,car_num,brand,guide_price,produce_time,car_type
  4. </sql>
  5. <select id="selectById" resultType="Car">
  6. select
  7. <include refid="selectAll" />
  8. from
  9. t_car
  10. where id = #{id}
  11. </select>
  12. </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对象关闭或提交之后,一级缓存才会被写入二级缓存中,此时二级缓存才可用

  1. <mapper namespace="com.chf.mapper.CarMapper">
  2. <cache />
  3. <sql id="selectAll">
  4. id,car_num,brand,guide_price,produce_time,car_type
  5. </sql>
  6. <select id="selectById2" resultType="Car">
  7. select
  8. <include refid="selectAll" />
  9. from
  10. t_car
  11. where
  12. id = #{id}
  13. </select>
  14. </mapper>
  1. @Data
  2. @AllArgsConstructor
  3. @NoArgsConstructor
  4. @ToString
  5. public class Car implements Serializable {
  6. private Long id;
  7. private String carNum;
  8. private String brand;
  9. private Double guidePrice;
  10. private String produceTime;
  11. private String carType;
  12. }

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

7、MyBatis使用PageHelper

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

一、limit分页

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

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

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

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

  • startIndex = (pageSize - 1) * pageSize

  1. @Mapper
  2. public interface CarMapper {
  3. /**
  4. * 分页查询
  5. * @param startIndex 起始下标
  6. * @param pageSize 每页显示的记录条数
  7. * @return
  8. */
  9. List<Car> selectByPage(@Param("startIndex") int startIndex,
  10. @Param("pageSize") int pageSize);
  11. }
  1. <mapper namespace="com.chf.mapper.CarMapper">
  2. <sql id="selectAllColumn">
  3. id,car_num,brand,guide_price,produce_time,car_type
  4. </sql>
  5. <select id="selectByPage" resultType="Car">
  6. select
  7. <include refid="selectAllColumn" />
  8. from
  9. t_car
  10. limit
  11. #{startIndex},#{pageSize}
  12. </select>
  13. </mapper>

二、PageHelper插件

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

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

  1. <dependency>
  2. <groupId>com.github.pagehelper</groupId>
  3. <artifactId>pagehelper-spring-boot-starter</artifactId>
  4. <version>1.2.12</version>
  5. </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]

}

网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发