【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=‘邮箱表‘;

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

建立每张表的时候应当注意唯一约束,像这里,一个网站下的邮箱一定是唯一的,不可能在新浪下同时存在两个名为"[email protected]"的邮箱名,因此对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
44
45     public long getId() {
46         return id;
47     }
48
49     public void setId(long id) {
50         this.id = id;
51     }
52
53     public Date getCreateTime() {
54         return createTime;
55     }
56
57     public void setCreateTime(Date createTime) {
58         this.createTime = createTime;
59     }
60
61     public Date getModifyTime() {
62         return modifyTime;
63     }
64
65     public void setModifyTime(Date modifyTime) {
66         this.modifyTime = modifyTime;
67     }
68
69     public int getWebId() {
70         return webId;
71     }
72
73     public void setWebId(int webId) {
74         this.webId = webId;
75     }
76
77     public String getMail() {
78         return mail;
79     }
80
81     public void setMail(String mail) {
82         this.mail = mail;
83     }
84
85     public String getUseFor() {
86         return useFor;
87     }
88
89     public void setUseFor(String useFor) {
90         this.useFor = useFor;
91     }
92
93     @Override
94     public String toString() {
95         return "MailDO [id=" + id + ", createTime=" + createTime + ", modifyTime=" + modifyTime + ", webId=" + webId + ", mail=" + mail + ", useFor="
96                 + useFor + "]";
97     }
98
99 }

注意实体类一定要重写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     <typeAliases>
 8         <typeAlias alias="Mail" type="org.xrq.mybatis.pojo.Mail"/>
 9     </typeAliases>
10
11     <environments default="development">
12         <environment id="development">
13             <transactionManager type="JDBC"/>
14             <dataSource type="POOLED">
15                 <property name="driver" value="com.mysql.jdbc.Driver"/>
16                 <property name="url" value="jdbc:mysql://localhost:3306/test"/>
17                 <property name="username" value="root"/>
18                 <property name="password" value="root"/>
19             </dataSource>
20         </environment>
21     </environments>
22
23     <mappers>
24         <mapper resource="mybatis/mail.xml"/>
25     </mappers>
26
27 </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, "[email protected]", "个人使用");
12         Mail mail2 = new Mail(2, "[email protected]", "企业使用");
13         Mail mail3 = new Mail(3, "[email protected]", "注册账号使用");
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, "[email protected]", "个人使用");
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肯定也是支持这个功能的。

时间: 2024-12-28 06:19:47

【MyBatis源码分析】环境准备的相关文章

Mybatis源码分析:环境设置Environment

Mybatis环境配置类Environment 在初始化Mybatis框架时,需要配置environment元素,该元素允许有多个,方便我们在不同环境下切换后端的存储.拥有一个id属性用于该环境,此id必须唯一,否则后续配置会覆盖前面的配置,该元素拥有两个子元素(事务管理器),(数据源),这两个元素后续会进行讲解,目前只需要记住名称即可.如下便是的相关配置,该配置定义了dev,test,prod三种环境 <configuration> <!--引入外部资源 --> <prop

MyBatis源码分析-SQL语句执行的完整流程

MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录.如何新建MyBatis源码工程请点击MyBatis源码分析-IDEA新建MyBatis源码工程. MyBatis框架主要完成的是以下2件事情: 根据JD

Mybatis源码分析

MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录.如何新建MyBatis源码工程请点击MyBatis源码分析-IDEA新建MyBatis源码工程. MyBatis框架主要完成的是以下2件事情: 根据JD

Mybatis源码分析之Cache二级缓存原理 (五)

一:Cache类的介绍 讲解缓存之前我们需要先了解一下Cache接口以及实现MyBatis定义了一个org.apache.ibatis.cache.Cache接口作为其Cache提供者的SPI(ServiceProvider Interface) ,所有的MyBatis内部的Cache缓存,都应该实现这一接口 Cache的实现类中,Cache有不同的功能,每个功能独立,互不影响,则对于不同的Cache功能,这里使用了装饰者模式实现. 看下cache的实现类,如下图: 1.FIFOCache:先进

mybatis源码分析(一)

mybatis源码分析(sqlSessionFactory生成过程) 1. mybatis框架在现在各个IT公司的使用不用多说,这几天看了mybatis的一些源码,赶紧做个笔记. 2. 看源码从一个demo引入如下: public class TestApp { private static SqlSessionFactory sqlSessionFactory; static { InputStream inputStream; String resource = "mybatis-confi

MyBatis源码分析-MyBatis初始化流程

MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简单的 XML 或注解,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录.如何新建MyBatis源码工程请点击MyBatis源码分析-IDEA新建MyBatis源码工程. MyBatis初始化的过程也就是创建Configura

mybatis源码分析之cache创建

XMLMapperBuilder.java //解析<cache /> 配置元素,创建cache对象 private void cacheElement(XNode context) throws Exception {     if (context != null) {       String type = context.getStringAttribute("type", "PERPETUAL");       Class<? exten

【MyBatis源码分析】select源码分析及小结

示例代码 之前的文章说过,对于MyBatis来说insert.update.delete是一组的,因为对于MyBatis来说它们都是update:select是一组的,因为对于MyBatis来说它就是select. 本文研究一下select的实现流程,示例代码为: 1 public void testSelectOne() { 2 System.out.println(mailDao.selectMailById(8)); 3 } selectMailById方法的实现为: 1 public M

MyBatis 源码分析 - 配置文件解析过程

* 本文速览 由于本篇文章篇幅比较大,所以这里拿出一节对本文进行快速概括.本篇文章对 MyBatis 配置文件中常用配置的解析过程进行了较为详细的介绍和分析,包括但不限于settings,typeAliases和typeHandlers等,本文的篇幅也主要在对这三个配置解析过程的分析上.下面,我们来一起看一下本篇文章的目录结构. 从目录上可以看出,2.3节.2.5节和2.8节的内容比较多.其中2.3节是关于settings配置解析过程的分析,除了对常规的 XML 解析过程分析,本节额外的分析了元