通过代码一步步学会Mybatis(一)(施工中...)

项目结构

  • 以下为总的项目结构,仅供更好的认识项目的框架,建议根据下面的内容一步步构造项目结构
    mybatis目录结构

Mybatis数据查询

Mybatis数据查询步骤
  • 创建实体类(entity)
  • 创建Mapper XML
  • 编写<select>SQL标签
  • 开启驼峰命名映射
  • 在batis-config.xml文件中增加<mapper>
  • SqlSession执行select语句

写代码
  • 创建entity实体类包(java→com.imooc.mybatis)
  • 创建Goods类(java→com.imooc.mybatis.entity)
    • 数据类型与数据库对应的表中的字段保持一致
    public class Goods {
        private Integer goodsId;//商品编号
        private String title;//标题
        private String subTitle;//子标题
        private Float originalCost;//原始价格
        private Float currentPrice;//当前价格
        private Float discount;//折扣率
        private Integer isFreeDelivery;//是否包邮 ,1-包邮 0-不包邮
        private Integer categoryId;//分类编号
        private List<GoodsDetail> goodsDetails;
    }
    
    • 生成Get/Set方法
  • 创建映射器mappers文件夹(resources→)
  • 创建goods.xml(resources→mappers),说明实体类和数据库中的表的对应关系
    • 书写<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">
    <mapper namespace="goods">
    </mapper>
    
    • 书写<select>标签
    <!-- namespace: 命名空间,区分不同的sql语句
         id: 下面Sql语句的id,用id代表下面的Sql语句
         resultType: 说明数据返回的对象-->
    <mapper namespace="goods">
      <select id="selectAll" resultType="com.imooc.mybatis.entity.Goods" useCache="false">
            select * from t_goods order by goods_id desc limit 10
      </select>
    </mapper>
    
  • mybatis_config.xml中添加<mapper>标签,声明goods.xml
<configuration>
  <mappers>
    <mapper resource="mappers/goods.xml"/>
  </mappers>
</configuration>
  • MyBatisTestor测试类(test→java→com.imooc.mybatis)中添加测试方法,测试是否查询成功
@Test
public void testSelectAll() throws Exception {
    SqlSession session = null;
    try{
      session = MyBatisUtils.openSession();
      List<Goods> list = session.selectList("goods.selectAll");
      for(Goods g : list){
        System.out.println(g.getTitle());
      }
    }catch (Exception e){
      throw e;
    }finally {
      MyBatisUtils.closeSession(session)
    } 
}
  • mybatis_config.xml中添加驼峰命名设置项,开启驼峰命名转换,将数据库中字段转换为驼峰形式,如:goods_id ==> goodsId
<configuration>
    <settings>
        <!-- goods_id ==> goodsId 驼峰命名转换 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
</configuration>


SQL传参

向SQL语句中动态传入参数

SQL传参分为单参数传递和多参数传递
  • 单参数传递:
    • parameterType=“Integer”
    • 使用parameterType指定参数的数据类型即可,SQL中#{value}提取参数
  • 多参数传递:
    • parameterType=“java.util.Map”
    • 使用parameterType指定Map接口,SQL中#{key}提取参数

写代码
  • 单参数传递,在goods.xml(resources→mappers)中新增<select>查询语句
<mapper>
<!-- 单参数传递,使用parameterType指定参数的数据类型即可,SQL中#{value}提取参数-->
    <select id="selectById" parameterType="Integer" resultType="com.imooc.mybatis.entity.Goods">
        select * from t_goods where goods_id = #{value}
    </select>
</mapper>
  • MyBatisTestor测试类(test→java→com.imooc.mybatis)中添加测试方法,测试是否查询成功
@Test
public void testSelectById() throws Exception {
    SqlSession session = null;
    try{
       session = MyBatisUtils.openSession();
        Goods goods = session.selectOne("goods.selectById" , 1603);// ("命名空间 . sql语句的id",传入的具体参数)
        System.out.println(goods.getTitle());
    }catch (Exception e){
        throw e;
    }finally {
        MyBatisUtils.closeSession(session);
    }
}
  • 多参数传递方式,在goods.xml(resources→mappers)中新增<select>查询语句
<mapper>
 <!-- 多参数传递时,使用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>
</mapper>
  • 在MyBatisTestor测试类(test→java→com.imooc.mybatis)中添加测试方法,测试是否查询成功
@Test
public void testSelectByPriceRange() throws Exception {
    SqlSession session = null;
    try{
        session = MyBatisUtils.openSession();
        Map param = new HashMap();
        param.put("min",100);
        param.put("max" , 500);
        param.put("limt" , 10);
        List<Goods> list = session.selectList("goods.selectByPriceRange", param);
        for(Goods g:list){
            System.out.println(g.getTitle() + ":" + g.getCurrentPrice());
        }
    }catch (Exception e){
        throw e;
    }finally {
        MyBatisUtils.closeSession(session);
    }
}


Mybatis获取多表关联查询结果

