MyBatis入门
一、概述
MyBaits是支持定制化SQL、存储过程以及高级映射的优秀的持久层框架;
jdbc缺陷:存在大量的硬编码灵活性低,大量的无关业务的处理编码,如连接和关闭数据库的操作,扩展优化不便;
优点:
1、避免传统JDBC硬编码,执行高效,是JDBC的延伸 2、使用XML将SQL与程序解耦,便于维护
3、POJO对象和数据库记录直接映射 4、完善的文档支持
应用场景:1.更加关注SQL优化的项目;2.需求频繁改动的项目
二、框架概念
框架是一种规则,保证开发者遵循相同的方式开发程序,框架提倡”不要重复造轮子”,对基础功能进行封装,极大提高了开发效率,统一的编码规则,利于团队管理和维护。
SSM开发框架:
- Spring:对象容器框架,对各个对象有效管理,底层框架;
- Spring MVC:替代Servlet,帮助我们更好的在web层面进行开发;
- MyBaties:简化数据库的交互,底层把JDBC进行了封装;
三、MyBatis开发流程
新建数据库连接时,注意加上时区测试
useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
比如连接icake数据库的URL
jdbc:mysql://localhost:3306/icake?useUnicode=true&characterEncoding=UTF-8&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
-
引入MyBatis依赖,利用maven进行管理
pom.xml中加入阿里云的仓库连接,下载依赖包更稳更快
<repositories> <repository> <id>aliyun</id> <name>aliyun</name> <url>https://maven.aliyun.com/repository/public</url> </repository> </repositories>
-
创建核心配置文件,使用xml文件配置。
比如:配置mybatis-config.xml文件 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <settings> <!-- goods_id ==> goodsId 驼峰命名转换 --> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <environments default="dev"> <environment id="dev"> <transactionManager type="JDBC"></transactionManager> ...
-
创建实体(Entity)类,设置属性,生成get、set方法
public class Goods { private Integer goodsId;//商品编号 private String title;//标题 ... public Integer getGoodsId() { return goodsId; } ... }
-
创建Mapper映射文件,实体类和数据表的映射
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!-- 不同的namespace用来区分不同的sql语句--> <!-- resultType结果类型时Goods对象,每一条记录都被包装成Goods对象--> <mapper namespace="goods"> <select id="selectAll" resultType="com.imooc.mybatis.entity.Goods" > select * from t_goods order by goods_id desc limit 10 </select> ... </mapper>
-
初始化SessionFactory,会话工厂。读取配置文件。比如读取mybatis-config.xml
//初始化工具类MyBatisUtils,在工具类中配置,保证SqlSessionFactory在应用中全局唯一 public class MyBatisUtils { //利用static(静态)属于类不属于对象,且全局唯一 private static SqlSessionFactory sqlSessionFactory = null; //利用静态块在初始化类时实例化sqlSessionFactory static { Reader reader = null; try { reader = Resources.getResourceAsReader("mybatis-config.xml"); sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader); ... public static SqlSession openSession(){ //默认SqlSession对自动提交事务数据(commit),设置false代表关闭自动提交,改为手动提交事务数据 return sqlSessionFactory.openSession(false); }
-
利用SqlSession对象操作数据,增,删,改,查。
//新建测试类MyBatisTestor,创建testSelectAll方法,编写java代码对数据库进行操作 public void testSelectAll() throws Exception { SqlSession session = null; try{ session = MyBatisUtils.openSession(); //goods.selectAll定位sql语句,返回的是一堆Goods对象,用list集合来存放 //session.selectList返回多条数据,session.selectOne()方法返回一条数据 List<Goods> list = session.selectList("goods.selectAll"); //for循环,getTitle()方法,输出对应表的标题 for(Goods g : list){ System.out.println(g.getTitle()); } }catch (Exception e){ throw e; }finally { MyBatisUtils.closeSession(session); } }
四、环境配置
mybatis-config.xml文件
MyBatis采用XML格式配置数据库环境信息,MyBaits环境配置标签,environment包含数据库驱动、URL、用户名与密码,, 。idea内置了连接数据库的方法。
例子:<!--配置环境,不同的环境不同的id名字-->
<environment id="dev">
<!--采用JDBC方式对数据库事务进行commit/rollback-->
<transactionManager type="JDBC"></transactionManager>
<!--采用连接池方式管理数据库连接-->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/db"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
五、MyBatis的核心对象
SqlSessionFactory
SqlSessionFactory是MyBatis的核心对象,用于加载配置文件,初始化MyBatis框架,创建SqlSession对象,保证SqlSessionFactory在应用中全局唯一
SqlSession
SqlSession是MyBatis操作数据库的核心对象,SqlSession使用JDBC方式与数据库交互,SqlSession对象提供了数据表CRUD对应方法,CURD是创建(Create)、更新(Update)、读取(Retrieve)和删除(Delete)操作。
初始化工具类MyBatisUtils,在工具类中配置,保证SqlSessionFactory在应用中全局唯一
/**
* MyBatisUtils工具类,创建全局唯一的SqlSessionFactory对象
*/
public class MyBatisUtils {
//利用static(静态)属于类不属于对象,且全局唯一
private static SqlSessionFactory sqlSessionFactory = null;
//利用静态块在初始化类时实例化sqlSessionFactory
static {
Reader reader = null;
try {
reader = Resources.getResourceAsReader("mybatis-config.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
} catch (IOException e) {
e.printStackTrace();
throw new ExceptionInInitializerError(e);
//初始化错误时,通过抛出异常ExceptionInInitializerError通知调用者
}
}
/**
* openSession 创建一个新的SqlSession对象
* @return SqlSession对象
*/
public static SqlSession openSession(){
//默认SqlSession对自动提交事务数据(commit)
//设置false代表关闭自动提交,改为手动提交事务数据
return sqlSessionFactory.openSession(false);
}
/**
* 释放一个有效的SqlSession对象
* @param session 准备释放SqlSession对象
*/
public static void closeSession(SqlSession session){
if (session != null) {
session.close();
}
}
}
六、MyBatis数据查询
数据查询步骤
-
创建实体类(Entity),类中的属性是根据数据表的字段创建的
-
创建Mapper XML
作用:说明创建的类中的属性和数据库中表的对应关系,说明了对象和表之间的映射关系。mapper文件中存放了要执行的sql语句
注意:xml文件最上面,加上xml声明和mybatis的dtd声明,dtd是约束文件,在dtd中定义对xml文件的约束,这样可以保证xml文件内容的有效性
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
- 要在mybatis-config.xml文件中声明mapper
<mappers>
<mapper resource="mappers/goods.xml"/>
</mappers>
- 编写SQL标签,编写要执行的sql语句
<!-- 不同的namespace用来区分不同的sql语句-->
<!-- resultType结果类型时Goods对象,每一条记录都被包装成Goods对象-->
<mapper namespace="goods">
<select id="selectAll" resultType="com.imooc.mybatis.entity.Goods" >
select * from t_goods order by goods_id desc limit 10
</select>
</mapper>
-
开启驼峰命名映射:比如:数据库表的某一字段名为:goods_id,创建类的对象属性为:goodsId(驼峰命名法)。名字不对应导致查询不到数据库中的数据。开启驼峰命名映射后,会把goods_id映射为goodsId
在mybatis-config.xml文件中配置驼峰命名映射
<settings>
<!-- goods_id ==> goodsId 驼峰命名转换 -->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
- SqlSession执行select语句
SQL传参
单参数传递,使用parameterType指定参数的数据类型即可,SQL中#{value}提取参数
1.mapper映射文件中配置映射:
<select id="selectById" parameterType="Integer"
resultType="com.imooc.mybatis.entity.Goods">
select * from t_goods where goods_id = #{value}
</select>
2.对应的java调用代码:
SqlSession session = MyBatisUtils.openSession();
//session.selectOne()方法返回一条数据
Goods goods = session.selectOne("goods.selectById",1602);
//getTitle()方法,输出对应表的标题
System.out.println(goods.getTitle());
多参数传递,使用parameterType指定Map接口,SQL中#{key}提取参数。parameterType=“java.util.Map”
注意:若映射文件中,SQL语句对应的id是全局唯一的,调用时也可以不写命名空间
Map的妙用
Map的适用场景:为SQL语句传入多个参数,查询返结果包含跨表字段
为SQL语句传入多个参数
<!-- 多参数传递时,使用parameterType指定Map接口,SQL中#{key}提取参数 -->
<select id="selectByPriceRange" parameterType="java.util.Map" resultType="com.imooc.mybatis.entity.Goods">
select * from t_goods
where
current_price between #{min} and #{max}
order by current_price
limit 0,#{limt}
</select>
//java中的调用代码:
SqlSession session = MyBatisUtils.openSession();
Map param = new HashMap();
param.put("min", 100);
param.put("max", 500);
param.put("limt", 10);
//selectByPriceRange前面可以不加goods这个namespace,前提是保证你这个sql全局唯一
List<Goods> list = session.selectList("selectByPriceRange", param);
for (Goods goods:list) {
System.out.println(goods.getTitle() + ": " + goods.getCurrentPrice());
}
查询返结果包含跨表字段
<select id="findGoodsMap" resultType="java.util.LinkedHashMap">
select g.* , c.category_name
from t_goods g , t_category c
where g.category_id = c.category_id
</select>
session = MyBatisUtils.openSession();
//session.selectList返回多条数据,session.selectOne()方法返回一条数据
//list集合中存放的是Map类型的数据,即存放的是键值对类型的数据
List<Map> list = session.selectList("goods.selectGoodsMap");
for (Map map : list) {
System.out.println(map);
}
利用LinkedHashMap保存多表关联结果,当resultType为Map时,查询出来的数据是无序的。
MyBatis会将每一条记录包装为LinkedHashMap,键-值对象
key是字段名 value是字段对应的值 , 字段类型根据表结构进行自动判断
优点: 易于扩展,易于使用
缺点: 太过灵活,无法进行编译时检查
ResultMap结果映射
goods.xml文件配置ResultMap结果映射
/**
* Goods类的扩展,DTO,即数据传输对象,用来将sql查询结果映射为Java对象,GoodsDTO承载了多表关联查询的结果
* 所有在sql中出现的字段,都可以在这里找到对应的属性
*/
public class GoodsDTO {
//Goods goods = new Goods(),实例化一个对象,goods即有了在Goods里面创建的许多属性和方法
private Goods goods = new Goods();
private Category category = new Category();
private String test;
public Goods getGoods() {
return goods;
...
}
<!--结果映射的id值来自select标签resultMap,type类型是要映射的java对象-->
<resultMap id="rmGoods" type="com.imooc.mybatis.dto.GoodsDTO">
<!--resultMap中有id标签和result标签,property是类的属性,column是主键字段
id标签,即主键标签,用来设置主键字段和类属性的映射。
result标签,用来设置除了主键以外的其他字段
-->
<!--设置主键字段与属性映射-->
<id property="goods.goodsId" column="goods_id"></id>
<result property="goods.title" column="title"></result>
...
<result property="category.categoryId" column="category_id"></result>
<result property="category.categoryName" column="category_name"></result>
...
</resultMap>
<select id="selectGoodsDTO" resultMap="rmGoods">
select g.* , c.* from t_goods g , t_category c
where g.category_id = c.category_id
</select>
session = MyBatisUtils.openSession();
//session.selectList返回多条数据,session.selectOne()方法返回一条数据
List<GoodsDTO> list = session.selectList("goods.selectGoodsDTO");
for (GoodsDTO g: list) {
System.out.println(g.getGoods().getTitle());
}
作用:ResultMap可以将复杂的查询结果映射为JavaDTO对象,可以轻松的获取到属性,不像map一样容易出错,缺点就是需要额外的设置映射规则
ResultMap常用于保存多表关联结果,是Map对象的替代,Map拥有更好扩展性,ResultMap则拥有更好的可读性
多人协作的时候是有必要的,小项目可以使用map
七、MyBatis数据写入
数据库事务是保证数据操作完整性的基础,MySQL workbench等客户端编写sql语句,执行后,成为事务日志,事务日志通过commit命令再提交到数据库表中
三种写操作
插入 -
<!--新增数据,#{title}自动提取来自Goods实体类中设置好的title属性值-->
<insert id="insert" parameterType="com.imooc.mybatis.entity.Goods" flushCache="true">
INSERT INTO t_goods(title, sub_title, original_cost, current_price, discount, is_free_delivery, category_id)
VALUES (#{title} , #{subTitle} , #{originalCost}, #{currentPrice}, #{discount}, #{isFreeDelivery}, #{categoryId})
<!-- 当前连接中最后产生的id号,获取后添加到Goods对象goodsId属性中 -->
<selectKey resultType="Integer" keyProperty="goodsId" order="AFTER">
select last_insert_id()
</selectKey>
</insert>
SqlSession session = MyBatisUtils.openSession();
Goods goods = new Goods();
//这里没有设置id,在goods.xml文件中通过selectKey标签自动添加到goodsId
goods.setTitle("测试商品");
goods.setSubTitle("测试子标题");
goods.setOriginalCost(200f);
goods.setCurrentPrice(100f);
goods.setDiscount(0.5f);
goods.setIsFreeDelivery(1);
goods.setCategoryId(43);
//insert()方法返回值代表本次成功插入的记录总数
int num = session.insert("goods.insert",goods);
//执行插入数据后,还要通过commit把新增数据的事务日志提交到数据库表中
session.commit();
注意:catch块中加上回滚
//commit提交后,数据库事务日志会清空,如果不为空,则说明异常,需要回滚,从新写入事务日志,再提交数据库
if (session != null) {
session.rollback();
}
注意:sqlSessionFactory.openSession(false);默认SqlSession对自动提交事务数据(commit),设置false代表关闭自动提交,改为手动提交事务数据
更新 -
<update id="update" parameterType="com.imooc.mybatis.entity.Goods">
UPDATE t_goods
SET
title = #{title} ,
sub_title = #{subTitle} ,
original_cost = #{originalCost} ,
current_price = #{currentPrice} ,
discount = #{discount} ,
is_free_delivery = #{isFreeDelivery} ,
category_id = #{categoryId}
WHERE
goods_id = #{goodsId}
</update>
session = MyBatisUtils.openSession();
//先获取739id对应的一条数据,返回的是一个goods对象
Goods goods = session.selectOne("goods.selectById", 739);
//修改id为739的标题
goods.setTitle("测试标题");
//执行更新sql语句,把修改好的goods对象数据传入到数据表中
int num = session.update("goods.update", goods);
//执行更新数据后,还要通过commit把新增数据的事务日志提交到数据库表中
session.commit();
删除 -
<delete id="delete" parameterType="Integer">
delete from t_goods where goods_id = #{value}
</delete>
//执行delete操作,删除id为739的那一条数据
int num = session.delete("goods.delete", 739);
//执行更新数据后,还要通过commit把新增数据的事务日志提交到数据库表中
session.commit();
八、MyBatis预防SQL注入攻击
SQL注入攻击是指攻击者利用SQL语句,导致输入的变成了sql语句的一部分
MyBatis两种传值方式
- ${ }文本替换,直接替换,未经任何转移,直接对SQL文本替换,会产生注入攻击风险,不推荐使用
- #{ }预编译传值,把输入的整体变成了一个字符串,使用预编译传值可以预防SQL注入
<select id="selectByTitle" parameterType="java.util.Map" resultType="com.imooc.mybatis.entity.Goods">
select * from t_goods where title = #{title}
${order}
</select>
session = MyBatisUtils.openSession();
Map param = new HashMap();
/*
${}原文传值
select * from t_goods
where title = '' or 1 =1 or title = '【德国】爱他美婴幼儿配方奶粉1段800g*2罐 铂金版'
*/
/*
#{}预编译
select * from t_goods
where title = "'' or 1 =1 or title = '【德国】爱他美婴幼儿配方奶粉1段800g*2罐 铂金版'"
*/
param.put("title","'' or 1=1 or title='【德国】爱他美婴幼儿配方奶粉1段800g*2罐 铂金版'");
param.put("order" , " order by title desc");
List<Goods> list = session.selectList("goods.selectByTitle", param);
for(Goods g:list){
System.out.println(g.getTitle() + ":" + g.getCurrentPrice());
}
九、MyBatis工作流程
工作流程:mybatis-config.xml—SqlSessionFactory—SqlSession—执行配置好的SQL —SessionClose