typora-copy-images-to: imgs

1.1. 8.2、传入的参数

1.1.1. 1.parameterType(了解)

CRUD标签都有一个属性parameterType,底层的statement通过它指定接收的参数类型。入参数据有以下几种类型:HashMap,基本数据类型(包装类),实体类

设置传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler) 推断出具体传入语句的参数类型。

说明:

在mybatis中入参的数据类型分为2种:

  1. 基本数据类型:int,string,long,Date;

  2. 复杂数据类型:类(JavaBean)和Map;

​ 说明:如果传递参数是数组或者集合,底层都会封装到Map集合中。

【示例】

public interface UserMapper {
    //根据id查询
    User findById(Integer id);
}
【基本类型数据】
<!--根据id查询-->
<!--parameterType="int" 表示sql语句参数id的类型,int是Integer的别名.MyBatis 可以通过类型处理器(TypeHandler) 根据接口中的方法User queryById(Integer id)参数类型推断出具体传入语句的参数类型。-->
<select id="findById" resultType="user" parameterType="int">
    select * from user where id = #{id}
</select>

【pojo类型】
<insert id="savetUser" parameterType="User">
  INSERT INTO user(...) values(#{userName},...);
</insert>

说明:对于parameterType属性可以不书写,那么MyBatis 就会通过类型处理器(TypeHandler) 根据接口中的方法User queryById(Integer id)参数类型推断出具体传入语句的参数类型。

1.1.2. 2.自增主键回填(了解)

需求:新增一条数据成功后,将这条数据的主键封装到实体类中,并查看主键的值。

方式1:使用insert标签的子标签selectKey+last_insert_id函数实现实现

属性 说明
keyColumn 主键在表中对应的列名
keyProperty 主键在实体类中对应的属性名
resultType 主键的数据类型
order BEFORE:会首先选择主键,设置 keyProperty 然后执行插入语句 AFTER: 在添加语句后执行查询主键的语句

测试代码:

1)接口

    /**
     * 插入功能
     * @param user
     */
    void saveAndGetkey(User user);

2)映射文件:

    <insert id="saveAndGetkey">
        insert into user values(null,#{username},#{age},#{birthday},#{sex},#{address})
        /*selectkey查询某个关键字段,一般与last_insert_id搭配使用,获取id*/
        /*keyProperty表示对应的pojo中的属性,keyColumn表示对应表中字段的属性 order表示主sql插入之后进行select last_insert_id()查询*/
        <selectKey keyColumn="id" keyProperty="id" order="AFTER" resultType="int">
            select last_insert_id()
        </selectKey>
    </insert>

3)测试

    @Test
    public void testAdd2(){
        User user = new User();
        user.setAddress("上海");
        user.setUsername("laofang");
        user.setBirthday(new Date());
        mapper.saveAndGetkey(user);
        //直接获取返回的主键id值
        System.out.println(user.getId());
    }

方式2:使用insert标签的属性useGeneratedKeys,keyProperty,keyColumn实现

参数说明:

属性 说明
useGeneratedKeys true 获取自动生成的主键,相当于select last_insert_id()
keyColumn 表中主键的列名
keyProperty 实体类中主键的属性名

说明:直接在insert标签中增加属性的方式,只适合于支持自动增长主键类型的数据库,比如MySQL或SQL Server;

测试代码:

接口:

    /**
     * 插入功能
     * @param user
     */
    void saveAndGetkey2(User user);

