MyBatis 手写使用端

63 阅读11分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第 19 天,点击查看活动详情

上期中,我们对自定义持久层框架的设计思路进行了分析,那么在本期中,根据上期分析的思路,来通过程序具体实现。

在上期自定义持久层框架思路分析的时候,我们分成了两部分,第一部分就是使用端,第二部分就是自定义尺寸框架本身,这两部分其实分别都对应着一个工程。

首先,来完成使用端程序实现,整个使用端程序的实现相对来比较简单的,我们只需要在使用端新建一个工程,在这个工程中完成两个功能。

第一,是引入自定义持久层框架的 jar 包。

第二件,是创建两个配置文件,一个 mybatis-config.xml 配置文件,用来提供数据库的配置信息,还有一个是 mapper.xml 配置文件,用来提 SQL 的配置信息。

接下来我们就按照前面分析的实现思路来完成具体的程序编写。

创建数据库

在完成自定义持久层框架之前,我们先来创建数据库以及数据库表。在这个自定义持久层框架中,还需要一定的数据支持。

CREATE TABLE `t_user`  (
  `user_id` int NOT NULL COMMENT 'id',
  `user_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '用户名',
  `age` int NULL DEFAULT NULL COMMENT '年龄',
  PRIMARY KEY (`user_id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

结果如下:

  image.png

创建工程

考虑到工程的维护性,我们选择 IDE 来新建一个 Maven 项目来使用 MyBatis。当然如果你更倾向了 Gradle,那么没有关系,你只需要更改添加依赖的方式即可。

在 IDE 上,你可以选择 Eclipse 或者 IDEA,当然我们更推荐你使用 IDEA,因为它的社区版已经足够我们学习使用了,而且它也是免费的,本小节我们以 IDEA 作为默认的开发环境。

打开 IDEA,选择 New Project,点击左侧的Maven项,然后 Next 新建项目,如下图:

  image.png

进入下一页后,输入对应的 GroupId 和 ArtifactId,如下图,你也可以选择自己心仪的 id,但是我们推荐你跟我们保持一致,这样在后面的学习中,你的配置和代码才能跟我们完全一致。

image.png

填完以后,点击 Next 直到出现 Finish,点击完成即可。

这个工程啊就创建完成了。在使用端的这个工程主要的完成两个功能。

第一个功能引入自定义持久层框架的依赖,在项目根目录下找到 pom.xml 文件,并向其中添加配置。而自定义持有层框架这个工程目前还没有创建出来,所以引入依赖这一步稍后再来进行引入。

创建 sql-mapper-config.xml 配置文件

现在只需要在使用端完成配置文件的创建即可,配置文件一般都要是创建在工程下的 resource这个目录下。创建 sql-mapper-config.xml 配置文件,将要在这个配置文件中完成对于数据库配置信息的一个配置。

<configuration>
    <!-- 数据库配置信息-->
    <dataSource>
        <property name="driverCalss" value="com.mysql.jc.jdbc.Driver" ></property>
        <property name="jdbcUrl" value="jdbc:mysql:127.0.0.1:3306/db_permanece" ></property>
        <property name="username" value="root" ></property>
        <property name="password" value="root" ></property>
    </dataSource>
</configuration>

在这个配置文件中,涉及到标签如下:

  • configuration:根标签。
  • dataSource:数据源配置信息标签,在这个标签下有 property 子标签,用于配置具体数据源的信息,比如数据库驱动信息、数据库连接地址、用户名以及密码。这里使用的是MySQL数据库,需要配置MySQL 数据库相关的信息,如果是其他数据库,就需配置其他相应的数据库信息。

创建 mapper.xml 配置文件

完成数据库配置信息编写之后,此时还需编写 SQL 的配置信息。首先创建 mapper.xml 配置文件。

但是在创建之前,我们先来思考一个问题了,在我们实际开发项目中,是不是应该会有很多个模块,不可能只有一个模块,这些模块可能有用户模块、商品模块、订单模块等等很多个模块。如果现在只有一个 mapper.xml ,那么所有模块的 SQL 语句都要写在这个 mapper.xml 配置文件中,这样做有没有弊端。肯定会有弊端,所有 SQL 语句编写在一个 mapper.xml 配置文件中,对于 SQL 操作和 SQL 编写都是非常的麻烦的,同时后期不易维护。

所以针对不同的模块来编写不同的对应的映射配置文件。比如用户模块,我们就创建 UserMapper.xml。UserMapper.xml 配置文件只用来编写用户模块相关的 SQL 语句。假如还有其他模块,比如商品模块,我们可以创建 ProductMapper.xml 配置文件,同时在这个配置文件中编写商品相关的 SQL 语句。

在这个自定义持久层框架的整个过程中,我们只需要使用用户模块举例,只创建用户模块相关的配置映射文件。

在这个映射配置文件中,主要来完成 SQL 编写和 SQL 配置。SQL 语句包括查询的 SQL 语句、修改的 SQL 语句,删除的 SQL 语句以及删除的 SQL 语句。

如果直接编写 SQL 语句,这个即不直观也不易维护,所以针对不同功能 SQL 语句,可以给出不同 SQL 语句的标签。

<mapper>
    <select>
        SELECT * FROM t_user;
    </select>
    <select>
        SELECT * FROM t_user WHERE  id=? and username=?
    </select>
    <insert>
        INSERT INTO t_user(id,username,age) VALUES (?,?,?)
    </insert>
    <update>
        UPDATE t_user SET age = #{age} WHERE id =?
    </update>
    <delete>
        DELETE FROM t_user WHERE id=? 
    </delete>
</mapper>

在这个 UserMapper.xml SQL 配置文件中,使用的标签如下:

  • mapper 标签:是 mapper 映射配置文件的跟标签,是 Java 方法和 SQL 语句之间的桥梁。
  • select 标签:用于映射 SQL 中的查询语句。
  • insert 标签:用于映射 SQL 中的插入语句。
  • update 标签:用于映射 SQL 中的更新语句。
  • delete 标签:用于映射 SQL 中的删除语句。

注意:在后期自定义持久层框架项目中,只实现 select 相关操作,其他操作类似,感兴趣的你可以自行实现。

假设在这个 UserMapper.xml 映射配置文件中有两条查询相关的 SQL 语句。一条 SQL 语句是查询所有的用户信息,另一条 SQL 语句根据用户 Id 查询某个用户信息。

<select>
    SELECT * FROM t_user;
</select>
<select>
    SELECT * FROM t_user WHERE id=? and username=?
</select>

那么,我们如何来定位映射配置文件中这两条搜狐语句或者定位到其中的一条 SQL 语句。只使用标签是无法定位到映射配置文件中具体要完成功能的 SQL 语句,只能定位到映射配置文件中所有与此标签相关的 SQL 语句。

因此,针对这些标签,给它设置唯一标识 id。这个标签中的 SQL 语句要完成那个业务功能。比如查询所有用户信息,可以将 id 设置为 selectList。同样的,其他标签也可以设置唯一标识。

<select id="selectList">
    SELECT * FROM t_user;
</select>
<select id="findByIdOne">
     SELECT * FROM t_user WHERE id=? and username=?
</select>

这样编写配置文件中的 SQL 配置,既能方便维护,同时定位到具体业务功能操作。

到这里,你觉得我们当前的这个映射配置文件的编写的如何了。其实还存在一些问题,存在什么问题,想一想。

假设项目只有一个用户模块,按照上面的实现没有任何问题,但是在实际企业项目中,不可能只有一个用户模块。

假如,某某企业要研发一个某某电商系统,这个电商系统有用户模块、商品模块以及订单模块。假设用户模块和商品模块中的映射配置文件内容一致,只不过实现具有 SQL 语句操作数据库表不一致。

<!-- 用户模块-->
<select id="selectList">
    SELECT * FROM t_user;
</select>
<select id="selectOne">
     SELECT * FROM t_user WHERE id=? and username=?
</select>


<!-- 商品模块-->
<select id="selectList">
    SELECT * FROM t_product;
</select>
<select id="selectOne">
     SELECT * FROM t_product WHERE id=? and productname=?
</select>

如果是上面这样编写配置文件,还能定位到唯一的 SQL 语句吗,肯定是不能。原因是当前项目中这么实现定位到两条一样的 SQL 语句,不知道操作的是用户模块还是商品模块。既然曾在了这样问题,如何解决这个问题呢。

此时,我们可以在设置一层唯一标识,这个不理解。举个简单例子,例如有位同学,他们都叫小米,假设某天,老师只叫小米同学,你来回答这道问题,此时这位同学不知道叫的是谁。此时,在小米的前面加一个姓氏,就是知道是哪位同学了。在这个映射配置文件中,也是同样的道理。

如何来修改当前映射配置文件,只需要在根标签 mapper 里面我们还需要加上 namespace 这个属性。

namespace 命名空间是每一个 mapper 文件所独有的,它唯一标识着一个 mapper。如何是用户模块,将其设置为 user,如何是商品模块,将其设置为 product。

<!-- 用户模块-->
<mapper namespace="user">
    //...
</mapper>
<!--商品模块-->
<mapper namespace="product">
    //...
</mapper>

这样,我们的 SQL 唯一标识由namespace 和 id 来组成,比如 user.selectList。由这两组成保证标识的唯一性。

对于 select、insert、update 以及 delete 标签,不能只有唯一标识 id 属性,还应该设置其他属性。

比如,select 标签,需要设置它的返回结果类型,比如设置 resultType 属性,语句返回值类型,如果返回的是集合,那应该设置为集合包含的类型。为什么设置 resultType 属性呢?想一想。

在之前学习 JDBC 操作数据库表时,获取每个属性值,把它封装成实体,存在的问题是手动封装结果集,较为繁琐。需要解决问题,完成数据库表中查询出来的字段值与实体中的属性值自动封装。要实现这个过程,需要使用反射和内省技术。

在用户模块中,SQL 语句执行查询出来每一条记录,假设我们现在都想封装到一个 user 实体中,那么也就意味着我们必须要拿到用户(User)实体的全路径,才能使用反射来获取到里面的属性,才能够完成字段值与属性值的一个自动自动映射封装。

resultType 属性应该把要封装实体的全路径声明出来,这样在自定义 MyBatis 持久层框架中进行配置信息解析时,才能够根据这个全路径使用反射技术完成自动映射封装。

<select id="selectList" resultType="com.itbbfx.pojo.User">
    // ...
</select>

在根据条件查询的时候,是需要参数的,那么这个参数将如何设置,类型是什么。同时,在传递多个参数的时,需要使用面向对象思路来实现,在一般情况下,将多个参数封装成某个实体进行传递。

假设我们目前要传递的参数是用户对象,比如要传递的参数如下所示:

User user=new User();
user.setUserId(1);
user.setUserName("itbbfx");

这时,这个 user 就被作为参数进行传递。在 UserMapper.xml 映射配置文件,我们需要完成的功能是获取 user 实体中 id 值和 username 值。

要实现这个效果,使用的技术还是反射。既然用到反射,当前参数的全路径必须提供,parameterType 属性来设置。

<select id="selectOne" resultType="com.itbbfx.pojo.User" parameterType ="com.itbbfx.pojo.User">
     SELECT * FROM t_user WHERE id= ? and username=?
</select>

参数的全路径设置之后,如何实现。使用反射取到这个参数它的全路径里面的某些属性的值。取到这些某些属性的值之后,就跟相应的占位符进行赋值。

这里有个问题,如何进行赋值操作以及 “?” 占位符是 JDBC 里面的占位符。首先,对于 JDBC 里的“?” 占位符使用自定义 “#{}”占位符来替换。为什么要自定义“#{}”占位符呢?目的具体获取参数中哪个属性的值,同时需要注意,在“#{}”占位符中填的值应该要与当前传入实体中的属性名保持一致。

<select id="findByIdOne" resultType="com.itbbfx.pojo.User" parameterType ="com.itbbfx.pojo.User">
    SELECT * FROM t_user WHERE user_id=#{userId} and user_name=#{userName}
</select>

介绍到这里,整个映射配置文件具体实现代码编写完成。

 

根据前面设计思路的分析,映射配置文件能够正常的加载到内存中,关键的一步需要在 sql-mapper-config.xml 中配置映射文件的全路径。这里需要使用 mappers 标签来配置映射文件的全路径。

<configuration>
   //...
   <mappers resource="UserMapper.xml"></mappers>
</configuration>

介绍到这里了,自定义持久层框架的使用端才算真正实现完成,下期课程中,我们将实现自定义持久层框架本身。

小结

本期课程实现很简单,创建项目,编写连个配置文件,一个 sql-mapper-config.xml 核心配置文件,一个是 UserMapper.xml 映射配置文件。核心配置文件主要实现数据库信息配置,映射配置文件主要实现 SQL 语句操作、返回结果类型以及参数类型设置。