二、持久层框架设计实现及MyBatis源码分析-自定义持久层框架(二)

在前一章节,我们对自定义持久层框架有了一个基本的思路,接下来我们主要就是需要完成自定义持久层级框架的编写

首先我们先来编写使用端代码,新建一个maven项目,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>study.lagou.com</groupId>
    <artifactId>persistence-test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.encoding>UTF-8</maven.compiler.encoding>
        <java.version>1.8</java.version>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
    </properties>

</project>

在项目的resources文件夹下创建sqlMapConfig.xml配置文件

<configuration>

    <dataSource>
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost/mybatis"></property>
        <property name="username" value="root"></property>
        <property name="password" value="111111"></property>
    </dataSource>

</configuration>

接着创建mapper.xml配置文件,但是我们在创建之前,先思考一个问题,在我们的一个项目当中,是不是应该会有很多个模块,不可能只有一个模块,可能有用户、商品、订单等等模块,如果我们只有一个mapper.xml文件话,所有的SQL语句都要写到这一个mapper.xml文件当中,这样做好吗?如果所有的SQL语句都写到一个mapper.xml文件,对于我们的操作和编写都是非常麻烦的,而且不易于阅读,为了使我们的mapper.xml文件当中的内容不至于太长,容易阅读和维护,所以我们推荐按照模块分别编写mapper.xml文件

接下来我们就按照不同的模块来编写不同的映射配置文件,比如当前我们有一个用户模块和商品模块,那么我们则需要编写两个映射配置文件

一个是UserMapper.xml,这个映射配置文件只用来编写用户相关的SQL语句
另外一个是ProduceMapper.xml,这个一配置文件只用来编写商品相关的SQL语句

接下来我们先以UserMapper.xml的编写来一步步演示,具体编写mapper.xml文件时需要注意的问题

<mapper>

    <!-- 查询所有的用户信息 -->
    <select>
        select * from user
    </select>

    <!-- 根据条件查询单个用户信息 -->
    <select>
        select * from user where id = ? and username = ?
    </select>
    
</mapper>

简单的编写完以上的UserMapper.xml文件,我们可以看到在我们UserMapper.xml文件中有两条SQL语句,那么我们如何能够定位到映射配置文件中这两条SQL语句呢(具体调用时如何区分应该调用哪一条),如何区分是查询所有还是查询单个?我们无法区分,所以我们考虑给每一个SQL语句给一个标识属性(id),有了以下改进版本

<mapper>

    <!-- 查询所有的用户信息 -->
    <select id="selectList">
        select * from user
    </select>

    <!-- 根据条件查询单个用户信息 -->
    <select id="selectOne">
        select * from user where id = ? and username = ?
    </select>

</mapper>

如此改进完成之后,我们则可以通过selectList、和selectOne定位到具体我们需要的是哪一条SQL语句了,接着思考,我们改进完成之后还有没有其它问题呢?

如果是项目当中只有这一个模块,是没有问题的,但是如果项目当中还存在其它模块,其它模块也是这么编写的呢?我们以ProduceMapper.xml来举例说明

<mapper>

    <!-- 查询所有的商品信息 -->
    <select id="selectList">
        select * from produce
    </select>

    <!-- 根据条件查询单个商品信息 -->
    <select id="selectOne">
        select * from produce where id = ? and username = ?
    </select>


</mapper>

ProduceMapper.xml文件中也是这么写的,取的标识名也是selectList和selectOne,只是查询的表名不一样,现在通过selectList和selectOne这两个标识还能定位到唯一一条SQL语句吗?因为selectList和selectOne在项目当中存在多个了,我们不能区分开来,所以我需要再加一层标识加以识别处理==>命名空间namespace

<mapper namespace="user">

    <!-- 查询所有的用户信息 -->
    <select id="selectList">
        select * from user
    </select>

    <!-- 根据条件查询单个用户信息 -->
    <select id="selectOne">
        select * from user where id = ? and username = ?
    </select>


</mapper>
<mapper namespace="produce">

    <!-- 查询所有的商品信息 -->
    <select id="selectList">
        select * from produce
    </select>

    <!-- 根据条件查询单个商品信息 -->
    <select id="selectOne">
        select * from produce where id = ? and produce_name = ?
    </select>


</mapper>

现在我们可以约定,SQL的唯一标识,可以由namespace+"."+id来组成,这样就能唯一标识出唯一一条SQL语句了,由namespace.id组合成的唯一标识,我们将它称之为statementId

