MyBatis入门

1.框架

1.1.概念

  • 框架是可被应用开发者定制的应用骨架
  • 框架是一种规则,保证开发者遵循相同的方式开发程序
  • 框架提倡”不要重复造轮子”,对基础功能进行封装

1.2.优点

  • 极大提高了开发效率
  • 统一的编码规则,利于团队管理
  • 灵活配置的应用,拥有更好的维护性

2.MyBatis框架

中文官网

  • MyBatis是优秀的持久层框架
  • MyBatis使用XML将SQL与程序解耦,便于维护
  • MyBatis学习简单,执行高效,是JDBC的延伸

开发流程

1.引入MyBatis依赖
2.创建核心配置文件
3.创建实体(Entity)类
4.创建Mapper映射文件
5.初始化SessionFactory
6.利用SqlSession对象操作数据

3.引入MyBatis依赖

使用IDEA新建一个Maven项目
在pom.xml文件中加入依赖(版本号随自己定)

<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis</artifactId>
  <version>3.5.3</version>
</dependency>

如果连接mysql数据库,我们还需要一份JDBC的依赖

<dependency>
	<groupId>mysql</groupId>
	<artifactId>mysql-connector-java</artifactId>
	<version>5.1.48</version>
</dependency>

4.创建核心配置文件

在项目src/main/resources目录下创建配置文件
一般命名为mybatis-config.xml,包含以下内容

  • MyBatis采用XML格式配置数据库环境信息
  • MyBaits环境配置标签
  • environment包含数据库驱动、URL、用户名与密码

例如

<!--配置环境,不同的环境不同的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>

完整写法

<?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>
    <!--设置默认指向的数据库-->
    <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"/>
                <!-- &amp;为 & 的转义写法 -->
                <property name="url" value="jdbc:mysql://localhost:3306/babytun?useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
        <!--可以选择配置环境,不同的环境不同的id名字-->
        <environment id="prd">
            <!-- 采用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/jdbc?useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="root"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

environment只可以配置一个数据库,每个配置好的environment可以当做一个数据源,而environments标签用于数据源的配置,可以配置多个数据源。

5.SqlSessionFactory对象和SqlSession对象

5.1.SqlSessionFactory对象

  • SqlSessionFactory是MyBatis的核心对象
  • 用于初始化MyBatis,创建SqlSession对象
  • 保证SqlSessionFactory在应用中全局唯一

5.2.SqlSession对象

  • SqlSession是MyBatis操作数据库的核心对象
  • SqlSession使用JDBC方式与数据库交互
  • SqlSession对象提供了数据表CRUD对应方法

1.检测mybatis-config.xml是否配置正确

在src/test/下创建一个测试类(用Junit运行测试)

package com.lhj;

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.Reader;
import java.sql.Connection;

public class MyBatisTestor {
    @Test
    public void test() throws Exception {
        //利用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();
            //创建数据库连接(测试用)一般不使用
            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();
            }
        }
    }
}

2.初始化工具类MyBatisUtils——保证SqlSessionFactory对象全局为一

在src/main/java下新建包和工具类

package com.lhj.mybatis.utils;

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 java.io.IOException;
import java.io.Reader;

/**
 * MyBatisUtils工具类,创建全局唯一的SqlSessionFactory对象
 */
public class MyBatisUtil {
    //利用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();
            //初始化错误时,通过抛出异常ExceptionInInitializerError通知调用者
            throw new ExceptionInInitializerError(e);
        }
    }

    /**
     * openSession 创建一个新的SqlSession对象
     *
     * @return SqlSession对象
     */
    public static SqlSession openSession() {
        //默认SqlSession对自动提交事务数据(commit)
        //设置false代表关闭自动提交,改为手动提交事务数据
        return sqlSessionFactory.openSession();
    }

    /**
     * 释放一个有效的SqlSession对象
     *
     * @param session 准备释放SqlSession对象
     */
    public static void closeSession(SqlSession session) {
        if (session != null) {
            session.close();
        }
    }
}

3.MyBatis数据查询

MyBatis数据查询步骤

使用的数据库建库脚本:提取码:gjh7
主要数据表为:
在这里插入图片描述

1.创建实体类(Entity)

在src/main/java/com/lhj/mybatis/entity/目录下新建一个goods.java实体类

