实际开发中应用细节
#{}和${}的区别?
'#{}':先编译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的值太长,也就是返回值的全限定类名太长,可以使用别名机制。
-
先在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>
-
在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>