mybatis总结

"welcome to ARTAvrilLavigne Blog"

Posted by ARTAvrilLavigne on May 13, 2018

一、mybatis知识总结

  pojo:不按mvc分层,只是java bean有一些属性,还有get、set方法
  domain:不按mvc分层,只是java bean有一些属性,还有get、set方法
  po:用在持久层,还可以再增加或者修改的时候,从页面直接传入action中,它里面的java bean 类名等于表名,属性名等于表的字段名,还有对应的get、set方法
  vo: view object表现层对象,主要用于在高级查询中从页面接收传过来的各种参数.好处是扩展性强
  bo: 用在servie层,现在企业基本不用.

这些po,vo, bo,pojo可以用在各种层面吗?
  可以,也就是po用在表现层,vo用在持久层不报错,因为都是普通的java bean没有语法错误.但是在企业最好不要混着用,因为这些都是设计的原则,混着用比较乱.不利于代码维护.

自学方法论: 理论 -> 实践 -> 理论 -> 实践 反复迭代三遍

总结:

  1. mybatis是一个持久层框架, 作用是跟数据库交互完成增删改查

  2. 原生Dao实现(需要接口和实现类)

  3. 动态代理方式(只需要接口)
      mapper接口代理实现编写规则:
      1)映射文件中namespace要等于接口的全路径名称
      2)映射文件中sql语句id要等于接口的方法名称
      3)映射文件中传入参数类型要等于接口方法的传入参数类型
      4)映射文件中返回结果集类型要等于接口方法的返回值类型

  4. #{}占位符:占位
    如果传入的是基本类型,那么#{}中的变量名称可以随意写
    如果传入的参数是pojo类型,那么#{}中的变量名称必须是pojo中的属性.属性.属性…

  5. ${}拼接符:字符串原样拼接
    如果传入的是基本类型,那么${}中的变量名必须是value
    如果传入的参数是pojo类型,那么${}中的变量名称必须是pojo中的属性.属性.属性…
    注意:使用拼接符有可能造成sql注入,在页面输入的时候可以加入校验,不可输入sql关键字,不可输入空格
  6. 映射文件:
    1)传入参数类型通过parameterType属性指定
    2)返回结果集类型通过resultType属性指定
  7. hibernate和mybatis区别:
    hibernate:它是一个标准的orm框架,比较重量级,学习成本高.
      优点:高度封装,使用起来不用写sql,开发的时候,会减低开发周期.
      缺点:sql语句无法优化
      应用场景:oa(办公自动化系统), erp(企业的流程系统)等,还有一些政府项目等
       总的来说,在用于量不大,并发量小的时候使用.
    mybatis:它不是一个orm框架, 它是对jdbc的轻量级封装, 学习成本低,比较简单
      优点:学习成本低, sql语句可以优化, 执行效率高,速度快
      缺点:编码量较大,会拖慢开发周期
      应用场景: 互联网项目,比如电商,P2P理财等
          总的来说是用户量较大,并发高的项目.
          ==========================================================================================

  8. 输入映射(就是映射文件中可以传入哪些参数类型)
      1)基本类型
      2)pojo类型
      3)Vo类型
  9. 输出映射(返回的结果集可以有哪些类型)
      1)基本类型
      2)pojo类型
      3)List类型
  10. 动态sql:动态的拼接sql语句,因为sql中where条件有可能多也有可能少
      1)where:可以自动添加where关键字,还可以去掉第一个条件的and关键字
      2)if:判断传入的参数是否为空
      3)foreach:循环遍历传入的集合参数
      4)sql:封装查询条件,以达到重用的目的

  11. 对单个对象的映射关系:
      1)自动关联(偷懒的办法):可以自定义一个大而全的pojo类,然后自动映射其实是根据数据库
      总的字段名称和pojo中的属性名称对应.
      2)手动关联: 需要指定数据库中表的字段名称和java的pojo类中的属性名称的对应关系.
      使用association标签
  12. 对集合对象的映射关系
      只能使用手动映射:指定表中字段名称和pojo中属性名称的对应关系
      使用collection标签
  13. spring和mybatis整合
      整合后会话工厂都归spring管理
      1)原生Dao实现:
      需要在spring配置文件中指定dao实现类
      dao实现类需要继承SqlSessionDaoSupport超类
      在dao实现类中不要手动关闭会话,不要自己提交事务.
      2)Mapper接口代理实现:
      在spring配置文件中可以使用包扫描的方式,一次性的将所有mapper加载

  14. 逆向工程:自动生成Pojo类,还可以自动生成Mapper接口和映射文件
      注意:生成的方式是追加而不是覆盖,所以不可以重复生成,重复生成的文件有问题.
      如果想重复生成将原来生成的文件删除

二、mybatis最初原始使用步骤总结

  • 1)配置mybatis-config.xml 全局的配置文件 (1、数据源,2、外部的mapper)
  • 2)创建SqlSessionFactory
  • 3)通过SqlSessionFactory创建SqlSession对象
  • 4)通过SqlSession操作数据库 CRUD
  • 5)调用session.commit()提交事务
  • 6)调用session.close()关闭会话