映射文件:

    <insert id="saveAndGetkey2" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
        insert into user values(null,#{username},#{age},#{birthday},#{sex},#{address})
    </insert>

测试:

    @Test
    public void testAdd3(){
        User user = new User();
        user.setAddress("北京");
        user.setUsername("于彪");
        user.setBirthday(new Date());
        mapper.saveAndGetkey2(user);
        //直接获取返回的主键id值
        System.out.println(user.getId());
    }

1.1.3. 3.单个参数,多个参数(掌握)

3.1.单个参数

单个参数:接口方法传入一个参数
【接口传参】
 User queryById(Integer id);
【接收参数】

1、通过#{参数名}接收

    <!--根据id查询-->
    <select id="queryById" resultType="User" parameterType="int">
        select *,user_name AS  userName from user where id = #{id}
    </select>

2、通过#{任意变量名}接收

    <!--根据id查询-->
    <select id="queryById" resultType="User" parameterType="int">
        select *,user_name AS  userName from user where id = #{abc}
    </select>
【结论】
当接口方法传入一个参数时,mybatis不做特殊处理,只需要#{任意变量名}都可接收;

3.2 多个参数

需求:根据用户名和性别查询用户

【接口传参】
//根据用户名和性别查询
User queryByUserNameAndSex(String userName, String sex);

【UserMapper.xml】

<select id="queryByUserNameAndSex" resultType="User">
        select * from user where username=#{username} and sex=#{sex}
</select>

【测试类】

    @Test
    public void queryByUserNameAndSex() throws Exception {
        User user = userMapper.queryByUserNameAndSex("孙悟空", "男");
        System.out.println("user = " + user);
    }

【结果】

此时会报参数绑定异常

解决方案:

方式1、使用参数索引获取:arg0,arg1(了解,不推荐)

<!--根据用户名和性别查询-->
<select id="queryByUserNameAndSex" resultType="User">
        select * from user where username=#{arg0} and sex=#{arg1}
</select>

说明:

这里的sql语句的参数书写顺序:select * from user where username=#{arg0} and sex=#{arg1}

和User user = userMapper.queryByUserNameAndSex("孙悟空", "男"); 传递实参顺序一致。

也就是说:username=#{arg0} ---》arg0的值是姓名孙悟空。

sex=#{arg1}---》arg1的值是性别男。

方式2:使用参数位置获取:param1,param2(了解,不推荐)

    <!--根据用户名和性别查询-->
   <select id="queryByUserNameAndSex" resultType="User">
        select * from user where username=#{param1} and sex=#{param2}
    </select>

方式3:使用命名参数获取,明确指定传入参数的名称:(掌握)

步骤一:在接口中传入参数时通过@Param指定参数名称

//根据用户名和性别查询
User queryByUserNameAndSex(@Param("username") String userName, @Param("sex") String sex);

步骤二:在接收参数时,通过指定的名称获取参数值;

    <!--根据用户名和性别查询-->
    <select id="queryByUserNameAndSex" resultType="User">
        <!--#{username}这里的username是@Param("username"),也就是说@Param("标识符")标识符是什么,这里就写什么-->
        select * from user where username=#{username} and sex=#{sex}
    </select>

小结:

1)入参是单个参数
   #{任意参数},建议使用@Param注解取参数名称
2)多个参数
   必须使用功能@Param注解取参数名称
   举例:
   User queryByUserNameAndSex(@Param("username") String userName,.....)
   sql: #{username}

1.1.4. 4pojo参数【掌握】

说明:接口方法传入pojo类型的数据时,mybatis底层直接使用pojo封装数据。 sql语句中 #{username}取值==》到pojo中调用 getUsername(){}

测试代码:

接口:

    /**
     * 插入功能
     * @param user
     */
    void saveAndGetkey2(User user);

映射文件:

    <insert id="saveAndGetkey2" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
        insert into user values(null,#{username},#{age},#{birthday},#{sex},#{address})
    </insert>

测试:

    @Test
    public void testAdd3(){
        User user = new User();
        user.setAddress("北京");
        user.setUsername("于彪");
        user.setBirthday(new Date());
        mapper.saveAndGetkey2(user);
        //直接获取返回的主键id值
        System.out.println(user.getId());
    }

小结:

使用pojo的话,xml中参数一定名称要与pojo中的属性名称一致,否则报错(binding exception);

1.1.5. 5.HashMap参数

需求:模拟用户登录,登录方法参数是Map集合,泛型都是String类型分别表示用户名和性别。

​ 注意事项:参数map中的key值是与SQL语句中 #{} 的取值名称一致。

代码实现:

映射文件:

<select id="login" resultType="User">
   select * from user where username=#{name} and sex=#{sex}
</select>

接口:

//login
User login(Map map);

测试:

@Test
public void login(){
    HashMap<String, String> map = new HashMap<>();
    map.put("name","白骨精");
    map.put("sex","女");
    User user = mapper.login(map);
    System.out.println(user.toString());
}

小结:

1)入参类型pojo
  sql映射文件获取pojo中数据:#{pojo中的属性名称}
2)入参是map
  sql映射文件获取pojo中数据:#{map中key}

1.1.6. 【6】入参小结:接口方法传入的参数

mybatis中能够接收2种类型的参数传入:

1.基本类型数据(单参和多参)
  1)单参:接口中使用@param("name")---->#{name}
  2)多参:接口中使用@param("name")----->#{name}

2.复杂数据类型(pojo和map)
   pojo:
      #{变量与pojo的属性名称要一致}
   map:
      #{名称与map中的key要一致}

1.2. 8.3、参数值的获取

参数值的获取指的是,statement获取接口方法中传入的参数。

获取参数,有两种方式:#{}${}

以根据id查询为例测试#{}和${}区别:

使用#{}接收参数:
   select * from user where id=? //预编译处理,防止sql注入
