Mybatis 快速入门(二)☁

725 阅读7分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第16天,点击查看活动详情

Mybatis 快速入门(二)

写在前面👀

审核大大,球球你了,俺今天稿子只完成了三分之一,我可以今天交吗?我保证明天12点之前补完剩下的内容,审核大大最帅(最漂亮)😘

今天主要讲讲MyBatis使用xml映射文件进行查询所有数据查询单行数据的操作

一、环境准备

1️⃣数据库准备

-- 创建海贼王人物表
CREATE TABLE tb_op (
    id INT PRIMARY KEY AUTO_INCREMENT, # 主键
    name VARCHAR(20),                  # 姓名
    gender VARCHAR(5),                 # 性别
    devil_fruit VARCHAR(30),           # 果实能力
    reward BIGINT,                     # 悬赏金,单位:贝利
    identity VARCHAR(10)               # 人物身份
);

-- 添加人物数据
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('路飞','男','人人果实-幻兽种-尼卡形态',1500000000,'海贼');
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('索隆','男',null,320000000,'海贼');
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('黄猿','男','闪闪果实',null,'海军');
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('亚尔丽塔','女','滑滑果实',5000000,'海贼');
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('伊万科夫','人妖','荷尔蒙果实',null,'革命军');
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('萨博','男','烧烧果实',602000000,'革命军');
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('小冯','人妖','模仿果实',32000000,'海贼');
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('艾斯','男','烧烧果实',550000000,'海贼');
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('达斯琪','女',null,null,'海军');
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('小萨蒂','女',null,null,'海军');
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('战国','男','人人果实-幻兽种-大佛形态',null,'海军');
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('熊','男','肉球果实',296000000,'革命军');
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('红发','男','面子果实',4028900000,'海贼');
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('罗宾','女','花花果实',130000000,'海贼');
INSERT INTO tb_op (name,gender,devil_fruit,reward,identity) VALUES ('Joker','男','线线果实',340000000,'天龙人');

2️⃣创建Op实体类

  • com.bighorn.pojo 包下创建 Op 实体类。
public class Op {
    private Integer id;
    private String name;
    private String gender;
    private String devilFruit;
    private Long reward;
    private String identity;
    /* 省略getter和setter方法 */
}    

3️⃣创建MyBatis工具类

/**
 * MyBatis工具类
 */
public class MyBatisUtils {
    //创建全局唯一的SqlSessionFactory对象
    private static SqlSessionFactory sqlSessionFactory = null;

    static {
        try {
            //1. 加载mybatis的核心配置文件,获取 SqlSessionFactory
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
            throw new ExceptionInInitializerError(e);
        }
    }

    /**
     * 获取SqlSession对象
     * @return SqlSession
     */
    public static SqlSession openSession() {
        return sqlSessionFactory.openSession();
    }

    /**
     * 关闭SqlSession,释放资源
     * @param sqlSession
     */
    public static void closeSession(SqlSession sqlSession) {
        if (sqlSession != null) {
            sqlSession.close();
        }
    }

    /**
     * 提交事务
     * @param sqlSession
     */
    public static void commitSession(SqlSession sqlSession){
        if (sqlSession != null) {
            sqlSession.commit();
        }
    }

    /**
     * 回滚事务
     * @param sqlSession
     */
    public static void rollbackSession(SqlSession sqlSession) {
        if (sqlSession != null) {
            sqlSession.rollback();
        }
    }

}

4️⃣编写测试类

  • 先测试下数据库连接是否正常
public class testMyBatis {
    //获取sqlSession对象
    private SqlSession sqlSession = MyBatisUtils.openSession();
    //获取OpMapper接口的代理对象
    private OpMapper opMapper = sqlSession.getMapper(OpMapper.class);
    @Test
    /**
     * 0.测试数据库连接
     */
    public void testSession() throws IOException {
        //1.打印sqlSession对象
        System.out.println(sqlSession);
        //2.释放资源
        sqlSession.close();
    }
}
  • 结果如下,没问题👇

image.png

5️⃣创建Dao接口和映射文件

  1. 接口和映射文件放在同一包下:com.bighorm.mapper
  2. 接口和映射文件名称要一样:OpMapper
  • 创建OpMapper接口
public interface OpMapper {}
  • 创建OpMapper.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">
<!--namespace元素: 绑定DAO接口,因此 namespace 的命名必须要跟接口同名。-->
<mapper namespace="com.bighorn.mapper.OpMapper">

</mapper>
  • mapper映射文件常见元素表👇
元素名称描述备注
mapper映射文件的根节点,只有namescape 一个属性namescape用于区分不同的 mapper,全局唯一绑定DAO接口,即面向接口编程。因此 namescape 的命名必须要跟接口同名。
select查询语句,最常用、最复杂的元素之一可以自定义参数,返回结果集等
insert插入语句执行后返回一个整数,代表插入的条数
update更新语句执行后返回一个整数,代表更新的条数
delete删除语句执行后返回一个整数,代表删除的条数
sqlsql语句片段可以在其他标签被引用
resultMap用来描述数据库结果集与对象的对应关系,它是最复杂、最强大的元素提供映射规则
cache配置给定命名空间的缓存-
cache-ref其它命名空间缓存配置的引用-

6️⃣安装MyBatisX插件

  • 安装方法
    • Ctrl+Alt+S打开设置-->Plugins-->在插件市场Marketplace搜索MyBatisX-->Install下载--->重启IDE!

image.png

  • 主要功能:
    • XML映射配置文件和接口方法间相互跳转,点这只小鸟就能跳转! image.png
    • 根据接口方法生成 statement image.png

