Mybatis


Mybatis笔记


mybatis的概述

简介

  • MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
  • Mybtais是一个ORM框架,轻量级的ORM框架。相对于重量级的ORM框架Hibernate而言,mybatis是一个半自动框架,而Hibernate是一个全自动框架。并且,Hibernate提出跨”平台”,Hinernate的跨平台是指Hinernate可以在多种数据库下进行操作,同一套代码支持多种数据库,HQL语句,Hibernate方言,对数据库操作进行翻译,根据不同的数据,将API翻译成不同的SQL,对数据进行操作,依赖ORM思想。
  • Mybatis是一个半自动的框架,早期Hibernate在流行时,开发者发现Hibernate虽然功能强大,但是由于如果想使用全自动功能,将Hibernate和数据库关心进行配置,配置很繁琐,其二,Hibernate对功能进行全方面的封装,将用户的操作,转化为SQL语句,然后进行数据库操作,整个转换过程是Hibernate,开发无法控制,如果要进行SQL语句优化是没法实现的。所以,在数据库压力逐渐增大的情况下,Hibernate框架性能问题就出现了。基于这样的情况,Mybatis框架应运而生,mybatis将SQL语句的定义控制权,完全交给了开发者,并且暴露一套API,对JDBC中:事务,参数,查询结果等进行配置处理。Mybatis也是基于ORM思想.

ORM

  • ORM:Object relation mapping

  • 对象关系映射

  • 将数据库信息和Java中的实体类进行映射

    映射图


mybatis入门

创建maven的Java项目

  • 如图:

    项目图1

    项目图2

引入mybatis相关的jar包依赖

-

    <!--mybatis依赖-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.4.6</version>
    </dependency>
<!--mybatis关联spring依赖-->
    <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis-spring</artifactId>
      <version>1.3.2</version>
    </dependency>
<!--数据库驱动依赖-->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>8.0.28</version>
    </dependency>
<!--连接池依赖-->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.2.8</version>
    </dependency>
<!--分页插件依赖-->
    <dependency>
      <groupId>com.github.pagehelper</groupId>
      <artifactId>pagehelper</artifactId>
      <version>5.2.0</version>
    </dependency>

创建实体类user

public class User {
    private Long id;
    private String name;
    private Integer age;
    private int sex;
    private String address;
    private Date birthday; 
}

编写映射文件usermapper接口

public interface UserMapper { 

   Map<String,Object>  queryById1(Long id);

    User queryById2(Long id);
}

编写映射文件usermapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.bjpowernode.mapper.UserMapper">
    <!--mapper 是根标签 只有一对
        namespace 是 这个标签的属性,必须要给
        给的值为:mapper接口的全限定名  (必须)
    -->

    <!--select  查询标签 ,( 同类标签 update , delete, insert )
            resultType  返回值类型,(类的全限定名) , 告知给Mybatis
             把sql语句的返回值封装成什么类型的对象

             标签体中 存放的是 sql
             #{变量}  占位符(图像对象导航语言) 相当于 ?
            select * from USER where id = #{id} 等价于
             select * from USER where id = ?

             id 表示唯一性 范围 :在当前xml文件中必须要唯一
         -->
    <select id="queryById1" resultType="java.util.Map">
        select * from USER where id = #{id}
    </select>

    <select id="queryById2"  resultType="com.bjpowernode.domain.User">
        select * from USER where id = #{id}
    </select>

编写mybatis-config.xml核心配置文件

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration> 
    
    <!-- 环境配置  default 属性  必须和其中一个子标签中的 id 属性 一致 表示
       当前使用的是id 对应的环境
        开发周期中 ,环境有 开发环境, 测试环境,生产环境
        id 属性 表示唯一性,在当前xml文件中必须要唯一
       -->
     <environments default="dev">
         <environment id="dev">
             <!--transactionManager  事务管理器 JDBC 是 MyBatis框架的默认事务管理器-->
             <transactionManager type="JDBC"/>
             <!--数据源(连接池) 目前使用的 是MyBatis默认的连接池 POOLed-->
             <dataSource type="POOLED">
                  <!--连接数据库的 四要素-->
                 <property name="driver" value="com.mysql.jdbc.Driver"/>
                 <property name="url" value="jdbc:mysql:///demo"/>
                 <property name="username" value="root"/>
                 <property name="password" value="123456"/>
             </dataSource>
         </environment>
     </environments>

    <!-- 关联 mapper的 映射文件 -->
    <mappers>
        <mapper resource="com/bjpowernode/mapper/UserMapper.xml"/>
    </mappers>

</configuration>

编写测试类

public class MyBatisTest {


