MyBatis 学习(二)

MyBatis 学习(二)

一、解决字段名与属性名不一致
1、数据库字段与属性名不一致
  • image-20210317225828623
  • public class User {
    //    测试当数据库的字段与对应的属性不一致是
        private int id;
        private String name;
        private int pwd;
    ....
        
    } 
    
        <!--通过id查询用户-->
        <select id="getUserById"  parameterType="int" resultType="com.study.pojo.User" >
            select * from mybatis.user where id = #{id}
        </select>
    

出现问题:

  • User{id=1, name='null', pwd=0}

    Process finished with exit code 0

解决:

  • 方式1:sql起别名(不可取)

    <!--解决方式1;起别名-->
        <select id="getUserById"  parameterType="int" resultType="com.study.pojo.User" >
            select id,username as name,password as pwd from mybatis.user where id = #{id}
        </select>
    
  • 方式二:使用resultMap 结果集映射:resultMap 元素是 MyBatis 中最重要最强大的元素

2、resultMap 结果集映射
  • 数据库:id,username,password

    User类:id,name,pwd

  • 使用resultMap(形成数据库字段名与实体类属性名映射关系)

     <resultMap id="UserMap" type="user">
            <!--column:数据库的字段名 property:实体类的属性名-->
            <result column="username" property="name"/>
            <result column="password" property="pwd"/>
        </resultMap>
        <select id="getUserById"  parameterType="int"  resultMap="UserMap" >
            select  * from mybatis.user where id = #{id}
        </select>
    

    User{id=1, name='yyb', pwd=123467}

    Process finished with exit code 0

  • ResultMap 的设计思想是,对简单的语句做到零配置,对于复杂一点的语句,只需要描述语句之间的关系就行了。

  • 实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份 resultMap 能够代替实现同等功能的数千行代码。

  • ResultMap 的优秀之处——你完全可以不用显式地配置它们。

二、日志

1、日志工厂
  • 如果一个数据操作执行sql是出现问题,我们需要排错,日志就是最好的助手!

  • 之前:输出 dug

  • 现在:日志工厂!

    logImpl 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING 未设置
  • SLF4J |【掌握】

  • LOG4J

  • LOG4J2

  • JDK_LOGGING

  • COMMONS_LOGGING

  • STDOUT_LOGGING 【掌握】

  • NO_LOGGING

在mybits中具体使用那个日志实现,在设置中设定!

  • STDOUT_LOGGING 标准输出(默认)
  • 在mybatis核心配置文件中设置
<!--标准的日志实现-->
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>

输出:

Opening JDBC Connection
Created connection 1166151249.
Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@45820e51]
== > Preparing: select * from mybatis.user where id = ?
== Parameters: 1(Integer)
== Columns: id, username, password
== Row: 1, yyb, 123467
== Total: 1
User{id=1, name='yyb', pwd=123467}
Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@45820e51]
Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@45820e51]
Returned connection 1166151249 to pool.

Process finished with exit code 0

