1.Mybatis简介
持久层框架,使用xml将sql与程序解耦,便于维护,是jdbc的延伸。
2.开发流程
引入Mybatis依赖
-
创建核心配置文件
在resources下面创建mybatis-config.xml文件,配置数据库环境信息<configuration> <!-- 设置默认指向的数据库--> <environments default="dev"> <!-- 配置环境,不同的环境不同的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/babytun?useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="111111"/> </dataSource> </environment> <environment id="prd"> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/babytun?useUnicode=true&characterEncoding=UTF-8"/> <property name="username" value="root"/> <property name="password" value="111112"/> </dataSource> </environment> </environments> </configuration> <mappers> <!-- 对应的sql mapper映射文件--> <mapper resource="mappers/goods.xml"/> </mappers> ------goods.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"> <!--命名空间类似于java中的包 不同表或者功能的sql语句,用于区分不同的sql语句--> <mapper namespace="goods"> <!-- 同一个namespqce下的id要唯一,resultType返回的结果类型,进行包装--> <select id="selectAll" resultType="org.example.entity.Goods"> select * from t_goods order by goods_id desc limit 10 </select> <!-- select单参数传值--> <select id="selectById" parameterType="Integer" resultType="org.example.entity.Goods"> select * from t_goods where goods_id = #{value} </select> <!-- select多个参数传值--> <select id="selectByPriceRange" parameterType="java.util.Map" resultType="org.example.entity.Goods"> select * from t_goods where current_price between #{min} and #{max} order by current_price limit 0,#{limit} </select> <!--利用LinkedHashMap保存多表关联结果 MyBatis会将每一条记录包装成为Map对象 key是字段名 value是字段对应的值,字段类型根据标结果进行自动判断 优点:易于扩展,易于使用 缺点:太过灵活,无法进行编译检查 --> <select id="selectGoodsMap" resultType="java.util.LinkedHashMap"> select g.*, c.category_name,'1' as test from t_goods g,t_category c where g.category_id = c.category_id limit 10 </select> </mapper> ---------对应实体类--------- //查询所有 List<Goods> list = session.selectList("goods.selectAll"); //单参数传值 Goods goods = session.selectOne("goods.selectById",1602); //多个参数传值 //可以不加namespace,确保id是唯一即可 List<Goods> list = session.selectList("selectByPriceRange",param); //多表关联 List<Map> list = session.selectList("goods.selectGoodsMap");
创建实体(Entity)类
创建Mapper映射文件
初始化SessionFactory
SessionFactory是Mybatis的核心对象,用于初始化Mybatis,创建SqlSession对象,是全局唯一的
- 利用SqlSession对象操作数据
SqlSession是Mybatis操作数据库的核心对象,使用JDBC方式与数据库交互,提供了CRUD对应的方法
2.1.MyBatis数据查询步骤
- 创建实体类(Entity)
- 创建Mapper XML
- 编写<select>SQL标签
- 开启驼峰命名映射
- 新增<mapper>
- SqlSession执行select语句
3.ResultMap结果映射
- ResulMap可以将查询结果映射为复杂类型的Java对象
- ResultMap适用于Java对象保存多表关联结果
- ResultMap支持对象关联查询登高级特性
xml中返回类型标签用的是resultMap
,并且创建DTO
<!-- 结果映射-->
<resultMap id="rmGoods" type="org.example.dto.GoodsDTO">
<!-- 设置主键字段与属性映射-->
<id property="goods.goodsId" column="goods_id"></id>
<!-- 设置非主键字段与属性映射-->
<result property="goods.originalCost" column="original_cost"></result>
<result property="goods.title" column="title"></result>
<result property="goods.subtitle" column="subtitle"></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="category.categoryId" column="category_id"></result>
<result property="category.categoryName" column="category_name"></result>
<result property="category.parentId" column="parent_id"></result>
<result property="category.categoryLevel" column="category_level"></result>
<result property="category.categoryOrder" column="category_order"></result>
<result property="test" column="test"></result>
</resultMap>
<select id="selectGoodsDTO" resultMap="rmGoods">
select g.*, c.*,'1' as test from t_goods g,t_category c where g.category_id = c.category_id limit 10
</select>
----------------------------------
public class GoodsDTO {
private Goods goods;
private Category category;
-----------------------------------
List<GoodsDTO> list = session.selectList("goods.selectGoodsDTO");
for (int i = 0; i < list.size(); i++) {
GoodsDTO goodsDTO = list.get(i);
System.out.print(goodsDTO.getGoods().getTitle() + "------");
System.out.println(goodsDTO.getCategory().getCategoryName());
}
4.数据插入
4.1.数据库事务
是保证数据库操作完整性的基础
- commit
- rollback
4.2.数据库写操作
4.2.1.插入-insert
使用<insert>标签,如果想要获得插入的id,可以使用
-
<selectKey>标签查询插入后的主键id
--goods.xml <!-- 数据插入--> <insert id="insert" parameterType="org.example.entity.Goods"> 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复制给goodsId <selectKey resultType="Integer" keyProperty="goodsId" order="AFTER"> select last_insert_id() </selectKey> </insert> --调用 @Test public void testInsert() { SqlSession session = null; try { session = MyBatisUtils.openSession(); Goods goods = new Goods(); 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); //提交事务 session.commit(); System.out.println(goods.getGoodsId()); }catch (Exception e) { if (session != null){ session.rollback();//回滚事务 } throw e; } MyBatisUtils.closeSession(session); }
-
useGeneratedKeys属性
--goods.xml <insert id="insert" parameterType="org.example.entity.Goods" useGeneratedKeys="true" keyProperty="goodsId" keyColumn="goods_id" > 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}) </insert> --调用 @Test public void testInsert() { SqlSession session = null; try { session = MyBatisUtils.openSession(); Goods goods = new Goods(); 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); //提交事务 session.commit(); System.out.println(goods.getGoodsId()); }catch (Exception e) { if (session != null){ session.rollback();//回滚事务 } throw e; } MyBatisUtils.closeSession(session); }
对比:
- selectKey标签是通用方案,适用于所有数据库,但是编写麻烦
- useGeneratedKeys属性只支持“自增主键”数据库,使用简单(Oracle不支持自增主键)
4.2.2.更新-update
-
goods.xml
<update id="update" parameterType="org.example.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>
-
测试方法
@Test public void testUpdate() { SqlSession session = null; try { session = MyBatisUtils.openSession(); Goods goods = session.selectOne("goods.selectById", 739); goods.setTitle("更新测试商品"); //insert方法返回值代表本次成功插入的记录数 int num = session.update("goods.update", goods); //提交事务 session.commit(); System.out.println(goods.getGoodsId()); }catch (Exception e) { if (session != null){ session.rollback();//回滚事务 } throw e; } MyBatisUtils.closeSession(session); }
4.2.3.删除-delete
-
goods.xml
<!-- 删除操作--> <delete id="delete" parameterType="Integer"> DELETE FROM t_goods WHERE goods_id = #{value} </delete>
-
测试类
@Test public void testDelete() { SqlSession session = null; try { session = MyBatisUtils.openSession(); //insert方法返回值代表本次成功插入的记录数 int num = session.delete("goods.delete", 739); //提交事务 session.commit(); }catch (Exception e) { if (session != null){ session.rollback();//回滚事务 } throw e; } MyBatisUtils.closeSession(session); }
5.SQL注入
SQL注入是指攻击者利用SQL漏洞,绕过系统约束,越权获取数据的攻击方式
SQL代码:
"select * from a where name = '" + name + "'";
正常情况:
name:张三 -》 select * from a where name = '张三';
SQL注入攻击:
name:' or 1=1 or 1= '
select * from a where name = ' '' or 1=1 or name='' ';
则可查询所有数据。
原因:
${}是文本替换,未经过任何处理对SQL文本替换,我们需要使用#{}预编译传值,使用预编译传值可以预防SQL注入
6.MyBatis工作流程
应用pom导入mybatis,mysql-connector-java依赖创建mybatis-config.xml,全局设置项,环境配置,mapper声明,通过SqlSessionFactoryBuilder创建全局唯一的SqlSessionFactory,通过SqlSessionFactory创建SqlSession,解析mapper.xml,执行对应的sql语句,数据修改时候需要调用commit方法,最后需要close对应的session。