Mybatis 配置文件标签学习

  1. <?xml version="1.0" encoding="UTF-8" ?>
  2. <!DOCTYPE configuration
  3. PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  4. "http://mybatis.org/dtd/mybatis-3-config.dtd">
  5. <configuration>
  6. <!--
  7. 1mybatis可以使用properties来引入外部properties配置文件的内容;
  8. resource:引入类路径下的资源
  9. url:引入网络路径或者磁盘路径下的资源
  10. -->
  11. <properties resource="dbconfig.properties"></properties>
  12. <!--
  13. 2settings包含很多重要的设置项
  14. setting:用来设置每一个设置项
  15. name:设置项名
  16. value:设置项取值
  17. -->
  18. <settings>
  19. <setting name="mapUnderscoreToCamelCase" value="true"/>
  20. </settings>
  21. <!-- 3typeAliases:别名处理器:可以为我们的java类型起别名
  22. 别名不区分大小写
  23. -->
  24. <typeAliases>
  25. <!-- 1typeAlias:为某个java类型起别名
  26. type:指定要起别名的类型全类名;默认别名就是类名小写;employee
  27. alias:指定新的别名
  28. -->
  29. <!-- <typeAlias type="com.atguigu.mybatis.bean.Employee" alias="emp"/> -->
  30. <!-- 2package:为某个包下的所有类批量起别名
  31. name:指定包名(为当前包以及下面所有的后代包的每一个类都起一个默认别名(类名小写),)
  32. -->
  33. <package name="com.atguigu.mybatis.bean"/>
  34. <!-- 3、批量起别名的情况下,使用@Alias注解为某个类型指定新的别名 -->
  35. </typeAliases>
  36. <!--
  37. 4environments:环境们,mybatis可以配置多种环境 ,default指定使用某种环境。可以达到快速切换环境。
  38. environment:配置一个具体的环境信息;必须有两个标签;id代表当前环境的唯一标识
  39. transactionManager:事务管理器;
  40. type:事务管理器的类型;JDBC(JdbcTransactionFactory)|MANAGED(ManagedTransactionFactory)
  41. 自定义事务管理器:实现TransactionFactory接口.type指定为全类名
  42. dataSource:数据源;
  43. type:数据源类型;UNPOOLED(UnpooledDataSourceFactory)
  44. |POOLED(PooledDataSourceFactory)
  45. |JNDI(JndiDataSourceFactory)
  46. 自定义数据源:实现DataSourceFactory接口,type是全类名
  47. -->
  48. <environments default="dev_mysql">
  49. <environment id="dev_mysql">
  50. <transactionManager type="JDBC"></transactionManager>
  51. <dataSource type="POOLED">
  52. <property name="driver" value="${jdbc.driver}" />
  53. <property name="url" value="${jdbc.url}" />
  54. <property name="username" value="${jdbc.username}" />
  55. <property name="password" value="${jdbc.password}" />
  56. </dataSource>
  57. </environment>
  58. <environment id="dev_oracle">
  59. <transactionManager type="JDBC" />
  60. <dataSource type="POOLED">
  61. <property name="driver" value="${orcl.driver}" />
  62. <property name="url" value="${orcl.url}" />
  63. <property name="username" value="${orcl.username}" />
  64. <property name="password" value="${orcl.password}" />
  65. </dataSource>
  66. </environment>
  67. </environments>
  68. <!-- 5databaseIdProvider:支持多数据库厂商的;
  69. type="DB_VENDOR"VendorDatabaseIdProvider
  70. 作用就是得到数据库厂商的标识(驱动getDatabaseProductName()),mybatis就能根据数据库厂商标识来执行不同的sql;
  71. MySQLOracleSQL Server,xxxx
  72. -->
  73. <databaseIdProvider type="DB_VENDOR">
  74. <!-- 为不同的数据库厂商起别名 -->
  75. <property name="MySQL" value="mysql"/>
  76. <property name="Oracle" value="oracle"/>
  77. <property name="SQL Server" value="sqlserver"/>
  78. </databaseIdProvider>
  79. <!-- 将我们写好的sql映射文件(EmployeeMapper.xml)一定要注册到全局配置文件(mybatis-config.xml)中 -->
  80. <!-- 6mappers:将sql映射注册到全局配置中 -->
  81. <mappers>
  82. <!--
  83. mapper:注册一个sql映射
  84. 注册配置文件
  85. resource:引用类路径下的sql映射文件
  86. mybatis/mapper/EmployeeMapper.xml
  87. url:引用网路路径或者磁盘路径下的sql映射文件
  88. file:///var/mappers/AuthorMapper.xml
  89. 注册接口
  90. class:引用(注册)接口,
  91. 1、有sql映射文件,映射文件名必须和接口同名,并且放在与接口同一目录下;
  92. 2、没有sql映射文件,所有的sql都是利用注解写在接口上;
  93. 推荐:
  94. 比较重要的,复杂的Dao接口我们来写sql映射文件
  95. 不重要,简单的Dao接口为了开发快速可以使用注解;
  96. -->
  97. <!-- <mapper resource="mybatis/mapper/EmployeeMapper.xml"/> -->
  98. <!-- <mapper class="com.atguigu.mybatis.dao.EmployeeMapperAnnotation"/> -->
  99. <!-- 批量注册: -->
  100. <package name="com.atguigu.mybatis.dao"/>
  101. </mappers>
  102. </configuration>