mybatis整体架构如下:

mybatis1
mybatis2

2.1、引入依赖(pom.xml)

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.2.8</version>
</dependency>

2.2、全局配置文件(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>
<properties>
	<property name="driver" value="com.mysql.jdbc.Driver"/>
	<property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis-110?useUnicode=true&amp;characterEncoding=utf-8&amp;allowMultiQueries=true"/>
	<property name="username" value="root"/>
    	<property name="password" value="123456"/>
   </properties>

   <!-- 环境,可以配置多个,default:指定采用哪个环境 -->
   <environments default="test">
      <!-- id:唯一标识 -->
      <environment id="test">
         <!-- 事务管理器,JDBC类型的事务管理器 -->
         <transactionManager type="JDBC" />
         <!-- 数据源,池类型的数据源 -->
         <dataSource type="POOLED">
            <property name="driver" value="com.mysql.jdbc.Driver" />
            <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis-110" />
            <property name="username" value="root" />
            <property name="password" value="123456" />
         </dataSource>
      </environment>
      <environment id="development">
         <!-- 事务管理器,JDBC类型的事务管理器 -->
         <transactionManager type="JDBC" />
         <!-- 数据源,池类型的数据源 -->
         <dataSource type="POOLED">
            <property name="driver" value="${driver}" /> <!-- 配置了properties,所以可以直接引用 -->
            <property name="url" value="${url}" />
            <property name="username" value="${username}" />
            <property name="password" value="${password}" />
         </dataSource>
      </environment>
   </environments>
  </configuration>

2.3、配置Map.xml(MyMapper.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:命名空间,随便写,一般保证命名空间唯一 -->
<mapper namespace="MyMapper">
   <!-- statement,内容:sql语句。id:唯一标识,随便写,在同一个命名空间下保持唯一
      resultType:sql语句查询结果集的封装类型,tb_user即为数据库中的表
    -->
   <select id="selectUser" resultType="com.zpc.mybatis.User">
      select * from tb_user where id = #{id}
   </select>
</mapper>

2.4、修改全局配置文件(mybatis-config.xml)

引入MyMapper.xml

<configuration>
   <!-- 环境,可以配置多个,default:指定采用哪个环境 -->
   <environments default="test">
      <!-- id:唯一标识 -->
      <environment id="test">
         <!-- 事务管理器,JDBC类型的事务管理器 -->
         <transactionManager type="JDBC" />
         <!-- 数据源,池类型的数据源 -->
         <dataSource type="POOLED">
            <property name="driver" value="com.mysql.jdbc.Driver" />
            <property name="url" value="jdbc:mysql://127.0.0.1:3306/ssmdemo" />
            <property name="username" value="root" />
            <property name="password" value="123456" />
         </dataSource>
      </environment>
   </environments>
   <!-- 引入MyMapper.xml -->
   <mappers>
     <mapper resource="mappers/MyMapper.xml" />
   </mappers>
</configuration>

2.5、构建sqlSessionFactory(MybatisTest.java)

// 指定全局配置文件
String resource = "mybatis-config.xml";
// 读取配置文件
InputStream inputStream = Resources.getResourceAsStream(resource);
// 构建sqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

2.6、打开sqlSession会话,并执行sql(MybatisTest.java)

// 获取sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 操作CRUD,第一个参数:指定statement,规则:命名空间+“.”+statementId
// 第二个参数:指定传入sql的参数:这里是用户id
User user = sqlSession.selectOne("MyMapper.selectUser", 1);
System.out.println(user);

MybatisTest.java完整代码:

import com.zpc.test.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.InputStream;

public class MybatisTest {
   public static void main(String[] args) throws Exception {
      // 指定全局配置文件
      String resource = "mybatis-config.xml";
      // 读取配置文件
      InputStream inputStream = Resources.getResourceAsStream(resource);
      // 构建sqlSessionFactory
      SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
      // 获取sqlSession
      SqlSession sqlSession = sqlSessionFactory.openSession();
      try {
         // 操作CRUD,第一个参数:指定statement,规则:命名空间+“.”+statementId
         // 第二个参数:指定传入sql的参数:这里是用户id
         User user = sqlSession.selectOne("MyMapper.selectUser", 1);
         System.out.println(user);
      } finally {
         sqlSession.close();
      }
   }
}

User.java

import java.text.SimpleDateFormat;
import java.util.Date;

public class User {
    private String id;
    private String userName;
    private String password;
    private String name;
    private Integer age;
    private Integer sex;
    private Date birthday;
    private String created;
    private String updated;

    public String getId() {
        return id;
    }

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

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Integer getSex() {
        return sex;
    }

    public void setSex(Integer sex) {
        this.sex = sex;
    }

    public Date getBirthday() {
        return birthday;
    }

    public void setBirthday(Date birthday) {
        this.birthday = birthday;
    }

    public String getCreated() {
        return created;
    }

    public void setCreated(String created) {
        this.created = created;
    }

    public String getUpdated() {
        return updated;
    }

    public void setUpdated(String updated) {
        this.updated = updated;
    }

    @Override
    public String toString() {
        return "User{" +
                "id='" + id + '\'' +
                ", userName='" + userName + '\'' +
                ", password='" + password + '\'' +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", sex=" + sex +
                ", birthday='" + new SimpleDateFormat("yyyy-MM-dd").format(birthday) + '\'' +
                ", created='" + created + '\'' +
                ", updated='" + updated + '\'' +
                '}';
    }
}

三、完整的CRUD操作(非动态代理方式,不推荐)

3.1、创建UserDao接口

import com.zpc.mybatis.pojo.User;
import java.util.List;

public interface UserDao {

    /**
     * 根据id查询用户信息
     *
     * @param id
     * @return
     */
    public User queryUserById(String id);

    /**
     * 查询所有用户信息
     *
     * @return
     */
    public List<User> queryUserAll();

    /**
     * 新增用户
     *
     * @param user
     */
    public void insertUser(User user);

    /**
     * 更新用户信息
     *
     * @param user
     */
    public void updateUser(User user);

    /**
     * 根据id删除用户信息
     *
     * @param id
     */
    public void deleteUser(String id);
}

3.2、创建UserDaoImpl

import com.zpc.mybatis.dao.UserDao;
import com.zpc.mybatis.pojo.User;
import org.apache.ibatis.session.SqlSession;
import java.util.List;

public class UserDaoImpl implements UserDao {
    public SqlSession sqlSession;

    public UserDaoImpl(SqlSession sqlSession) {
        this.sqlSession = sqlSession;
    }

    @Override
    public User queryUserById(String id) {
        return this.sqlSession.selectOne("UserDao.queryUserById", id);
    }

    @Override
    public List<User> queryUserAll() {
        return this.sqlSession.selectList("UserDao.queryUserAll");
    }

    @Override
    public void insertUser(User user) {
        this.sqlSession.insert("UserDao.insertUser", user);
    }

    @Override
    public void updateUser(User user) {
        this.sqlSession.update("UserDao.updateUser", user);
    }

    @Override
    public void deleteUser(String id) {
        this.sqlSession.delete("UserDao.deleteUser", id);
    }

}

3.3、编写UserDao对应的UserDaoMapper.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:命名空间,随便写,一般保证命名空间唯一 -->
<mapper namespace="UserDao">
    <!-- statement,内容:sql语句。id:唯一标识,随便写,在同一个命名空间下保持唯一
       resultType:sql语句查询结果集的封装类型,tb_user即为数据库中的表
     -->
    <!--<select id="queryUserById" resultType="com.zpc.mybatis.pojo.User">-->
    <!--select * from tb_user where id = #{id}-->
    <!--</select>-->

    <!--使用别名-->
    <select id="queryUserById" resultType="com.zpc.mybatis.pojo.User">
      select
       tuser.id as id,
       tuser.user_name as userName,
       tuser.password as password,
       tuser.name as name,
       tuser.age as age,
       tuser.birthday as birthday,
       tuser.sex as sex,
       tuser.created as created,
       tuser.updated as updated
       from
       tb_user tuser
       where tuser.id = #{id};
   </select>

    <select id="queryUserAll" resultType="com.zpc.mybatis.pojo.User">
        select * from tb_user;
    </select>

    <!--插入数据-->
    <insert id="insertUser" parameterType="com.zpc.mybatis.pojo.User">
        INSERT INTO tb_user (
        user_name,
        password,
        name,
        age,
        sex,
        birthday,
        created,
        updated
        )
        VALUES
        (
        #{userName},
        #{password},
        #{name},
        #{age},
        #{sex},
        #{birthday},
        now(),
        now()
        );
    </insert>

    <update id="updateUser" parameterType="com.zpc.mybatis.pojo.User">
        UPDATE tb_user
        <trim prefix="set" suffixOverrides=",">
            <if test="userName!=null">user_name = #{userName},</if>
            <if test="password!=null">password = #{password},</if>
            <if test="name!=null">name = #{name},</if>
            <if test="age!=null">age = #{age},</if>
            <if test="sex!=null">sex = #{sex},</if>
            <if test="birthday!=null">birthday = #{birthday},</if>
            updated = now(),
        </trim>
        WHERE
        (id = #{id});
    </update>

    <delete id="deleteUser">
        delete from tb_user where id=#{id}
    </delete>
</mapper>

然后需要在mybatis-config.xml中添加配置:

<mappers>
    <!-- 第一种方式:使用相对于类路径的资源引用 -->
    <mapper resource="mappers/UserDaoMapper.xml"/>
    <!-- 第二种方式:注解方式可以使用如下配置方式 -->
    <mapper class="com.zpc.mybatis.dao.UserMapper"/>
</mappers>

四、动态代理Mapper实现类(推荐)

动态代理总结:
  使用mapper接口不用写接口实现类即可完成数据库操作,使用非常简单,也是官方所推荐的使用方法。使用mapper接口的必须具备以下几个条件:

  • Mapper的namespace必须和mapper接口的全路径一致
  • Mapper接口的方法名必须和sql定义的id一致
  • Mapper接口中方法的输入参数类型必须和sql定义的parameterType一致
  • Mapper接口中方法的输出参数类型必须和sql定义的resultType/resultMap一致

4.1、创建UserMapper接口(对应原UserDao)

public interface UserMapper {
   
   /**
    * 登录(直接使用注解指定传入参数名称)
    * @param userName
    * @param password
    * @return
    */
   public User login(@Param("userName") String userName, @Param("password") String password);
   
   /**
    * 根据表名查询用户信息(直接使用注解指定传入参数名称)
    * @param tableName
    * @return
    */
   public List<User> queryUserByTableName(@Param("tableName") String tableName);
   
   /**
    * 根据Id查询用户信息
    * @param id
    * @return
    */
   public User queryUserById(Long id);
   
   /**
    * 查询所有用户信息
    * @return
    */
   public List<User> queryUserAll();
   
   /**
    * 新增用户信息
    * @param user
    */
   public void insertUser(User user);
   
   /**
    * 根据id更新用户信息
    * @param user
    */
   public void updateUser(User user);
   
   /**
    * 根据id删除用户信息
    * @param id
    */
   public void deleteUserById(Long id);
}

4.2、创建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:命名空间,随便写,一般保证命名空间唯一 ,为了使用接口动态代理,这里必须是接口的全路径名-->
<mapper namespace="com.zpc.mybatis.dao.UserMapper">
    <!--
       1.#{},预编译的方式preparedstatement,使用占位符替换,防止sql注入,一个参数的时候,任意参数名可以接收
       2.${},普通的Statement,字符串直接拼接,不可以防止sql注入,一个参数的时候,必须使用${value}接收参数
     -->
    <select id="queryUserByTableName" resultType="com.zpc.mybatis.pojo.User">
        select * from ${tableName}
    </select>

    <select id="login" resultType="com.zpc.mybatis.pojo.User">
        select * from tb_user where user_name = #{userName} and password = #{password}
    </select>

    <!-- statement,内容:sql语句。
       id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
       resultType:sql语句查询结果集的封装类型,使用动态代理之后和方法的返回类型一致;resultMap:二选一
       parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
     -->
    <select id="queryUserById" resultType="com.zpc.mybatis.pojo.User">
        select * from tb_user where id = #{id}
    </select>
    <select id="queryUserAll" resultType="com.zpc.mybatis.pojo.User">
        select * from tb_user
    </select>
    <!-- 新增的Statement
       id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
       parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
       useGeneratedKeys:开启主键回写
       keyColumn:指定数据库的主键
       keyProperty:主键对应的pojo属性名
     -->
    <insert id="insertUser" useGeneratedKeys="true" keyColumn="id" keyProperty="id"
            parameterType="com.zpc.mybatis.pojo.User">
        INSERT INTO tb_user (
        id,
        user_name,
        password,
        name,
        age,
        sex,
        birthday,
        created,
        updated
        )
        VALUES
        (
        null,
        #{userName},
        #{password},
        #{name},
        #{age},
        #{sex},
        #{birthday},
        NOW(),
        NOW()
        );
    </insert>
    <!-- 
       更新的statement
       id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
       parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
     -->
    <update id="updateUser" parameterType="com.zpc.mybatis.pojo.User">
        UPDATE tb_user
        <trim prefix="set" suffixOverrides=",">
            <if test="userName!=null">user_name = #{userName},</if>
            <if test="password!=null">password = #{password},</if>
            <if test="name!=null">name = #{name},</if>
            <if test="age!=null">age = #{age},</if>
            <if test="sex!=null">sex = #{sex},</if>
            <if test="birthday!=null">birthday = #{birthday},</if>
            updated = now(),
        </trim>
        WHERE
        (id = #{id});
    </update>
    <!-- 
       删除的statement
       id:唯一标识,随便写,在同一个命名空间下保持唯一,使用动态代理之后要求和方法名保持一致
       parameterType:参数的类型,使用动态代理之后和方法的参数类型一致
     -->
    <delete id="deleteUserById" parameterType="java.lang.String">
        delete from tb_user where id=#{id}
    </delete>
</mapper>

4.3、全局配置文件mybatis-config.xml引入UserMapper.xml

<mappers>
    <!-- 第一种方式:使用相对于类路径的资源引用 -->
    <mapper resource="mappers/UserMapper.xml"/>
    <!-- 第二种方式:注解方式可以使用如下配置方式 -->
    <mapper class="com.zpc.mybatis.dao.UserMapper"/>
</mappers>

五、Mapper XML文件详解

5.1、#{}和${}区别

  • #{}只是表示占位,与参数的名字无关,如果只有一个参数会自动对应,相当于PreparedStatement使用占位符去替换参数,可以防止sql注入。(相当于会加一对引号’xxxx’)
  • ${}是进行字符串拼接,相当于sql语句中的Statement,使用字符串去拼接sql;$可以是sql中的任一部分传入到Statement中,不能防止sql注入。
1、=======================推荐=======================

/**
 * 根据表名查询用户信息(直接使用注解指定传入参数名称)
 *
 * @param tableName
 * @return
 */
public List<User> queryUserByTableName(@Param("tableName") String tableName);

<select id="queryUserByTableName" resultType="com.zpc.mybatis.pojo.User">
    select * from ${tableName}
</select>

2、=======================#{}多个参数时=======================

/**
 * 登录(直接使用注解指定传入参数名称)
 *
 * @param userName
 * @param password
 * @return
 */
public User login( String userName, String password);

<select id="login" resultType="com.zpc.mybatis.pojo.User">
    select * from tb_user where user_name = #{userName} and password = #{password}
</select>

3、=======================报错=======================

org.apache.ibatis.exceptions.PersistenceException: 
Error querying database.  Cause: org.apache.ibatis.binding.BindingException: Parameter 'userName' not found. Available parameters are [0, 1, param1, param2]
Cause: org.apache.ibatis.binding.BindingException: Parameter 'userName' not found. Available parameters are [0, 1, param1, param2]

4、=======================解决方案一=======================

<select id="login" resultType="com.zpc.mybatis.pojo.User">
    select * from tb_user where user_name = #{0} and password = #{1}
</select>

5、=======================解决方案二=======================

<select id="login" resultType="com.zpc.mybatis.pojo.User">
    select * from tb_user where user_name = #{param1} and password = #{param2}
</select>

6、=======================最终解决方案=======================

/**
 * 登录(直接使用注解指定传入参数名称)
 *
 * @param userName
 * @param password
 * @return
 */
public User login(@Param("userName") String userName, @Param("password") String password);

<select id="login" resultType="com.zpc.mybatis.pojo.User">
    select * from tb_user where user_name = #{userName} and password = #{password}
</select>

通常在方法的参数列表上加上一个注释@Param(“xxxx”) 显式指定参数的名字,然后通过${}或#{}
sql语句动态生成的时候,使用${}
sql语句中某个参数进行占位的时候#{}

面试题(#、$区别):

/**
 * #号
 * @param username1
 * @return
 */
User queryUserListByName1(@Param("username1") String username1);

/**
 * $号
 * @param username2
 * @return
 */
User queryUserListByName2(@Param("username2") String username2);

<select id="queryUserListByName1" resultType="com.zpc.mybatis.pojo.User">
    select * from tb_user WHERE user_name=#{username1}
</select>

<select id="queryUserListByName2" resultType="com.zpc.mybatis.pojo.User">
    select * from tb_user WHERE user_name='${username2}'//手动加了引号
</select>

5.2、like模糊查询用法

  mybatis中对于使用like来进行模糊查询的几种方式:

方式一:使用'%${xxxxx}%'
<select id="queryUsersLikeUserName" resultType="User">
	select * from tb_user where user_name like '%${userName}%'
</select>
注意:由于$是参数直接注入的,导致这种写法大括号里面不能注明jdbcType(如:'%${userName,jdbcType=VARCHAR}%'),不然会报错
缺点:可能会引起sql的注入,平时尽量避免使用${...}
方式二:使用"%"#{userName}"%"
<select id="queryUsersLikeUserName" resultType="User">
	select * from tb_user where user_name like "%"#{userName}"%"
</select>
注意:因为#{xxx}解析成sql语句时候,会在变量外侧自动加单引号'',所以这里 % 需要使用双引号"",不能使用单引号'',不然会查不到任何结果
方式三:使用CONCAT()函数连接参数形式
<select id="queryUsersLikeUserName" resultType="User">
	select * from tb_user where user_name like CONCAT('%',#{userName},'%')
</select>

5.3、resultMap用法

  resultMap是mybatis中最重要的元素,使用resultMap可以解决两大问题:

  • POJO属性名和表结构字段名不一致的问题(有些情况下也不是标准的驼峰格式)
  • 完成高级查询,比如说:一对一、一对多、多对多

  解决表字段名和属性名不一致的问题有两种方法:
1、如果是驼峰似的命名规则可以在mybatis配置文件中设置
2、使用resultMap解决

<!--
type:返回的结果集对应的java的实体类型
id:resultMap的唯一标识
autoMapping:默认完成映射,如果已开启驼峰匹配,可以解决驼峰匹配
-->
<resultMap type="User" id="resultUser" autoMapping="true">
   <!--
   指定主键
   cloumn:数据库中的列名
   property:java实体类中的属性名
   -->
   <id column="id" property="id"/>
   <!-- 使用result配置数据库列名和java实体类中的属性名对应 -->
   <result column="user_name" property="userName"/>
</resultMap>

======使用======
<select id="queryUserById" resultMap="resultUser">
   select * from tb_user where id = #{id}
</select>

5.4、sql抽取使用

格式:

<sql id="xxxx"></sql>
<include refId="xxxx" />

例如:在UserMapper.xml中定义如下片段:

<sql id="commonSql">
		id,
		user_name,
		password,
		name,
		age,
		sex,
		birthday,
		created,
		updated	
</sql>

则可以在UserMapper.xml中使用它:
<select id="queryUserById" resultMap="userResultMap">
	select <include refid="commonSql"></include> from tb_user where id = #{id}
</select>

<select id="queryUsersLikeUserName" resultType="User">
	select <include refid="commonSql"></include> from tb_user where user_name like "%"#{userName}"%"
</select>

六、动态sql

  动态sql用法有:

  • if标签
  • choose、when、otherwise标签
  • where、set标签
  • foreach标签
  • trim标签

6.1、if标签

字符串String类型:
<select id="queryUserList" resultType="com.zpc.mybatis.pojo.User">
    select * from tb_user WHERE sex=1
    <if test="name!=null and name.trim()!=''">
      and name like '%${name}%'
    </if>
</select>

<select id="queryUserList" resultType="com.zpc.mybatis.pojo.User">
    select * from tb_user WHERE sex=1
    <if test="name!=null and name!=''">
      and name=#{name}
    </if>
</select>

整型int类型:
<select id="queryUserList" resultType="com.zpc.mybatis.pojo.User">
    select * from tb_user WHERE sex=1
    <if test="taskId!=null and taskId!=''">
      and taskId=#{taskId}
    </if>
</select>
注意:当传入的taskId为0时,此if条件语句是进不去的。因为Mybatis中if标签中比较传入数字taskId和字符串''时,会把这个字符串''转换成数字0,然后再和这个数字taskId判断,因此该if条件语句进不去

解决:如果存在taskId为0的情况,则改为如下所示:
<select id="queryUserList" resultType="com.zpc.mybatis.pojo.User">
    select * from tb_user WHERE sex=1
    <if test="taskId!=null and taskId!='' or taskId==0">
      and taskId=#{taskId}
    </if>
</select>

6.2、choose、when、otherwise标签

<select id="queryUserListByNameOrAge" resultType="com.zpc.mybatis.pojo.User">
    select * from tb_user where sex=1
    <!--
    1.一旦有条件成立的when,后续的when则不会执行
    2.当所有的when都不执行时,才会执行otherwise
    -->
    <choose>
        <when test="name!=null and name!=''">
            and name like '%${name}%'
        </when>
        <when test="age!=null">
            and age = #{age}
        </when>
        <otherwise>
            and name='李知恩'
        </otherwise>
    </choose>
</select>

6.3、where和set标签

where标签使用:

<!--
<where>标签的作用:如果该便签包含的元素中有返回值,就插入一个where
当if条件不满足的时候,where元素中没有任何内容,所以SQL中不会出现where
-->
<select id="queryUserListByNameAndAge" resultType="com.zpc.mybatis.pojo.User">
    select * from tb_user
    <!--如果多出一个and,会自动去除,如果缺少and或者多出多个and则会报错-->
    <where>
        <if test="name!=null and name.trim()!=''">
            and name like '%${name}%'
        </if>
        <if test="age!=null">
            and age = #{age}
        </if>
    </where>
</select>

set标签两种使用(一般用在update语句中):

方式一:推荐(加上trim标签)
<!--
1、某个属性若不符合if条件则那个属性不会被更新修改
2、<trim prefix="set" suffixOverrides=",">会去除多余的逗号
3、now()表示当前时间
-->
<update id="updateUser" parameterType="com.zpc.mybatis.pojo.User">
    UPDATE tb_user
    <trim prefix="set" suffixOverrides=",">
        <if test="userName!=null">user_name = #{userName},</if>
        <if test="password!=null">password = #{password},</if>
        <if test="name!=null and name.length()>0">name = #{name},</if>
        <if test="age!=null">age = #{age},</if>
        <if test="sex!=null">sex = #{sex},</if>
	date = now(),
        <if test="birthday!=null">birthday = #{birthday},</if>
    </trim>
    where id = #{id}
</update>

方式二:
<!--
<set>标签的作用:如果该标签包含的元素中有返回值,就插入一个set
如果set后面的字符串是以逗号结尾的,就将最后这个逗号去除
-->
<update id="updateUser" parameterType="com.zpc.mybatis.pojo.User">
    UPDATE tb_user
    <set>
        <if test="userName!=null">user_name = #{userName},</if>
        <if test="password!=null">password = #{password},</if>
        <if test="name!=null and name.length()>0">name = #{name},</if>
        <if test="age!=null">age = #{age},</if>
        <if test="sex!=null">sex = #{sex},</if>
	date = now(),
        <if test="birthday!=null">birthday = #{birthday},</if>
    </set>
    where id = #{id}
</update>

6.4、foreach标签

<select id="queryUserListByIds" resultType="com.zpc.mybatis.pojo.User">
    select * from tb_user where id in
    <foreach collection="idList" item="id" open="(" close=")" separator=",">
        #{id}
    </foreach>
</select>

6.5、trim标签

        <where>和<set>标签都可以用trim标签实现,并且底层就是通过TrimSqlNode实现的
        
        <where>标签对应的trim实现:
            <trim prefix="where" prefixOverride="AND | OR ">
            
        <set>标签对应的trim实现:
            <trim prefix="set" suffixOverrides=",">
        
        提示:
            prefixOverride中AND和OR后面的空格不能省略,为了避免匹配到andes或
            orders等单词。实际上prefixOverride包含"AND""OR""AND\n""OR\n"
            "AND\r""OR\r""AND\t""OR\t"
            
        <trim>标签的四个属性:
            prefix:当trim元素包含内容时,会给内容增加prefix指定的前缀
            prefixOverride:当trim元素包含内容时,会把内容中匹配的前缀字符串去掉
            suffix:当trim元素包含内容时,会给内容增加prefix指定的后缀
            suffixOverride:当trim元素包含内容时,会把内容中匹配的后缀字符串去掉

七、高级查询

  • 关联-association:用于一对一和多对一的关系
  • 集合-collection:用于一对多的关系
例如:
public class Person{
	private id;
	private name;
	private age;
	// 身份证对象
	private IDCard card;                -->Person的resultMap中使用association
	// 银行卡集合
	private List<BankCard> cardList;    -->Person的resultMap中使用collection

}

一对一的例子:

人和身份证的关系是一对一
public class Card {
	private Integer id;
	private String code;
	
	// 省略get、set方法
}

public class User {
	private Integer id;
	private String name;
	private Integer age;
	private Card card;
	
	//省略get、set方法
}

<mapper namespace="com.huawei.mapper.PersonMapper">
	<resultMap type="com.huawei.pojo.Person" id="personMapper">
		<id property="id" column="id"/>
		<result property="name" column="name"/>
		<result property="age" column="age"/>
		<association property="card" column="card_id" 
			select="com.huawei.mapper.CardMapper.selectCardById"
			javaType="com.huawei.pojo.Card">
		</association>
	</resultMap>
	<select id="selectPersonById" parameterType="int" resultMap="personMapper">
		select * from tb_person where id = #{id}
	</select>
</mapper>
	
<mapper namespace="com.huawei.mapper.CardMapper">
	<select id="selectCardById" parameterType="int" resultType="com.huawei.pojo.Card">
		select * from tb_card where id = #{id} 
	</select>
</mapper>

一对多的例子:

班级和学生是一对多的关系
public class Clazz {
	private Integer id;
	private String code;
	private String name;
    	// 班级与学生是一对多的关系
	private List<Student> studentList;
	
	// 省略set/get方法
}

public class Student {
	private Integer id;
	private String name;
	// 学生与班级是多对一的关系
	private Clazz clazz;
	
	// 省略set/get方法
}

collection来实现一对多的映射:
<mapper namespace="com.huawei.mapper.ClazzMapper">
	<select id="selectClazzById" parameterType="int" resultMap="clazzResultMap">
		select * from tb_clazz where id = #{id}
	</select>
	<resultMap type="com.huawei.pojo.Clazz" id="clazzResultMap">
		<id property="id" column="id"/>
		<result property="code" column="code"/>
		<result property="name" column="name"/>
		<!-- property: 指的是集合属性的值, ofType:指的是List集合中元素的类型, fetchType:设置lazy懒加载, javaType:集合类型 -->
		<collection property="studentList" ofType="com.huawei.pojo.Student"
		column="id" javaType="ArrayList"
		fetchType="lazy" select="com.huawei.mapper.StudentMapper.selectStudentByClazzId">
			<id property="id" column="id"/>
			<result property="name" column="name"/>
		</collection>
	</resultMap>
</mapper>

多对一的例子:

association来实现多对一的映射:
<mapper namespace="com.huawei.mapper.StudentMapper">
	<select id="selectStudentById" parameterType="int" resultMap="studentResultMap">
		select * from tb_clazz c,tb_student s where c.id = s.id and s.id = #{id}
	</select>
	<select id="selectStudentByClazzId" parameterType="int" resultMap="studentResultMap">
		select * from tb_student where clazz_id = #{id}
	</select>
	<resultMap type="com.huawei.pojo.Student" id="studentResultMap">
		<id property="id" column="id"/>
		<result property="name" column="name"/>
		<!--  property:定义对象的属性名   javaType:属性的类型 -->
		<association property="clazz" javaType="com.huawei.pojo.Clazz">
			<id property="id" column="id"/>
			<result property="code" column="code"/>
			<result property="name" column="name"/>
		</association>
	</resultMap>
</mapper>

八、sql语句中特殊字符’<’处理

在xml中,”<”、”>”、”&”等字符是不能直接存入的,否则xml语法检查时会报错,如果想在xml中使用这些符号,必须将其转义。
方法一:使用xml中的字符实体转义替代

&   &amp;       和号
<   &lt;        小于
>   &gt;        大于
"   &quot;      双引号
'   &apos;      单引号

如:<if test="startDateTime!=null"> and mm.ttime &gt; to_date(#{startDateTime},'yyyy-mm-dd hh24:mi:ss')</if>
注意:
  (1) 转义序列字符之间不能有空格
  (2) 转义序列必须以";"结束
  (3) 单独出现的"&"不会被认为是转义的开始
  (4) 区分大小写

方法二:使用<![CDATA[]]>这个标记将包含的内容将表示为纯文本

例如:
<select id="query" parameterType="Entity"  resultType="java.util.HashMap">
       SELECT id, name, age 
       FROM person
       WHERE age <![CDATA[ > ]]> 23 and id <![CDATA[ <= ]]> 100
 </select>
 
注意:
  (1) 不允许嵌套使用 
  (2) "]]>"这部分不能包含空格或者换行

九、缓存机制

  Mybatis缓存机制分为一级缓存和二级缓存
  在mybatis中,一级缓存默认是开启的,并且一直无法关闭。一级缓存是sqlSession级的,即利用同一个sqlSession执行查询相同的SQL,数据会直接从缓存中取。一个sqlSession会对应new一个调度器Excutor对象,每次Excutor去执行SQL前都会去缓存里看一下是不是之前执行过了,当然了本质上就是从一个Map里面找key的操作,执行过了就直接去缓存里取,没有则访问数据库。
  二级缓存是Mapper级别的,可以这么理解只要是执行了同一个mapper.xml中的同一个SQL,都会直接从缓存里取,不管是不是同一个sqlSession。二级缓存需要去自己开启,在mapper.xml中写标签或者也可以在配置文件里配置cacheEnabled=true,如果开启了二级缓存,查询数据的循序为二级缓存--->一级缓存--->数据库。即可以二级缓存和一级缓存同时开启,不存在选择一个的关系。

一级缓存注意:

  • MyBatis的一级缓存最大范围是SqlSession内部,有多个SqlSession或者分布式的环境下,数据库写操作会引起脏数据
  • MyBatis一级缓存的生命周期和SqlSession一致
  • MyBatis一级缓存内部设计简单,只是一个没有容量限定的HashMap,在缓存的功能性上有所欠缺
  • mybatis和spring整合后进行mapper代理开发,不支持一级缓存

二级缓存注意:

  • MyBatis的二级缓存相对于一级缓存来说,实现了SqlSession之间缓存数据的共享,同时粒度更加的细,能够到namespace级别,通过Cache接口实现类不同的组合,对Cache的可控性也更强
  • MyBatis在多表查询时,极大可能会出现脏数据,有设计上的缺陷,安全使用二级缓存的条件比较苛刻
  • 在分布式环境下,由于默认的MyBatis Cache实现都是基于本地的,分布式环境下必然会出现读取到脏数据,需要使用集中式缓存将MyBatis的Cache接口实现,有一定的开发成本,直接使用Redis,Memcached等分布式缓存可能成本更低,安全性也更高

二级缓存的脏读:
1、脏读的产生
Mybatis的二级缓存是和命名空间绑定的,所以通常情况下每一个Mapper映射文件都有自己的二级缓存,不同的mapper的二级缓存互不影响。这样的设计一不注意就会引起脏读,从而导致数据一致性的问题。引起脏读的操作通常发生在多表关联操作中,比如在两个不同的mapper中都涉及到同一个表的增删改查操作,当其中一个mapper对这张表进行查询操作,此时另一个mapper进行了更新操作刷新缓存,然后第一个mapper又查询了一次,那么这次查询出的数据是脏数据。出现脏读的原因是他们的操作的缓存并不是同一个。

2、脏读的避免

  • mapper中的操作以单表操作为主,避免在关联操作中使用mapper
  • 在关联操作的mapper中配置参照缓存

3、避免脏读的配置
Mapper接口和XML配置使用的是同一个命名空间。因此他们只能使用同一个缓存。
法一:

在Mapper接口中配置缓存然后在XML使用参照缓存
@CacheNarnespaceRef(RoleMapper.class) public interface RoleMapper { }

<!--那么在XML中只能使用参照缓存-->
<cache-ref narnespace="com.huawei.mapper.RoleMapper"/>

法二:

或者可以配置XML然后Mapper接口使用参照缓存
@CacheNarnespaceRef(RoleMapper.class) public interface RoleMapper { }

<mapper narnespace="com.huawei.mapper.RoleMapper"> 
      <cache eviction="FIFO" flushinterval="60000" Size="512" readOnly="false"/>
</mapper>

总结:

[1]https://www.cnblogs.com/diffx/p/10611082.html
[2]https://blog.csdn.net/zhenwei1994/article/details/81876278
[3]https://blog.csdn.net/wang852575989/article/details/100173875
[4]https://blog.csdn.net/yangbaggio/article/details/89711259
[5]https://www.cnblogs.com/songsongblue/p/10185569.html
[6]https://blog.csdn.net/weixin_42447959/article/details/90746328
[7]https://tech.meituan.com/2018/01/19/mybatis-cache.html
[8]https://www.cnblogs.com/lanqi/p/9283516.html


みなさんのごおうえんをおねがいします~~