package com.lhj.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;//分类编号

    public Integer getGoodsId() {
        return goodsId;
    }

    public void setGoodsId(Integer goodsId) {
        this.goodsId = goodsId;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getSubTitle() {
        return subTitle;
    }

    public void setSubTitle(String subTitle) {
        this.subTitle = subTitle;
    }

    public Float getOriginalCost() {
        return originalCost;
    }

    public void setOriginalCost(Float originalCost) {
        this.originalCost = originalCost;
    }

    public Float getCurrentPrice() {
        return currentPrice;
    }

    public void setCurrentPrice(Float currentPrice) {
        this.currentPrice = currentPrice;
    }

    public Float getDiscount() {
        return discount;
    }

    public void setDiscount(Float discount) {
        this.discount = discount;
    }

    public Integer getIsFreeDelivery() {
        return isFreeDelivery;
    }

    public void setIsFreeDelivery(Integer isFreeDelivery) {
        this.isFreeDelivery = isFreeDelivery;
    }

    public Integer getCategoryId() {
        return categoryId;
    }

    public void setCategoryId(Integer categoryId) {
        this.categoryId = categoryId;
    }
}

2.创建Mapper XML

在src/main/resources/目录下创建目录mappers,并在目录下创建一个goods.xml,用来建立实体类和数据表的映射关系

3.编写<select>SQL标签

<?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">
    <select id="selectAll" resultType="com.lhj.mybatis.entity.Goods">
        SELECT * FROM t_goods ORDER BY goods_id DESC LIMIT 10
    </select>
</mapper>

namespace:可以用来划分一些列的<select>的标签,可以当java中的包来理解
id:用来标识不同的<select>
resultType:把执行SQL语句后得到的数据全部包装成的对象,实体类与表结构字段一致时可以自动封装数据到实体类中

id在一个namespace中必须为唯一,在不同的namespace中id可以相同
<select>标签内编写标准的SQL语句

4.开启驼峰命名映射

数据表字段,一般都用下划线分隔,但是创建实体类属性要采用驼峰命名法,导致实体类属性和数据表字段不一致从而无法获得值
此时有几种解决问题的方法,开启驼峰命名映射,或者在Mapper XML中使用<resultMap>结果映射
开启驼峰命名法:
使用前提:数据库表设计按照规范“字段名中各单词使用下划线"_“划分”;
实现效果:用”_"线分割后的第二个字母,去掉下划线后大写。

<configuration>
	<settings>
        <!-- goods_id ==> goodsId 驼峰命名转换 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
    ......
<configuration>

5.新增 mapper标签

需要在项目src/main/resources/mybatis-config.xml文件中的<configuration>中对goods.xml文件进行申明

	......
	<dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url"
                          value="jdbc:mysql://localhost:3306/jdbc?useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
	<!--上文内容-->
	<mappers>
        <mapper resource="mappers/goods.xml"/>
    </mappers>
	<!--下文内容-->
</configuration>

6.SqlSession执行select语句

在测试类中编写以下方法进行打印输出

@Test
    public void testSelectAll() throws Exception {
        SqlSession sqlSession = null;
        try {
            sqlSession = MyBatisUtil.openSession();
            List<Goods> goods = sqlSession.selectList("goods.selectAll");
            for (Goods i : goods) {
                System.out.println(i.getTitle());
            }
        } catch (Exception e) {
            throw e;
        } finally {
            MyBatisUtil.closeSession(sqlSession);
        }
    }

Mybatis的SQL传参

传入单个参数

如果要执行的查询需要动态传入一个参数。

<mapper namespace="goods">
	<!-- 单参数传递,使用parameterType指定参数的数据类型即可,SQL中#{value}提取参数-->
	<select id="selectById" parameterType="Integer" resultType="com.lhj.mybatis.entity.Goods">
        SELECT *FROM t_goods WHERE goods_id= #{value}
	</select>
</mapper>

parameterType:设置传入参数数据的类型
#{value}:传入一个参数时的固定写法,在调用时会自动将参数填入此处

传入方式:

@Test
    public void testSelectById() throws Exception {
        SqlSession sqlSession = null;
        try {
            sqlSession = MyBatisUtil.openSession();
            Goods goods = sqlSession.selectOne("goods.selectById", 1602);
            System.out.println(goods.getTitle());
        } catch (Exception e) {
            throw e;
        } finally {
            MyBatisUtil.closeSession(sqlSession);
        }
    }

传入多个参数

多参数的传入,要把parameterType设置为键值对的Map格式,#{ }中的内容只要为键名就可以获取到值。

