MyBatis入门

161 阅读7分钟

开发流程

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

单元测试

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

JUnit 4使用方法

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

使用Maven 新建项目 选择Maven 下一步 image.png image.png 在pom.xml中添加依赖 image.png

 <?xml version="1.0" encoding="UTF-8"?>
 <project xmlns="http://maven.apache.org/POM/4.0.0"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
   <modelVersion>4.0.0</modelVersion>
 ​
   <groupId>org.example</groupId>
   <artifactId>meven-junit4</artifactId>
   <version>1.0-SNAPSHOT</version>
   //指定下载服务器
   <repositories>
     <repository>
       <id>aliyun</id>
       <name>aliyun</name>
       <url>https://maven.aliyun.com/repository/public</url>
     </repository>
   </repositories>
   //依赖
   <dependencies>
     <dependency>
       <groupId>junit</groupId>
       <artifactId>junit</artifactId>
       <version>4.12</version>
     </dependency>
   </dependencies>
 </project>

左侧出现对应包表示下载成功 image.png 引入JUnit jar包 新建Java项目 新建文件夹lib将jar包导入 image.png 将lib目录进入项目库中 image.png image.png 生成测试用例类 先写一个加减乘除类

 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;
     }
 }

image.png 鼠标右键 image.png 选择test image.png 生成测试代码 image.png image.png 添加测试内容

 public class CalculatorTest {
     private Calculator cal = new Calculator();
     @Test
     public void add() {
         int result = cal.add(1, 2);
         System.out.println(result);
     }
 ​
     @Test
     public void subtract() {
         int result = cal.subtract(1, 2);
         System.out.println(result);
     }
 ​
     @Test
     public void multiply() {
         int result = cal.multiply(1, 2);
         System.out.println(result);
     }
 ​
     @Test
     public void divide() {
         float result = cal.divide(1, 2);
         System.out.println(result);
     }
 }

运行 image.png

MyBatis基本使用

配置文件:mybatis-config.xml MyBatis采用XML格式配置数据库环境信息 MyBatis环境配置标签 environment包含数据驱动、URL、用户名与密码 image.png 使用Maven创建新项目 添加依赖 pom.xml

 <?xml version="1.0" encoding="UTF-8"?>
 <project xmlns="http://maven.apache.org/POM/4.0.0"
          xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
 ​
     <groupId>org.example</groupId>
     <artifactId>mybatis</artifactId>
     <version>1.0-SNAPSHOT</version>
     <repositories>
         <repository>
             <id>aliyun</id>
             <name>aliyun</name>
             <url>https://maven.aliyun.com/repository/public</url>
         </repository>
     </repositories>
     <dependencies>
         <dependency>
             <groupId>org.mybatis</groupId>
             <artifactId>mybatis</artifactId>
             <version>3.5.1</version>
         </dependency>
         <dependency>
             <groupId>mysql</groupId>
             <artifactId>mysql-connector-java</artifactId>
             <version>5.1.47</version>
         </dependency>
     </dependencies>
     <properties>
         <maven.compiler.source>8</maven.compiler.source>
         <maven.compiler.target>8</maven.compiler.target>
     </properties>
 ​
 </project>

使用IDE创数据库 image.png 填写mysql信息 点击Test Connection进行测试 通过后点击ok image.png 导入数据库脚本 image.png 等待运行成功 image.png 配置MyBatis

 <?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;characterEncoding=UTF-8"/>
                 <property name="username" value="root"/>
                 <property name="password" value="123456"/>
                 <!--...-->
             </dataSource>
         </environment>
         <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://192.168.1.1:3306/babytun?useUnicode=true&amp;characterEncoding=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对应方法 引入单元测试依赖 image.png 新建包创建单元测试类 image.png 编写测试代码

 package com.mybatis;
 ​
 import org.apache.ibatis.session.SqlSession;
 import org.apache.ibatis.session.SqlSessionFactory;
 import org.apache.ibatis.session.SqlSessionFactoryBuilder;
 import org.junit.Test;
 ​
 import org.apache.ibatis.io.Resources;
 ​
 import java.io.IOException;
 import java.io.Reader;
 import java.sql.Connection;
 ​
 public class MybatisTestor {
     @Test
     public void testSqlSessionFactory() throws IOException {
         //利用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("sqlSessionFactory加载成功");
         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();
             }
         }
     }
 }
 ​

image.png

初始化工具类MyBatisUtils

新建工具类 image.png 代码

 package com.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 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();
         }
 ​
     }
 }
 ​

测试工具类 MybatisTestor.java

     /**
      * MyBatisUtils使用指南
      **/
     @Test
     public void testMyBatisUtils(){
         SqlSession sqlSession = null;
         try{
             sqlSession = MyBatisUtils.openSession();
             Connection connection =sqlSession.getConnection();
             System.out.println(connection);
         }catch (Exception e){
             throw e;
         }finally {
             MyBatisUtils.closeSession(sqlSession);
         }
     }

