• 【MyBatis源码分析】环境准备


    前言

    之前一段时间写了【Spring源码分析】系列的文章,感觉对Spring的原理及使用各方面都掌握了不少,趁热打铁,开始下一个系列的文章【MyBatis源码分析】,在【MyBatis源码分析】文章的基础之上,可以继续分析数据库连接池、Spring整合MyBatis源码、Spring事物管理tx等等。

    【MyBatis源码分析】整个文章结构相较【Spring源码分析】稍微改一改,后者会在每一部分源码分析的开头列出要分析的源码的实例,比如:

    • 分析Bean流程加载,就会先写Bean的代码示例及xml中配置Bean的示例
    • 分析AOP流程,就会先写AOP的代码及xml中配置AOP的示例

    【MyBatis源码分析】系列文章,在本文中会一次性地将所有的代码示例写完,之后就针对这些代码一部分一部分进行分析,探究MyBatis原理。

    其实MyBatis代码示例,我在之前的文章里面记得至少写了两遍,完全可以拿之前的文章作为例子,但是这里要再写一遍,就希望分享给网友朋友们一点态度:作为一个程序员,还是应当多去写代码,多去实践,不要认为之前写过的东西就没必要再写一遍,之前懂的内容就没必要再学习一遍,温故知新,写得越多用得越熟练,思考得越多成长越快

    SQL准备

    首先还是建表,这里准备一段SQL:

     1 drop table if exists mail;
     2 
     3 create table mail 
     4 (
     5   id          int         auto_increment not null comment '主键id',
     6   create_time datetime    not null  comment '创建时间',
     7   modify_time timestamp   not null  comment '修改时间',
     8   web_id      int         not null  comment '站点id,1表示新浪,2表示QQ,3表示搜狐,4表示火狐',
     9   mail        varchar(50) not null  comment '邮箱名',
    10   use_for     varchar(30)           comment '邮箱用途',
    11   primary key(id),
    12   index use_for(use_for),
    13   unique index web_id_mail(web_id, mail)
    14 )charset=utf8 engine=innodb comment='邮箱表';

    很多人可能有不止一个邮箱,新浪的、腾讯的、搜狐的,每个邮箱可能又有不一样的用途,这里就拿邮箱做一个例子。

    建立每张表的时候应当注意唯一约束,像这里,一个网站下的邮箱一定是唯一的,不可能在新浪下同时存在两个名为"123@sina.com"的邮箱名,因此对web_id+mail做唯一索引。

    建立实体类

    建立完毕SQL之后,第二步一定是为表建立在Java层面的实体类,在SQL层面不同的词语使用"_"分割,在Java层面不同的词语则使用驼峰命名法

    • 对于类名/接口名/枚举类,使用首字母大写的驼峰命名法
    • 对于字段,使用首字母小写的驼峰命名法

    现在为mail表建立实体类:

     1 public class Mail {
     2 
     3     /**
     4      * 主键id
     5      */
     6     private long id;
     7     
     8     /**
     9      * 创建时间
    10      */
    11     private Date createTime;
    12     
    13     /**
    14      * 修改时间
    15      */
    16     private Date modifyTime;
    17     
    18     /**
    19      * 网站id,1表示新浪,2表示QQ,3表示搜狐,4表示火狐
    20      */
    21     private int webId;
    22     
    23     /**
    24      * 邮箱
    25      */
    26     private String mail;
    27     
    28     /**
    29      * 用途
    30      */
    31     private String useFor;
    32     
    33     public Mail() {
    34         
    35     }
    36     
    37     public Mail(int webId, String mail, String useFor) {
    38         this.webId = webId;
    39         this.mail = mail;
    40         this.useFor = useFor;
    41     }
    42 
    43     public long getId() {
    44         return id;
    45     }
    46 
    47     public void setId(long id) {
    48         this.id = id;
    49     }
    50 
    51     public Date getCreateTime() {
    52         return createTime;
    53     }
    54 
    55     public void setCreateTime(Date createTime) {
    56         this.createTime = createTime;
    57     }
    58 
    59     public Date getModifyTime() {
    60         return modifyTime;
    61     }
    62 
    63     public void setModifyTime(Date modifyTime) {
    64         this.modifyTime = modifyTime;
    65     }
    66 
    67     public int getWebId() {
    68         return webId;
    69     }
    70 
    71     public void setWebId(int webId) {
    72         this.webId = webId;
    73     }
    74 
    75     public String getMail() {
    76         return mail;
    77     }
    78 
    79     public void setMail(String mail) {
    80         this.mail = mail;
    81     }
    82 
    83     public String getUseFor() {
    84         return useFor;
    85     }
    86 
    87     public void setUseFor(String useFor) {
    88         this.useFor = useFor;
    89     }
    90 
    91     @Override
    92     public String toString() {
    93         return "MailDO [id=" + id + ", createTime=" + createTime + ", modifyTime=" + modifyTime + ", webId=" + webId + ", mail=" + mail + ", useFor=" 
    94                 + useFor + "]";
    95     }
    96     
    97 }

    注意实体类一定要重写toStirng()方法,便于定位问题。

    建立数据访问层

    下一步,个人喜好是建立数据访问层,对于数据访问层通常有如下约定:

    • 数据访问层使用Dao命名,它定义了对表的基本增删改查操作
    • 数据访问层之上使用Service命名,它的作用是对于数据库的多操作进行组合,比如先查再删、先删再增、先改再查再删等等,这些操作不会放在Dao层面去操作,而会放在Service层面去进行组合

    那么,首先定义一个MailDao,我定义增删改查五个方法,其中查询两个方法,一个查单个,一个查列表:

     1 public interface MailDao {
     2 
     3     /**
     4      * 插入一条邮箱信息
     5      */
     6     public long insertMail(Mail mail);
     7     
     8     /**
     9      * 删除一条邮箱信息
    10      */
    11     public int deleteMail(long id);
    12     
    13     /**
    14      * 更新一条邮箱信息
    15      */
    16     public int updateMail(Mail mail);
    17     
    18     /**
    19      * 查询邮箱列表
    20      */
    21     public List<Mail> selectMailList();
    22     
    23     /**
    24      * 根据主键id查询一条邮箱信息
    25      */
    26     public Mail selectMailById(long id);
    27     
    28 }

    接着是Dao的实现类,通常以"Impl"结尾,"Impl"是关键字"Implements"的缩写,表示接口实现类的意思。MailDao的实现类就命名为MailDaoImpl了,代码为:

     1 public class MailDaoImpl implements MailDao {
     2 
     3     private static final String NAME_SPACE = "MailMapper.";
     4     
     5     private static SqlSessionFactory ssf;
     6     
     7     private static Reader reader;
     8     
     9     static {
    10         try {
    11             reader = Resources.getResourceAsReader("mybatis/config.xml");
    12             ssf = new SqlSessionFactoryBuilder().build(reader);
    13         } 
    14         catch (IOException e) {
    15             e.printStackTrace();
    16         }
    17     }
    18     
    19     @Override
    20     public long insertMail(Mail mail) {
    21         SqlSession ss = ssf.openSession();
    22         try {
    23             int rows = ss.insert(NAME_SPACE + "insertMail", mail);
    24             ss.commit();
    25             if (rows > 0) {
    26                 return mail.getId();
    27             }
    28             return 0;
    29         } catch (Exception e) {
    30             ss.rollback();
    31             return 0;
    32         } finally {
    33             ss.close();
    34         }
    35     }
    36 
    37     @Override
    38     public int deleteMail(long id) {
    39         SqlSession ss = ssf.openSession();
    40         try {
    41             int rows = ss.delete(NAME_SPACE + "deleteMail",  id);
    42             ss.commit();
    43             return rows;
    44         } catch (Exception e) {
    45             ss.rollback();
    46             return 0;
    47         } finally {
    48             ss.close();
    49         }
    50     }
    51 
    52     @Override
    53     public int updateMail(Mail mail) {
    54         SqlSession ss = ssf.openSession();
    55         try {
    56             int rows = ss.update(NAME_SPACE + "updateMail", mail);
    57             ss.commit();
    58             return rows;
    59         } catch (Exception e) {
    60             ss.rollback();
    61             return 0;
    62         } finally {
    63             ss.close();
    64         }
    65     }
    66 
    67     @Override
    68     public List<Mail> selectMailList() {
    69         SqlSession ss = ssf.openSession();
    70         try {
    71             return ss.selectList(NAME_SPACE + "selectMailList");
    72         } finally {
    73             ss.close();
    74         }
    75     }
    76 
    77     @Override
    78     public Mail selectMailById(long id) {
    79         SqlSession ss = ssf.openSession();
    80         try {
    81             return ss.selectOne(NAME_SPACE + "selectMailById", id);
    82         } finally {
    83             ss.close();
    84         }
    85     }
    86     
    87 }

    具体代码就不看了,会在第二篇文章开始分析。

    建立MyBatis配置文件

    接着就是建立MyBatis的配置文件了,MyBatis的配置文件有两个,一个是环境的配置config.xml,一个是具体SQL的编写mail.xml。首先看一下config.xml:

     1 <?xml version="1.0" encoding="UTF-8" ?>
     2 <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
     3 "http://mybatis.org/dtd/mybatis-3-config.dtd">
     4 
     5 <configuration>
     6 
     7     <properties resource="properties/db.properties" />
     8 
     9     <settings>
    10         <setting name="cacheEnabled" value="true" />
    11         <setting name="lazyLoadingEnabled" value="true"/>
    12         <setting name="useGeneratedKeys" value="true"/>
    13     </settings>
    14 
    15     <typeAliases>
    16         <typeAlias alias="Mail" type="org.xrq.mybatis.pojo.Mail"/>
    17     </typeAliases>
    18 
    19     <environments default="development">
    20         <environment id="development">
    21             <transactionManager type="JDBC"/>
    22             <dataSource type="POOLED">
    23                 <property name="driver" value="${driveClass}"/>
    24                 <property name="url" value="${url}"/>
    25                 <property name="username" value="${userName}"/>
    26                 <property name="password" value="${password}"/>
    27             </dataSource>
    28         </environment>
    29     </environments>
    30     
    31     <mappers>
    32         <mapper resource="mybatis/mail.xml"/>
    33     </mappers>
    34     
    35 </configuration>

    接着是编写SQL语句的mail.xml:

     1 <?xml version="1.0" encoding="UTF-8" ?>
     2 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     3 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
     4 
     5 <mapper namespace="MailMapper">
     6 
     7     <resultMap type="Mail" id="MailResultMap">
     8         <result column="id" property="id" />
     9         <result column="create_time" property="createTime" />
    10         <result column="modify_time" property="modifyTime" />
    11         <result column="web_id" property="webId" />
    12         <result column="mail" property="mail" />
    13         <result column="use_for" property="useFor" />
    14     </resultMap>
    15 
    16     <sql id="fields">
    17         id, create_time, modify_time, web_id, mail, use_for
    18     </sql>
    19     
    20     <sql id="fields_value">
    21         null, now(), now(), #{webId}, #{mail}, #{useFor}
    22     </sql>
    23 
    24     <insert id="insertMail" parameterType="Mail" useGeneratedKeys="true" keyProperty="id">
    25         insert into mail(
    26             <include refid="fields" />
    27         ) values(
    28             <include refid="fields_value" />
    29         );
    30     </insert>
    31 
    32     <delete id="deleteMail" parameterType="java.lang.Long">
    33         delete from mail where id = #{id};
    34     </delete>
    35     
    36     <update id="updateMail" parameterType="Mail">
    37         update mail 
    38         <set>
    39             <if test="web_id != 0">
    40                 web_id = #{webId}
    41             </if>
    42             <if test="mail != null">
    43                 mail = #{mail}
    44             </if>
    45             <if test="use_for != null">
    46                 use_for = #{useFor}
    47             </if>
    48         </set>
    49         where id = #{id};
    50     </update>
    51 
    52     <select id="selectMailList" resultMap="MailResultMap">
    53         select <include refid="fields" /> from mail;
    54     </select>
    55     
    56     <select id="selectMailById" resultMap="MailResultMap" parameterType="java.lang.Long">
    57         select <include refid="fields" /> from mail where id = #{id};
    58     </select>
    59     
    60 </mapper>

    这个mail.xml我尽量写得全一点,这样后面分析的时候都会有代码示例,mail.xml中包括:

    • resultMap
    • <sql>标签
    • 插入主键返回主键id
    • 动态sql

    建立单元测试代码

    软件的正确性离不开良好的测试,通常测试有两种方式:

    • 写main函数,这种方式我基本不使用,除非是测试一个很小的功能点比如Math.round这种,这种代码写完我也会直接删除的,不会留着提交到代码库上
    • 使用单元测试工具比如junit,这是我常用的方式

    其实很多公司的JD上面也有写着"能编写良好的单元测试代码",跑main函数的方式我个人真的是不太推荐。

    接着看一下单元测试代码:

     1 public class TestMyBatis {
     2 
     3     private static MailDao mailDao;
     4     
     5     static {
     6         mailDao = new MailDaoImpl();
     7     }
     8     
     9     @Test
    10     public void testInsert() {
    11         Mail mail1 = new Mail(1, "123@sina.com", "个人使用");
    12         Mail mail2 = new Mail(2, "123@qq.com", "企业使用");
    13         Mail mail3 = new Mail(3, "123@sohu.com", "注册账号使用");
    14         System.out.println(mailDao.insertMail(mail1));
    15         System.out.println(mailDao.insertMail(mail2));
    16         System.out.println(mailDao.insertMail(mail3));
    17     }
    18     
    19     @Test
    20     public void testDelete() {
    21         System.out.println(mailDao.deleteMail(1));
    22     }
    23     
    24     @Test
    25     public void testUpdate() {
    26         Mail mail = new Mail(2, "123@qq.com", "个人使用");
    27         mail.setId(2);
    28         System.out.println(mailDao.updateMail(mail));
    29         System.out.println(mailDao.selectMailById(2));
    30     }
    31     
    32     @Test
    33     public void testSelectOne() {
    34         System.out.println(mailDao.selectMailById(2));
    35     }
    36     
    37     @Test
    38     public void testSelectList() {
    39         List<Mail> mailList = mailDao.selectMailList();
    40         if (mailList != null && mailList.size() != 0) {
    41             for (Mail mail : mailList) {
    42                 System.out.println(mail);
    43             }
    44         }
    45     }
    46     
    47 }

    正确的情况下,应当五个方法跑出来全部是绿色的进度条。

    当然,单元测试也可以单独跑每一个,我个人使用Eclipse/MyEclipse,都是支持的,相信其他IDE肯定也是支持这个功能的。

  • 相关阅读:
    linux启动init流程(转)
    .bash_profile .bashrc profile 文件的作用的执行顺序(转)
    Linux常用命令
    面试中常见的问题
    systemd启动过程(转)
    .bashrc文件是干什么的(转)
    关于 profile文件(转)
    从MVC框架看MVC架构的设计(转)
    Java高级软件工程师面试考纲(转)
    关于Python中的lambda
  • 原文地址:https://www.cnblogs.com/xrq730/p/6792031.html
Copyright © 2020-2023  润新知