概述

​ MyBatis 是支持普通 SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。MyBatis 使用简单的 XML注解用于配置原始映射,将接口和 Java 的POJOs(Plain Ordinary Java Objects,普通的 Java对象)映射成数据库中的记录。

相关介绍
  • ​ 持久层

    javaEE的三层架构:表现层、业务层、持久层

    负责将数据保存到数据库的那一层代码

Mybatis的简化

硬编码->配置文件

Class.forName("com.mysql.jdbc.Driver");
String url="jdbc:mysql:///mybatis?useSSL=false";
String user="root";
String password="123456";
Connection conn= (Connection) DriverManager.getConnection(url,user,password);
<configuration>
    <!--    类型别名-->
<!--    <typeAliases>-->
<!--        <typeAlias type="com.mybatis.pojo.User" alias="User"></typeAlias>-->
<!--    </typeAliases>-->
    <!-- 包扫描的方式 自动扫描包中的po类,自动定义别名,别名是类名  -->
    <typeAliases>
        <package name="com.mybatis.pojo"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///mybatis?useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
<!--        加载sql映射文件-->
<!--        <mapper resource="com/mybatis/mapper/UserMapper.xml"/>-->
<!--        包扫描,可以不用写路径-->
        <package name="com.mybatis.mapper"/>
    </mappers>
</configuration>
String sql="update tb_user set age=? where id=?";
PreparedStatement preparedStatement = conn.prepareStatement(sql);
<select id="selectAll" resultType="user">
    select *
    from tb_user
</select>

操作繁琐->自动完成

preparedStatement.setInt(1,26);
preparedStatement.setInt(2,2);
int count=preparedStatement.executeUpdate();

例如封装查询User表的信息

Result rs=preparedStatement.executeQuery();
ArrayList<User>users=new ArrayList<>();
while(rs.next())
{
	int id=rs.getInt("id");
	String name=rs.getString("name");
	............
	...........
	User user=new User();
	user.setid(id);
	user.setname(name);
	.............
	.............
	users.add(user);

}
// 获取Sqlsession对象,执行sql
        SqlSession sqlSession =SessionFactory.openSession();
//执行sql
        List<User> users = sqlSession.selectList("com.mybatis.mapper.UserMapper.selectAll");

Mybatis几乎免除了所有的JDBC代码以及设置参数和获取结果集的工作

基本步骤

  1. 创建模块,导入mybatis坐标

  2. 编写Mybatis的核心配置文件->替换连接信息,解决硬编码问题

  3. 定义POJO类

  4. 编写SQL映射文件->统一管理SQL语句,也是解决硬编码问题

  5. 业务层使用

    1. 加载核心配置文件,获取SqlSessionFactory对象

    2. 获取Session对象,执行SQL

    3. 释放资源

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.25</version>
</dependency>
<?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>
<!--    别名-->
    <typeAliases>
    <!-- 包扫描-->
        <package name="com.mybatis.pojo"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql:///mybatis?useSSL=false"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
<!--        加载sql映射文件-->
<!--        <mapper resource="com/mybatis/mapper/UserMapper.xml"/>-->
<!--        包扫描,可以不用写路径-->
        <package name="com.mybatis.mapper"/>
    </mappers>
</configuration>
package com.mybatis.pojo;

public class User {
    private Integer id;
    private String name;
    private Integer _age;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer get_age() {
        return _age;
    }

    public void set_age(Integer _age) {
        this._age = _age;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", _age=" + _age +
                '}';
    }

}

Mapper代理

问题:

在业务层使用的时候,使用sql的映射是使用namespace.id的方式,这里还是有硬编码的问题

而且使用的时候还需要去查看相关的sql映射id,并不是很方便

解决:

Mapper代理