使用${}接收参数:
   select * from user where id=1 //参数值直接拼接到sql中,会有sql注入的风险

1.2.1. 1.#{}取值

使用#{}的sql是进行预编译的,可以防止sql注入;

1.2.2. 2.${} 取值

注意:${id} 获取id值时,必须使用命名参数取值@param:

​ 补充:如果是取单个值,也可使用${value}获取

1)映射文件

    <!--根据id查询  parameterType可以省略-->
    <select id="findById2" resultType="user">
        select * from user where id = ${id}
    </select>

2)接口

    /**
     * 根据id查询
     * @param id
     * @return 用户信息
     */
    User findById2(@Param("id") Integer id);

3)测试

    @Test
    public void findById2(){
        User user = mapper.findById2(3);
        System.out.println(user);
    }

小结:

面试:#{}和${}取值有什么区别?

1)
#{}:作用,预编译,防止sql注入;
${}:传入的参数与sql直接拼接,有sql注入的风险;
2)如果传入的是string类型
  对应#{xxx} ,会自动给sql添加单引号‘’;
  但是使用‘${xxx}’,需要手动自己添加

1.2.3. 3.${}取值的应用场景

​ 在一些特殊的应用场景中,需要对SQL语句部分(不是参数)进行拼接,这个时候就必须使用${}来进行拼接,不能使用#{}.例如:

    1、企业开发中随着数据量的增大,往往会将数据表按照年份进行分表,如:2017_user,2018_user....,对这些表进行查询就需要动态把年份传入进来,而年份是表名的一部分,并不是参数,JDBC无法对其预编译,所以只能使用${}进行拼接:  
    SELECT * FROM ${year}_user;

    2、根据表名查询数据总记录数:
        SELECT COUNT(*) FROM user
        SELECT COUNT(*) FROM order
        SELECT COUNT(*) FROM  ${tableName}
简言之:如果需要设置到SQL中的不是查询的条件,只能使用${}拼接;

示例:

需求:查询user表中的总记录数。

1)映射文件

 <select id="selectCountByTableName" resultType="int">
      select count(*) from ${tableName}
 </select>

2)接口

 //需求:查询user表中的总记录数。
 int selectCountByTableName(@Param("tableName") String table);

3)测试类

   @Test
    public void selectCountByTableName(){
        int count = userMapper.selectCountByTableName("user");
        System.out.println("count = " + count);
    }

小结:

${}使用方式及场景?

使用方式:
  1)接口中参数 @Param("参数名称")
  2)sql映射文件: select * from ${参数名称}
场景:
   一切sql需要拼接的场景;

1.2.4. 4.${}取值注意事项 (了解)

【 ${}获取单个值】

${} 获取单个值时,最好是通过命名参数的形式获取。如果不指定参数的,也可以使用${value}来获取传入的单个值;

传入参数:没有指定参数名称

User selectUserById(Integer id);

获取参数通过${value}获取

<select id="selectUserById" resultType="user">
       select * from user where id = ${value}
 </select>

【${}获取配置文件中的值】

有时候,我们如果非要使用$来接收参数,将login修改如下:

    <!--根据用户名和性别查询-->
    <select id="queryByUserNameAndSex" resultType="User">
        SELECT  * FROM  user WHERE  user_name = '${username}' AND  sex = #{sex}
    </select>

说明:上述sql语句中:SELECT FROM user WHERE user_name = *'${username}' AND sex = #{sex}

对于 '${username}' 加单引号是因为${}获取数据的方式直接将获取的数据拼接到字符串上,并不会加引号,如果获取的值是数值型,没有问题,但是如果是字符类型就会有问题,所以需要加上引号进行拼接。

假设${username}获取的值是锁哥,那么不加单引号效果是:

SELECT  * FROM  user WHERE  user_name = 锁哥 AND  sex = #{sex} 
显然是不可以的,而加上单引号效果就是:
SELECT  * FROM  user WHERE  user_name = '锁哥' AND  sex = #{sex}

测试方法:

    @Test
    public void queryByUserNameAndPassword( ){
        User user = userMapper.queryByUserNameAndSex("孙悟空", "男");
        System.out.println("user = " + user);
    }

执行测试:发现测试方法中传递的参数明明是孙悟空,却获取到了jdbc.properties资源文件中的root.

原因:$可以获取资源文件中的数据,比如mybatis-config.xml中使用$获取连接信息就是。

<property name="driver" value="${driver}"/>

由于映射文件中使用的${username}的参数名称正好和资源文件jdbc.properties中的key相同,因此获取到了资源文件中的数据。

