MyBatis源码分析之环境准备篇
前言
之前一段时间写了【Spring源码分析】系列的文章,感觉对Spring的原理及使用各方面都掌握了不少,趁热打铁,开始下一个系列的文章【MyBatis源码分析】,在【MyBatis源码分析】文章的基础之上,可以继续分析数据库连接池、Spring整合MyBatis源码、Spring事物管理tx等等。
【MyBatis源码分析】整个文章结构相较【Spring源码分析】稍微改一改,后者会在每一部分源码分析的开头列出要分析的源码的实例,比如:
分析Bean流程加载,就会先写Bean的代码示例及xml中配置Bean的示例
分析AOP流程,就会先写AOP的代码及xml中配置AOP的示例
【MyBatis源码分析】系列文章,在本文中会一次性地将所有的代码示例写完,之后就针对这些代码一部分一部分进行分析,探究MyBatis原理。
其实MyBatis代码示例,我在之前的文章里面记得至少写了两遍,完全可以拿之前的文章作为例子,但是这里要再写一遍,就希望分享给网友朋友们一点态度:作为一个程序员,还是应当多去写代码,多去实践,不要认为之前写过的东西就没必要再写一遍,之前懂的内容就没必要再学习一遍,温故知新,写得越多用得越熟练,思考得越多成长越快。
SQL准备
首先还是建表,这里准备一段SQL:
drop table if exists mail;
create table mail (
id int auto_increment not null comment '主键id',
create_time datetime not null comment '创建时间',
modify_time timestamp not null comment '修改时间',
web_id int not null comment '站点id,1表示新浪,2表示QQ,3表示搜狐,4表示火狐',
mail varchar(50) not null comment '邮箱名',
use_for varchar(30) comment '邮箱用途',
primary key(id),
index use_for(use_for),
unique index web_id_mail(web_id, mail)
)charset=utf8 engine=innodb comment='邮箱表';
很多人可能有不止一个邮箱,新浪的、腾讯的、搜狐的,每个邮箱可能又有不一样的用途,这里就拿邮箱做一个例子。
建立每张表的时候应当注意唯一约束,像这里,一个网站下的邮箱一定是唯一的,不可能在新浪下同时存在两个名为”123@sina.com”的邮箱名,因此对web_id+mail做唯一索引。
建立实体类
建立完毕SQL之后,第二步一定是为表建立在Java层面的实体类,在SQL层面不同的词语使用”_”分割,在Java层面不同的词语则使用驼峰命名法:
对于类名/接口名/枚举类,使用首字母大写的驼峰命名法
对于字段,使用首字母小写的驼峰命名法
现在为mail表建立实体类:
public class Mail {
/**
* 主键id
*/
private long id;
/**
* 创建时间
*/
private Date createTime;
/**
* 修改时间
*/
private Date modifyTime;
/**
* 网站id,1表示新浪,2表示QQ,3表示搜狐,4表示火狐
*/
private int webId;
/**
* 邮箱
*/
private String mail;
/**
* 用途
*/
private String useFor;
public Mail() {}
public Mail(int webId, String mail, String useFor) {
this.webId = webId;
this.mail = mail;
this.useFor = useFor;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getModifyTime() {
return modifyTime;
}
public void setModifyTime(Date modifyTime) {
this.modifyTime = modifyTime;
}
public int getWebId() {
return webId;
}
public void setWebId(int webId) {
this.webId = webId;
}
public String getMail() {
return mail;
}
public void setMail(String mail) {
this.mail = mail;
}
public String getUseFor() {
return useFor;
}
public void setUseFor(String useFor) {
this.useFor = useFor;
}
@Override
public String toString() {
return "MailDO [id=" + id + ", createTime=" + createTime + ", modifyTime=" + modifyTime + ", webId=" + webId + ", mail=" + mail + ", useFor="
+ useFor + "]";
}
}
注意实体类一定要重写toStirng()方法,便于定位问题。
建立数据访问层
下一步,个人喜好是建立数据访问层,对于数据访问层通常有如下约定:
数据访问层使用Dao命名,它定义了对表的基本增删改查操作
数据访问层之上使用Service命名,它的作用是对于数据库的多操作进行组合,比如先查再删、先删再增、先改再查再删等等,这些操作不会放在Dao层面去操作,而会放在Service层面去进行组合
那么,首先定义一个MailDao,我定义增删改查五个方法,其中查询两个方法,一个查单个,一个查列表:
public interface MailDao {
/**
* 插入一条邮箱信息
*/
public long insertMail(Mail mail);
/**
* 删除一条邮箱信息
*/
public int deleteMail(long id);
/**
* 更新一条邮箱信息
*/
public int updateMail(Mail mail);
/**
* 查询邮箱列表
*/
public List<Mail> selectMailList();
/**
* 根据主键id查询一条邮箱信息
*/
public Mail selectMailById(long id);
}
接着是Dao的实现类,通常以”Impl”结尾,”Impl”是关键字”Implements”的缩写,表示接口实现类的意思。MailDao的实现类就命名为MailDaoImpl了,代码为:
public class MailDaoImpl implements MailDao {
private static final String NAME_SPACE = "MailMapper.";
private static SqlSessionFactory ssf;
private static Reader reader;
static {
try {
reader = Resources.getResourceAsReader("mybatis/config.xml");
ssf = new SqlSessionFactoryBuilder().build(reader);
}
catch (IOException e) {
e.printStackTrace();
}
}
@Override
public long insertMail(Mail mail) {
SqlSession ss = ssf.openSession();
try {
int rows = ss.insert(NAME_SPACE + "insertMail", mail);
ss.commit();
if (rows > 0) {
return mail.getId();
}
return 0;
} catch (Exception e) {
ss.rollback();
return 0;
} finally {
ss.close();
}
}
@Override
public int deleteMail(long id) {
SqlSession ss = ssf.openSession();
try {
int rows = ss.delete(NAME_SPACE + "deleteMail", id);
ss.commit();
return rows;
} catch (Exception e) {
ss.rollback();
return 0;
} finally {
ss.close();
}
}
@Override
public int updateMail(Mail mail) {
SqlSession ss = ssf.openSession();
try {
int rows = ss.update(NAME_SPACE + "updateMail", mail);
ss.commit();
return rows;
} catch (Exception e) {
ss.rollback();
return 0;
} finally {
ss.close();
}
}
@Override
public List<Mail> selectMailList() {
SqlSession ss = ssf.openSession();
try {
return ss.selectList(NAME_SPACE + "selectMailList");
} finally {
ss.close();
}
}
@Override
public Mail selectMailById(long id) {
SqlSession ss = ssf.openSession();
try {
return ss.selectOne(NAME_SPACE + "selectMailById", id);
} finally {
ss.close();
}
}
}
具体代码就不看了,会在第二篇文章开始分析。
建立MyBatis配置文件
接着就是建立MyBatis的配置文件了,MyBatis的配置文件有两个,一个是环境的配置config.xml,一个是具体SQL的编写mail.xml。首先看一下config.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="properties/db.properties" />
<settings>
<setting name="cacheEnabled" value="true" />
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="useGeneratedKeys" value="true"/>
</settings>
<typeAliases>
<typeAlias alias="Mail" type="org.xrq.mybatis.pojo.Mail"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driveClass}"/>
<property name="url" value="${url}"/>
<property name="username" value="${userName}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mybatis/mail.xml"/>
</mappers>
</configuration>
接着是编写SQL语句的mail.xml:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="MailMapper">
<resultMap type="Mail" id="MailResultMap">
<result column="id" property="id" />
<result column="create_time" property="createTime" />
<result column="modify_time" property="modifyTime" />
<result column="web_id" property="webId" />
<result column="mail" property="mail" />
<result column="use_for" property="useFor" />
</resultMap>
<sql id="fields">
id, create_time, modify_time, web_id, mail, use_for
</sql>
<sql id="fields_value">
null, now(), now(), #{webId}, #{mail}, #{useFor}
</sql>
<insert id="insertMail" parameterType="Mail" useGeneratedKeys="true" keyProperty="id">
insert into mail(
<include refid="fields" />
) values(
<include refid="fields_value" />
);
</insert>
<delete id="deleteMail" parameterType="java.lang.Long">
delete from mail where id = #{id};
</delete>
<update id="updateMail" parameterType="Mail">
update mail
<set>
<if test="web_id != 0">
web_id = #{webId}
</if>
<if test="mail != null">
mail = #{mail}
</if>
<if test="use_for != null">
use_for = #{useFor}
</if>
</set>
where id = #{id};
</update>
<select id="selectMailList" resultMap="MailResultMap">
select <include refid="fields" /> from mail;
</select>
<select id="selectMailById" resultMap="MailResultMap" parameterType="java.lang.Long">
select <include refid="fields" /> from mail where id = #{id};
</select>
</mapper>
这个mail.xml我尽量写得全一点,这样后面分析的时候都会有代码示例,mail.xml中包括:
resultMap
<sql>标签
插入主键返回主键id
动态sql
建立单元测试代码
软件的正确性离不开良好的测试,通常测试有两种方式:
写main函数,这种方式我基本不使用,除非是测试一个很小的功能点比如Math.round这种,这种代码写完我也会直接删除的,不会留着提交到代码库上
使用单元测试工具比如junit,这是我常用的方式
其实很多公司的JD上面也有写着”能编写良好的单元测试代码”,跑main函数的方式我个人真的是不太推荐。
接着看一下单元测试代码:
public class TestMyBatis {
private static MailDao mailDao;
static {
mailDao = new MailDaoImpl();
}
@Test
public void testInsert() {
Mail mail1 = new Mail(1, "123@sina.com", "个人使用");
Mail mail2 = new Mail(2, "123@qq.com", "企业使用");
Mail mail3 = new Mail(3, "123@sohu.com", "注册账号使用");
System.out.println(mailDao.insertMail(mail1));
System.out.println(mailDao.insertMail(mail2));
System.out.println(mailDao.insertMail(mail3));
}
@Test
public void testDelete() {
System.out.println(mailDao.deleteMail(1));
}
@Test
public void testUpdate() {
Mail mail = new Mail(2, "123@qq.com", "个人使用");
mail.setId(2);
System.out.println(mailDao.updateMail(mail));
System.out.println(mailDao.selectMailById(2));
}
@Test
public void testSelectOne() {
System.out.println(mailDao.selectMailById(2));
}
@Test
public void testSelectList() {
List<Mail> mailList = mailDao.selectMailList();
if (mailList != null && mailList.size() != 0) {
for (Mail mail : mailList) {
System.out.println(mail);
}
}
}
}
正确的情况下,应当五个方法跑出来全部是绿色的进度条。
MyBatis源码分析之环境准备篇的更多相关文章
- 【MyBatis源码分析】环境准备
前言 之前一段时间写了[Spring源码分析]系列的文章,感觉对Spring的原理及使用各方面都掌握了不少,趁热打铁,开始下一个系列的文章[MyBatis源码分析],在[MyBatis源码分析]文章的 ...
- 鸿蒙内核源码分析(编译环境篇) | 编译鸿蒙看这篇或许真的够了 | 百篇博客分析OpenHarmony源码 | v50.06
百篇博客系列篇.本篇为: v50.xx 鸿蒙内核源码分析(编译环境篇) | 编译鸿蒙防掉坑指南 | 51.c.h.o 编译构建相关篇为: v50.xx 鸿蒙内核源码分析(编译环境篇) | 编译鸿蒙防掉 ...
- MyBatis 源码分析系列文章导读
1.本文速览 本篇文章是我为接下来的 MyBatis 源码分析系列文章写的一个导读文章.本篇文章从 MyBatis 是什么(what),为什么要使用(why),以及如何使用(how)等三个角度进行了说 ...
- 精尽MyBatis源码分析 - Spring-Boot-Starter 源码分析
该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...
- MyBatis源码分析-SQL语句执行的完整流程
MyBatis 是支持定制化 SQL.存储过程以及高级映射的优秀的持久层框架.MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.MyBatis 可以对配置和原生Map使用简 ...
- Mybatis源码分析--关联表查询及延迟加载原理(二)
在上一篇博客Mybatis源码分析--关联表查询及延迟加载(一)中我们简单介绍了Mybatis的延迟加载的编程,接下来我们通过分析源码来分析一下Mybatis延迟加载的实现原理. 其实简单来说Myba ...
- MyBatis 源码分析系列文章合集
1.简介 我从七月份开始阅读MyBatis源码,并在随后的40天内陆续更新了7篇文章.起初,我只是打算通过博客的形式进行分享.但在写作的过程中,发现要分析的代码太多,以至于文章篇幅特别大.在这7篇文章 ...
- MyBatis 源码分析 - 插件机制
1.简介 一般情况下,开源框架都会提供插件或其他形式的拓展点,供开发者自行拓展.这样的好处是显而易见的,一是增加了框架的灵活性.二是开发者可以结合实际需求,对框架进行拓展,使其能够更好的工作.以 My ...
- MyBatis 源码分析 - 缓存原理
1.简介 在 Web 应用中,缓存是必不可少的组件.通常我们都会用 Redis 或 memcached 等缓存中间件,拦截大量奔向数据库的请求,减轻数据库压力.作为一个重要的组件,MyBatis 自然 ...
随机推荐
- JS函数与BOM
[函数的声明及调用]1.函数声明的格式:function 函数名(参数1,参数2,....){//函数体return结果;}函数名(参数1的值,参数2的值,....)>>>函数的调用 ...
- Java ssh 框架 hibernate 详细理解
Hibernate框架技术相信对大多数的 java 程序员并不陌生,数据表之间的关系如何通过Hibernate来建立,需要我们认真的分析数据表中数据项之间的交互: 数据库表的之间的关系有: (1)一对 ...
- npm install fetchmatedata慢的解决办法
最近在开发webpack工程时,第一步npm install这里超级慢,一直停着,显示:"fetchMetadata: sill mapToRegistry uri https://regi ...
- 点击滚动图片JS部分代码以及css设置注意事项
下面js代码可以实现8张图片点击左右按钮后切换的过渡动画效果 var pslul11=document.getElementById('pslul11')var pslspan1=document.g ...
- linux网络设置和虚拟机克隆转移之后Error:No suitable device found:no device found for connection 'System eth0'问题解决
以root用户登录 #vi /etc/sysconfig/network-scripts/ifcfg-eth0 #编辑配置文件,添加修改以下内容 BOOTPROTO=static #启用 ...
- Android系统--输入系统(十二)Dispatch线程_总体框架
Android系统--输入系统(十二)Dispatch线程_总体框架 1. Dispatch线程框架 我们知道Dispatch线程是分发之意,那么便可以引入两个问题:1. 发什么;2. 发给谁.这两个 ...
- string___assign
#include <iostream> #include <iterator> #include <string> int main() { std::string ...
- c#实现windows远程桌面连接程序
c#实现windows远程桌面连接程序 使用winform制作windows远程桌面连接程序,windows自带了远程桌面连接,我们需要将远程桌面连接集成 到自己的winform程序,并实现管理远程主 ...
- Android GreenDAO3.0——介绍
引言 最近,学东西比较零散,各种知识混杂,于是记下学习记录,免得又忘了. 官方网址:http://greenrobot.org/greendao/documentation/introduction/ ...
- JEESZ分布式框架简介
声明:该框架面向企业,是大型互联网分布式企业架构,后期会介绍Linux上部署高可用集群项目. 项目基础功能截图(自提供了最小部分) 介绍 1. 项目核心代码结构截图 <modules& ...