image.png

MyBatis数据查询

MyBatis数据查询步骤

  • 创建实体类(Entity)
  • 创建Mapper XML
  • 编写 select SQL标签
  • 开启驼峰命名映射
  • 新增 mapper
  • SqlSession执行select语句

创建实体类

image.png

package com.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;
    }


}

创建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">
<mapper namespace="goods">
  <select id="selectAll" resultType="com.mybatis.entity.Goods">
    select * from t_goods order by goods_id desc limit 10
  </select>
</mapper>

在mybatis-config.xml中进行声明 image.png 测试 MybatisTestor.java中

@Test
    public void testSelectAll(){
    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中添加

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

image.png 测试结果 image.png image.png

SQL传参

查询 select image.png 在goods..xml中添加一个select

<select id="selectById" parameterType="Integer" resultType="com.mybatis.entity.Goods">
    select * from t_goods where goods_id = #{value}
</select>

image.png 测试 MybatisTestor.java中

@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);
}
}

测试结果: image.png

按价格范围查询

goods.xml中

<select id="selectByPriceRange" parameterType="java.util.Map" resultType="com.mybatis.entity.Goods">
        select * from t_goods
        where
            current_price between #{min} and #{max}
        order by current_price
        limit 0,#{limt}
</select>

image.png 测试 MybatisTestor.java中

@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);
        }
    }

image.png

获取多表关联查询结果

利用LinkedHashMap保存多表关联结果 MyBatis会将每一条记录包装为LinkedHashMap对象 key是字段名 value是字段对应的值 , 字段类型根据表结构进行自动判断 优点: 易于扩展,易于使用 缺点: 太过灵活,无法进行编译时检查 goods.xml中

<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>

测试: MybatisTestor.java中

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

image.png

image.png

ResultMap结果映射

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

创建Category类 image.png

package com.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;
    }

}

创建GoodsDTO类 image.png

package com.mybatis.dto;

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

public class GoodsDTO {
    private Goods goods = new Goods();
    private Category category = new Category();
    

    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;
    }
}

goods.xml中

<!--结果映射-->
<resultMap id="rmGoods" type="com.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>
</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>

测试: MybatisTestor.java中

@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);
}
}

image.png

MyBatis数据写入

数据库事务 数据库事务是保证数据操作完整性的基础 数据先写入日志中后同时写入数据表 如果有数据执行不成功则发生回滚 要么全部成功要么全部回滚 image.png

MyBatis写操作包含三种

  • 插入-
  • 更新-
  • 删除-

插入-

<insert id="insert"parameterType="com.mybatis.entity.Goods">
  INSERT INTO 'babytun'.'t goods'( 'title', 'sub_title', 'original_cost' , 'current price')
  VALUES ( #{title}, #{subTitle}, #{originalCost}, #{currentPrice})
  <selectKey resultType="int" keyProperty="goodsld" order="AFTER">
    <!-- 当前连接中最后产生的id号 -->
  select last insert id()
</selectKey>
</insert>

插入数据 goods.xml

<insert id="insert" parameterType="com.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>

MybatisTestor.java

@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();//提交事务数据
}catch (Exception e){
    if(session!=null)
        session.rollback();//回滚事务
    throw e;
}finally {
    MyBatisUtils.closeSession(session);
}
}

测试 image.png

selectKey与useGeneratedKeys的区别

selectKey标签用法

<insert id="insert" parameterType="com.mybatis.entity.Goods">
insert into sql语句
<!-- resultType主键类型  keyProperty主键属性   order在insert之前或之后执行 -->
  <selectKey resultType="Integer" keyProperty="goodsId" order="AFTER">
    select last_insert_id()
  </selectKey>
</insert>

useGeneratedKeys属性用法

<insert id="insert
  parameterType="com.mybatis.entity.Goods"
  useGeneratedKeys="true"
  keyProperty="goodsId"
  keyColumn="goods_id">
  INSERT INTO SQL语句 
  </insert>

二者区别-显示与隐示

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

selectKey标签是通用方案,适用与所有数据库,但编写麻烦 useGeneratedKeys属性只支持“自增主键”数据库,使用简单

更新与删除操作

更新-

<update id="update" parameterType="com.mybatis.entity.Goods">
	UPDATE t_goods
  SET 'title' = #{title}
  WHERE 'goods_id'=#{goodsId}
</update>

删除-

<delete id="delete" parameterType="Integer">
  delete from t_goods where goods_id = #{value}
</delete>

更新操作 goods.xml中

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

MybatisTestor.java中

@Test
    public void testUpdate() throws Exception{
    SqlSession session = null;
try{
    session = MyBatisUtils.openSession();
    Goods goods = session.selectOne("goods.selectById", 500);
    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);
}
}

测试结果 image.png 删除操作 goods.xml

<delete id="delete" parameterType="Integer">
  delete from t_goods where goods_id = #{value}
</delete>

MybatisTestor.java中

@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);
}
}

测试结果 image.png ##