properties

  1. <!--
  2. 1mybatis可以使用properties来引入外部properties配置文件的内容;
  3. resource:引入类路径下的资源
  4. url:引入网络路径或者磁盘路径下的资源
  5. -->
  6. <properties resource="dbconfig.properties"></properties>

settings

  1. <!--
  2. 2settings包含很多重要的设置项
  3. setting:用来设置每一个设置项
  4. name:设置项名
  5. value:设置项取值
  6. -->
  7. <settings>
  8. <setting name="mapUnderscoreToCamelCase" value="true"/>
  9. </settings>

typeAliases

    <typeAliases>
        <!-- 1、typeAlias:为某个java类型起别名
                type:指定要起别名的类型全类名;默认别名就是类名小写;employee
                alias:指定新的别名
         -->
        <!-- <typeAlias type="com.atguigu.mybatis.bean.Employee" alias="emp"/> -->

        <!-- 2、package:为某个包下的所有类批量起别名 
                name:指定包名(为当前包以及下面所有的后代包的每一个类都起一个默认别名(类名小写),)
        -->
        <package name="com.atguigu.mybatis.bean"/>

        <!-- 3、批量起别名的情况下,使用@Alias注解为某个类型指定新的别名 -->
    </typeAliases>

environments

    <!-- 
        4、environments:环境们,mybatis可以配置多种环境 ,default指定使用某种环境。可以达到快速切换环境。
            environment:配置一个具体的环境信息;必须有两个标签;id代表当前环境的唯一标识
                transactionManager:事务管理器;
                    type:事务管理器的类型;JDBC(JdbcTransactionFactory)|MANAGED(ManagedTransactionFactory)
                        自定义事务管理器:实现TransactionFactory接口.type指定为全类名

                dataSource:数据源;
                    type:数据源类型;UNPOOLED(UnpooledDataSourceFactory)
                                |POOLED(PooledDataSourceFactory)
                                |JNDI(JndiDataSourceFactory)
                    自定义数据源:实现DataSourceFactory接口,type是全类名
         -->

      <environments default="dev_mysql">
        <environment id="dev_mysql">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}" />
                <property name="url" value="${jdbc.url}" />
                <property name="username" value="${jdbc.username}" />
                <property name="password" value="${jdbc.password}" />
            </dataSource>
        </environment>

        <environment id="dev_oracle">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="${orcl.driver}" />
                <property name="url" value="${orcl.url}" />
                <property name="username" value="${orcl.username}" />
                <property name="password" value="${orcl.password}" />
            </dataSource>
        </environment>
    </environments>

databaseIdProvider

    <!-- 5、databaseIdProvider:支持多数据库厂商的;
         type="DB_VENDOR":VendorDatabaseIdProvider
             作用就是得到数据库厂商的标识(驱动getDatabaseProductName()),mybatis就能根据数据库厂商标识来执行不同的sql;
             MySQL,Oracle,SQL Server,xxxx
      -->
    <databaseIdProvider type="DB_VENDOR">
        <!-- 为不同的数据库厂商起别名 -->
        <property name="MySQL" value="mysql"/>
        <property name="Oracle" value="oracle"/>
        <property name="SQL Server" value="sqlserver"/>
    </databaseIdProvider>

mappers

