MyBatis之入门知识

MyBatis框架简介

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

MyBatis官方文档

https://mybatis.org/mybatis-3/zh/index.html

MyBatis开发流程
  1. 引入MyBatis依赖
  2. 创建核心配置文件
  3. 创建实体(Entity)类
  4. 创建Mapper映射文件
  5. 初始化SessionFactory
  6. 利用SqlSession对象操作数据
JUnit4单元测试工具

单元测试是指对软件中的最小可测试单元进行检查和验证
测试用例是指编写一段代码对已有功能(方法)进行校验
JUnit 4是Java中最著名的单元测试工具,主流IDE内置支持

JUnit4使用方法

引入JUnit Jar包或增加Maven依赖
编写测试用例验证目标方法是否正确运行
在测试用例上增加@Test注解开始单元测试

JUnit4测试案例
<!--单元测试添加依赖代码-->
<dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
// 四则运算功能实现类
public class Calculator {
    // 加法运算
    public int add(int a, int b){
        return a + b;
    }
    // 减法运算
    public int subtract(int a, int b){
        return a - b;
    }
    // 乘法运算
    public int multiply(int a, int b){
        return a * b;
    }
    // 除法运算
    public float divide(int a, int b){
        if (b==0){
            throw new ArithmeticException("除数不能为0");
        }
        return (a*1f) / b;
    }
}
// 测试类
public class CalculatorTest {
    private Calculator cal = new Calculator();
    // 命名规则:
    // 1. 与原方法保持一致
    // 2. 在原方法前增加test前缀
    @Test
    public void testAdd(){
        int result = cal.add(1, 2);
        System.out.println(result);
    }
    @Test
    public void testSubtract(){
        int result = cal.subtract(1, 2);
        System.out.println(result);
    }
    @Test
    public void testMultiply(){
        int result = cal.multiply(1, 2);
        System.out.println(result);
    }
    @Test
    public void testDivide(){
        float result = cal.divide(1, 2);
        System.out.println(result);
    }
}
MyBatis配置文件

一般使用mybatis-config.xml进行配置:
MyBatis采用XML格式配置数据库环境信息
MyBaits环境配置标签<environment>
environment包含数据库驱动、URL、用户名与密码

<!-- 添加MyBatis的依赖 -->
<dependencies>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.10</version>
        </dependency>
<!-- 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>
    <!-- 设置默认指向的数据库 -->
    <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&amp;characterEncoding0=UTF-8"/>
               <property name="username" value="root"/>
               <property name="password" value="123456"/>
           </dataSource>
       </environment>
   </environments>
</configuration>
SqlSessionFactory

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

SqlSession

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

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;

public class MyBatisTester {
    @Test
    public void testSqlSessionFactory() throws IOException {
        // 默认从当前的类路径下以字符流的形式加载mybatis-config.xml配置文件, 利用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) {
            throw new RuntimeException(e);
        }finally {
            if (sqlSession != null){
                // 如果type="POOLED",代表使用连接池,close则是将连接回收到连接池中
                // 如果type="UNPOOLED",代表直连,close则会调用Connection.close()方法关闭连接
                sqlSession.close();
            }
        }

    }
}
MyBatisUtils工具类
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 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();
            // 初始化错误时,通过抛出异常ExceptionInInitializerError通知调用者
            throw new ExceptionInInitializerError(e);
        }

    }

    /**
     * openSession 创建一个新的SqlSession对象
     * @return SqlSession对象
     */
    public static SqlSession openSession(){
        return sqlSessionFactory.openSession();
    }

    /**
     * 释放一个有效的SqlSession对象
     * @param session 准备释放SqlSession对象
     */
    public static void closeSession(SqlSession session){
        if (session != null){
            session.close();
        }
    }
// 测试方法代码
@Test
    public void testMyBatisUtils(){
        SqlSession sqlSession = null;
        try {
            sqlSession = MyBatisUtils.openSession();
            Connection connection = sqlSession.getConnection();
            System.out.println(connection);
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            MyBatisUtils.closeSession(sqlSession);
        }
    }
MyBatis数据查询

查询步骤:

  1. 创建实体类(Entity)
  2. 创建Mapper XML
  3. 编写 <select>SQL标签
  4. 开启驼峰命名映射
  5. 新增<mapper>
  6. SqlSession执行select语句
//1. 创建实体类(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 -->
<!-- 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">
    <!-- 不同的命名空间可以有相同的id,相同的命名空间id不能相同; resultType代表把每一个返回结果都帮装成一个Goods对象-->
    <select id="selectAll" resultType="com.imooc.mybatis.entity.Goods">
        select * from t_goods order by goods_id desc limit 10
    </select>
