MyBatis-参数处理与查询结果映射

实际开发中应用细节

#{}和${}的区别?

'#{}':先编译sql语句,再给占位符传值,底层是PreparedStatement实现,可以防止sql注入,比较常用。

'#{}'传来的字符串类型的数据会自带单引号,比如:user_name=#{name} 会编译为 user_name='张三'

${}:先进行sql语句拼接,再编译sql语句,底层是Statement实现。存在sql注入现象。只有在需要进行sql关键字拼接的情况下才会用到。

${}传来的字符串类型的数据不带单引号,比如:user_name=#{name} 会编译为 user_name=张三 。这样会报语法错误。

优先使用#{}

那么什么时候使用${}呢?

在使用sql拼接的时候会用到,比如一个数据的查询结果是升序还是降序有前端传来的参数决定。因此使用${}更合适:

order by age=${orderType} 会编译为 order by age=asc。

如果使用#{}处理的话会编译为order by age='asc' 会报语法错误!

当拼接表名时也会用到。

一些数据会分表存储。比如日志表,因为日志数据会非常大,可以每天生成一个日志表,每张表以当天的日期作为名称,例如:

log_2022-02-11、log_2022-02-12、log_2022-02-13.

这样查某一天的日志时,可以将log_和日期进行拼接 然后查询,此时就要用到${}

批量删除

