SpringBoot结合MyBatis 【超详细】

慈云数据 2024-04-14 技术支持 53 0

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的事务管理机制(两种)

         JDBC表示事务管理器
         MANAGED表示事务事务管理器

        JDBC事务管理器: MyBatis框架自己管理事务,自己采用原生的JDBC代码去管理事务:

        // 关闭自动提交 开启事务
        connection.setAutoCommit(false); 
        ....业务处理
        // 手动提交 使用JDBC事务管理器的话,底层创建的事务管理器对象:JdbcTransaction对象。
        connection.commit();
        // 如果编写的代码是下面的代码
        // 表示没有开启事务。因为这种方式压根不会执行:conn.setAutoCommit(false)。
        SqlSession sqlSession = sqlSessionFactory.openSession(true); 

        在JDBC事务中,没有执行conn.setAutoCommit(false);那么autoCommit就是true。 如果autoCommit是true,就表示没有开启事务。只要执行任意一条DML语句就提交一次。代码如下MANAGED事务管理器中有展示。

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

        • JDBC中的事务: 如果你没有在JDBC代码中执行下面这条语句,那么默认的autoCommit是true。

          // 开启事务
          connection.setAutoCommit(false);
          // 手动提交事务
          connection.commit();
        • 只要你的autoCommit(自动提交)是true,就表示没有开启事务。

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

          二、快速入门

          第一步:引入依赖

          
              org.mybatis.spring.boot
              mybatis-spring-boot-starter
              3.5.3
          
          
          
              junit
              junit
              test
          
          
          
              org.projectlombok
              lombok
          

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

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

          spring:
            datasource:
              driver-class-name: com.mysql.cj.jdbc.Driver
              url: jdbc:mysql://localhost:3306/powernode
              username: root
              password: root
          mybatis:
            mapper-locations: classpath:mapper/*.xml
            #目的是为了省略resultType里的代码量
            type-aliases-package: com.chf.pojo
            configuration:
              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);
          }
          
              
              
                  insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
                  values(null,#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
              
              
              
                  delete from t_car where id = #{dasdad}
              
              
                  update t_car set
                      car_num=#{carNum},
                      brand=#{brand},
                      guide_price=#{guidePrice},
                      produce_time=#{produceTime},
                      car_type=#{carType}
                  where id=#{id}
              
          
          @SpringBootTest
          public class Mybatis001IntroduceApplicationTests {
              @Autowired
              private CarMapper carMapper;
              @Test
              void testInsert(){
                  Car car = new Car(null,"111","奔驰",30.00,"2022-10-2","新能源");
                  int count = carMapper.insert(car);
                  System.out.println((count == 1 ? "插入成功" : "插入失败"));
              }
              @Test
              void testDelete(){
                  int count = carMapper.delete(4L);
                  System.out.println((count == 1 ? "删除成功" : "删除失败"));
              }
              @Test
              void 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核心配置(复习)

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

          
          
              
              
                  
              	
                      
                      
                      
                      
                          
                          
                          
                          
                  	
              	
                  
                  
                  
                      
                      
                          
                          
                          
                          
                  	
             		     
           
              
              
                  
              
          
          @SpringBootTest
          public class ConfigurationTest{
          	@Test
          	void 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 {
              @Autowired
              private AccountMapper accountMapper;
              @Override
              public Account selectByActno(String actno) {
                  Account account = accountMapper.selectByActno(actno);
                  return account;
              }
              @Override
              public int updateByActno(Account account) {
                  int count = accountMapper.updateByActno(account);
                  return count;
              }
              @Override
              @Transactional
              public void transfer(String fromActno,String toActno,double money)
                      throws MoneyNotEnoughException,TransferException {
                  Account fromAct = selectByActno(fromActno);
                  if(fromAct.getBalance()  
          

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

          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 {
              @Autowired
              private 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博客

          放一点笔记出来品品

              
              
                  select
                      id,
                      car_num carNum,
                      brand,
                      guide_price guidePrice,
                      produce_time produceTime,
                      car_type carType
                  from
                      t_car
                  order by
                      produce_time ${ascOrDesc}
              
              
                  select
                      id,
                      car_num carNum,
                      brand,
                      guide_price guidePrice,
                      produce_time produceTime,
                      car_type carType
                  from
                      t_car
                  where
                      car_type=#{carType}
              
              
              
                  delete from t_car
                  where id in (${ids})
              
              
                  select
                      id,
                      car_num carNum,
                      brand,
                      guide_price guidePrice,
                      produce_time produceTime,
                      car_type carType
                  from
                      t_car
                  where
                      
                      
                      brand like "%"#{brand}"%"
              
          

           

          二、别名机制与mapper标签

          # yml配置
          mybatis:
            #基于SpringBoot的mapper标签
            mapper-locations: classpath:mapper/*.xml
            #基于SpringBoot的别名机制用于配合xml中的resultType
            type-aliases-package: com.chf.pojo
            configuration:
              log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
          	
                  
                  
                  
                  
                  
                  
                  
                  
              
          	
                     
               
              
          	

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

          @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 selectById(Long id);
                List selectByName(String name);
                List selectByBirth(Date birth);
                List selectBySex(Character sex);
            }

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

            二、Map参数

            ①、插入信息

            @Mapper
            public interface StudentMapper{
                /**
                 * 保存学生信息,通过Map参数,以下是单个参数,但是参数的类型不是简单类型,是Map集合
                 * @param map
                 * @return
                 */
                int insertStudentByMap(Map map);
            }
            	
                    insert into
                        t_student
                    values
                        (null,#{姓名},#{年龄},#{身高},#{生日},#{性别})
                
            

            ②、查询单个汽车信息

            @Mapper
            public interface CarMapper{
                /**
                 * 根据id获取汽车信息,将汽车信息放到Map集合中
                 * @param id
                 * @return
                 */
                Map selectByIdRetMap(Long id);
            }
            	
                    select
                        id,
                        car_num carNum,
                        brand,
                        guide_price guidePrice,
                        produce_time produceTime,
                        car_type carType
                    from
                        t_car
                    where
                        id = #{id}
                
            

            ③、返回多个Map

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

            @Mapper
            public interface CarMapper{
                /**
                 * 查询所有的Car信息返回一个放Map集合的List集合
                 * @return
                 */
                List selectAllRetListMap();
            }
            	
                
                    select
                        id,
                        car_num carNum,
                        brand,
                        guide_price guidePrice,
                        produce_time produceTime,
                        car_type carType
                    from
                        t_car
                
            

             ④、返回Map

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

            @Mapper
            public interface CarMapper{
                /**
                 * 查询所有的Car返回一个大Map结合
                 * Map集合的key是每条记录的主键值
                 * Map集合的value的每条记录
                 * @return
                 */
                @MapKey("id")
                Map selectAllRetMap();
            }
                
                    select
                        id,
                        car_num carNum,
                        brand,
                        guide_price guidePrice,
                        produce_time produceTime,
                        car_type carType
                    from
                        t_car
                
            

            {

            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);
            }
            	
                    insert into
                        t_student
                    values
                        (null,#{name},#{age},#{height},#{birth},#{sex})
                
            

            四、多参数(@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 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 selectAllByResultMap();
              }
              	
                  
                      
                      
                      
                      
                      
                      
                      
                      
                  
                  
                  
                      select
                          id,
                          car_num,
                          brand,
                          guide_price,
                          produce_time,
                          car_type
                      from
                          t_car
                  
              

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

              使用这种方式的前提是:属性名遵循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 selectAllByResultMapTwo();
              }
                  
                      select
                          id,
                          car_num,
                          brand,
                          guide_price,
                          produce_time,
                          car_type
                      from
                          t_car
                  
              

              六、获取总记录条数

              @Mapper
              public interface CarMapper{
                  /**
                   * 获取Car的总记录条数
                   * @return
                   */
                  Long selectTotal();
              }
                  
                      select
                          count(*)
                      from
                          t_car
                  
              

              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 selectByMultiCondition(@Param("brand") String brand,
                                                       @Param("guidePrice") Double guidePrice,
                                                       @Param("carType") String carType);
                  }
                      
                      
                          select
                              id,car_num,brand,guide_price,produce_time,car_type
                          from
                              t_car
                          where
                              1 = 1
                              
                                  and brand like "%"#{brand}"%"
                              
                              
                                  and guide_price >= #{guidePrice}
                              
                              
                                  and car_type like "%"#{carType}"%"
                              
                      
                  

                  二、where标签

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

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

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

                    @Mapper
                    public interface CarMapper {
                        /**
                         * 使用where标签,让where子句更加的智能
                         * @param brand
                         * @param guidePrice
                         * @param carType
                         * @return
                         */
                        List selectByMultiConditionWithWhere(@Param("brand") String brand,
                                                                  @Param("guidePrice") Double guidePrice,
                                                                  @Param("carType") String carType);
                    }
                        
                        
                            select
                                id,car_num,brand,guide_price,produce_time,car_type
                            from
                                t_car
                            
                                
                                    and brand like "%"#{brand}"%"
                                
                                
                                    and guide_price >= #{guidePrice}
                                
                                
                                    and car_type like "%"#{carType}"%"
                                
                            
                        
                    

                    三、trim标签

                    @Mapper
                    public interface CarMapper {
                        /**
                         * 使用trim标签
                         * @param brand
                         * @param guidePrice
                         * @param carType
                         * @return
                         */
                        List selectByMultiConditionWithTrim(@Param("brand") String brand,
                                                                 @Param("guidePrice") Double guidePrice,
                                                                 @Param("carType") String carType);
                    }
                        
                        
                            select
                                id,car_num,brand,guide_price,produce_time,car_type
                            from
                                t_car
                            
                                
                                    brand like "%"#{brand}"%" and
                                
                                
                                    guide_price >= #{guidePrice} and
                                
                                
                                    car_type like "%"#{carType}"%"
                                
                            
                        
                    

                    四、set标签

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

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

                    @Mapper
                    public interface CarMapper {
                        /**
                         * 使用set标签进行更新
                         * @param car
                         * @return
                         */
                        int updateBySet(Car car);
                    }
                        
                            update
                                t_car
                            
                                car_num = #{carNum},
                                brand = #{brand},
                                guide_price = #{guidePrice},
                                produce_time = #{produceTime},
                                car_type = #{carType}
                            
                            where
                                id = #{id}
                        
                    

                    五、choose where otherwise

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

                    Mapper映射语法格式:

                    	
                    	
                    	
                    

                    等同于Code语法格式:

                    if(){
                    }else if(){
                    }else if(){
                    }else{
                    }
                    @Mapper
                    public interface CarMapper {
                        /**
                         * 使用choose when otherwise标签
                         * @param brand
                         * @param guidePrice
                         * @param carType
                         * @return
                         */
                        List selectByChoose(@Param("brand") String brand,
                                                 @Param("guidePrice") Double guidePrice,
                                                 @Param("carType") String carType);
                    }
                        
                            select
                                id,car_num,brand,guide_price,produce_time,car_type
                            from
                                t_car
                            
                                
                                    
                                        brand like "%"#{brand}"%"
                                    
                                    
                                        guide_price >= #{guidePrice}
                                    
                                    
                                        car_type like "%"#{carType}"%"
                                    
                                
                            
                        
                    

                     

                    六、foreach标签

                    ①批量删除

                    @Mapper
                    public interface CarMapper {
                        /**
                         * 根据id批量删除 foreach
                         * @param ids
                         * @return
                         */
                        int deleteByIds(@Param("ids") Long[] ids);
                    }
                        
                        
                            delete from
                                t_car
                            where
                                id in
                                
                                    #{aaa}
                                
                        
                    

                    ②批量添加

                    @Mapper
                    public interface CarMapper {
                        /**
                         * 批量插入,一次插入多条Car信息
                         * @param cars
                         * @return
                         */
                        int insertBatch(@Param("cars") List cars);
                    }
                        
                            insert into
                                t_car
                            values
                                
                                    (null,#{car.carNum},#{car.brand},#{car.guidePrice},#{car.produceTime},#{car.carType})
                                
                        
                    

                    七、sql、include标签

                    sql标签用来声明sql片段

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

                    作用:代码复用、易维护

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

                        
                        
                            id,
                            car_num,
                            brand,
                            guide_price,
                            produce_time,
                            car_type
                        
                        
                        
                            select
                                
                            from
                                t_car
                        
                    

                    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);
                      }
                          
                          
                          
                              
                              
                              
                              
                          
                          
                              select
                                  s.sid,s.sname,c.cid,c.cname
                              from
                                  t_stu s
                              left join
                                  t_class c
                              on
                                  s.cid = c.cid
                              where
                                  s.sid = #{sid}
                          
                      

                      第二种方式:association

                      @Mapper
                      public interface StudentMapper {
                          /**
                           * 一条SQL语句,association
                           * @param id
                           * @return
                           */
                          Student selectByIdAssociation(Integer id);
                      }
                          
                          
                              
                              
                              
                                  
                                  
                              
                          
                          
                              select
                                  s.sid,s.sname,c.cid,c.cname
                              from
                                  t_stu s
                              left join
                                  t_class c
                              on
                                  s.cid = c.cid
                              where
                                  s.sid = #{sid}
                          
                      

                       第三种方式:分步查询

                      @Mapper
                      public interface StudentMapper {
                          /**
                           * 分步查询第一步:先根据学生的sid查询学生的信息
                           * @param id
                           * @return
                           */
                          Student selectByIdStep1(Integer id);
                      }
                      @Mapper
                      public interface ClassMapper {
                          /**
                           * 分步查询第二步:根据cid获取班级信息
                           * @param id
                           * @return
                           */
                          Class selectByIdStep2(Integer id);
                      }
                          
                          
                          
                          
                              
                              
                              
                          
                          
                              select
                                  sid,sname,cid
                              from
                                  t_stu
                              where
                                  sid = #{sid}
                          
                      
                          
                          
                              select
                                  cid,cname
                              from
                                  t_class
                              where
                                  cid = #{cid}
                          
                      

                      二、多对一延迟加载

                      实际开发中的模式:

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

                          
                          
                              
                              
                              
                          
                          
                              select
                                  sid,sname,cid
                              from
                                  t_stu
                              where
                                  sid = #{sid}
                          
                      
                      mybatis:
                        mapper-locations: classpath:mapper/*.xml
                        type-aliases-package: com.chf.pojo
                        configuration:
                          log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
                          #实际开发中,大部分都是需要使用延迟加载的
                          #延迟加载的全局开关,默认值false为不开启
                          lazy-loading-enabled: true

                      三、一对多

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

                      在Class(教室)类中添加List 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 studentList;
                        }

                        第一种方式:collection

                            
                                
                                
                                
                                
                                
                                    
                                    
                                
                            
                            
                                select
                                    c.cid,c.cname,s.sid,s.sname
                                from
                                    t_class c
                                left join
                                    t_stu s
                                on
                                    c.cid = s.cid
                                where
                                    c.cid = #{cid}
                            
                        

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

                        第二种方式:分步查询

                        @Mapper
                        public interface ClassMapper {
                            /**
                             * 分步查询第一步:根据班级编号获取班级信息
                             * @param cid
                             * @return
                             */
                            Class selectByStep1(Integer cid);
                        }
                        @Mapper
                        public interface StudentMapper {
                            /**
                             * 根据班级编号查询学生信息
                             * @param cid
                             * @return
                             */
                            List selectByStep2(Integer cid);
                        }
                            
                            
                                
                                
                                
                            
                            
                                select
                                    cid,cname
                                from
                                    t_class
                                where
                                    cid = #{cid}
                            
                        
                        	
                            
                                select
                                    sid,sname
                                from
                                    t_stu
                                where
                                    cid = #{cid}
                            
                        

                        四、一对多延迟加载

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

                        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);
                          }
                              
                                  id,car_num,brand,guide_price,produce_time,car_type
                              
                              
                                  select
                                      
                                  from
                                      t_car
                                  where id = #{id}
                              
                          

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

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

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

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

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

                            二、二级缓存

                            二级缓存的范围是SqlSessionFactory

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

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

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

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

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

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

                                    
                                    
                                        id,car_num,brand,guide_price,produce_time,car_type
                                    
                                    
                                        select
                                            
                                        from
                                            t_car
                                        where
                                            id = #{id}
                                    
                                
                                @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 = (pageNum - 1) * pageSize

                                    @Mapper
                                    public interface CarMapper {
                                        /**
                                         * 分页查询
                                         * @param startIndex 起始下标
                                         * @param pageSize 每页显示的记录条数
                                         * @return
                                         */
                                        List selectByPage(@Param("startIndex") int startIndex,
                                                               @Param("pageSize") int pageSize);
                                    }
                                        
                                            id,car_num,brand,guide_price,produce_time,car_type
                                        
                                        
                                            select
                                                
                                            from
                                                t_car
                                            limit
                                                #{startIndex},#{pageSize}
                                        
                                    

                                    二、PageHelper插件

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

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

                                        com.github.pagehelper
                                        pagehelper-spring-boot-starter
                                        1.2.12
                                    

                                    这个极其重要,需要在核心启动类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]

                                    }

微信扫一扫加客服

微信扫一扫加客服

点击启动AI问答
Draggable Icon