2、Log4j
  • 是什么是log4j

    • Log4j是Apache的一个开源项目,通过使用Log4j,我们可以控制日志信息输送的目的地是控制台、文件、GUI组件,甚至是套接口服务器、NT的事件记录器、UNIX Syslog守护进程等;
    • 可以控制每一条日志的输出格式;
    • 通过定义每一条日志信息的级别,我们能够更加细致地控制日志的生成过程。
    • 通过一个配置文件来灵活地进行配置,而不需要修改应用的代码。
  • 如何使用log4j

    • 1、导入log4j的依赖jar。如果使用maven项目,也可以选择在pom.xml中新增依赖:

       <!--log4j-->
      <dependency>
      	<groupId>log4j</groupId>
      	<artifactId>log4j</artifactId>
      	<version>1.2.16</version>
      </dependency>
      
      • 2、配置log4j(通用版)。在src/main/resources/db.properties创建
      log4j.rootLogger=DEBUG,console,file
      
      #控制台输出的相关设置
      log4j.appender.console = org.apache.log4j.ConsoleAppender
      log4j.appender.console.Target = System.out
      log4j.appender.console.Threshold=DEBUG
      log4j.appender.console.layout = org.apache.log4j.PatternLayout
      log4j.appender.console.layout.ConversionPattern=【%c】-%m%n
      
      #文件输出的相关设置
      log4j.appender.file = org.apache.log4j.RollingFileAppender
      log4j.appender.file.File=./log/yyb.log
      log4j.appender.file.MaxFileSize=10mb
      log4j.appender.file.Threshold=DEBUG
      log4j.appender.file.layout=org.apache.log4j.PatternLayout
      log4j.appender.file.layout.ConversionPattern=【%p】【%d{yy-MM-dd}】【%c】%m%n
      
      #日志输出级别
      log4j.logger.org.mybatis=DEBUG
      log4j.logger.java.sql=DEBUG
      log4j.logger.java.sql.Statement=DEBUG
      log4j.logger.java.sql.ResultSet=DEBUG
      log4j.logger.java.sql.PreparedStatement=DEBUG
      
    • 3、配置log4j为日志实现

      <!--日志输出-->
          <settings>
              <setting name="logImpl" value="LOG4J"/>
          </settings>
      
    • 4、log的使用,直接测试运行的测试了类

      image-20210320114136681

1、简单使用:编写log4j的测试类

  • 导入的包:import org.apache.log4j.Logger;
    
  • 日志对象,参数为当前类的class

static Logger logger = Logger.getLogger(UserDaoMapperTest.class);
  • 测试输出不同级别:

    //Log4j测试
    @Test
     public  void testLog4J(){
           logger.info("info:进入了log4j方式");
           logger.debug("dabug:进入了dabug方式");
           logger.error("error:进入了error方式");
    
    }
    
    image-20210320120702936

    并在工程目录下自动生成:log日志

    image-20210320120830796

三、分页

1、什么是分页

  • 将大量数据展示分割成多页显示,减少数据的处理量

2、Sql语法Limit分页

  • SELECT * FROM USER LIMIT startIndex,pageSize;
    SELECT * FROM USER LIMIT 3 ;#[0,3]
    
    limit n 查找前n行记录
    limit n,m 从第n行开始,查找m行记录(起始行为第0行)3
    

3、使用Limit实现分页,核心SQL

  • 接口功能Mapper

    //通过分页limit查询用户
        List<User> getUserByLimit(Map<String,Integer> map);
    
  • 功能实现Mapper.xml

    <!--使用limit查询-->
    <select id="getUserByLimit" parameterType="map" resultMap="UserMap" >
        select * from mybatis.user limit #{startIndex},#{pageSize}
    </select>
    
  • 功能测试Test

    /*通过limit查询用户信息*/
    @Test
    public  void getUserByLimt(){
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
    
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
    
        Map<String,Integer> map = new HashMap();
        map.put("startIndex",1);
        map.put("pageSize",3);
    
        List<User> userByLimit = mapper.getUserByLimit(map);
        userByLimit.forEach(user-> System.out.println(user));
        sqlSession.close();
    
    }
    

4、使用RowBounds实现分页(面向对象实现不在对sql操作)

  • //通过分页RowBounds查询用户
    List<User> getUserByRowBounds();
    
  • <!--使用RoeBounds查询-->
    <select id="getUserByRowBounds" resultMap="UserMap" >
        select * from mybatis.user
    </select>
    
  • /*通过RowBounds查询用户信息*/
    @Test
    public  void getUserByRowBounds() {
        SqlSession sqlSession = MyBatisUtils.getSqlSession();
         //不在使用SQL
        RowBounds rowBounds = new RowBounds(1,3);
    
        //通过代码层面实现分页
        List<User> users = sqlSession.selectList("com.study.dao.UserMapper.getUserByRowBounds",null,rowBounds);
        users.forEach(user-> System.out.println(user));
    
        sqlSession.close();
    
    
    }
    

