JDBC
- SQL 夹杂在java代码中,耦合度高
- 不易维护,频繁修改
- 代码冗长,开发效率高
MyBatis
- 轻量级,性能出色
- SQL 和 Java 编码分开,Java 代码专注业务 ,SQL语句专注数据
MyBatis 配置文件
配置连接数据库环境
引入映射文件
ORM(Object Relationship Mapping) 对象映射关系
- 类 对应 表
- 属性 对应 字段
- 对象 对应 记录、行
一张表通常对应一个mapper及一个mapper接口
mapper映射文件 namespace需要和mapper接口全类名保持一致。
mapper映射文件 SQL语句的id要和mapper接口中的方法名一致。
sql注入问题
某个网站的登录验证SQL查询代码为:
strSQL = "SELECT * FROM users WHERE name = '" + userName + "' and pw = '"+ passWord +"';"
恶意填入:
userName = “1’ OR ‘1’='1”;
passWord = “1’ OR ‘1’='1”;
那么最终SQL语句变成了:
strSQL = "SELECT * FROM users WHERE name = '1' OR '1'='1' and pw = '1' OR '1'='1';"
因为WHERE条件恒为真,这就相当于执行:
strSQL = "SELECT * FROM users;"
因此可以达到无账号密码亦可登录网站。
PreprareStatement
在使用参数化查询的情况下,数据库系统(eg:MySQL)不会将参数的内容视为SQL指令的一部分来处理,而是在数据库完成SQL指令的编译后,才套用参数运行,因此就算参数中含有破坏性的指令,也不会被数据库所运行。
即SQL语句在程序运行前已经进行了预编译,当运行时动态地把参数传给PreprareStatement时,即使参数里有敏感字符如 or ‘1=1’、数据库也会作为一个参数一个字段的属性值来处理而不会作为一个SQL指令。
补充1:在页面输入的时候可以加入校验,不可输入sql关键字,不可输入空格,也可以防止SQL注入。
补充2:PreparedStatement比 Statement 更快
使用 PreparedStatement 最重要的一点好处是它拥有更佳的性能优势,SQL语句会预编译在数据库系统中。执行计划同样会被缓存起来,它允许数据库做参数化查询。使用预处理语句比普通的查询更快,因为它做的工作更少(数据库对SQL语句的分析,编译,优化已经在第一次查询前完成了)。为了减少数据库的负载,生产环境中德JDBC代码你应该总是使用PreparedStatement 。值得注意的一点是:为了获得性能上的优势,应该使用参数化sql查询而不是字符串追加的方式。
MyBatis
MyBatis 获取参数 的 两种 方式 ${} 和 #{}
${} 字符拼接 本质
#{} 占位符 本质 (参数赋值)
MyBatis 获取各种参数
- 单个参数
<!-- User getUserByUserName(String uname);-->
<!-- #{} 传输单个字符 括号里可以是任意 字符(建议传本含义)-->
<!-- ${} 注意单引号-->
<select id="getUserByUserName" resultType="User">
<!-- select * from t_user where uname = #{uname}-->
select * from t_user where uname = '${uname}'
</select>
- 多个参数
<!-- ser checkLogin(String uname,String pwd);-->
<!-- 多个参数的时候,MyBatis 会先把 参数放在map集合中 -->
<!-- 以 arg0, arg1, param1, param2 为key,参数值为value, Available parameters are [arg0, arg1, param1, param2]-->
<select id="checkLogin" resultType="User">
<!-- select * from t_user where uname = #{param1} and pwd = #{param2} -->
<!-- select * from t_user where uname = #{arg0} and pwd = #{arg1}-->
select * from t_user where uname = '${arg0}' and pwd = '${param2}'
</select>
- 参数手动放入map集合,直接参数传map集合
<!-- User checkLoginByMap(Map<String,Object> mp);-->
<!-- 这里key 可以自定义-->
<select id="checkLoginByMap" resultType="User">
select * from t_user where uname = #{uname} and pwd = #{pwd}
</select>
- 参数是实体类类型(常用)
<!-- int insertUser(User user);-->
<!-- 以属性的方式访问属性值-->
<insert id="insertUser">
insert into t_user values(null,#{uname},#{pwd},#{email},#{type})
</insert>
- @param 命名参数
<!-- User checkLoginByParam("uname") String uname,@Param("pwd") String pwd);-->
<!-- 等价第二种和第三种结合 省略自动创建map ,可以用自定义key 和 param1 、 param2 ...-->
<select id="checkLoginByParam" resultType="User">
<!-- select * from t_user where uname = #{uname} and pwd = #{pwd}-->
select * from t_user where uname = #{param1} and pwd = #{param2}
</select>
查询返回map集合
适用没有实体类映射的查询,以字段为key,字段对应数据为value;Map<String,Object>
<!-- Map<String,Object> getUserByIdToMap(@Param("uid")int uid);-->
<!-- 查询一条数据-->
<select id="getUserByIdToMap" resultType="map">
select * from t_user where uid = #{uid}
</select>
<!-- @MapKey("uid")-->
<!-- Map<String,Object> getAllUsersToMap(@Param("uid")int uid);-->
<!-- 查询多条数据-->
<!--加了注解后,会以某个字段作为key ,该条所有数据拼接后作为value,类似json数组-->
<select id="getAllUsersToMap" resultType="map">
select * from t_user
</select>
MyBatis 一级缓存
SqlSession 的默认开启缓存
同一个SqlSession ,会将第一个查询数据进行缓存,再次查询相同数据就会从缓存中取,不从数据库取。
使一级缓存失效的四种情况
- 不同的SqlSession对应不同一级缓存
- 同一个SqlSession但查询条件不同
- 同一个SqlSession两次查询中执行了任意增删改操作
- 同一个SqlSession两次查询中手动清空了缓存
MyBatis 二级缓存
映射文件Cache标签开启 核心配置文件全局开启cacheEnable
- 通过SqlSessionFactory创建的SqlSession都可以用这个缓存
- 二级缓存必须在SqlSession关闭或提交之后有效
- 查询的数据所转换的实体类型必须实现序列化接口
- 两次查询中执行了任意增删改操作也会让二级缓存失效
缓存只适用于查询操作
缓存顺序
- 先查二级缓存,再一级缓存,最后数据库
- SqlSession关闭后,一级缓存会转到二级缓存
MyBatis 逆向工程
根据数据库表直接自动创建pojo和mapper接口
MyBatis 分页
pageNum:当前页的页码
pageSize:每页显示的条数
size:当前页显示的真实条数
total:总记录数
pages:总页数
prePage:上一页的页码
nextPage:下一页的页码
isFirstPage/isLastPage:是否为第一页/最后一页
hasPreviousPage/hasNextPage:是否存在上一页/下一页
navigatePages:导航分页的页码数
navigatepageNums:导航分页的页码,[1,2,3,4,5]