MyBatis SQL参数传递(掌握)
SQL映射器Mapper接口(掌握)Myb
atis批量操作(理解掌握)
(多对一)关联映射(掌握)
(一对多,多对多)集合映射
MyBatis原理回顾(Object Relational Mapping,简称ORM)
ORM的缺点是会牺牲程序的执行效率和会固定思维模式。
从系统结构上来看,采用ORM的系统一般都是多层系统,系统的层次多了,效率就会降低。ORM是一种完全的面向对象的做法,而面向对象的做法也会对性能产生一定的影响。
在我们开发系统时,一般都有性能问题。性能问题主要产生在算法不正确和与数据库不正确的使用上。ORM所生成的代码一般不太可能写出很高效的算法,在数据库应用上更有可能会被误用,主要体现在对持久对象的提取和和数据的加工处理上,如果用上了ORM,程序员很有可能将全部的数据提取到内存对象中,然后再进行过滤和加工处理,这样就容易产生性能问题。
在对对象做持久化时,ORM一般会持久化所有的属性,有时,这是不希望的。
但ORM是一种工具,工具确实能解决一些重复,简单的劳动。这是不可否认的。但我们不能指望工具能一劳永逸的解决所有问题,有些问题还是需要特殊处理的,但需要特殊处理的部分对绝大多数的系统,应该是很少的。
S(spring)S(springmvc)M(mybatis)集成(掌握)
MyBatis是一个ORM持久化框架,应用到系统持久层(Dao);
一个MyBatis的应用程序都以一个SqlSessionFactory对象(单例)的实例为核心
SqlSession对象完全包含以数据库为背景的所有执行SQL操作的方法
MyBatis是针对数据库交互的一个辅助框架,也是对jdbc做了的简单封装,以xml配置代替Java代码来管理数据库的交互细节!!
MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口(Mapper)和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中一条记录。
MyBatis是一个支持普通SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis消除了几乎所有的JDBC代码和参数的手工设置以及对结果集的检索封装。
加入mybatis
- MyBatis SQL参数传递
#{OGNL表达式} 和 ${OGNL表达式} 的区别
1.1. #{OGNL表达式}
MyBatis会把这个表达式使用?(占位符)替换,作为一个sql参数使用:推荐使用
比如name的值为:
定义SQL: select * from t_user where name = #{name}
最终SQL: select * from t_user where name = ?
1.2. ${OGNL表达式}
MyBatis会把这个表达式的值替换到sql中,作为sql的组成部分;
该方式主要用于程序拼接SQL;
如果sql中使用${OGNL},并且参数的类型是(integer,string....)那么OGNL表达式可以写成任意东西;
1.3. ${OGNL}表达式的应用场景:
1.3.1. 不能用在登录,会出现sql注入
比如登录功能,在输入name的时候可以人为的制造成功:
User user=new User();
user.setName("\"admin\" or \"1=1\" ");
user.setPassword("\"admin\" ");
定义SQL: select id,name,password from t_user where name = ${name} and password=${password}
最终SQL: select id,name,password from t_user where name="test" or "1=1" and password="test" 出现sql注入
可以用在order by + limit的场景
#{}和${}的使用
resultMap和ParameterMap书写拼写要使用#{},resultType 和parameterType类型使用${},使用例子如下:
Select ID,COMMAND from Message where COMMAND=#{command}
Select ID,COMMAND from Message where COMMAND=‘${command}’
前者解析为:
Select ID,COMMAND from Message where COMMAND=?具有预编译效果
后者解析为:
Select ID,COMMAND from Message where COMMAND=段子 不具有预编译效果
但是,例如当页面向后台传递一个列名(属性名)的时候,是不希望被预编译出一个?的,此时要用到$格式;
如:加上 order by${param} ,此时param是一个列名。
#{}和 ognl表达式
一般参数的拼写还是保证统一风格为好,便于人读。
经常碰到这样的面试题目:#{}和${}的区别是什么?
网上的答案是:#{}是预编译处理,$ {}是字符串替换。mybatis在处理#{}时,会将sql中的#{}替换为?号,调用PreparedStatement的set方法来赋值;mybatis在处理 $ { } 时,就是把 ${ } 替换成变量的值。使用 #{} 可以有效的防止SQL注入,提高系统安全性。
对于这个题目我感觉要抓住两点:
(1)$ 符号一般用来当作占位符,常使用Linux脚本的人应该对此有更深的体会吧。既然是占位符,当然就是被用来替换的。知道了这点就能很容易区分$和#,从而不容易记错了。
(2)预编译的机制。预编译是提前对SQL语句进行预编译,而其后注入的参数将不会再进行SQL编译。我们知道,SQL注入是发生在编译的过程中,因为恶意注入了某些特殊字符,最后被编译成了恶意的执行操作。而预编译机制则可以很好的防止SQL注入。
最后想说的是,对于mybatis 以及 sql 而言,每一个考点背后都是有一个深刻的思想存在的,应该好好的体会。这样才能真正的做到技术提升,成为技术大牛。
这第三个例子虽然说的是#{}和{}通用的问题,也就是说在此种情况下#{}和
{}是通用的,只不过需要些小的转换.如例子中需要手动
拼接单引号 ' ' 到变量值的前后,确保sql语句正常.
简单说#{}是经过预编译的,是安全的,而${}是未经过预编译的,仅仅是取变量的值,是非安全的,存在sql注入.
这里先说一下只能的情况,从我们前面的例子中也能看出,orderby是肯定只能用
{}了,用#{}会多个' '导致sql语句失效.此外还有一个like 语句后也需要用${},简单想一下
就能明白.由于仅仅是简单的取值,所以以前sql注入的方法适用此处,如果我们orderby语句后用了
{},那么不做任何处理的时候是存在sql注入危险的.你说怎么防止,那我只
能悲惨的告诉你,你得手动处理过滤一下输入的内容,如判断一下输入的参数的长度是否正常(注入语句一般很长),更精确写查询一下输入的参数
- SQL映射器Mapper接口
MyBatis基于代理机制,可以让我们无需再编写Dao的实现。直接把以前的dao接口定义成符合规则的Mapper。
Mybatis的批量操作(foreach)
<!-- delete from product where id in ( ? , ? ) 批量删除:
collection="list":传入的list,相当于map的key,通过list得到传入的整个集合的值;
index="index" :每次循环的索引
item="id" :每次循环的这个值
open="(" :以什么开始
separator=",":分隔符
close=")":以什么结束
-->
<delete id="deleteBatch" parameterType="java.util.List">
delete from product where id
in
<foreach collection="list" index="index" item="id" open="("
separator="," close=")">
#{id}
</foreach>
</delete>
<!-- 批量插入:
insert into product(productName,salePrice,costPrice,cutoff,supplier,brand,dir_id)
values (?,?,?,?,?,?,?) , (?,?,?,?,?,?,?)
parameterType:传入参数是:java.util.List的类型的;
对传入的list进行foreach循环:取出值填入插入的values中:
collection="list":这个不能修改,表示传入的一个list类似于一个key是list,得到传入的list集合
index="index":每一次遍历的序号
item="item":每次遍历的这个对象别名,可以修改
separator=",":每遍历一次后的分隔符
-->
<insert id="insertBatch" parameterType="java.util.List">
insert into
product(productName,salePrice,costPrice,cutoff,supplier,brand,dir_id)
values
<foreach collection="list" index="index" item="item"
separator=",">
(#{item.productName},#{item.salePrice},#{item.costPrice},#{item.cutoff},#{item.supplier},#{item.brand},#{item.dir_id})
</foreach>
</insert>
关联映射(多对一)
多对一:
多个员工属于一个部门,domain:在员工这方创建一个部门:在多方有一个外键
一对多:
一个部门有多个员工:Domain:在部门有一个员工list:在多方有一个外键
多对多:
角色和权限:一个角色拥有对个权限,一个权限属于多个角色
Domain:使用list表示:使用中间表维护关系
parameterType ,入参的全限定类名或类型别名
keyColumn ,设置数据表自动生成的主键名。对特定数据库(如PostgreSQL),若自动生成的主键不是第一个字段则必须设置
keyProperty ,默认值unset,用于设置getGeneratedKeys方法或selectKey子元素返回值将赋值到领域模型的哪个属性中
useGeneratedKeys ,取值范围true|false(默认值),设置是否使用JDBC的getGenereatedKeys方法获取主键并赋值到keyProperty设置的领域模型属性中。MySQL和SQLServer执行auto-generated key field,因此当数据库设置好自增长主键后,可通过JDBC的getGeneratedKeys方法获取。但像Oralce等不支持auto-generated key field的数据库就不能用这种方法获取主键了
statementType ,取值范围STATEMENT,PREPARED(默认值),CALLABLE
flushCache ,取值范围true(默认值)|false,设置执行该操作后是否会清空二级缓存和本地缓存
timeout ,默认为unset(依赖jdbc驱动器的设置),设置执行该操作的最大时限,超时将抛异常
databaseId ,取值范围oracle|mysql等,表示数据库厂家,元素内部可通过`<if test="_databaseId = 'oracle'">`来为特定数据库指定不同的sql语句
---------------------
作者:零下