之前的查询获得的都是单表的查询结果,resultType能承载所有的数据,这种情况在实际项目开发时是比较少的,大部分企业级应用都是多表关联查询产生复杂的结果集,查询字段会横跨很多张表。单独指定某一个实体无法承载所有表的字段,遇到这种情况该怎么做呢?

  • 利用resultType="java.util.LinkedHashMap"保存多表关联结果
  • MyBatis会将每一条记录包装为LinkedHashMap对象(默认结果为HashMap,获得的结果顺序紊乱,所以强制使用LinkedHashMap)
  • key是字段名 value是字段对应的值 , 字段类型根据表结构进行自动判断
  • 优点: 易于扩展,易于使用
  • 缺点: 太过灵活,无法进行编译时检查

写代码
  • goods.xml(resources→mappers)中新增<select>查询语句
<mapper>
<!-- 利用LinkedHashMap保存多表关联结果
        MyBatis会将每一条记录包装为LinkedHashMap对象
        key是字段名  value是字段对应的值 , 字段类型根据表结构进行自动判断
        优点: 易于扩展,易于使用
        缺点: 太过灵活,无法进行编译时检查
     -->
    <select id="selectGoodsMap" resultType="java.util.LinkedHashMap" flushCache="true">
        select g.* , c.category_name,'1' as test from t_goods g , t_category c
        where g.category_id = c.category_id
    </select>
</mapper>
  • MyBatisTestor测试类(test→java→com.imooc.mybatis)中添加测试方法,测试是否查询成功
@Test
public void testSelectGoodsMap() throws Exception {
    SqlSession session = null;
    try{
        session = MyBatisUtils.openSession();
        List<Map> list = session.selectList("goods.selectGoodsMap");
        for(Map map : list){
            System.out.println(map);
        }
     }catch (Exception e){
        throw e;
    }finally {
        MyBatisUtils.closeSession(session);
    }
}


ResultMap结果映射

Map并不是一个最好的解决方案,因为Map虽然灵活,扩展强大,但是在开发的时候体验并不好,但是如果不使用Map,就会衍生出来一个新的问题,如何用对象的方式,保存关联查询的结果呢?这时就可以使用ResultMap结果映射这个配置来解决这个问题。

ResultMap
  • ResultMap可以将查询结果映射为复杂类型的Java对象
  • ResultMap适用于Java对象保存多表关联结果
  • ResultMap支持对象关联查询等高级特性

写代码

思考:在Mybatis获取多表关联查询结果的案例中通过书写selectGoodsMap来实现了保存关联的查询结果,但是使用对象是LinkedHashMap。如果此时希望将这个结果使用Java对象进行保存,该怎么做?

<select id="selectGoodsMap" resultType="java.util.LinkedHashMap" flushCache="true">
       select g.* , c.category_name,'1' as test from t_goods g , t_category c
       where g.category_id = c.category_id
</select>

分析:对sql语句进行分析,涉及到两个表,分别是t_goods商品信息表以及t_category分类信息表,同时字段也是来自这两张表。对于这种复合的查询结果使用Goods实体对象无法全部承载,所以需要扩展出Java对象进行保存。

  • 创建dto实体类包(java→com.imooc.mybatis)
    用于存放数据传输对象,里面的对象对原始的对象进行扩展,用于数据保存和传递。
  • 创建GoodsDTO类(java→com.imooc.mybatis.dto)
    对于原始Goods实体类进行扩展。
    所有的sql字段都可以在 GoodsDTO中找到对应的属性。
//Data Transfer Object--数据传输对象
public class GoodsDTO {
    private Goods goods = new Goods();
    private Category category = new Category();
    private String test;

    public Goods getGoods() {
        return goods;
    }

    public void setGoods(Goods goods) {
        this.goods = goods;
    }

    public Category getCategory() {
        return category;
    }

    public void setCategory(Category category) {
        this.category = category;
    }

    public String getTest() {
        return test;
    }

    public void setTest(String test) {
        this.test = test;
    }
}
  • 创建Category实体类(java→com.imooc.mybatis.entity)
public class Category {
    private Integer categoryId;
    private String categoryName;
    private Integer parentId;
    private Integer categoryLevel;
    private Integer categoryOrder;
}
  • 对其添加Getter/Setter方法

问题:如何让Mybatis对其进行对应赋值?

  • goods.xml(resources→mappers)中新增<select>查询语句,在resultMap进行结果映射
<mapper>
<!--结果映射-->
    <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="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"/>
    </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
    </select>
</mapper>
  • 在MyBatisTestor测试类(test→java→com.imooc.mybatis)中添加测试方法,测试是否查询成功
@Test
public void testSelectGoodsDTO() throws Exception {
    SqlSession session = null;
    try{
        session = MyBatisUtils.openSession();
        List<GoodsDTO> list = session.selectList("goods.selectGoodsDTO");
        for (GoodsDTO g : list) {
            System.out.println(g.getGoods().getTitle());
        }
    }catch (Exception e){
        throw e;
    }finally {
        MyBatisUtils.closeSession(session);
    }
}
  • 执行结果可以看到,GoodsDTO获取到了goods、category和test的所有对象,sql中所有的字段都被承载进来
    GoodsDTO获取的对象