    @Test
    public void testFind() throws IOException {

       //1. 加载 MyBatis 的 核心配置文件
        InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
         
        //2.加载inputStream 流 ,获取SqlSessionFactory 工厂对象
        SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 3.获取 sqlSession对象
        SqlSession sqlSession = sessionFactory.openSession();
        // 4. 调用MyBatis接口层的API
        /* 第一个参数   namespece 的值 +"." + sql的 id属性的值
        *  第二个参数   sql语句中需要传入的参数*/
//        User user = sqlSession.selectOne("com.bjpowernode.mapper.UserMapper.queryById2", 1L);

//        Map map = sqlSession.selectOne("com.bjpowernode.mapper.UserMapper.queryById1",1L);
         List list =  sqlSession.selectList("com.bjpowernode.mapper.UserMapper.queryById2",1L);
          list.forEach(System.out::println);

        //map.forEach((x,y)->System.out.println(x+":" + y));
//        System.out.println("user = " + user);

        // 5. 提交事务 当操作是查询操作的时候,可以不需要事务
        sqlSession.commit();
        // 6. 释放资源
        sqlSession.close();
    }

结果映射

#和$语法的区别

  • 在mybatis中,会将开发者定义的sql进行解析,解析分为了2类sql:

    1. 静态sql ,在解析时,直接将参数拼接到sql中,这种就是静态sql

    2. 动态sql,在解析时,会使用?这个占位符,替代参数

  • 这两种解析方式,mybatis是根据${}和#{}进行区分的

    1. ${}的sql是静态sql
    2. #{}的sql是动态sql
  • 不论是静态sql,还是动态sql都能获取传递参数,但是${}是使用的字符拼接,#{}使用PreparedStatement进行参数的预处理。

  • 在一定程度上说,${}能实现的功能,#{}都能实现,并且由于#{}PreparedStatement进行SQL的预处理,一定程度上可以防止SQL注入攻击。所以在开发中,能使用#{}尽量使用#{}。PreparedStatement预处理的本质是将参数进行转换为字符串,当做参数字符串处理。所以,如果参数信息是一个特殊的关键字,例如: 数据库名,表名,函数名,内置关键字,使用预处理,则关键字转为了字符串,无效了,此时必须使用字符串拼接。

sql片段

  • 在开发中,需要书写大量的SQL语句,并且这些SQL数据很大部分内容是重复内容,基于这样的情况,mybatis提供模板,可以在模板中定义需要使用sql语句的部分内容,然后在需要使用到这个部分内容的地方直接引入。
  • 使用sql标签,定义SQL片段,使用include引入,sql片段

模糊查询

  • like concat(‘%’,关键字,’%’)
  • like ‘%关键字%’
  • mybatis推荐的bind标签
  • 注意:建议只使用第一种方式

多参数问题

  • 当mybatis传递参数存在多个时,mybatis支持三种方案:

    1. argx 形式:arg 表示参数,x表示参数索引

    2. paramX形式: param表示参数,x表示第几个参数

    3. 使用注解为参数取别名 @Param

分页查询

  • MySql 中 是通过limit 关键字 进行分页 的Limit ? , ?

  • 第一个参数:表示 开始索引 (从0开始)

  • 第二个参数:表示每页显示的条数

    • 开始索引的值 = (当前页-1)*每页显示的条数
  • 在mybatis中,分页查询解决方案最本质上回归原生SQL进行处理。

  • 查询总页数,使用limit函数进行查询相应的数据

  • mybatis中,提供了一个RowBounds这个类,用于进行分页查询数据

  • 使用分页插件:PageHelper(推荐)

新增自增长

  • 在mybatis中,支持数据库的自增长功能,因为在某些特殊的业务场景中,当前数据的ID,可能是另外某些数据的业务ID。

  • 例如:

    1. 订单:总订单和子订单,每个子订单会有总订单ID,先插入总订单,并且要获取总订单ID
  • 插入子订单

    Mybatis在insert指令中,提供了2个属性:useGeneratedKeys、keyProperty、keyColumn

    useGeneratedKeys:表示使用数据库自增长

    keyProperty:自增长的列对应的类中的属性

    keyColumn:自增长的列

    Mybatis会自动将增长的值,封装到传入参数的属性中。

