02_Mybatis__SQL映射文件

MyBatis官方文档【中文版】https://mybatis.org/mybatis-3/zh/sqlmap-xml.html

1、增删改查

  • cache – 该命名空间的缓存配置。
  • cache-ref – 引用其它命名空间的缓存配置。
  • resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
  • parameterMap – 老式风格的参数映射。此元素已被废弃,并可能在将来被移除!请使用行内参数映射。文档中不会介绍此元素。
  • sql – 可被其它语句引用的可重用语句块。
  • insert – 映射插入语句。
  • update – 映射更新语句。
  • delete – 映射删除语句。
  • select – 映射查询语句。

EmployeeMapper.java

public void addEmp(Employee employee);

public void updateEmp(Employee employee);

public void deleteEmpById(Integer id);

EmployeeMapper.xml

<!-- public void addEmp(Employee employee); -->
<!--
        parameterType:可以省略
     -->
<insert id="addEmp" parameterType="com.yuanwu.mybatis.bean.Employee">
    insert tbl_employee(last_name,gender,email)
    values(#{lastName},#{gender},#{email})
</insert>

<!-- public void updateEmp(Employee employee); -->
<update id="updateEmp">
    update tbl_employee
    set last_name=#{lastName},gender=#{gender},email=#{email}
    where id=#{id}
</update>

<!-- public void deleteEmpById(Integer id); -->
<delete id="deleteEmpById">
    delete from tbl_employee where id=#{id}
</delete>

MyBatisTest.java

@Test
    public void test03() throws IOException {
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        //1、获取到的SqlSession不会自动提交数据
        SqlSession openSession = sqlSessionFactory.openSession();

        try {
            EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
            //测试添加
            // Employee employee = new Employee(null, "Jerry", "0", "Jerry@yuanwu.com");
            // mapper.addEmp(employee);

            //测试修改
            // Employee employee = new Employee(1, "Back", "1", "Back@yuanwu.com");
            // mapper.updateEmp(employee);

            //测试删除
            mapper.deleteEmpById(1);

            //1、手动提交数据
            openSession.commit();
        } finally {
            openSession.close();
        }
    }

2、insert_获取自增主键的值

<!--
	获取自增主键的值:
		mybatis支持自增主键,自增主键值的获取,mybatis也是利用	statement.getGeneratedKeys();
    useGeneratedKeys="true";使用自增主键获取主键值策略
    keyProperty:指定对应的主键属性,也就是mybatis获取到主键值以后,将这个值封装给JavaBean的某个属性
-->
<insert id="addEmp" parameterType="com.yuanwu.mybatis.bean.Employee"
        useGeneratedKeys="true" keyProperty="id">
    insert tbl_employee(last_name,gender,email)
    values(#{lastName},#{gender},#{email})
</insert>

3、insert_获取非自增主键的值

<!--
        Oracle不支持自增:Oracle使用序列来模拟自增
        每次插入的数据的主键是从序列中拿到的值,如何拿?
    -->
<insert id="addEmp" databaseId="oracle">
    <!-- keyProperty:查出的主键值封装给JavaBean的哪个属性
                order="BEFORE" > 当前sql在插入sql之前运行
                        AFTER > 当前sql在插入sql之后运行
                resultType:查出的数据的返回类型

            BEFORE运行顺序:
                先运行selectKey 查询id的sql,查出的id值封装给JavaBean的id属性
                然后运行插入的sql,就可以取出id属性对应的值
        -->
    <selectKey keyProperty="id" order="BEFORE" resultType="Integer">
        <!-- 编写查询主键的sql语句 -->
        select EMPLOYEES_SEQ.nextval from dual
    </selectKey>
    <!-- 插入时的主键是从序列中拿到的 -->
    insert into employees(EMPLOYEE_ID,LAST_NAME,EMAIL)
    values(#{id},#{lastName},#{email})
</insert>

4、参数处理_单个参数&多个参数&命名

EmployeeMapper.java

public Employee getEmpByIdAndLastName(
        @Param("id") Integer id,@Param("lastName") String lastName);

EmployeeMapper.xml

<!-- public Employee getEmpByIdAndLastName(Integer id,String lastName); -->
<select id="getEmpByIdAndLastName" resultType="com.yuanwu.mybatis.bean.Employee">
    select * from tbl_employee where id = #{id} and last_name=#{lastName}
</select>

MyBatis.java

@Test
public void test04() throws IOException {
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    //1、获取到的SqlSession不会自动提交数据
    SqlSession openSession = sqlSessionFactory.openSession();

    try {
        EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
        //多个参数查询
        Employee employee = mapper.getEmpByIdAndLastName(2, "Jerry");
        System.out.println(employee);
    } finally {
        openSession.close();
    }
}

mybatis参数处理.txt 总结

传入单个参数:
        #{参数名}:取出参数值

传入多个参数:
    多个参数会被封装成一个map,
        key:param1, param2...或者参数的索引
        value:传入的参数值
    #{}就是从map中获取指定的key的值;

    绑定异常:
        Cause: org.apache.ibatis.binding.BindingException:
        Parameter 'id' not found.
        Available parameters are [arg1, arg0, param1, param2]
    操作:
        方法:public Employee getEmpByIdAndLastName(Integer id,String lastName);
        取值:id = #{id} and last_name=#{lastName}

命名参数:明确指定封装参数时map的key;@Param("id")
    多个参数会被封装成一个map,
        key:使用@Param注解指定的值
        value:参数值
    #{指定的key}取出对应参数值

5、参数处理_POJO&Map&TO

POJO:
如果多个参数正好是我们业务逻辑的数据模型,我们可以直接传入pojo;【传入Javabean对象】
    #{属性名},取出传入的pojo的属性值

Map:
如果多个参数不是业务逻辑的数据模型,没有对应的pojo,我们也可以传入map;【不经常使用】
    #{key},取出map中对应的值

TO
如果多个参数不是业务逻辑的数据模型,但是要经常使用,推荐编写一个TO(Transfer Object)数据传输对象

EmployeeMapper.xml

<!-- public Employee getEmpByMap(Map<String,Object> map); -->
<select id="getEmpByMap" resultType="com.yuanwu.mybatis.bean.Employee">
    select * from tbl_employee where id=#{id} and last_name=#{lastName}
</select>

EmployeeMapper.java

public Employee getEmpByMap(Map<String,Object> map);

MyBatis.java

@Test
public void test04() throws IOException {
    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    //1、获取到的SqlSession不会自动提交数据
    SqlSession openSession = sqlSessionFactory.openSession();

    try {
        EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
        //多个参数查询
        // Employee employee = mapper.getEmpByIdAndLastName(2, "Jerry");
        Map<String,Object> map = new HashMap<>();
        map.put("id", 2);
        map.put("lastName","Jerry");
        Employee employee = mapper.getEmpByMap(map);
        System.out.println(employee);
    } finally {
        openSession.close();
    }
}

6、源码分析_参数封装map的过程

1.MapperProxy > 2.MapperMethod > 3.ParamNameResolver 
===================结合源码,mybatis怎么处理参数===============================

ParamNameResolver解析参数封装map
//1、names:{ 0 -> id,  1 -> lastName}:构造器的时候就确定好了

    确定流程:
        1、获取每个标注了@param注解的参数的@Param的值:id,lastName;赋值给name;【name = ((Param) annotation).value();】
        2、每次解析一个参数给map中保存信息:(key:参数索引,value:name的值)
             name的值:
                    标注了param注解:注解的值
                    没有标注:
                        1、全局配置:useActualParamName(jdk1.8):name=参数名;【在全局配置中配置了useActualParamName属性】
                        2、name = String.valueOf(map.size());相当于当前元素的索引
            { 0 -> id,  1 -> lastName,2=2}【如果没标注注解,索引是2,参数也是2】

args[2, "Jerry"]:
public Object getNamedParams(Object[] args) {
    final int paramCount = names.size();
    //1、如果参数为null,直接返回
    if (args == null || paramCount == 0) {
      return null;

    //2、如果只有一个元素,并且没有标注Param注解,返回第一个key, args[0],单个参数
    } else if (!hasParamAnnotation && paramCount == 1) {
      return args[names.firstKey()];

    //3、多个元素或者标注Param注解
    } else {
      final Map<String, Object> param = new ParamMap<>();
      int i = 0;

      //4、遍历names集合:{ 0 -> id,  1 -> lastName,2=2}
      for (Map.Entry<Integer, String> entry : names.entrySet()) {

        //names集合的value作为key,names集合的key又作为取值的参考args[0]
        //最终 ==>    eg:{ id=args[0]:1, lastName=args[1]:Jerry, 2=args[2] }
        param.put(entry.getValue(), args[entry.getKey()]);

        // add generic param names (param1, param2, ...)
        //额外的将每一个参数也保存到map中,并使用新的key:param1...paramN
        //也就是==> 既可以使用有Param注解的#{指定的key},也可以使用#{param1}
        final String genericParamName = GENERIC_NAME_PREFIX + (i + 1);
        // ensure not to overwrite parameter named with @Param
        if (!names.containsValue(genericParamName)) {
          param.put(genericParamName, args[entry.getKey()]);
        }
        i++;
      }
      return param;
    }
  }
}

7、参数处理_#与$取值区别

==============================参数值的获取==============================
#{}:可以获取map中的值或者pojo对象属性的值
${}:可以获取map中的值或者pojo对象属性的值
    区别:
        #{}:是以预编译的形式,将参数设置到sql语句中,PreparedStatement;防止sql注入
        ${}:取出的值直接拼装在sql语句中,会有安全问题;
        大多数情况下,我们取参数的值都应该使用#{}

        表名字段名都不支持预编译,字段值可以

        但是,原生jdbc不支持占位符的地方我们就可以使用${}进行取值
        比如分表、排序

8、参数处理_#取值时指定参数相关规则

#取值时指定参数相关规则
        jdbcType通常需要在某种特定的条件下被设置:
            在数据库为null时,有些数据库可能不能识别mybatis对null的默认处理。比如Oracle(报错);

            因为mybatis对所有的null都映射的是原生jdbc的OTHER类型,Oracle不认识

            全局配置中,jdbcTypeForNull=OTHER【默认】,Oracle不支持,有两种方式
            1、#{email,jdbcType=OTHER}
            2、jdbcTypeForNull=NULL
                   <setting name="jdbcTypeForNull" value="NULL"/>【全局配置】

9、select_返回List

<!-- public List<Employee> getEmpsByLastNameLike(String lastName);
        resultType:如果返回的是一个集合,要写集合中元素的类型
    -->
<select id="getEmpsByLastNameLike" resultType="com.yuanwu.mybatis.bean.Employee">
    select * from tbl_employee where last_name like #{lastName}
</select>

10、select_记录封装Map

EmployeeMapper.java

//多条记录封装一个map,Map<Integer,Employee>:键是这条记录的主键,值是记录封装后的JavaBean
//告诉mybatis封装这个map的时候使用哪个属性作为map的key
@MapKey("id")
public Map<Integer, Employee> getEmpByLastNameLikeReturnMap(String lastName);

//返回一条记录的map,key=列名,值就是对应的值
public Map<String, Object> getEmpByIdReturnMap(Integer id);

EmployeeMapper.xml

<!-- public Map<String, Object> getEmpByIdReturnMap(Integer id); -->
<select id="getEmpByIdReturnMap" resultType="map">
    select * from tbl_employee where id=#{id}
</select>

<!-- public Map<Integer, Employee> getEmpByLastNameLikeReturnMap(String lastName); -->
<select id="getEmpByLastNameLikeReturnMap" resultType="com.yuanwu.mybatis.bean.Employee">
    select * from tbl_employee where last_name like #{lastName}
</select>

MyBatisTest.java

// Map<String, Object> map = mapper.getEmpByIdReturnMap(3);
// System.out.println(map);

Map<Integer, Employee> map = mapper.getEmpByLastNameLikeReturnMap("%r%");
System.out.println(map);	//{2=Employee{id=2, lastName='Jerry', gender='0', email='Jerry@yuanwu.com'}, 3=Employee{id=3, lastName='Jerry', gender='0', email='Jerry@yuanwu.com'}}

11、select_resultMap【自定义结果映射规则】

EMployeeMapperPlus.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.yuanwu.mybatis.dao.EmployeeMapperPlus">

    <!-- 自定义某个JavaBean的封装规则
            type:自定义规则的Java类型
            id:唯一标识符,方便引用
     -->
    <resultMap id="MyEmp" type="com.yuanwu.mybatis.bean.Employee">
        <!-- 指定主键列的封装规则
            id定义主键会底层有优化;
                column:指定哪一列
                property:指定对应的JavaBean属性
        -->
        <id column="id" property="id"/>
        <!-- result定义普通列封装规则 -->
        <result column="last_name" property="lastName"/>
    <!--    其他不指定的列会自动封装,只要写resultMap就把全部的映射规则都写上-->
        <result column="email" property="email"/>
        <result column="gender" property="gender"/>
    </resultMap>

    <!-- resultMap:自定义结果集映射规则 -->
    <!--    public Employee getEmpById(Integer id);-->
    <select id="getEmpById" resultMap="MyEmp">
        select * from tbl_employee where id=#{id}
    </select>
</mapper>

12、select_resultMap_级联属性

EmployeeMapperPlus.xml


<!--
    模拟场景一:
        查询Employee的同时查询员工对应的部门
        Employee===Department 【一个Employee 对应 一个部门,每一个员工都有部门信息】
        一个员工有与之对应的部门信息;
                id  last_name  gender  email     【d_id     did  dept_name【private Department dept;】
-->

    <!--
        联合查询:级联属性,封装结果集
    -->
    <resultMap id="MyDifEmp" type="com.yuanwu.mybatis.bean.Employee">
        <id column="id" property="id"/>
        <result column="last_name" property="lastName"/>
        <result column="gender" property="gender"/>
        <result column="email" property="email"/>
        <result column="did" property="dept.id"/>
        <result column="dept_name" property="dept.departmentName"/>
    </resultMap>
    <!--  public Employee getEmpAndDept(Integer id);  -->
    <select id="getEmpAndDept" resultMap="MyDifEmp">
        SELECT e.id id,e.last_name last_name,e.gender gender,e.email email,e.d_id d_id,
        d.id did,d.dept_name dept_name
        FROM tbl_employee e,tbl_dept d
        WHERE e.d_id=d.id AND e.id=#{id}
    </select>

测试类

Employee empAndDept = mapper.getEmpAndDept(2);
System.out.println(empAndDept);
System.out.println(empAndDept.getDept());
/*
Employee{id=2, lastName='Jerry', gender='0', email='Jerry@yuanwu.com'}
Department{id=1, departmentName='开发部'}
*/

13、select_resultMap_association属性

    <!--
      使用association标签定义关联单个对象的封装规则;
  -->
    <resultMap id="MyDifEmp2" type="com.yuanwu.mybatis.bean.Employee">
        <id column="id" property="id"/>
        <result column="last_name" property="lastName"/>
        <result column="gender" property="gender"/>
        <result column="email" property="email"/>

        <!-- association标签可以指定联合的JavaBean对象
            property="dept":指定哪个属性是联合对象
            javaType:指定这个属性对象的类型【不能省略】
        -->
        <association property="dept" javaType="com.yuanwu.mybatis.bean.Department">
            <id column="did" property="id"/>
            <result column="dept_name" property="departmentName"/>
        </association>
    </resultMap>
    <!--  public Employee getEmpAndDept(Integer id);  -->
    <select id="getEmpAndDept" resultMap="MyDifEmp2">
        SELECT e.id id,e.last_name last_name,e.gender gender,e.email email,e.d_id d_id,
      d.id did,d.dept_name dept_name
        FROM tbl_employee e,tbl_dept d
        WHERE e.d_id=d.id AND e.id=#{id}
    </select>

14、select_resultMap_association分布查询

DepartmentMapper.xml

<!--  public Department getDeptById(Integer id);  -->
<select id="getDeptById" resultType="com.yuanwu.mybatis.bean.Department">
    select id,dept_name departmentName from tbl_dept where id=#{id}
</select>

EmployeeMapperPlus.xml

<!--  使用association进行分布查询
        1、先按照员工id查询员工信息
        2、根据查询员工信息中的d_id值 拿去部门表查出部门信息
        3、部门设置到员工中;
    -->
    <!--     id  last_name  gender  email     d_id   -->
    <resultMap id="MyEmpByStep" type="com.yuanwu.mybatis.bean.Employee">
        <id column="id" property="id"/>
        <result column="last_name" property="lastName"/>
        <result column="gender" property="gender"/>
        <result column="email" property="email"/>
        <!-- association定义关联对象的封装规则
            select:表明当前属性是调用select指定方法查出的结果
            column:将哪一列的值传给这个方法

            流程:
                使用select指定的方法(传入column指定的这列参数的值)查出对象,并封装给property指定的属性
        -->
        <association property="dept" select="com.yuanwu.mybatis.dao.DepartmentMapper.getDeptById" column="d_id">

        </association>
    </resultMap>
    <!-- public Employee getEmpByIdStep(Integer id); -->
    <select id="getEmpByIdStep" resultMap="MyEmpByStep">
        select * from tbl_employee where id=#{id}
    </select>

测试类

Employee empByIdStep = mapper.getEmpByIdStep(2);
System.out.println(empByIdStep);
System.out.println(empByIdStep.getDept());
/*
Employee{id=2, lastName='Jerry', gender='0', email='Jerry@yuanwu.com'}
Department{id=1, departmentName='开发部'}
*/

15、select_resultMap_懒加载

mybatis-config.xml 【在全局配置文件中,设置两个属性】

    <!--  可以使用延迟加载【懒加载】:
        Employee==>Dept 【Employee对象包含一个Dept属性】
            我们每次查询Employee对象的时候,都将一起查询处理。
            我们希望部门信息在我们需要使用的时候再去查询;
            分布查询的基础之上加上两个配置;
      -->
<!-- 显式的指定需要更改的配置的值,即使它是默认的。-->
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>

测试类

System.out.println(empByIdStep.getLastName());
System.out.println(empByIdStep.getDept());
/*
DEBUG 10-16 21:55:46,405 ==>  Preparing: select * from tbl_employee where id=?   (BaseJdbcLogger.java:143) 
DEBUG 10-16 21:55:46,447 ==> Parameters: 2(Integer)  (BaseJdbcLogger.java:143) 
DEBUG 10-16 21:55:46,551 <==      Total: 1  (BaseJdbcLogger.java:143) 
Jerry

DEBUG 10-16 21:55:46,552 ==>  Preparing: select id,dept_name departmentName from tbl_dept where id=?   (BaseJdbcLogger.java:143) 
DEBUG 10-16 21:55:46,553 ==> Parameters: 1(Integer)  (BaseJdbcLogger.java:143) 
DEBUG 10-16 21:55:46,562 <==      Total: 1  (BaseJdbcLogger.java:143) 
Department{id=1, departmentName='开发部'}
*/
posted @ 2020-10-24 12:58  san只松鼠  阅读(150)  评论(0)    收藏  举报