</mapper>
<!-- 4. 在<configuration>中开启驼峰命名映射 -->
<settings>
        <!-- goods_id ==> goodsId 驼峰命名转换  -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>
<!-- 5. 新增<mapper> -->
<mappers>
        <mapper resource="mappers/goods.xml"/>
    </mappers>   
//  6. SqlSession执行select语句
@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的SQL参数传递
<!-- goods.xml文件内容 -->
<!-- parameterType为传入参数的包装类,单参数情况 -->
    <select id="selectById" parameterType="Integer" resultType="com.imooc.mybatis.entity.Goods">
    select * from t_goods where goods_id = #{value}
    </select>

    <!-- 多参数情况 -->
    <select id="selectByPriceRange" parameterType="java.util.Map" resultType="com.imooc.mybatis.entity.Goods">
        select * from t_goods
        where
            /* min和max还有limit为Map中的key值,传入的参数为value值 */
            current_price between #{min} and #{max}
        order by current_price
        limit 0, #{limit}
    </select>
// 测试类文件内容
	@Test
    public void testSelectById() throws Exception {
        SqlSession session = null;
        try {
            session = MyBatisUtils.openSession();
            Goods goods = session.selectOne("goods.selectById", 1602);
            System.out.println(goods.getTitle());

        } catch (Exception e) {
            throw e;
        } finally {
            MyBatisUtils.closeSession(session);
        }
    }

    @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("limit", 10);
            List<Goods> list = session.selectList("goods.selectByPriceRange", param);
            for (Goods g : list){
                System.out.println(g.getTitle());
            }

        } catch (Exception e) {
            throw e;
        } finally {
            MyBatisUtils.closeSession(session);
        }
    }
MyBatis多表关联查询
<!-- goods.xml文件内容 -->
<!-- 利用LinkedHashMap保存多表关联结果
MyBatis会将征一条记录包装为LinkedHashMap对象
key是字段名value是字段对应的值,字段类型根据表结构进行白动判断
优点:易于扩展,易于使用
缺点:太过灵活,无法进行编泽的时检查
 -->
    <select id="selectGoodsMap" resultType="java.util.LinkedHashMap">
    select g.*, c.category_name from t_goods g , t_category c
    where g.category_id = c.category_id
    </select>
</mapper>
// 测试类文件方法内容
	@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结果映射

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

<!-- goods.xml文件内容 -->
<!-- 结果映射 -->
    <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.category_name, '1' as test from t_goods g , t_category c
        where g.category_id = c.category_id
    </select>
// DTO类,用于多表联合查询
// 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;
    }
}

// 测试类文件内容
	@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);
        }
    }
MyBatis对数据库的写入操作
insert插入数据
<!-- goods.xml文件内容 -->
<insert id="insert" parameterType="com.imooc.mybatis.entity.Goods">
        INSERT INTO t_goods(title,sub_title,original_cost,current_price, discount, is_free_delivery)
        VALUES (#{title}, #{subTitle}, #{originalCost}, #{currentPrice}, #{discount}, #{isFreeDelivery})
        /* 将最后生成的数据的主键回填到goods对象中*/
        <selectKey resultType="Integer" keyProperty="goodsId" order="AFTER">
            select last_insert_id()
        </selectKey>
    </insert>
// 测试类文件内容
	@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);
        }
    }
selectKey和useGenerateKeys的区别

selectKey标签需要明确编写获取最新主键的SQL语句
useGeneratedKeys属性会自动根据驱动生成对应SQL语句
selectKey适用于所有的关系型数据库
useGeneratedKeys只支持"自增主键"类型的数据库
selectKey标签是通用方案,适用于所有数据库,但编写麻烦
useGeneratedKeys属性只支持"自增主键"数据库,使用简单

<!-- useGenerateKeys的用法 -->
<insert id="new_insert" parameterType="com.imooc.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)
        VALUES (#{title}, #{subTitle}, #{originalCost}, #{currentPrice}, #{discount}, #{isFreeDelivery})
    </insert>
MyBatis的更新与删除
<!-- goods.xml文件内容 -->
<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>

    <delete id="delete" parameterType="Integer">
        delete from t_goods where goods_id = #{value}
    </delete>
// 测试类文件内容
@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);
        }
    }

    @Test
    public void testDelete() throws Exception {
        SqlSession session = null;
        try {
            session = MyBatisUtils.openSession();
            int num = session.delete("goods.delete", 739);
            session.commit(); // 提交事务数据
        } catch (Exception e) {
            if (session != null){
                session.rollback(); // 回滚事务
            }
            throw e;
        } finally {
            MyBatisUtils.closeSession(session);
        }
    }
MyBatis预防SQL注入攻击

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

MyBatis工作流程

截图来自慕课网

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值