    <insert id="insert" parameterType="com.example.domain.User"  useGeneratedKeys="true" keyProperty="id" keyColumn="id">
        insert  into user (name,pwd) value (#{name},#{pwd})
    </insert>
    
    public static void main(String[] args) throws Exception {
        SqlSession session = SqlSessionUtil.getSession();
        //获取UserMapper接口的具体代理对象
        UserMapper mapper = session.getMapper(UserMapper.class);
        User user = new User();
        user.setName("Lucy");
        user.setPwd("123456");
        System.out.println("新增后user对象:" + user.getId());
        session.commit();
        session.close();
    }
    

动态sql

  • 动态SQL是指根据不同的参数,产生不同的SQL语句。这样的SQL就是动态SQL。

    Mybatis提供了一下标签,用于动态SQL的生成:

    1. if

    2. foreach

    3. choose

    4. where

    5. set

  • if标签

    • 在if标签中,test属性是必须有,test属性值是一个表达式,如果表达式值为true,则if标签包裹的内容会拼接在当前sql上。
    • and 并且
    • or 或者
    • == 等于
    • != 不等于
    <select id="selectByCondition"  resultMap="baseMap">
        select
        <include refid="columnId"/>
        from user
        where 1=1
        <if test="keyword!=null and keyword!=''">
              and   name like concat('%',#{keyword},'%')
        </if> 
    </select>
    
  • foreach

    • Select * from user where id in (1,2,3,4)

      循环标签,循环标签多用于批量操作。

      例如:批量新增,批量删除,批量查询等等

      1. collection 待循环的容器

      2. item 指代 每次循环容器中的元素

      3. open 开始循环是拼接字符串

      4. close 循环结束拼接字符串

      5. separator 每次循环之间拼接的字符串

      6. index 循环索引

     <!-- (   #{id},#{id},#{id},#{id}   )-->
        <select id="selectByIds" resultMap="baseMap">
            Select * from user where id in
           <foreach collection="ids" item="id" open="(" close=")" separator=",">
               #{id}
           </foreach>
        </select>
    
    <!--    <insert id="batchInsertUser">-->
    <!--        insert into user (name ,age,sex,birthday,address) values (#{name} ,#{age},#{sex},#{birthday},#{address}),(#{name} ,#{age},#{sex},#{birthday},#{address})-->
    <!--    </insert>-->
    
        <insert id="batchInsertUser">
            insert into user (name ,age,sex,birthday,address) values
            <foreach collection="users" separator="," item="user">
                (#{user.userName} ,#{user.age},#{user.sex},#{user.birthday},#{user.address})
            </foreach>
        </insert>
    
        <delete id="deleteByIds">
            delete from user where id in
            <foreach collection="array" item="id" close=")" open="(" separator=",">
                 #{id}
            </foreach>
        </delete>
    
    List<User> selectByIds(@Param("ids") List<Long> ids);
    
    void batchInsertUser(@Param("users") List<User> users);
    
    void deleteByIds(Long[] ids);
    
    
    @Test
    public void testDeleteByIds() {
        // 1.获取sqlSession对象
        SqlSession sqlSession = MyBatisUtil.openSession();
        // 2. 获取UserMapper的代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        Long[]  ids = {1L,2L,3L};
    
        userMapper.deleteByIds(ids);
    }
    
    @Test
    public void testBatchInsertUsers(){
        // 1.获取sqlSession对象
        SqlSession sqlSession = MyBatisUtil.openSession();
        // 2. 获取UserMapper的代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = new ArrayList<User>();
        User user = new User();
        user.setUserName("lucy");
        user.setSex(0);
        user.setAddress("武侯区");
        user.setAge(10);
        user.setBirthday(new Date());
        users.add(user);
        User user1 = new User();
        user1.setUserName("结衣");
        user1.setSex(0);
        user1.setAddress("双流区");
        user1.setAge(20);
        user1.setBirthday(new Date());
        users.add(user1);
         userMapper.batchInsertUser(users);
    
    }
    
    
    @Test
    public void testSelectByIds(){
        // 1.获取sqlSession对象
        SqlSession sqlSession = MyBatisUtil.openSession();
        // 2. 获取UserMapper的代理对象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
        List<Long> ids = new ArrayList<Long>();
        ids.add(1L);
        ids.add(2L);
        ids.add(3L);
        List<User> list = userMapper.selectByIds(ids);
        for (User user : list) {
            System.out.println(user);
        }
    }
    
  • choose标签

    • 多条件分支标签:choose.

      在choose标签,自上而下执行when中表达式,如果表达式为true,则将相应的字符串拼接在sql后面,且终止判断。如果所有的表达式都为false,则将otherwise中字符串,拼接在sql后面

  • where标签

    • 在mybatis中,存在sql条件,当有多个sql条件时,需要处理and关键字问题,因为where后面的第一个条件不需要and,解决方案:

      • 在where后面 拼接 1=1 类似的条件,这样其他条件都不是第一个条件,都需要拼接and

      • mybatis 提供了where标签,取代where关键字,默认去掉第一个条件 and

    • 注意:

      • 建议,只在查询语句中使用where标签,因为当where标签中的条件都不成立时,会没有where关键字。
    <select id="selectList2" parameterType="map" resultMap="BaseResultMap" >
        select  <include refid="BASE_COLUMN" /> from user
        <where>
            <if test="name != null and name !=''">
                 and name like concat('%',#{name},'%')
            </if>
            <if test="sex != null and sex !=''">
                 and sex = #{sex}
            </if>
        </where>
    </select>
    <!--  反面例子  如果name 值是空字符串  会怎么样??  -->
    <delete id="deleteByName" >
        delete from user
        <where>
            <if test="name != null and name !=''">
                name = #{name}
            </if>
        </where>
    </delete>
    
  • set标签

    • set标签是取代sql语句中的set关键字。set表示后面数据的更新。各个字段,需要使用逗号分隔。
    • set标签可以去掉最后一个逗号。
     <!--
     where 标签 可以帮我们去掉 多余的and 连接符
     set 标签 可以 帮我们去掉 多余的逗号 -->
    <update id="updateByCondition">
        update user
        <set>
            <if test="userName!=null">
                name=#{userName},
            </if>
            <if test="sex!=null">
                sex=#{sex},
            </if>
            <if test="age!=null">
                age=#{age},
            </if>
            <if test="birthday!=null">
                birthday=#{birthday},
            </if>
            <if test="address!=null">
                address=#{address},
            </if>
    
        </set>
        where id = #{id}
    </update>
    

联表查询

关联查询方法

  • 当属性中有需要赋值对象的属性时,该属性使用association关键字
// 需求:查询员工列表,以及对应的部门信息 (方案一: 关联查询)
@Test
public void testEmployeeList(){
    SqlSession sqlSession = MyBatisUtil.openSession();
    EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
    List<Employee> list = employeeMapper.list();
    for (Employee employee : list) {
        System.out.println("employee = " + employee);
    }
}
 <resultMap id="baseMap" type="com.bjpowernode.domain.Employee">
        <id column="id" property="id"/>
        <result column="username" property="username"/>
        <result column="password" property="password"/>
        <result column="email" property="email"/>
        <result column="admin" property="admin"/>
        <result column="age" property="age"/>
        <result property="dept.id" column="d_id"/>
        <result property="dept.name" column="d_name"/>
        <!--association  用来封装 对象属性 (单个对象)
         property  属性  (对应的是类中对象属性的名称)
            子标签  (id ,result)
        javaType  (property属性的值)变量的类型, MyBatis 的底层 需要根据
        javaType的值,通过反射创建对象,赋值给dept 变量
         -->
<!--       <association property="dept" javaType="com.bjpowernode.domain.Department">-->
<!--           &lt;!&ndash;id 标签 用在主键上面&ndash;&gt;-->
<!--           <id column="d_id" property="id"/>-->
<!--           &lt;!&ndash;result 标签 用在非主键上面&ndash;&gt;-->
<!--           <result column="d_name" property="name"/>-->
<!--       </association>-->
        <!--
          columnPrefix  是对column 列的 前缀 进行抽取
        -->
        <association property="courses" javaType="com.bjpowernode.edu.domain.Course">
                <id property="id" column="id"/>
                <result property="courseName" column="course_name"/>
        </association>
    </resultMap>
      <select id="list" resultMap="baseMap">
          select emp.id,emp.username,emp.email,emp.age,emp.admin,d.id d_id,d.name d_name from employee emp
left join department d  on emp.dept_id = d.id
      </select>

额外sql方法

  • 当属性中有需要赋值的是集合的属性时,使用collection 关键字

    <resultMap id="baseMap" type="com.bjpowernode.domain.Employee">
            <id column="id" property="id"/>
            <result column="username" property="username"/>
            <result column="password" property="password"/>
            <result column="email" property="email"/>
            <result column="admin" property="admin"/>
            <result column="age" property="age"/>
            <result property="dept.id" column="d_id"/>
            <result property="dept.name" column="d_name"/>
             <!--
          collection 表示集合标签, 用来封装集合属性的(如果类中有集合(对象)属性,那么就选择是Collection)
          property  属性  表示 类中的集合属性的名称
          columnPrefix 属性 表示 列的前缀抽取
          ofType  属性 表示集合中的对象的类型 (可以省略,当是发送额外sql 的时候可以省略)
        -->
        <collection property="lessonList" ofType="courseLesson" column="id"
                            select="com.bjpowernode.edu.mapper.CourseLessonMapper.selectCourseLessonById"/>
    </resultMap>
    <select id="list" resultMap="baseMap">
        select id, username, email from employee
      </select>
    

文章作者: 勾魂大猩猩
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 勾魂大猩猩 !
  目录