<mapper namespace="goods">
	<!-- 多参数传递时,使用parameterType指定Map接口,SQL中#{key}提取参数 -->
	<select id="selectByPriceRange" parameterType="java.util.Map" resultType="com.lhj.mybatis.entity.Goods">
        SELECT * FROM t_goods WHERE current_price BETWEEN #{min} AND #{max} ORDER BY current_price LIMIT 0,#{limit}
    </select>
</mapper>

传入方式

	@Test
    public void testSelectByPriceRange() throws Exception {
        SqlSession sqlSession = null;
        try {
            sqlSession = MyBatisUtil.openSession();
            Map map = new HashMap();
            map.put("min", 100);
            map.put("max", 500);
            map.put("limit", 10);
            List<Goods> goods = sqlSession.selectList("goods.selectByPriceRange", map);
            for (Goods i : goods) {
                System.out.println(i.getTitle() + "\t" + i.getCurrentPrice());
            }
        } catch (Exception e) {
            throw e;
        } finally {
            MyBatisUtil.closeSession(sqlSession);
        }
    }

MyBatis获取多表关联查询结果

将resultType类型设置为Map
编写<select>SQL标签

<mapper namespace="goods">
	<select id="selectGoodsMap" resultType="java.util.Map">
        SELECT t_goods.*,t_category.category_name FROM t_goods,t_category WHERE t_goods.category_id=t_category.category_id
    </select>
</mapper>

执行select语句

	@Test
    public void testSelectGoodsMap() throws Exception {
        SqlSession sqlSession = null;
        try {
            sqlSession = MyBatisUtil.openSession();
            List<Map> list = sqlSession.selectList("goods.selectGoodsMap");
            for (Map i : list) {
                System.out.println(i);
            }
        } catch (Exception e) {
            throw e;
        } finally {
            MyBatisUtil.closeSession(sqlSession);
        }
    }

返回输出的Map中字段是混乱的,所以要把输出的类型定义为LinkedHashMap,才能使字段按顺序输出。

<mapper>
	<!-- 利用LinkedHashMap保存多表关联结果
        MyBatis会将每一条记录包装为LinkedHashMap对象
        key是字段名  value是字段对应的值 , 字段类型根据表结构进行自动判断
        优点: 易于扩展,易于使用
        缺点: 太过灵活,无法进行编译时检查
     -->
    <select id="selectGoodsMap" resultType="java.util.LinkedHashMap">
        SELECT t_goods.*,t_category.category_name FROM t_goods,t_category WHERE t_goods.category_id=t_category.category_id
    </select>
</mapper>

ResultMap结果映射

  • ResultMap可以实现将查询结果映射为复杂类型的对象
  • ResultMap常用于保存多表关联结果,是Map对象的替代
  • Map拥有更好扩展性,ResultMap则拥有更好的可读性

编写<select>SQL标签

<mapper namespace="goods">
	<select id="selectGoodsDTO" resultMap="">
        SELECT t_goods.*,t_category.category_name,'1' AS test FROM t_goods,t_category WHERE t_goods.category_id=t_category.category_id
    </select>
</mapper>

新增加了一个输出时值都为1的text字段。select的resultMap属性还为空。
我们需要扩展一个Java对象来对输出结果进行保存。

新建一个包/src/main/java/com/lhj/mybatis/dto , dto包内一般放特殊的java对象,叫做:数据传输对象,dto内对象都是对原始的entity的对象进行拓展,用于数据的保存和传递
在dto包下新建GoodsDTO.java用来对Goods实体类进行扩展

package com.lhj.mybatis.com.lhj.mybatis.dto;

import com.lhj.mybatis.entity.Goods;

public class GoodDTO {
    private Goods goods;
    private String categoryName;
    private String test;

    public Goods getGoods() {
        return goods;
    }

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

    public String getCategoryName() {
        return categoryName;
    }

    public void setCategoryName(String categoryName) {
        this.categoryName = categoryName;
    }

    public String getTest() {
        return test;
    }

    public void setTest(String test) {
        this.test = test;
    }
}

继续编写goods.xml
在<mapper>标签内添加<resultMap>标签用来配置结果映射

<mapper>
	<resultMap id="rmGoods" type="com.lhj.mybatis.dto.GoodDTO">
        <!--设置主键字段与属性映射-->
        <id property="goods.goodsId" column="goods_id"></id>
        <!--设置非主键字段与属性映射-->
        <result property="goods.title" column="title"></result>
        <result property="goods.subTitle" column="sub_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>