批量删除时,参数一般为字符串,比如传来的参数id值为:'1,2,3,4'。直接用where id in (#{id})会失效。

这里应该使用$() 。sql会编译为where id in (1,2,3,4) 结果执行成功。

模糊查询

方案一:使用sql语句拼接 where name like '%${name}%'

方案二:使用concat函数 where name like concat('%',#{name},'%') 此时会编译为 where name like concat('%','涨三','%') 执行成功。

方案三:where name like "%"#{name}"%"

别名机制

当XxxMapper.xml中resultType的值太长,也就是返回值的全限定类名太长,可以使用别名机制。

  1. 先在mybatis-config.xml中配置如下:

    xml 复制代码
    <!--    起别名,typeAliases标签要放在properties、settings 标签后面-->
        <typeAliases>
          <!--  type:指定给哪个类型起别名
            alias:指定别名。别名不区分大小写
            然后在CarMapper.xml文件中就可以直接使用别名car来代替全类名com.ali.pojo.Car了
            -->
            <typeAlias type="com.ali.pojo.Car" alias="car"/>
        </typeAliases>

    这里的alias属性其实可以省略。当alias省略时,别名就是类的简名,比如:

    复制代码
     <typeAlias type="com.ali.pojo.Car"/> 的别名就是car、CAR等,不区分大小写。

​ 还有更加方便的一种方式,可以指定包名,mybatis会自动给这个包下的所i有类起别名,别名就是类名:

xml 复制代码
    <typeAliases>
        <package name="com.ali.pojo"/>
    </typeAliases>
  1. 在CarMapper.xml中就可以使用别名了

    xml 复制代码
    <select id="selectCarById" resultType="car">
        SELECT id, car_num AS carNum, brand, guide_price AS guidePrice, produce_time AS produceTime, car_type AS carType
        FROM t_car
        WHERE id = #{id}
    </select>

注意:这里的别名不区分大小写。即使写成resultType="Car" 也会执行成功!

mapper的配置

xml 复制代码
    <mappers>
<!--        执行xxxMapper.xml文件中的SQL语句-->
<!--        resource属性自动从类的路径下加载xxxMapper.xml文件
            url属性直接从文件系统绝对路径加载xxxMapper.xml文件,不推荐,移植性太差。
            class属性指定一个全限定接口名,mybaits会去这个接口的同级目录下查找对应Mapepr.xml文件
            比如以下配置,Mybatis会去com.ali.mapper包下查找CarMapper.xml文件。
            -->
        <mapper class="com.ali.mapper.CarMapper"/>
        <mapper resource="CarMapper.xml"/>
        <!--package:表示从指定的包下查找所有的xxxMapper.xml文件,并且自动加载它们。
			前提必须把xml文件和接口放在统计目录下。并且名字一致。
			实际开发中 使用这种方式-->
        <package name="com.ali.mapper"/>
    </mappers>

插入数据时获取自动生成的主键

xml 复制代码
<!-- useGeneratedKeys="true" 表示使用JDBC的getGeneratedKeys方法获取数据库自动生成的主键值,
 keyProperty="id"表示将获取到的主键值封装到Car对象的id属性中。-->
 <insert id="insertCarGetId" useGeneratedKeys="true" keyProperty="id">
     INSERT INTO t_car (id, car_num, brand,guide_price,produce_time,car_type)
     VALUES (null, #{carNum}, #{brand},#{guidePrice},#{produceTime},#{carType})
 </insert>

这样设置后,在新增时传入的car对象在sql执行完后,主键的值可以从car.getId()方法中获得。

MyBatis参数处理

简单类型参数

byte、int、short、long、char、float、double、String、Date,及其对应的包装类都适用简单类型参数。

下面以Long类型为例:

xml 复制代码
<!--    对应的接口方法: Car selectCarById(Long id);
        parameterType属性:指定传入参数的类型。在这个例子中,传入参数是一个Long类型的id。
        mybatis框架自带类型推断机制,所以大部分情况下parameterType属性可以省略不写,
       -->
    <select id="selectCarById" resultType="car" parameterType="java.lang.Long">
        SELECT id, car_num AS carNum, brand, guide_price AS guidePrice, produce_time AS produceTime, car_type AS carType
        FROM t_car
        WHERE id = #{id}
    </select>

Map参数

xml 复制代码
<!--    对应的接口方法: int insertCar(Map<String,Object> map);
        参数类型时map集合。
-->
        <insert id="insertCar" parameterType="map">
            INSERT INTO t_car (id, car_num, brand,guide_price,produce_time,car_type)
            VALUES (null, #{carNum}, #{brand},#{guidePrice},#{produceTime},#{carType})
        </insert>

实体类参数

xml 复制代码
<!--对应的接口方法: int insertCarByPojo(Car car);
参数类型是Pojo类。-->
<insert id="insertCarByPojo" >
    INSERT INTO t_car (id, car_num, brand,guide_price,produce_time,car_type)
    VALUES (null, #{carNum}, #{brand},#{guidePrice},#{produceTime},#{carType})
</insert>

多参数

xml 复制代码
<!--对应的接口方法: List<Car> selectCarByNameAndCarType(String name,String carType);
    这种多参数的情况,mybatis框架会将多个参数封装成一个map集合,并且map会这样设置:
    map.put("arg0",第一个参数值);  map.put("arg1",第二个参数值);
    map.put("param1",第一个参数值);  map.put("param2",第二个参数值);
   所以#{arg0} 和#{param1}都可以获取到第一个参数值,#{arg1}和#{param2}都可以获取到第二个参数值。
-->
<select id="selectCarByNameAndCarType" resultType="car" parameterType="java.lang.Long">
    SELECT id, car_num AS carNum, brand, guide_price AS guidePrice, produce_time AS produceTime, car_type AS carType
    FROM t_car
    WHERE brand = #{arg0} and car_type = #{arg1}
</select>

@Param注解

xml 复制代码
  <!--对应的接口方法: List<Car> selectCarByNameAndCarType2(@Param("name") String name,@Param("carType") String carType);
        @Param注解会将底层封装的map中arg0、arg1替换成我们指定的key值name和carType。
         param1、param2不会被替换,可以继续使用
-->
    <select id="selectCarByNameAndCarType2" resultType="car" parameterType="java.lang.Long">
        SELECT id, car_num AS carNum, brand, guide_price AS guidePrice, produce_time AS produceTime, car_type AS carType
        FROM t_car
        WHERE brand = #{name} and car_type = #{carType}
    </select>

MyBatis查询语句

当sql返回多条记录时,不能使用单个pojo对象接收,不然会报错。

单条查询时,用List 接收是没有任何问题的。

返回Map

当返回的数据,没有合适的实体类对应的话,可以采用Map接收。key是字段名,value是字段值。

当返回多条数据时,使用List 接收。

xml 复制代码
<!--   对应的接口方法: Map<String,Object> selectCarByIdRetMap(Long id);
     resultType="map"表示将查询结果封装成一个map集合,map的key是列名,value是列值。
     如果需要查询多条记录并封装成map集合,可以使用resultType="list"来封装成一个list集合,list集合中的每个元素都是一个map集合。
-->
    <select id="selectCarByIdRetMap" resultType="map" >
        SELECT id, car_num AS carNum, brand, guide_price AS guidePrice, produce_time AS produceTime, car_type AS carType
        FROM t_car
        WHERE id = #{id}
    </select>

返回Map<String,Map>

以每条记录的主键作为key,一条记录作为value

xml 复制代码
<!--   接口对应的方法:
   @MapKey("id") // 这个注解表示map的key是id列的值,value是整个记录封装成的map集合。
   List<Map<Long,Map<String,Object>> selectAllCarRetMap();
   -->
   <select id="selectAllCarRetMap" resultType="map">
       SELECT id, car_num AS carNum, brand, guide_price AS guidePrice, produce_time AS produceTime, car_type AS carType
       FROM t_car
   </select>

resultMap结果映射

查询结果的列名和Java对象的属性名对应不上怎么办?

方案一:as 起别名

方案二:使用resultMap进行结果映射

方案三:开启驼峰命名自动映射

xml 复制代码
<!--定义一个结果映射,在结果映射中,指定数据库表的字段和Java对象的属性之间的映射关系。
id属性是结果映射的唯一标识,将来要用在select标签中;type属性是结果映射对应的Java类型。-->
<resultMap id="carResultMap" type="Car">
    <!--建议配上主键,因为主键在结果映射中有特殊的意义,
    主键会被mybatis框架缓存起来,如果查询结果中有重复的主键值,mybatis框架会将它们封装成同一个Java对象。-->
    <id property="id" column="id"></id>
    <!--property指定Java对象的属性,column指定数据库表的字段。
    当数据库表的字段名和Java对象的属性名一致时,可以不配置-->
    <result property="carNum" column="car_num"></result>
    <result property="guidePrice" column="guide_price"></result>
    <result property="produceTime" column="produce_time"></result>
    <result property="carType" column="car_type"></result>
</resultMap>
<!--resultMap表示指定使用哪个结果映射,resultMap的值时resultMap标签的id-->
<select id="selectAllCarResultMap" resultMap="carResultMap">
    SELECT * FROM t_car
</select>

开启驼峰命名自动映射

前提时Java的属性名和数据库表的字段名必须符合命名规范。

可以将car_type对应为carType

在mybatis-config.xml文件中进行如下配置即可开启该功能,默认值是false:

xml 复制代码
<settings>
    <setting name="mapUnderscoreToCameCase" value="true"/>
</settings>

返回总记录条数

xml 复制代码
<!--  对应的接口方法: long selectTotalCount();   -->
  <select id="selectTotalCount" resultType="long">
      SELECT count(*) FROM t_car
  </select>
相关推荐
狂奔小菜鸡2 小时前
Day40 | Java中的ReadWriteLock读写锁
java·后端·java ee
SimonKing3 小时前
JetBrains 用户狂喜!这个 AI 插件让 IDE 原地进化成「智能编码助手」
java·后端·程序员
狂奔小菜鸡3 小时前
Day39 | Java中更灵活的锁ReentrantLock
java·后端·java ee
NE_STOP16 小时前
MyBatis-配置文件解读及MyBatis为何不用编写Mapper接口的实现类
java
后端AI实验室21 小时前
用AI写代码,我差点把漏洞发上线:血泪总结的10个教训
java·ai
程序员清风1 天前
小红书二面:Spring Boot的单例模式是如何实现的?
java·后端·面试
belhomme1 天前
(面试题)Redis实现 IP 维度滑动窗口限流实践
java·面试
Be_Better1 天前
学会与虚拟机对话---ASM
java
开源之眼1 天前
《github star 加星 Taimili.com 艾米莉 》为什么Java里面,Service 层不直接返回 Result 对象?
java·后端·github