步骤

  1. 定义与SQL映射文件同名的Mapper接口,两者要在同一目录下
  2. SQL映射文件的namespace要为Mapper接口的全限定名
  3. Mapper接口中定义方法,方法名为sql的id,保持参数类型和返回值相同
  4. 业务层编码
    1. SqlSession的getMapper方法获取Mapper接口的代理对象
    2. 代理对象调用方法完成sql

Mapper和SQL的映射文件在同一目录下,并且同名,则在mybatis的核心文件中加载SQL映射文件的时候可以使用包扫描的方式

<package name="com.mybatis.mapper"/>

resource下建多级目录xxx/xxx/xxx不能用xxx.xxx.xxx,创建与Mapper接口同路径,编译时会将两者放到一起

SQL映射文件

参数占位符:

  1. #{}、${}

    1. #{}: 执行SQL时,会将#{}占位符替换为?,将来自动设置参数值 预编译

    ${}: 拼SQL。 会存在SQL注入问题

    1. 使用时机:

      • 参数传递,都使用#{}
      • 如果要对表名、列名进行动态设置,只能使用${}进行sql拼接。
  2. parameterType:

    对于有参数的mapper接口方法,我们在映射配置文件中应该配置 ParameterType 来指定参数类型。只不过该属性都可以 省略

    用于设置参数类型,该参数可以省略,一般不写,在接口方法中有定义

  3. SQL语句中特殊字符处理:

    • ​ 转义字符
    • <![CDATA[内容]1> (如<)

字段映射

有时候POJO类的字段名和数据库的字段名不一样,查询到的数据不能封装到实体类里面(会赋值为属性类型的默认值),所以在查询的时候需要做一个字段的映射

mapper接口中的映射方法参数对应的的是SQL语句的参数,这里数据类型的对应没有那么严格,SQL的结果封装对应的是resultType类型,如果要封装为POJO对象,则要求要字段一一对应

<select id="selectAll" resultType="user">
        select id,name,age as _age
        from tb_user
</select>
sql片段
<sql id="_age">
    id,name,age as _age
</sql>
<select id="selectAll" resultType="user">
        select <include refid="_age"></include>
        from tb_user
</select>
  • type:返回类型
  • column:列名
  • property:字段名
  • select中的resultType要换成resultMap属性
<resultMap id="ageMap" type="user">
    <result column="age" property="_age"/>
</resultMap>
<select id="selectByid" resultMap="ageMap">
        select * from tb_user where id <![CDATA[
        <
        ]]>#{id}
</select>

条件查询

当查询语句中的参数多的时候,就需要方法参数映射的方式传入SQL语句中

<!--    多条件查询-->
<select id="selectCD" resultMap="ageMap">
    select *
    from tb_user where name like #{name} and age like #{age};
</select>

多条件

@Param(“SQL参数占位符名称”) 参数名

List<User> selectCD(@Param("name") String name1,@Param("age") String age1);
String name="李";
String age="1";
name="%"+name+"%";
age="%"+age+"%";
.................
    .......
List<User> users = mapper.selectCD(name,age);

对象封装

接口方法名(参数对象);

参数对象的属性名要与SQL的字段对应,如#{name},会去找 参数对象.name,所以也需要getter和setter方法,方法名没有严格要求,但一定要有返回对应字段。

SUser是age为String的类,这里是为了强调可以不用SQL返回结果的POJO类

List<User> selectCD(SUser user);
SUser su=new SUser();
su.setge(age);
su.setName(name);
。。。。。。。。。。。
    。。。。。。
List<User> users = mapper.selectCD(su);

Map集合

会将Map集合里面的Key值对应的Value值映射到SQL对应字段

List<User> selectCD(Map map);
HashMap<String, String> Map = new HashMap<String,String>();
Map.put("name","%李%");
Map.put("age","%1%");
.....................
....................
List<User> users = mapper.selectCD(Map);

动态条件查询

动态 SQL 是 MyBatis 的强大特性之一。如果你使用过 JDBC 或其它类似的框架,你应该能理解根据不同条件拼接 SQL 语句有多痛苦,例如拼接时要确保不能忘记添加必要的空格,还要注意去掉列表最后一个列名的逗号。利用动态 SQL,可以彻底摆脱这种痛苦。

有时候条件输入并不是都输入,这个时候需要缺少参数的多条件SQL语句也能执行

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach

多条件-动态查询

<!--    动态查询-多条件-->
    <select id="Dycon_Mul" resultMap="ageMap">
        select *
        from tb_user
        where
              <if test="name !='' and name!=null">
                  name=#{name}
              </if>
              <if test="age!='' and age!=null">
                  and age=#{age}
              </if>
    </select>
//     多条件动态
     List<User> Dycon_Mul(SUser su);
import com.mybatis.mapper.UserMapper;
import com.mybatis.pojo.SUser;
import com.mybatis.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class Dycon_Multest {
    public static void main(String[] args) throws IOException {
        String s = "mybatis-config.xml";
        InputStream resourceAsStream = Resources.getResourceAsStream(s);
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = build.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        SUser su = new SUser();
        su.setName("zs");
//不输入age参数
//        su.setge("20");
        List<User> users = mapper.Dycon_Mul(su);
        for(User u :users){
            System.out.println(u);
        }
    }
}

可以看到name没有在sql语句中

[DEBUG]  [main] c.m.m.U.Dycon_Mul - ==>  Preparing: select * from tb_user where name=? 
[DEBUG]  [main] c.m.m.U.Dycon_Mul - ==> Parameters: zs(String) 
[DEBUG]  [main] c.m.m.U.Dycon_Mul - <==      Total: 2 
User{id=6, name='zs', _age=20}
User{id=7, name='zs', _age=21}
问题

但是可以发现这个sql写法有一个缺陷,就是当第一个参数省略时,拼接的sql是不合法的

import com.mybatis.mapper.UserMapper;
import com.mybatis.pojo.SUser;
import com.mybatis.pojo.User;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;

import java.io.IOException;
import java.io.InputStream;
import java.util.List;

public class Dycon_Multest {
    public static void main(String[] args) throws IOException {
        String s = "mybatis-config.xml";
        InputStream resourceAsStream = Resources.getResourceAsStream(s);
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = build.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        SUser su = new SUser();
//省略名字
//        su.setName("zs");
        su.setge("20");
        List<User> users = mapper.Dycon_Mul(su);
        for(User u :users){
            System.out.println(u);
        }
    }
}

可以看到sql语句为select * from tb_user where and age=?,这是不合法的

[DEBUG]  [main] c.m.m.U.Dycon_Mul - ==>  Preparing: select * from tb_user where and age=? 
[DEBUG]  [main] c.m.m.U.Dycon_Mul - ==> Parameters: 20(String) 
Exception in thread "main" org.apache.ibatis.exceptions.PersistenceException: 
### Error querying database.  Cause: com.mysql.jdbc.exceptions.jdbc4.MySQLSyntaxErrorException: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'and age='20'' at line 6
解决方案

应该要想办法将拼接的sql语句变成合法

1、恒等式让所有条件格式都一样

<!--    动态查询-多条件-->
    <select id="Dycon_Mul" resultMap="ageMap">
        select *
        from tb_user
        where 1=1
              <if test="name !='' and name!=null">
                 and name=#{name}
              </if>
              <if test="age!='' and age!=null">
                  and age=#{age}
              </if>
    </select>

select * from tb_user where 1=1 and age=?

[DEBUG]  [main] c.m.m.U.Dycon_Mul - ==>  Preparing: select * from tb_user where 1=1 and age=? 
[DEBUG]  [main] c.m.m.U.Dycon_Mul - ==> Parameters: 20(String) 
[DEBUG]  [main] c.m.m.U.Dycon_Mul - <==      Total: 1 
User{id=6, name='zs', _age=20}

2、<where>标签替换where

where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。

<!--    动态查询-多条件-->
    <select id="Dycon_Mul" resultMap="ageMap">
        select *
        from tb_user
        <where>
            <if test="name !='' and name!=null">
                and name=#{name}
            </if>
            <if test="age!='' and age!=null">
                and age=#{age}
            </if>
        </where>


    </select>

可以看到where自动省略了name

select * from tb_user WHERE age=?

[DEBUG]  [main] c.m.m.U.Dycon_Mul - ==>  Preparing: select * from tb_user WHERE age=? 
[DEBUG]  [main] c.m.m.U.Dycon_Mul - ==> Parameters: 20(String) 
[DEBUG]  [main] c.m.m.U.Dycon_Mul - <==      Total: 1 
User{id=6, name='zs', _age=20}

单条件-动态查询

有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。

<!--    单条件查询-->
    <select id="Dycon_Sin" resultType="User">
        select * from tb_user
        where
        <choose>
            <when test="name !='' and name!= null">
                name = #{name}
            </when>
            <when test="age!=''and age!=null">
                age=#{age}
            </when>
        </choose>
    </select>
public class Dycon_Sintest {
    @Test
    public static void main(String[] args) throws IOException {
        String s = "mybatis-config.xml";
        InputStream resourceAsStream = Resources.getResourceAsStream(s);
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(resourceAsStream);
        SqlSession sqlSession = build.openSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        SUser su=new SUser();
        //这里设置了两个参数
        su.setge("20");
        su.setName("zs");
        List<User> users = mapper.Dycon_Sin(su);
        for(User u :users){
            System.out.println(u);
        }

    }
}

select * from tb_user where name = ?

可以看到只用了SQL语句中的第一个参数

[DEBUG]  [main] c.m.m.U.Dycon_Sin - ==>  Preparing: select * from tb_user where name = ? 
[DEBUG]  [main] c.m.m.U.Dycon_Sin - ==> Parameters: zs(String) 
[DEBUG]  [main] c.m.m.U.Dycon_Sin - <==      Total: 2 
User{id=6, name='zs', _age=null}
User{id=7, name='zs', _age=null}
问题

当没有输入值时,就会出现SQL语句不合法的情况,比如select * from tb_user where,所以需要一个default的情况

解决方案

1、<otherwise>标签

例如:

<!--    单条件查询-->
    <select id="Dycon_Sin" resultMap="ageMap">
        select * from tb_user
        where
        <choose>
            <when test="name !='' and name!= null">
                name = #{name}
            </when>
            <when test="age!=''and age!=null">
                age=#{age}
            </when>
            <otherwise>
                1=1
            </otherwise>
        </choose>
    </select>

​ 那么产生的SQL语句为select * from tb_user where 1=1 ,就可以避免报错

2、<where>标签

像上面的otherwise情况,只是为了SQL语句合法而写的

使用where标签可以省略otherwise部分

<!--    单条件查询-->
    <select id="Dycon_Sin" resultMap="ageMap">
        select * from tb_user
        <where>
            <choose>
                <when test="name !='' and name!= null">
                    name = #{name}
                </when>
                <when test="age!=''and age!=null">
                    age=#{age}
                </when>
            </choose>
        </where>
    </select>

结果:

会自动把SQL语句中的where去除,保证语句的合法

select * from tb_use

[DEBUG]  [main] c.m.m.U.Dycon_Sin - ==>  Preparing: select * from tb_user 
[DEBUG]  [main] c.m.m.U.Dycon_Sin - ==> Parameters:  
[DEBUG]  [main] c.m.m.U.Dycon_Sin - <==      Total: 7 
User{id=1, name='zhangsan', _age=22}
User{id=2, name='lisi', _age=26}
User{id=3, name='wangwu', _age=17}
User{id=4, name='李四', _age=19}
User{id=5, name='李三', _age=18}
User{id=6, name='zs', _age=20}
User{id=7, name='zs', _age=21}

Process finished with exit code 0