四、使用注解开发

4、1面向接口编程
  • 接口本质是一种规范和约束,反映了系统设计者对系统的抽象理解。换一种说法,面向接口编程,写业务的不需要遵守。当设计和实现分离的时候,面向接口编程是一种解决问题的很好方式,但是当设计和分离是一个人的时候,就显得非常没有必要。
  • 在web开发的工作,mvc也是比较常用的开发模式,这种开发模式通常将代码分成三层(控制层、业务逻辑层、数据库访问层),无论是较早的SSH、SSM、还是现在比较流行的spring boot框架,都提倡面向接口编程,这样可以降低层与层之间的耦合
4、2 使用注解简单操作
  • 它们映射的语句可以不用 XML 来配置,而可以使用 Java 注解来配置。

  • /*4、功能接口*/
    public interface UserMapper {
        @Select("select * from mybatis.user")
        List<User> getUserInfo();
        }
    
  • 核心配置文件中:

    <mappers>
        <!--使用注解开发绑定接口-->
        <mapper class="com.study.dao.UserMapper"/>
    </mappers>
    
  • 测试

    public class UserTest {
        @Test
        public  void getUserInfo(){
            SqlSession sqlSession = MyBatisUtils.getSqlSession();
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            List<User> userInfo = mapper.getUserInfo();
            userInfo.forEach(user -> System.out.println(user));
            sqlSession.close();
        }
    }
    
  • 使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从心,还会让你本就复杂的 SQL 语句更加混乱不堪。 因此,如果你需要做一些很复杂的操作,最好用 XML 来映射语句。

  • 比如:在处理数据库字段和对应属性名的问题上不能解决该问题。

4、3 MyBatis详细执行流程

4、4 注解版CRUD

  • 在MybatisUtils工具类上自动开启事务提交

    public static SqlSession getSqlSession() {
      /*true:自动提交事务不用手动commit*/
      return sqlSessionFactory.openSession(true);
    }
    
  • Mapper使用注解CRUD

/*4、功能接口*/
public interface UserMapper {
    @Select("select * from mybatis.user")
    List<User> getUserInfo();

    //若方法上的有多参数并且类型为基本数据类型和String的必须加上@Param("")注解(与sql的#{}变量是一一对应的)
    @Select("select * from mybatis.user where id = #{id} or username = #{name}")
    List<User>  getUserByIdOrName(@Param("id") int id,@Param("name") String username);

    @Insert("insert into user(id,username,password)  values(#{id},#{username},#{password})")
    boolean addUser(User user);
    
    @Update("update user set username=#{username},password=#{password} where id =#{id}")
    boolean updateUser(User user);

