【MyBatis原始碼分析】環境準備

五月的倉頡發表於2017-05-01

前言

之前一段時間寫了【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肯定也是支援這個功能的。

 

相關文章