整体介绍
◆讲解MyBaits基础入门
◆讲解MyBatisi高级特性
MyBatis介绍
框架(Framework)的作用
生活中框架
软件开发中框架
◆框架是可被应用开发者定制的应用骨架
◆框架是一种规则,保证开发者遵循相同的方式开发程序
◆框架提倡”不要重复造轮子”,对基础功能进行封装
封装了增删查改等功能,解决JDBC操作麻烦的痛点,对立再统一形成框架
框架的优点
◆极大提高了开发效率
◆统一的编码规则利于团队管理
◆灵活配置的应用(XML/JSON等)拥有更好的维护性
SSM开发框架
Spring提供了底层对象管理,是框架下的框架,其他框架都要基于Spring进行开发,SpringMVC提供Web层面交互(替代Servlet),Mybatis提供对数据库的便捷操作(替代JDBC)
//可以看出技术从矛盾中产生,对立面通过第三方(用来解决对立)再统一成更高级的技术,形成复杂的现象,我们需要从矛盾中找出最主要的对立面即主要矛盾,次要矛盾由主要矛盾衍生而来
什么是Mybatis
◆MyBatis,是优秀的持久层框架
◆MyBatis使用XML将SQL与程序解耦,便于维护
◆MyBatis学习简单,执行高效,是JDBC的延伸
所谓持久就是将数据存入数据库以防重启丢失
mybatis – MyBatis 3 | 简介
MyBatis开发流程
◆引入MyBatis依赖
◆创建核心配置文件
◆创建实体(Entity)类
◆创建Mapper映射文件
◆初始化SessionFactory
◆利用SqlSession对象操作数据
单元测试与Unit4
单元测试
◆单元测试是指对软件中的最小可测试单元进行检查和验证
◆测试用例是指编写一段代码对已有功能(方法)进行校验
◆JUnit4是java中最著名的单元测试工具,主流lDE内置支持
JUnit4是JUnit的第四代版本
JUnit4使用方法
◆引入JUnit Jar包或增加Maven依赖
◆编写测试用例验证目标方法是否正确运行
◆在测试用例上增加@Test注解开始单元测试
下载并安装 ·junit-team/junit4 Wiki ·GitHub
两个核心文件
导入依赖的第一种办法:新建java工程,新建lib目录,导入两个jar包,在工程结构中导入两个依赖
导入依赖的第二种办法:新建maven工程,在pom.xml中写入
导入成功后即可在外部库找到两个依赖包
推荐第二种方法导入依赖,maven工程有专门的test文件夹存放测试用例,可以用来测试类,一般命名方式为测试类名+Test/Testor.java
Calculater.java文件:
在test目录下新建CalculaterTest.java
在Test.java文件运行即可得到答案结果,注意点击特定的函数名右键即可单独运行此函数,或者项目结构选中右键也可快速运行,右键项目名可以运行所有测试类
左边可看到所以函数方法运行成功
技巧:可以选中需要测试的java文件,选择code→Generate→Test,勾选需要指定的测试方法,即可按照原先目录结构快速创建测试类
MyBatis基本使用
MyBatis环境配置
使用xml保存配置信息,一般默认名为mybatis-config.xml文件
◆MyBatis采用XML格式配置数据库环境信息
◆MyBaits环境配置标签<environment>
◆environment包含数据库驱动、URL、用户名与密码
通过xml引入mybatis和mysql-connector-java两个驱动
右边栏选择DataBase,连接上数据库
Database选择数据库连接下的数据库名,在schemas下右键导入sql文件
也可以右键编辑表结构
配置好数据库后,接下来就是mybatis的环境配置
在resource目录下,新建xml文件,一般默认命名为mybatis-config.xml,主要配置在<configuration>标签对中
id属性表示命名环境名,从而可以便捷进行切换
需要引入mybatis – MyBatis 3 | 入门
SqlSessionFactory
◆SqlSessionFactory是MyBatis的核心对象
◆用于初始化MyBatis,创建SqlSession对象
◆保证SqlSessionFactory在应用中全局唯一
sql的增删查改由这个工厂类创建出来的对象SqlSession进行调用
SqlSession
◆SqlSession是MyBatisa操作数据库的核心对象
◆SqlSession使用JDBC方式与数据库交互
◆SqlSessioni对象提供了数据表CRUD对应方法
先加载mybatis-config.xml文件
在pom.xml加载junit
然后再test文件夹下新建包com.imooc.mybatis
在包下新建MyBatisTestor.java测试类
package com.imooc.mybatis;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.IOException;
import java.io.Reader;
import java.sql.Connection;
//JUNIT单元测试类
public class MyBatisTestor {
/**
* 初始化SqlSessionFactory
* @throws IOException
*/
//必须添加@Test注解,junit才会对其进行执行
@Test
public void testSqlSessionFactory() throws IOException {
//利用Reader加载classpath下的mybatis-config.xml核心配置文件
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
//初始化SqlSessionFactory对象,同时解析mybatis-config.xml文件
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
System.out.println("SessionFactory加载成功");
SqlSession sqlSession = null;
try {
//创建SqlSession对象,SqlSession是JDBC的扩展类,用于与数据库交互
sqlSession = sqlSessionFactory.openSession();
//创建数据库连接(测试用 测试是否加载成功 此句代码mybatis会自动完成)
Connection connection = sqlSession.getConnection();
System.out.println(connection);
}catch (Exception e){
e.printStackTrace();
}finally {
if(sqlSession != null){
//如果type="POOLED",代表使用连接池,close则是将连接回收到连接池中
//如果type="UNPOOLED",代表直连,close则会调用Connection.close()方法关闭连接
sqlSession.close();
}
}
}
}
初始化工具类MyBatisUtils
如何保证SqlSessionFactory全局唯一?
java文件夹下新建com.imooc.mybatis.utis包
新建MyBatisUtils.java文件
使用static关键字修饰,属于类而不属于对象,可以直接通过类名调用
新增static块初始化静态对象
在MyBatisTestor.java测试类下即可进行测试
sqlsession内置了很多数据库的使用方法
MyBatis数据查询
◆创建实体类(Entity)
◆创建Mapper XML
◆编写<select>SQL标签
◆开启驼峰命名映射
◆新增<mapper>
◆SqlSession执行select语句
如何提取商品数据表里面的数据?按照上面的步骤进行
- 新建包com.imooc.mybatis.entity
新增商品类,Goods.java,属性要与数据表中的字段对上
写好属性后,快速生成get与set方法
- resourse目录下新建子目录mappers,表示映射器
保存的都是xml文件,新建goods.xml,用来说明实体类与哪个表和字段对应
映射器的dtd文件
使用mapper标签对
resultType表示数据返回的对象,sql语句执行后的每一条结果包装成实体对象,id表示sql语句名
namespace表示命名空间,类似java里面的包,用来进行区分,增加namespace,就能区分调用是哪个sql语句,相同namesapce下id属性唯一,但不同namespace下id属性可以相同
mybatis需要认识goods.xml文件,需要在mybatis-config.xml中进行声明
mybatis启动时会自动加载这个xml文件
3. 接下来在MyBatisTestor.java文件下进行测试
右键 testSelectAll()单独运行
selectList是SqlSession的内置方法,根据我们需要查询所有的商品所以选择这个方法
getTitle()是我们定义的方法
数据库底层多单词使用了下划线分割(goods_id),但是在我们实体中属性强制使用驼峰(goodsId),导致属性与字段名不符,MyBatis无法帮我们完成设置工作,所以很多属性值为null
解决办法是在MyBatis-config.xml文件中,在<configuration>标签对里添加驼峰命名转换法:
SQL传参
通过动态传入参数进行查询
单参数查询
在goods.xml中:
parameterType="Integer"
表示传入的参数为整形
使用#{value}
接收参数
然后进行测试:
因为纸船一个参数,所以只有一条记录,选择方法selectOne()方法。1603由goods.xml中语句可知是goods_id,通过goods_id查出商品标题
多参数查询
goods.xml文件中添加
java.util.Map
是采用键值对的方式保存数据,多参数打包成map就可以传入多参数数值了,resultType表示返回结果
继续进行测试:
如果保证goods.selectByPriceRange全局唯一,就可以不用加上goods了
因为多参数查询,有多条结果,所以使用selectList()方法
MyBatis获取多表关联查询结果
上面大的resultType获得的结果都是单表里面的,在工作中大部分是多表联合得到复杂的结果集
不仅参数可以使用map进行传入多个参数,结果集resultType也能使用map集将字段名与字段值封装成一个集合便于后续java操作
要求:对于t_goods这张表在查询数据时,将category这个字段也查询出来
使用java.util.Map,里面的hashmap会由于自身机制(通过hash值)导致结果乱序,所以改用java.util.LinkedHashMap,LinkedHashMap是采用链表式存储,当提取数据时会按照插入顺序进行提取,所以不会乱序
比如第一张表有1,2字段,第二张表我们要第3字段,查询出来结果可能不会是1,2,3,而是2,3,1等结果顺序
使用LinkedHashMap非常灵活,比如添加上’1’ as test,就会增加test字段,值为1
集合一般使用Map来结尾
添加测试方法
ResultMap结果映射
通过LinkedHashMap也无法很好解决问题
如何用对象的方式保存多表关联查询的结果?
◆ResultMap可以将查询结果映射为复杂类型的Java对象
◆ResultMap适用于Java对象保存多表关联结果
◆ResultMap支持对象关联查询等高级特性
不再原来基础上进行重载,不要为了保存查询结果在原来类上修改,原来的类已经通过JDBC连接上数据库,修改耗费时间与精力,增加冗余属性,无法对应字段,不如重新新建一个拓展对象,对结果进行保存
新建包com.imooc.mybatis.dto
新建GoodsDTO.java
dto是数据传输对象,dto一般都是为了在原有类上添加拓展
private Goods goods = new Goods();
创建了全新的Goods对象
添加属性Category和test继续对应字段
添加配置goods.xml
category_name和test字段在GoodsDTO里
使用resultMap,而不是resultType
我们需要新增resultMap标签,用来映射属性与字段名
<!--结果映射-->
<resultMap id="rmGoods" type="com.imooc.mybatis.dto.GoodsDTO">
<!--设置主键字段与属性映射-->
<id property="goods.goodsId" column="goods_id"></id>
<!--设置非主键字段与属性映射-->
<result property="goods.title" column="title"></result>
<result property="goods.originalCost" column="original_cost"></result>
<result property="goods.currentPrice" column="current_price"></result>
<result property="goods.discount" column="discount"></result>
<result property="goods.isFreeDelivery" column="is_free_delivery"></result>
<result property="goods.categoryId" column="category_id"></result>
<result property="categoryName" column="category_name"></result> <result property="test" column="test"/>
</resultMap>
property表示属性名,column表示字段名一一映射下来
categoryName、test属于goodsDTO的,所以不用写前缀
t_goods数据表字段:
t_category数据表字段:
添加测试:
其中List<GoodsDTO> list
中GoodsDTO与type="com.imooc.mybatis.dto.GoodsDTO"
中映射的GoodsDTO保持一致
MyBatis数据写入操作
前几小节学习的是MyBatis数据查询操作,这次重点放在MyBatis数据插入操作
数据库事务
数据库事务是保证数据操作完整性的基础
提交成功后,事务日志清空
当数据中断,会发起回滚操作,不会写入到数据表中
MyBatis写操作包含三种
◆插入-<insert>
◆更新-<update>
◆删除-<delete>
插入-<insert>
进行测试
代码不写good_Id是因为我们插入数据时根本不知到是第几条了,所以一般设置为自动生成,即主键自增
然而我们发现新插入的数据,并没有将其Id回填到goodsId中,所以System.out.println(goods.getGoodsId());值为null,(id主键编号是会自动生成的)是不利于我们对数据的操作的
插入成功后如何获取到id主键?
增加配置项,使用selectKey标签对
order="AFTER"
表示执行完插入操作后在执行select last_insert_id()
语句
last_insert_id()是mysql自带的,表示当前连接最后产生的id号,resultType="Integer"
为整形,承载这个最后id号
selectKey与useGeneratedKeys的区别
selectKey标签用法
useGeneratedKeys属性用法
二者区别-显示与隐示
◆selectKey标签需要明确编写获取最新主键的SQL语句
select last_insert_id()
◆useGeneratedKeys属性会自动根据驱动生成对应SQL语句
如果从mysql迁移到sqlserver,优先使用useGeneratedKeys,因为sql语句不兼容
二者区别-应用场景不同
◆selectKey:适用于所有的关系型数据库
◆useGeneratedKeys只支特”自增主键”类型的数据库
总结
◆selectKey标签是通用方案,适用于所有数据库,但编写麻烦
◆useGeneratedKeys属性只支持”自增主键”数据库,使用简单
更新-<update>
在goods.xml文件
通过goods_id进行选择更新
进行测试用例:
不建议手动set组织数据,而是通过获取goods.selectById,再进行修改,这样对原数据影响最小
删除-<delete>
测试用例
MyBatis预防SQL注入攻击
SQL注入攻击
◆SQL注入是指攻击者利用SQL漏洞,绕过系统约束越权获取数据的攻击方式
MyBatis两种传值方式
◆${}文本替换,未经任何处理对SQL文本替换
◆#{}预编译传值,使用预编译传值可以预防SQL注入
#{}预编译传值会将特殊符号一并传入,导致无法匹配,预防sql注入
测试用例
为什么保留原文传值?比如我们需要排序时,无论升序还是降序,都需要动态传入,如果使用预编译,就会将 " order by title desc"
看成字符串(保留双引号),而不是sql语句(不保留双引号)拼接上去
MyBatis工作流程