    @Delete("delete from user where id =#{id}")
    boolean delete(@Param("id") int id);

}
  • 测试类

    public class UserTest {
        @Test
        public  void getUserByIdOrName(){
            SqlSession sqlSession = MyBatisUtils.getSqlSession();
            UserMapper mapper = sqlSession.getMapper(UserMapper.class);
            List<User> users = mapper.getUserByIdOrName(1, "lijing");
            users.forEach(user -> System.out.println(user));
            sqlSession.close();
            
         ........
        }
    
  • 注:若接口方法上的有多参数并且类型为基本数据类型和String的必须加上@Param("")注解(与sql的#{}变量是一一对应的)

    ​ 引用类型的属性不用此注解

4、5 Lombok
  • Lombok项目是一个Java库,它会自动插入您的编辑器和构建工具中,从而使您的Java更加有趣。永远不要再编写另一个getter或equals方法,带有一个注释的类将具有功能齐全的生成器,自动执行日志记录变量等等。

1、使用Lombok

  • 1、在IDEA中安装lombok的插件

  • 2、在Maven中导入lombok的依赖

    <!--lombok-->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.16.20</version>
    </dependency>
    
  • 3、注解功能

    @Getter and @Setter
    @FieldNameConstants
    @ToString
    @EqualsAndHashCode
    @AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
    @Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
    @Data
    @Builder,@SuperBuilder,@Singular,@Delegate
    @Value
    @Accessors
    @Wither, @With, @SneakyThrows
    @val, @var
    experimental @var, @UtilityClass

  • @Data:无参构造,getter and setter,toString、hashCode、equals

  • @AllArgsConstructor:有参构造

  • @NoArgsConstructor:无参构造

4、6 多对一处理
  • 多对一
  • image-20210321115856930
  • 多个学生,对应一个老师
  • 对于学生而言:(关联),多个学生,关联一个老师【多对一】
  • 对于老师而言:(集合),一个老师,有很多学生【一对多】

SQL导入:

CREATE TABLE `teacher` ( `id` INT ( 10 ) NOT NULL, `name` VARCHAR ( 30 ) DEFAULT NULL, 
                       PRIMARY KEY ( `id` ) ) ENGINE = INNODB DEFAULT CHARSET = utf8

INSERT INTO teacher(`id`, `name`) VALUES (1, '王老师'); 

CREATE TABLE `student` (
 `id` INT(10) NOT NULL,
 `name` VARCHAR(30) DEFAULT NULL,
 `tid` INT(10) DEFAULT NULL,
 PRIMARY KEY (`id`),
 KEY `fktid` (`tid`),
 CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8

INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('1', '小明', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('2', '小红', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('3', '小张', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('4', '小李', '1'); 
INSERT INTO `student` (`id`, `name`, `tid`) VALUES ('5', '小王', '1');

测试环境搭建

  • 1、在Maven中导入相关依赖,(特别是lombok可以减少代码量(代码生成器))
  • 2、在resource下创建mybatis的核心配置文件
  • 3、创建mybatis的工具类(创建SqlSessionFactory和SqlSession)
  • 4、创建实体类(Student(组合引入Teacher(形成多对一的关系)),Teacher)
  • 5、创建Mapper接口(功能方法)
  • 6、mybatis的核心配置文件绑定注册Mapper接口或文件(包名)
  • 7、在resource下创建同类的包名和相关接口的Mapper.xml
  • 8、测试类查询是否成功。

需求:

  • 查询所有的学生信息,以及对应老师的的信息。

  • 分析:

    <!--
      查询所有的学生信息,以及对应老师的的信息。
      sql :select s.id,s.name,t.name from student s,teacher t where s.tid=t.id;
      1、查询出所有学生的信息
      2、根据学生的tid查询出老师的信息
    -->
    
  • 解决:

    • 方式一:按照查询嵌套处理

    • <select id="getStudent" resultMap="StudentTeacher">
          select * from student 
      </select>
      <resultMap id="StudentTeacher" type="Student">
          <result property="id" column="id"/>
          <result property="name" column="name"/>
          <!--关联-->
          <!--复杂的属性,需要单独处理 对象使用:association 集合:collection ;javaType:表字段类型定义为Java的复杂类型-->
          <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
      </resultMap> 
      <select id="getTeacher" resultType="Teacher">
          select  * from teacher
      </select>
      
    • 方式二:按照结果嵌套处理

        <!--方式2:按照结果嵌套处理-->
          <select id="getStudent2" resultMap="StudentTeacher2">
              select s.id sid,s.name sname,t.name tname
              from student s,teacher t
              where s.tid=t.id;
      
          </select>
          <!--property:实体类的属性值;column:数据库的子段值-->
          <resultMap id="StudentTeacher2" type="Student">
              <result property="id" column="sid"/>
              <result property="name" column="sname"/>
              <association property="teacher" javaType="Teacher" >
                  <result property="name" column="tname"/>
              </association>
          </resultMap>
      
column 数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName) 方法的参数一样。
javaType 一个 Java 类的完全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。
select 用于加载复杂类型属性的映射语句的 ID,它会从 column 属性中指定的列检索数据,作为参数传递给此 select 语句
resultMap 结果映射的 ID,可以将嵌套的结果集映射到一个合适的对象树中。 它可以作为使用额外 select 语句的替代方案。它可以将多表连接操作的结果映射成一个单一的 ResultSet。这样的 ResultSet 将会将包含重复或部分数据重复的结果集。为了将结果集正确地映射到嵌套的对象树中,MyBatis 允许你 “串联”结果映射,以便解决嵌套结果集的问题。
4、7 一对多处理
  • eg:一个老师拥有多个学生!

    需求:获取指定老师下的学生

  • Student实体类

    @Data
    public class Student {
        private int id;
        private String name;
        private int tid;
    
    }
    
  • Teacher实体类

    @Data
    public class Teacher {
       private int  id;
       private String name;
       /*关联一对多关系*/
       private List<Student> students;
    }
    
  • TeacherMapper接口

    public interface TeacherMapper {
    
        /*获取指定老师下的学生*/
        Teacher getTeacher2(@Param("id") int id);
        
         Teacher getTeacher3(@Param("id") int id);
       
    }
    
  • TeacherMapper.xml实现

    <!--mapper核心配置文件-->
    <mapper namespace="com.study.dao.TeacherMapper">
       <select id="getTeacher" resultType="Teacher">
           select * from teacher;
       </select>
       <!--方式一:按照结果嵌套处理-->
       <select id="getTeacher2" resultMap="TeacherByStudents">
           select s.id sid,s.name sname,t.name tname,t.id tid
           from student s,teacher t
           where s.tid=t.id and t.id=#{id} ;
       </select>
       <resultMap id="TeacherByStudents" type="Teacher">
           <result property="id" column="tid"/>
           <result property="name" column="tname"/>
           <!--处理复杂类型是集合类型是使用collection
            javaType:指定属性的类型(eg:Student student)
            ofType:指定集合中的泛型信息,使用ofType获取
           -->
           <collection property="students" ofType="Student">
               <result property="id" column="sid"/>
               <result property="name" column="sname"/>
               <result property="tid" column="tid"/>
    
           </collection>
    
       </resultMap>
    
       <!--方式二:按照结果嵌套处理-->
       <select id="getTeacher3" resultMap="TeacherByStudents3" >
           select * from teacher where id =#{id}
    
       </select>
       <resultMap id="TeacherByStudents3" type="Teacher" >
           <!--column="id":老师的id-->
           <collection property="students" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId" column="id"/>
       </resultMap>
    
       <select id="getStudentByTeacherId" resultType="Student">
          select * from student where tid =#{tid}
       </select>
    </mapper>
    
    /**
    * Teacher(id=1, name=王老师, students=[
    * Student(id=1, name=小明, tid=1),
    * Student(id=2, name=小红, tid=1),
    * Student(id=3, name=小张, tid=1),
    * Student(id=4, name=小李, tid=1),
    * Student(id=5, name=小王, tid=1)]
    * )
    *
    */
    
  • 注:复杂的属性,需要单独处理 对象使用:association 集合:collection 。javaType:表字段类型定义为Java的复杂类型;javaType:指定属性的类型(eg:Student student)ofType:指定集合中的泛型信息,使用ofType获取

五、动态SQL

1、什么是动态SQL

  • 动态SQL就是指根据不同的条件(业务场景)生成不同的SQL语句

  • 例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。

用过 JSTL 或任何基于类 XML 语言的文本处理器,你对动态 SQL 元素可能会感觉似曾相识。在 MyBatis 之前的版本中,需要花时间了解大量的元素。借助功能强大的基于 OGNL 的表达式,MyBatis 3 替换了之前的大部分元素,大大精简了元素种类,现在要学习的元素种类比原来的一半还要少。

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach
2、搭建环境
  • sql

    CREATE TABLE `blog`(
    `id` VARCHAR(50) NOT NULL COMMENT '博客id',
    `title` VARCHAR(100) NOT NULL COMMENT '博客标题',
    `author` VARCHAR(30) NOT NULL COMMENT '博客作者',
    `create_time` DATETIME NOT NULL COMMENT '创建时间',
    `views` INT(30) NOT NULL COMMENT '浏览量'
    )ENGINE=INNODB DEFAULT CHARSET=utf8
    
  • 基础环境如上所示

  • 3、添加ID自动生成的工具类

    //抑制警告
    @SuppressWarnings("all")
    public class IDUtils {
        public  static String getId(){
            //使用UUID自动创建自增id(若删除其中的id还是连续的)
            return UUID.randomUUID().toString().replace("-","");
    
        }
        @Test
        public void test(){
            System.out.println(IDUtils.getId());
            System.out.println(IDUtils.getId());
            System.out.println(IDUtils.getId());
        }
    }
    
  • 4、Blog实体类(与数据的字段不一致)

    @Data
    public class Blog {
        private String  id;
        private String title;
        private String author;
        //与数据的字段不一致(create_time)
        private Date createTime;
        private int views;
    
    }
    

    解决:

    在配置文件中添加settings设置

    <!--是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。-->
    <setting name="mapUnderscoreToCamelCase" value="true"/>
    
3、IF
  • 用于动态条件判断不同的条件会查询的结果会有所不同

  • 比如:在搜索是可以按照姓名查询又可以按照书名查询

  • <select id="queryBlogIF" parameterType="map" resultType="blog">
      select * from blog where  1=1
      <if test="title != null">
         and title like  "%"#{title}"%"
      </if><if test="author != null">
         and author = #{author}
    </if>
       
    

    这条语句提供了可选的查找文本功能。;如果传入了 “title” 参数,那么就会对 “title” 一列进行模糊查找并返回对应的 BLOG 结果(细心的读者可能会发现,“title” 的参数值需要包含查找掩码或通配符字符)。

    • test:判断表达式(OGNL)遇见特殊符号应该去写转义字符:&&、''等字符
    • where 1=1:在where条件后面加了一条判断1=1,这样当下面if条件中的任何一个判断失败后,都不会影响整个sql语句。
    • 建议方式:
      where和if 进行组合,当条件不成立时,if条件后的内容包括and也不会存在,因此不会对整个sql语句产生影响。注意and关键字要放在每个语句中的库表字段赋值的前面。因为,一旦判断不成功, 会把对应的and关键字去掉(还有or关键字)
  • where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。当没有条件设置是查找的是全部的数据

    <select id="queryBlogIF" parameterType="map" resultType="blog">
     select * from blog
     <where>
       <if test="title != null">
          and title like  "%"#{title}"%"
       </if>
         <if test="author != null">
          and author = #{author}
        </if>
     </where>
    </select>
    
  • 测试类

    @Test
    public void queryBlog(){
       SqlSession sqlSession = MyBatisUtils.getSqlSession();
       BlogMapper
               mapper = sqlSession.getMapper(BlogMapper.class);
    
       HashMap map = new HashMap();
        map.put("title","Ja");
       //map.put("author","lijing");
       List<Blog> blogs = mapper.queryBlogIF(map);
       for (Blog blog : blogs) {
           System.out.println(blog);
       }
       sqlSession.close();
    
    }
    

    Blog(id=9e82914025964ba08caa0960ddf5160a, title=Java, author=yyb, createTime=Mon Mar 22 22:43:04 CST 2021, views=1000)

4、choose(when、otherwise)
  • 有时候,我们不想使用所有的条件,而只是想从多个条件中只选择一个使用

  • 针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

    <select id="queryBlogChoose" parameterType="map" resultType="blog">
        select * from blog
       <where>
           <choose>
             <when test="title != null">and title like "%"#{title}"%"</when>
             <when test="author != null"> and author = #{author}</when>
             <otherwise>and views = #{views} </otherwise>
           </choose>
       </where>
    </select>
    
    
  • 情况一:当没有传入条件时或传入的条件没有符合choose中的条件他会默认执行的语句但结果肯定是null

  • ==> Preparing: select * from blog WHERE views = ?
    ==> Parameters: null

  • 情况二:当传入的条件符合其中一个就会执行

  • 情况三:当传入的条件多个时他只会默认执行第一个传入的参数的when条件

5、trim(where、set)
  • set 元素可以用于动态包含需要更新的列,忽略其它不更新的列
  • set 元素会动态地在行首插入 SET 关键字,并会删掉额外的逗号(这些逗号是在使用条件语句给列赋值时引入的)
<!--update更新博客-->
<update id="updateBlog" parameterType="map">
    update mybatis.blog
     <set>
         <if test="title != null">title = #{title},</if>
         <if test="author != null">author = #{author},</if>
         <if test="views != null"> views = #{views},</if>
         create_time = now()
     </set>
   where id =#{id}
</update>
  • 通过自定义 trim 元素来定制 where 元素的功能。比如,和 where 元素等价的自定义 trim 元素为:

    <trim prefix="WHERE" prefixOverrides="AND |OR ">
      ...
    </trim>
    
  • prefixOverrides 属性会忽略通过管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子会移除所有 prefixOverrides 属性中指定的内容,并且插入 prefix 属性中指定的内容。

  • set 元素等价的自定义 trim 元素吧:

    <trim prefix="SET" suffixOverrides=",">
      ...
    </trim>
    

所谓的动态SQL,本质就是SQL语句,只是我们可以在SQL层面里,去执行一个逻辑代码

6、SQL片段
  • 将一些公共(相同)的sql语句抽取出来实现代码的复用

1、使用sql标签抽取公共的部分

<!--使用sql片段将公共的sql提取出来在需要使用的地方引用即可-->
<sql id="queryBlog">
    <if test="title != null">
        and title like  "%"#{title}"%"
    </if>
    <if test="author != null">
        and author = #{author}
    </if>
</sql>

2、在需要使用的地方使用include标签引用即可

<select id="queryBlogIF" parameterType="map" resultType="blog">
 select * from blog
 <where>
   <include refid="queryBlog"/>
 </where>
</select>

注意点:

  • 最好基于单表来定义SQL片段!

    SQL片段中不要存在where标签!

7、foreach

动态 SQL 的另一个常见使用场景是对集合进行遍历(尤其是在构建 IN 条件语句的时候

<!--只查找博客的前三条记录-->
<!--select * from `blog` where 1=1 and (id=1 or id =2 or id=3);-->
<select id="queryBlogForeach" resultType="blog" parameterType="map">
    select * from blog
    <where>
        <foreach collection="ids" item="id" open="and ("open="and ("separator="or">
            id = #{id}
        </foreach>
    </where>

</select>
@Test
public void queryBlogForeach(){
    SqlSession sqlSession = MyBatisUtils.getSqlSession();
    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
    HashMap map = new HashMap();
    List<Integer> list = new ArrayList<>();
    list.add(1);
    list.add(2);
    list.add(3);
    map.put("ids",list);
    List<Blog> blogs = mapper.queryBlogForeach(map);
    for (Blog blog : blogs) {
        System.out.println(blog);

    }
    sqlSession.close();

}
  • foreach 元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内使用的集合项(collection="ids")list属性和list中的每个元素(item="id")。它也允许你指定开头open="and ("与结尾的字符串open="and ("以及集合项迭代之间的分隔符separator="or"。这个元素也不会错误地添加多余的分隔符,看它多智能!

  • 可以将任何可迭代对象(如 List、Set 等)、Map 对象或者数组对象作为集合参数传递给 foreach。当使用可迭代对象或者数组时,index 是当前迭代的序号,item 的值是本次迭代获取到的元素。当使用 Map 对象(或者 Map.Entry 对象的集合)时,index 是键,item 是值。

8、小结:

动态SQL就是在拼接SQL语句,我们只要保证SQL的准确性,按照SQL的格式,去排列组合就可以了

建议:

  • 先在Mysql中写出完整的SQL语句测试是否成功,在再xml中写
原文地址:https://www.cnblogs.com/yyb6/p/14590313.html