​ 这种错误发生的根本原因是因为资源文件中的key和参数名称重名了,为了解决这个文件,我们只需要将资源文件中的key设置成唯一的不会被重复的key即可。

解决方案:在资源文件中的key中添加前缀

修改资源文件:

jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/heima37
jdbc.username=root
jdbc.password=root

修改mybatis-config.xml:

<dataSource type="POOLED">
    <!-- 配置连接信息 -->
    <property name="driver" value="${jdbc.driverClass}" />
    <property name="url" value="${jdbc.url}" />
    <property name="username" value="${jdbc.username}" />
    <property name="password" value="${jdbc.password}" />
</dataSource>

1.3. 8.4、结果映射

​ 在使用原生的JDBC操作时,对于结果集ResultSet,需要手动处理。

​ mybatis框架提供了resultType和resultMap来对结果集进行封装。

注意:只要一个方法有返回值需要处理,那么 resultType和resultMap必须有一个

1.3.1. 1.resultType

    从sql语句中返回的期望类型的类的完全限定名或别名。 注意如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身。可以使用 resultType 或 resultMap,但不能同时使用。

1.1 返回值是基本类型

例如 int ,string ===>resultType="书写对应的基本类型别名或者全名即可"

​ 测试:findNameById:

1)基本类型 int short double ... 别名: _基本类型名称
2)包装类 类 String ArrayList .... 别名:类名首字母小写
3)自定义类 扫包取别名 类首字母小写(大写也可)

1.2 返回值为一个pojo(User)对象时

​ 测试:findById:

1.3 返回值为一个List\

​ 当返回值为List集合时,resultType需要设置成集合中存储的具体的pojo数据类型:

​ 测试:findAllUsers:

1.4 返回值为map

【1】返回一条数据,封装到map中

需求:查询id是1的数据,将查询的结果封装到Map

1)映射文件配置

   <!--根据id查询  parameterType可以省略-->
    <select id="findById3" resultType="map">
        select * from user where id = #{id}
    </select>

2)接口:

    /**
     * 根据id查询
     * @param id
     * @return 用户信息
     */
    Map findById3(Integer id);

3)测试:

    @Test
    public void findById3(){
        Map<String,Object> map=mapper.findById3(3);
        System.out.println(map);
    }

1604717188997

【2】返回多条数据,封装到map中

​ 需求:查询数据表所有的数据封装到Map集合中

​ 要求: Key值为一条记录的主键,Value值为pojo的对象.

   //需求:查询数据表所有的数据封装到Map<String,User>集合中
    @MapKey("id")
    Map<Integer, User> selectReturnMap();

说明:需要在接口的方法上使用注解@MapKey指定数据表中哪一列作为Map集合的key,否则mybatis不知道具体哪个列作为Map集合的key.

1)映射文件:

    <select id="getMapUser" resultType="map">
        select * from user
    </select>

2)接口:


    @MapKey("id") //指定返回的数据中那个属性作为key值
    Map<Integer,User> getMapUser();

3)测试:

    @Test
    public void testManyMap(){
        Map<Integer, User> mapUser = mapper.getMapUser();
        System.out.println(mapUser);
    }

1604717509503

小结:

1)查询结果单条记录,封装到map下,那么key是表的字段名称,value对应字段值
2)查询结果多条记录,封装到map下,接口需要@MapKey("字段名称")需要注解指定字段名称,value是对应map中value泛型

1.3.2. 2.resultMap(掌握)

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

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

mybatis针对结果映射有如下解决方式: 解决方案1:在sql语句中使用别名

<select id="queryById" resultType="user" parameterType="int">
   select *,name as username from user where id = #{id}
</select>

解决方案2:参考驼峰匹配 --- mybatis-config.xml 的时候

<settings>
   <setting name="mapUnderscoreToCamelCase" value="true" />
</settings>

注意:这种解决方案只能解决列名是下划线命名.

解决方案3:resultMap自定义映射

【需求】

使用resultMap完成结果集的封装(resultSet===》JavaBean)

【实现步骤】

手动配置实体类属性和表字段映射关系的步骤如下:
1、    配置自定义结果集<resultMap>
2、    配置id映射
3、    配置其他普通属性的映射

步骤一:将驼峰匹配注释掉 ​ 一旦注释掉驼峰匹配,那么再通过queryUserById查询的结果中,用户名就无法封装了,此时我们可以尝试使用ResultMap来解决这个问题。

步骤二:配置resultMap

resultMap标签的作用:自定义结果集,自行设置结果集的封装方式

id属性:resultMap标签的唯一标识,不能重复,一般是用来被引用的
type属性:结果集的封装类型
autoMapping属性:操作单表时,不配置默认为true,如果pojo对象中的属性名称和表中字段名称相同,则自动映射。

