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