二、一些偷懒的方法

  • mybatis-config.xmlMybatis核心配置文件里,在typeAliases标签写入实体类所在包名,这样就不用每次都书写类的全限定名称了;在mappers标签里写入映射文件所在包名,这样就不用写每个mapper文件的相对路径了
<!--起别名操作-->
<typeAliases>
    <!--name属性的值是实体类所在包-->
    <package name="com.bighorn.pojo"/>
</typeAliases>
<!--加载sql映射文件-->
<mappers>
    <package name="com.bighorn.mapper"/>
</mappers>

三、select标签常用属性

属性名称描 述备注
id它和 Mapper 的命名空间组合起来使用,是唯一标识符,供 MyBatis 调用如果命名空间+id不唯一,那么 MyBatis 抛出异常
parameterType表示传入 SQL 语句传入参数类型的全限定名或别名。它是一个可选属性,MyBatis 能推断出具体传入语句的参数支持基本数据类型和 JavaBean、Map 等复杂数据类型
resultTypeSQL 语句执行后返回的类型(全限定名或者别名)。如果是集合类型,返回的是集合元素的类型,返回时可以使用 resultType 或 resultMap 之一-
resultMap它是映射集的引用,与 元素一起使用,返回时可以使用 resultType 或 resultMap 之一是 MyBatis 最复杂的元素,可以配置映射规则、级联、typeHandler 等
flushCache用于设置在调用 SQL 语句后是否要求 MyBatis 清空之前查询的本地缓存和二级缓存默认值为 false,如果设置为 true,则任何时候只要 SQL 语句被调用都将清空本地缓存和二级缓存
useCache启动二级缓存的开关,默认值为 true,表示将査询结果存入二级缓存中-
timeout用于设置超时参数,单位是秒(s),超时将抛出异常-
fetchSize获取记录的总条数设定默认值是数据库厂商提供的 JDBC 驱动所设置的条数
statementType告诉 MyBatis 使用哪个 JDBC 的 Statement 工作,取值为 STATEMENT(Statement)、 PREPARED(PreparedStatement)、CALLABLE(CallableStatement)-
resultSetType这是针对 JDBC 的 ResultSet 接口而言,其值可设置为 FORWARD_ONLY(只允许向前访问)、SCROLL_SENSITIVE(双向滚动,但不及时更新)、SCROLLJNSENSITIVE(双向滚动,及时更新)-

四、查询所有数据

1️⃣编写接口方法

/**
  * 1.查询所有人物的数据,返回一个JavaBean对象的List集合
  * @return List<Op>
  */
List<Op> selectAll();

2️⃣编写映射文件

<select id="selectAll" resultType="Op">
    select *
    from tb_op;
</select>

3️⃣编写测试方法

@Test
/**
  * 1.查询所有数据
  */
public void testSelectAll() {
    //1.1执行方法
    List<Op> opList = opMapper.selectAll();
    for (Op op : opList
        ) {System.out.println(op);}
    System.out.println(opList);
    //1.2释放资源
    sqlSession.close();
}
  • 有个小问题:tb_op表的devil_fruit字段名与实体类Op的属性名devilFruit不匹配,查询该字段时,不能自动封装,显示为null,如下图👇

image.png

4️⃣优化方案

数据库表的字段名称和实体类属性名称不一样,则不能自动封装,解决方案有如下三种

1.给字段起别名

  • as 关键字给字段起和实体类属性名一样的别名
<select id="selectAll" resultType="Op">
    select
    id, name,gender,devil_fruit as devilFruit,reward,identity
    from tb_op;
</select>

2.使用sql片段

  • 缺点:不同的操作都要重新定义一次别名,比较繁琐
<sql id="op_column">
    id, name,gender,devil_fruit as devilFruit,reward,identity
</sql>
<select id="selectAll" resultType="Op">
    select
    <include refid="op_column"></include>
    from tb_op;
</select>

3.使用resultMap

  • 推荐使用这种方法!
  1. 定义resultMap标签
  2. select标签中使用resultMap属性
<!--
    id:唯一标识
    type:映射类型
        -->
<resultMap id="OpResultMap" type="Op">
    <!--
        id:完成主键字段的映射
        result:完成一般字段的映射
        column:表字段名称
        property:类属性名称
              -->
    <result column="devil_fruit" property="devilFruit"></result>
</resultMap>
<select id="selectAll" resultMap="OpResultMap">
    select *
    from tb_op;
</select>

4.优化后的测试结果

image.png

五、查询单行数据

1️⃣编写接口方法

/**
  * 2.根据id查询单条数据,返回一个Op对象
  * @param id
  * @return Op
  */
Op searchById(int id);

2️⃣编写映射文件

  • parameterType:指定传入参数的类型。缺省属性,因为MyBatis 能推断出具体传入语句的参数。
<!--    根据id查询单条记录-->
<select id="searchById" parameterType="int" resultMap="OpResultMap">
    select *
    from tb_op
    where id = #{id};
</select>

3️⃣编写测试方法

@Test
/**
  * 2.根据id查询单行数据
  */
public void testSearchById(){
    //查询id为1的数据
    int id=1;
    //2.1执行方法
    Op op = opMapper.searchById(id);
    System.out.println(op.toString());
    //2.2释放资源
    sqlSession.close();
}
  • 结果如下👇

image.png

4️⃣参数占位符

  • mybatis提供了两种参数占位符
    • #{} :执行SQL时,会将 #{} 占位符替换为?,从下图可以看出使用底层使用的是 PreparedStatement,可以防止sql注入

image.png

  • ${} :拼接SQL。从下图可以看出底层使用的是 Statement,会存在SQL注入问题。

image.png

写在后面🍻

感谢观看啦✨
有什么不足,欢迎指出哦💖
掘金的运营同学审核辛苦了💗