MyBatis数据插入操作

数据库事务
是保证数据操作完整性的基础
Mybatis写操作包含三种

  • 插入 - <insert>
  • 更新 - <update>
  • 删除 - <delete>

写代码
  • goods.xml(resources→mappers)中新增<insert>插入语句
    • parameterType指向实体类(Goods)
    • 主键回填:当执行完<insert>的sql语句后,会自动执行last_insert_id()这个sql语句,将得到的值回填到Goods的goodId属性中
      • resultType: 承载以下sql语句的返回值类型
      • keyProperty: 需要对应的Goods中的goodsId主键的属性
      • order: 执行顺序,执行语句执行完了以后执行这条语句
<mapper>
<!--flushCache="true"在sql执行后强制清空缓存-->
    <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})
    <!--主键回填
        resultType: 承载以下sql语句的返回值类型
        keyProperty: 需要对应的Goods中的goodsId主键的属性
        order: 执行顺序,执行语句执行完了以后执行这条语句-->
      <selectKey resultType="Integer" keyProperty="goodsId" order="AFTER">
          <!--用于获取当前连接最后产生的id号-->
          select last_insert_id()
      </selectKey>
    </insert>
</mapper>
  • MyBatisTestor测试类(test→java→com.imooc.mybatis)中添加测试方法,测试是否插入成功
@Test
public void testInsert() throws Exception {
    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;
    }finally {
        MyBatisUtils.closeSession(session);
    }
}
  • 执行结果可以看到,goods获取到了数据库回填的goodsId,数据成功插入数据库中
    goods
    数据库信息


selectKey与useGeneratedKeys的区别

两只都是用于在插入数据时将最新的主键值进行返回

selectKey:

  • 需要明确编写获取最新主键的SQL语句
  • 适用所有的关系型数据库

useGeneratedKeys:

  • 会自动根据驱动生成对应SQL语句
  • 只支持“自增主键”类型的数据库
  • 用selectKey方式进行回填
<insert id="insert"  parameterType="com.imooc.mybatis.entity.Goods">
  INSERT INTO SQL语句
  <selectKey resultType="Integer" keyProperty="goodId" order="AFTER">
    select last_insert_id()
  </selectKey>
</insert>
  • 用useGeneratedKeys方式进行回填
<insert id="insert"
    parameterType="com.imooc.mybatis.entity.Goods"
    useGeneratedKeys="true"
    keyProperty="goodId"
    keyColumn="goods_id">
  INSERT INTO SQL语句
</insert>


更新与删除操作

写代码
  • goods.xml(resources→mappers)中新增<update>更新语句
<mapper>
    <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>
</mapper>
  • MyBatisTestor测试类(test→java→com.imooc.mybatis)中添加测试方法,测试是否更新成功
@Test
public void testUpdate() throws Exception {
    SqlSession session = null;
    try{
        session = MyBatisUtils.openSession();
        Goods goods = session.selectOne("goods.selectById", 739);
        goods.setTitle("更新测试商品");
        int num = session.update("goods.update" , goods);
        session.commit();//提交事务数据
    }catch (Exception e){
        if(session != null){
            session.rollback();//回滚事务
        }
        throw e;
    }finally {
        MyBatisUtils.closeSession(session);
    }
}
  • 执行结果可以看到,数据更新成功
    数据库信息
  • goods.xml(resources→mappers)中新增<delete>删除语句
<mapper>
    <delete id="delete" parameterType="Integer">
        delete from t_goods where goods_id = #{value}
    </delete>
</mapper>
  • MyBatisTestor测试类(test→java→com.imooc.mybatis)中添加测试方法,测试是否删除成功
@Test
public void testDelete() throws Exception {
    SqlSession session = null;
    try{
        session = MyBatisUtils.openSession();
        int num = session.delete("goods.delete" , 740);
        session.commit();//提交事务数据
    }catch (Exception e){
        if(session != null){
            session.rollback();//回滚事务
        }
        throw e;
    }finally {
        MyBatisUtils.closeSession(session);
    }
}
  • 执行结果可以看到,goods_id为740的数据删除成功
    数据库信息


预防SQL注入攻击

SQL注入攻击是指攻击者利用SQL漏洞,绕过系统约束,越权获取数据的攻击方式

Mybatis两种传值方式

  • ${}文本替换,未经任何处理对SQL文本替换
  • #{}预编译传值,使用预编译传值可以预防SQL注入


MyBatis工作流程

应用
mybatis-config.xml全局设置项
环境配置
mapper声明
SqlSession Factory
build
SqlSession
FactoryBuilder
SqlSessionmapper.xml
insert|update|delete|selectcommit
rollback
Session Close
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值