</mapper>

resultMap的
id:resultMap的唯一标识符,在<select>标签的resultMap属性中使用,使这个<select>标签查询结果使用指定resultMap结果映射的规则进行数据的赋值
type:来指定数据转化为某个dto的对象

<resultMap>标签内的子标签:
<id>标签:类似于主键,必须存在。
<result>标签:用来填写除了id之外的其他字段
property属性:表示属性名,如果是dto的类属性的属性,则用“.”分割上下层关系
column属性:表示数据表字段名
执行select语句

	@Test
    public void testSelectGoodsMap() throws Exception {
        SqlSession sqlSession = null;
        try {
            sqlSession = MyBatisUtil.openSession();
            List<Map> list = sqlSession.selectList("goods.selectGoodsMap");
            for (Map i : list) {
                System.out.println(i);
            }
        } catch (Exception e) {
            throw e;
        } finally {
            MyBatisUtil.closeSession(sqlSession);
        }
    }
扩展

我们希望在获取了t_category表的categoryName字段之外还想获取其他字段信息。
我们可以参考t_goods表的做法,
首先在src/main/java/com/lhj/mybatis/entity/目录下新建一个goods.java实体类

package com.lhj.mybatis.entity;

public class Category {
    private Integer categoryId;
    private String categoryName;
    private Integer parentId;
    private Integer categoryLevel;
    private Integer categoryOrder;

    public Integer getCategoryId() {
        return categoryId;
    }

    public void setCategoryId(Integer categoryId) {
        this.categoryId = categoryId;
    }

    public String getCategoryName() {
        return categoryName;
    }

    public void setCategoryName(String categoryName) {
        this.categoryName = categoryName;
    }

    public Integer getParentId() {
        return parentId;
    }

    public void setParentId(Integer parentId) {
        this.parentId = parentId;
    }

    public Integer getCategoryLevel() {
        return categoryLevel;
    }

    public void setCategoryLevel(Integer categoryLevel) {
        this.categoryLevel = categoryLevel;
    }

    public Integer getCategoryOrder() {
        return categoryOrder;
    }

    public void setCategoryOrder(Integer categoryOrder) {
        this.categoryOrder = categoryOrder;
    }
}

修改GoodDTO

package com.lhj.mybatis.dto;

import com.lhj.mybatis.entity.Category;
import com.lhj.mybatis.entity.Goods;

public class GoodDTO {
    private Goods goods;
    private Category 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;
    }
}

修改goods.xml的<resultMap>和<select>标签

<mapper>
	<select id="selectGoodsDTO" resultMap="rmGoods">
        SELECT t_goods.*,t_category.*,'1' AS test FROM t_goods,t_category WHERE t_goods.category_id=t_category.category_id
    </select>

    <resultMap id="rmGoods" type="com.lhj.mybatis.dto.GoodDTO">
        <!--设置主键字段与属性映射-->
        <id property="goods.goodsId" column="goods_id"></id>
        <!--设置非主键字段与属性映射-->
        <result property="goods.title" column="title"></result>
        <result property="goods.subTitle" column="sub_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>
</mapper>

4.MyBatis数据写入

数据库事务

数据库事务是保证数据操作完整性的基础。
所有来自客户端的增加,删除,修改命令,先交给事务执行,当客户端向数据库发出"commit"命令时,事务日志才会将事务日志中的指令全部按顺序执行,指令全部执行完毕之后,事务日志中的指令全部清空。如果事务日志中有命令未执行成功,则数据库会进行数据回滚,数据回滚到事务日志执行之前。

MyBatis写操作包含三种

先设置SqlSession不自动提交事务数据
修改MyBatisUtil.java

	public static SqlSession openSession(){
        //默认SqlSession对自动提交事务数据(commit)
        //设置false代表关闭自动提交,改为手动提交事务数据
        return sqlSessionFactory.openSession(false);
    }

插入<insert>

在goods.xml文件中编写<insert>标签

<mapper>
    <insert id="insert" parameterType="com.lhj.mybatis.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})
        <selectKey resultType="Integer" keyProperty="goodsId" order="AFTER">
            SELECT last_insert_id()
        </selectKey>
    </insert>
</mapper>

<insert>标签的
id:插入标签的唯一标识符
parameterType:传入数据的对象类型