<!-- 将我们写好的sql映射文件(EmployeeMapper.xml)一定要注册到全局配置文件(mybatis-config.xml)中 -->
    <!-- 6、mappers:将sql映射注册到全局配置中 -->
    <mappers>
        <!-- 
            mapper:注册一个sql映射 
                注册配置文件
                resource:引用类路径下的sql映射文件
                    mybatis/mapper/EmployeeMapper.xml
                url:引用网路路径或者磁盘路径下的sql映射文件
                    file:///var/mappers/AuthorMapper.xml

                注册接口
                class:引用(注册)接口,
                    1、有sql映射文件,映射文件名必须和接口同名,并且放在与接口同一目录下;
                    2、没有sql映射文件,所有的sql都是利用注解写在接口上;
                    推荐:
                        比较重要的,复杂的Dao接口我们来写sql映射文件
                        不重要,简单的Dao接口为了开发快速可以使用注解;
        -->
        <!-- <mapper resource="mybatis/mapper/EmployeeMapper.xml"/> -->
        <!-- <mapper class="com.atguigu.mybatis.dao.EmployeeMapperAnnotation"/> -->

        <!-- 批量注册: -->
        <package name="com.atguigu.mybatis.dao"/>
    </mappers>

Mapper.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.atguigu.mybatis.dao.EmployeeMapper">
<!-- 
namespace:名称空间;指定为接口的全类名
id:唯一标识
resultType:返回值类型
#{id}:从传递过来的参数中取出id值
public Employee getEmpById(Integer id);
 -->

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

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

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

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

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

     <select id="getEmpById" resultType="com.atguigu.mybatis.bean.Employee">
        select * from tbl_employee where id = #{id}
    </select>
    <select id="getEmpById" resultType="com.atguigu.mybatis.bean.Employee"
        databaseId="mysql">
        select * from tbl_employee where id = #{id}
    </select>
    <select id="getEmpById" resultType="com.atguigu.mybatis.bean.Employee"
        databaseId="oracle">
        select EMPLOYEE_ID id,LAST_NAME    lastName,EMAIL email 
        from employees where EMPLOYEE_ID=#{id}
    </select>

    <!-- public void addEmp(Employee employee); -->
    <!-- parameterType:参数类型,可以省略, 
    获取自增主键的值:
        mysql支持自增主键,自增主键值的获取,mybatis也是利用statement.getGenreatedKeys();
        useGeneratedKeys="true";使用自增主键获取主键值策略
        keyProperty;指定对应的主键属性,也就是mybatis获取到主键值以后,将这个值封装给javaBean的哪个属性
    -->
    <insert id="addEmp" parameterType="com.atguigu.mybatis.bean.Employee"
        useGeneratedKeys="true" keyProperty="id" databaseId="mysql">
        insert into tbl_employee(last_name,email,gender) 
        values(#{lastName},#{email},#{gender})
    </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属性对应的值
        AFTER运行顺序:
            先运行插入的sql(从序列中取出新值作为id);
            再运行selectKey查询id的sql;
         -->
        <selectKey keyProperty="id" order="BEFORE" resultType="Integer">
            <!-- 编写查询主键的sql语句 -->
            <!-- BEFORE-->
            select EMPLOYEES_SEQ.nextval from dual 
            <!-- AFTER:
             select EMPLOYEES_SEQ.currval from dual -->
        </selectKey>

        <!-- 插入时的主键是从序列中拿到的 -->
        <!-- BEFORE:-->
        insert into employees(EMPLOYEE_ID,LAST_NAME,EMAIL) 
        values(#{id},#{lastName},#{email<!-- ,jdbcType=NULL -->}) 
        <!-- AFTER:
        insert into employees(EMPLOYEE_ID,LAST_NAME,EMAIL) 
        values(employees_seq.nextval,#{lastName},#{email}) -->
    </insert>

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

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


</mapper>

namespace | id | resultType

<!-- 
namespace:名称空间;指定为接口的全类名
id:唯一标识
resultType:返回值类型
#{id}:从传递过来的参数中取出id值
public Employee getEmpById(Integer id);
 -->

mybatis的参数处理

单参数

  • 单个参数的情况下 mybatis不会做特殊处理
  • {参数名/任意名取出参数值}

    多参数

  • 首先要要建立一个概念,多个参数在mybatis中会封装成为一个map

    • key : param1….paramN 或者参数的索引也可以
    • value : 传入的参数值
  • {} 就是从map中获取指定的key的值

    ```java 异常: org.apache.ibatis.binding.BindingException: Parameter ‘id’ not found. Available parameters are [1, 0, param1, param2] 操作:
      方法:public Employee getEmpByIdAndLastName(Integer id,String lastName);
      取值:#{id},#{lastName}
    
    上面这个异常在没有进行参数命名的时候,会发生

- 错误展示
- ![image.png](https://cdn.nlark.com/yuque/0/2021/png/1608527/1632321243409-02ac0ecd-7ae7-487d-94ba-2d1e99fad316.png#clientId=u3963eff7-483f-4&from=paste&id=u7332f871&margin=%5Bobject%20Object%5D&name=image.png&originHeight=172&originWidth=891&originalType=binary&ratio=1&size=144335&status=done&style=none&taskId=u12d43686-3a02-4159-9cd4-1f986c52c41)

![image.png](https://cdn.nlark.com/yuque/0/2021/png/1608527/1632321264549-5fd2f652-6afb-4fe2-8916-f6e21d53dbd3.png#clientId=u3963eff7-483f-4&from=paste&height=186&id=u0abb2743&margin=%5Bobject%20Object%5D&name=image.png&originHeight=371&originWidth=1330&originalType=binary&ratio=1&size=404712&status=done&style=none&taskId=u8215ecb9-f0fb-4d63-9f75-2f68490680a&width=665)
```java

    // 多个参数查询emp
    // @param 注解 明确指定封装查询参数map的key value  对应的值
//    public Employee getEmpByIdAndLastName(@Param("id") Integer id, @Param("lastName") String lastName);
    public Employee getEmpByIdAndLastName(@Param("id") Integer id,String lastName);

当前代码中只有一个 命名参 即使用了@param 注解标注的参数,这样在封装的时候 可以看到后续的异常提示中
有id参数,但是lastName 则是notfound 。

命名参数操作

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

pojo

  • 如果多个参数正好是业务逻辑的数据模型,则可以直接传入pojo
  • {属性名} 取出传入的pojo属性值

    ```java 接口声明方法 public boolean updateEmp(Employee employee);

xml

{属性名} 取出传入的pojo的属性值进行sql拼接注册

<update id="updateEmp">
    update tbl_employee
    set last_name = #{lastName},email=#{email},gender=#{gender}
    where id =#{id}
</update>
<a name="EiWaA"></a>
## Map 

- 如果多个参数不是业务模型的数据,没有对应的pojo 且不经常使用,为了方便, 也可以将map类型数据作为参数传入
- #{key} 取出map中对应的值
```java
接口声明方法
    // 传入一个map来进行查询
    public Employee getEmpByMap(Map<String, Object> map);

xml 入参数方法实际同pojo一致
    <select id="getEmpByMap" resultType="com.addicated.bean.Employee">
        select id,last_name lastName,email,gender from tbl_employee where id = #{id}  and last_name = #{lastName}

    </select>
测试方法
    @Test
    public void testMybatis02() throws IOException {
        SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
        // 获取到的sqlsession不会自动提交数据
        SqlSession openSession = sqlSessionFactory.openSession();
        try {
            EmployeeMapper employeeMapper = openSession.getMapper(EmployeeMapper.class);
            Map<String, Object> map = new HashMap<>();
            map.put("id",3);
            map.put("lastName","shihu");
            Employee empByMap = employeeMapper.getEmpByMap(map);

            System.out.println(empByMap);
            openSession.commit();
        }finally {
            openSession.close();
        }

    }

DTO (重点

  • 如果多个参数不是业务模型中的数据,但是需要经常使用,推荐编写一个TO (Transfer Object)数据传输对象
  • 例如 分页类, 并不是业务模型中的数据,但是有需要经常使用。 ```java Page{ int index; int size; }
<a name="fHPn9"></a>
## 集合取值&思考
```java
public Employee getEmp(@Param("id")Integer id,String lastName);
    取值:id==>#{id/param1}   lastName==>#{param2}

public Employee getEmp(Integer id,@Param("e")Employee emp);
    取值:id==>#{param1}    lastName===>#{param2.lastName/e.lastName}

##特别注意:如果是Collection(List、Set)类型或者是数组,
         也会特殊处理。也是把传入的list或者数组封装在map中。
            key:Collection(collection),如果是List还可以使用这个key(list)
                数组(array)
public Employee getEmpById(List<Integer> ids);
    取值:取出第一个id的值:   #{list[0]}

源码分析

  • 上面学习了很多中情况mybatis是如何取到查询参数的,现在结合源码来看看 mybatis是如何做到参数封装取值的。

image.png

(@Param("id")Integer id,@Param("lastName")String lastName);
ParamNameResolver解析参数封装map的;
//1、names:{0=id, 1=lastName};构造器的时候就确定好了  直接初始化

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


args【1,"Tom",'hello'】:

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

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

    //3、多个元素或者有Param标注
    } else {
      final Map<String, Object> param = new ParamMap<Object>();
      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]:args【1,"Tom"】:
          //eg:{id=args[0]:1,lastName=args[1]:Tom,2=args[2]}
        param.put(entry.getValue(), args[entry.getKey()]);


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