在映射文件中自定义结果集类型:

<!--type="user" 表示结果集的封装类型是user-->
<resultMap id="userResultMap" type="User" autoMapping="true">
   <!--配置主键映射关系-->
   <id column="id" property="id"></id>
   <!--配置用户名的映射关系  column 表示数据表列  property表示pojo的属性-->
   <result column="user_name" property="userName"></result>  
</resultMap>

步骤三:修改查询语句的statement 在查询语句的select标签中通过resultMap属性可以引用自定义结果集作为数据的封装方式。

<!-- resultMap属性:引用自定义结果集作为数据的封装方式.属性值是自定义resultMap标签的id属性值,这里表示通过id引入自定义的resultMap标签
-->
<select id="queryById" resultMap="userResultMap">
    select * from user where id = #{id}
</select>

测试代码:

    @Test
    public void queryById() throws Exception {
        //获取mapper接口的动态代理实现
        User user = userMapper.queryById(1);
        System.out.println("user = " + user);
    }

注意:测试完记得将驼峰命名的配置重新开启,因为其他的测试方法还要用。

1604719674020

【小结】

1.resultMap标签的作用?

将查询的结果与pojo进行映射关联;

2.resultMap有哪些属性和子标签?

<resultMap id="唯一标识" type="映射的pojo类" autoMapping="true">
   <!-- 主键映射-->
   <id column="表中主键名称" property="主键对应pojo中属性名称"/>
    <!-- 非主键字段映射-->
   <result  column="表中普通字段名称" property="表普通字段对应pojo中属性名称"/>
</resultMap>

<select id="xxx" resultMap="标签id">
   select .....
</select>

1.4. 9.1、引用当前文件中的SQL片段

​ sql标签可以定义一个sql片段,在需要使用该sql片段的地方,通过\标签来使用。

1.4.1. 【1】定义SQL片段

<!--定义SQL片段-->
<sql id="commonSql">
         id,
         user_name AS userName,
          age,
          sex,
          birthday,
            address
</sql>

注意:SQL片段必须设置id属性;

1.4.2. 【2】使用SQL片段

​ 在SQL语句中通过<include>标签引入SQL片段;

<select id="queryById"  resultType="User">
        select <include refid="commonSql"/> from user where id = #{id}
</select>

1.5. 9.2、引入独立文件中的SQL片段

​ 很多时候同一个sql片段,可能在多个映射文件中都有使用,如果每一个映射文件都编写一个相同的sql就比较麻烦,因此可以将通用的sql片段都定义在一个专门存放sql片段的映射文件中,然后由其他映射文件引用它即可。 如下,在src目录下新增CommonSQL.xml文件:

1.5.1. 【1】新建SQL片段文件

​ 复制一份映射文件,将SQL片段写入即可

【CommonSQL.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="CommonSQL">
    <sql id="commonSql">
         id,
         user_name AS userName,
          age,
          sex,
          birthday,
            address
    </sql>
    <sql id="xxx">
      xxxx
    </sql>
</mapper>

1.5.2. 【2】在mybatis核心配置文件mybatis-config.xml引入SQL片段文件

​ 定义好sql片段的映射文件之后,接下来就该使用它了,首先应该把该映射文件引入到mybatis的全局配置文件中(mybatis-config.xml):

<mappers>
    <mapper resource="CommonSQL.xml"/>
</mappers>

1.5.3. 【3】引用SQL片段

​ 最后在需要使用该sql片段的地方通过include标签的refId属性引用该sql片段:<include refId=”名称空间.sql片段的id” /> 在UserMapper.xml的映射文件中,进一步改造根据用户名查询用户信息

<select id="findUserById" resultType="user">
     select <includ refid="CommonSQL.CommonSQL"/> from user where id=#{id}
</select>

1.6. 【小结】

小结:sql片段的作用及使用有哪些?

1)映射文件内部定义且引用的语法格式:
  <sql id="片段唯一标识">
    公共的sql片段
  </sql>
  <select id="xxx" ...>
    select <include refid="片段唯一标识"/> from xxx
  </select>
2)外部引用
 1.创建一个与映射文件格式一致的xml
 2.定义一个命名空间,要唯一
 3. 语法定义:
 <sql id="片段唯一标识">
    公共的sql片段
  </sql>

  映射文件使用:
  1.核心配置文件加载公共sql片段xml文件
  2.映射文件引用:
  <select id="xxx" ...>
    select <include refid="命名空间.片段唯一标识"/> from 表名
  </select>

results matching ""

    No results matching ""