<selectKey>标签将insert的数据的主键返回,直接拿到新增数据的主键,以便后续使用。
<selectKey>标签的:
resultType:指定主键类型
keyProperty:对应的model中的主键的属性名
resultType:AFTER 表示 SELECT LAST_INSERT_ID() 在insert执行之后执行,多用与自增主键,BEFORE 表示 SELECT LAST_INSERT_ID() 在insert执行之前执行,这样的话就拿不到主键了,这种适合那种主键不是自增的类型

执行insert语句

	@Test
    public void Insert() throws Exception {
        SqlSession sqlSession = null;
        try {
            sqlSession = MyBatisUtil.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 = sqlSession.insert("goods.insert", goods);
            sqlSession.commit();//提交事务数据
            System.out.println(goods.getGoodsId());//获取<selectKey>标签返回的新增数据的主键
        } catch (Exception e) {
            if (sqlSession != null) {
                sqlSession.rollback();//回滚事务
            }
            throw e;
        } finally {
            MyBatisUtil.closeSession(sqlSession);
        }
    }
获取新增数据的主键的两种方式:selectKeyuseGeneratedKeys

selectKey的写法

<mapper>
	<insert id="insert" parameterType="com.lhj.mybatis.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})
        <selectKey resultType="Integer" keyProperty="goodsId" order="AFTER">
            SELECT last_insert_id()
        </selectKey>
    </insert>
</mapper>

useGeneratedKeys的写法

<mapper>
	<insert id="insert" parameterType="com.lhj.mybatis.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>
</mapper>

useGeneratedKeys:是否开启useGeneratedKeys功能,默认值为:false
keyProperty:对应主键的属性名
keyColumn:对应数据库的主键的字段名
执行语句的java语句写法一致。

区别
  • selectKey标签需要明确编写获取最新主键的SQL语句
  • selectKey适用于所有关系型数据库
  • useGeneratedKeys属性会自动根据驱动生成对应SQL语句
  • useGeneratedKeys只支持“自增主键”类型的数据库

不支持自增主键的数据库,例如Oracle中selectKey的用法

<mapper>
	<insert id="insert" parameterType="com.lhj.mybatis.entity.Goods">
        INSERT INTO SQL语句
        <selectKey resultType="Integer" keyProperty="goodsId" order="BEFORE">
            SELECT seq_goods.nextval as id from dual
        </selectKey>
    </insert>
</mapper>

seq_goods为Oracle序列,nextval用来获取下一个序列值,

总结
  • selectKey标签是通用方案,适用于所有数据库,但编写麻烦
  • useGeneratedKeys只支持“自增主键”类型的数据库,使用简单(优先推荐)

更新<update>

在goods.xml文件中编写<update>标签

<mapper>
	<update id="update" parameterType="com.lhj.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>

执行update语句

	@Test
    public void testUpdate() throws Exception {
        SqlSession session = null;
        try{
            session = MyBatisUtil.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 {
            MyBatisUtil.closeSession(session);
        }
    }

使用了之前案例中的SQL传入单个参数案例中的selectById
执行返回的num的值是当前操作更新数据的条数

删除<delete>

在goods.xml文件中编写<delete>标签

<mapper>
	<delete id="delete" parameterType="Integer">
        DELETE FROM t_goods WHERE goods_id = #{value}
    </delete>
</mapper>

执行delete语句

	public void testUpdate() throws Exception {
        SqlSession session = null;
        try{
            session = MyBatisUtil.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 {
            MyBatisUtil.closeSession(session);
        }
    }

执行返回的num的值是当前操作删除数据的条数

5.MyBatis预防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 1='' ->  select * from a

MyBatis两种传值方式

  • ${ }文本替换,未经任何处理对SQL文本替换,一般用来SQL语句的拼接
  • #{ }预编译传值,使用预编译传值可以预防SQL注入,安全性高

实例

在goods.xml文件中编写<select>标签

<mapper>
	<select id="selectByTitle" parameterType="java.util.Map" resultType="com.imooc.mybatis.entity.Goods">
        SELECT * FROM t_goods WHERE title = #{title} ${order}
    </select>
</mapper>

执行delete语句

	@Test
    public void testSelectByTitle() throws Exception {
        SqlSession session = null;
        try{
            session = MyBatisUtil.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());
            }
        }catch (Exception e){
            throw e;
        }finally {
            MyBatisUtil.closeSession(session);
        }
    }

6.MyBatis工作流程

在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值