接着往下分析,现在我们能够获取到唯一一条SQL语句进行执行,但是执行完成后,返回的结果集还是需要我们手动去解析放到到具体的对象属性当中,这个手动封装返回结果集的过程较为繁琐,我需要借助于反射、内省等机制,完成返回结果集与对象属性的自动解析和封装,那么我们必须获取到对应对象的全路径才能够做操作,所以我们还需要给有返回结果集的查询语句,添加resultType属性,将返回对象的全路径放置进去,我们才能通过反射、内省等技术进行操作处理

<mapper namespace="user">

    <!-- 查询所有的用户信息 -->
    <select id="selectList" resultType="study.lagou.com.persistence.test.pojo.User">
        select * from user
    </select>

    <!-- 根据条件查询单个用户信息 -->
    <select id="selectOne" resultType="study.lagou.com.persistence.test.pojo.User">
        select * from user where id = ? and username = ?
    </select>

</mapper>

至此,对selectList方法,我们能够完成基本的操作,但是我们接着看selectOne方法,在执行selectOne方法的时候,我们需要传入参数,才能够构建查询的SQL语句,所以在selectOne方法上我们还得添加paramterType属性,而针对多个参数的传递,还是得基于面向对象的思想进行传递,所以接下来我们再对selectOne方法进行完善

<mapper namespace="user">

    <!-- 查询所有的用户信息 -->
    <select id="selectList" resultType="study.lagou.com.persistence.test.pojo.User">
        select * from user
    </select>

    <!-- 根据条件查询单个用户信息 -->
    <select id="selectOne" resultType="study.lagou.com.persistence.test.pojo.User" 
            paramterType="study.lagou.com.persistence.test.pojo.User">
        select * from user where id = ? and username = ?
    </select>


</mapper>

将参数对象通过paramterType属性给到自定义持久层框架,持久层框架需要做的就是通过反射,取到参数对象的属性值,取到对应的属性值之后,我们要给对应的占位符进行赋值,但是我们怎么定位到将哪个属性值赋值到哪一个占位符上面呢?显然,我们通过以“?”做为占位符的方法,无法完成对应,所以我们需要对占位符进行改进,改进成#{属性}的方式来进行处理,注意:#{}当中的属性,需要与paramterType属性对象中的属性一一对应才行,不能随便给!

<mapper namespace="user">

    <!-- 查询所有的用户信息 -->
    <select id="selectList" resultType="study.lagou.com.persistence.test.pojo.User">
        select * from user
    </select>

    <!-- 根据条件查询单个用户信息 -->
    <select id="selectOne" resultType="study.lagou.com.persistence.test.pojo.User"
            paramterType="study.lagou.com.persistence.test.pojo.User">
        select * from user where id = #{id} and username = #{username}
    </select>


</mapper>

我们在占位符中给的是id和username两个属性,那么paramterType对应对象中一定存在这两个属性值

package study.lagou.com.persistence.test.pojo;

/**
 * @Description: 功能描述
 * @Author houjh
 * @Email: happyxiaohou@gmail.com
 * @Date: 2021-1-27 22:13
 */
public class User {
    /**
     * 主键信息
     */
    private Integer id;
    /**
     * 用户名称
     */
    private String username;
    /**
     * 用户密码
     */
    private String password;
    /**
     * 用户昵称
     */
    private String nickname;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getNickname() {
        return nickname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", username='" + username + '\'' +
                ", password='" + password + '\'' +
                ", nickname='" + nickname + '\'' +
                '}';
    }
}

至此,我们编写映射配置文件算是基本上完成了,但是上一节当中,我们在分析自定义持久层框架思路的时候,分析出自定义持久层框架的第一步就是加载配置文件,我们没有必要通过getResourceAsStream(String path)方法对配置文件加载两次,而是直接在sqlMapConfig.xml存放一份映射配置文件的全路径,后面通过解析sqlMapConfig.xml文件就可以获取到mapper.xml映射配置文件的全路径,对mapper文件进行操作,所以我们再对sqlMapConfig.xml配置文件进行完善

<configuration>

    <dataSource>
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost/mybatis"></property>
        <property name="username" value="root"></property>
        <property name="password" value="111111"></property>
    </dataSource>

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

至此,我们使用端代码编写完成

具体代码对应下载地址:https://gitee.com/happymima/mybatis.git

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,907评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,987评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,298评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,586评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,633评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,488评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,275评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,176评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,619评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,819评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,932评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,655评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,265评论 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,871评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,994评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,095评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,884评论 2 354

推荐阅读更多精彩内容