序中序
本系列旨在探讨mybatis底层逻辑,顺便以源码出发,探究Java语言的若干机制。
同时,此系列采取工程代码开发中敏捷的原则,不会像以往一样动辄写数千字,而是以“小步快跑”的形式,快速对自己所学的知识作出反应,总结、分享出来。
本文的第一、二节讲述一些网页应用,ORM和持久化的基本概念,第三节会讲一个最简单的mybatis使用的实例(正如网上大部分博客那样)。
一、网页应用
以一个不知名博主的主页为例:https://www.jianshu.com/u/6d6837a8715a
当我们在浏览器输入上面这个链接,按下回车的时候,浏览器就会根据这个链接向相关的服务器请求数据(详情可见《无线世界》系列)。服务器会返回html,js和css文件给我们的电脑,我们电脑上的浏览器会解析这些文件,并且将的页面展示给我们看。
这种模式被成为浏览器/服务器模式(B/S Browser/Server)。另外一种常见的叫做客户端/服务器模式(C/S Client/Server),咱也不懂,咱也不去说。
但是上的博主、用户这么多,不可能为每一个人都单独建立一套html/js/css文件,于是采取了比较讨巧的做法:只用一份html/js/css文件来用作页面的展示,而将用户名,文章,动态等信息存进数据库中,为每一个用户配置一个唯一id,用这个id来关联用户的数据。
这就可以引申到MVC(Model-View-Control)模型的基本思想。M指业务模型,V指用户界面,C指控制器(用于业务模型和用户界面的同步等)。控制器处理用户的入参(比如上例中链接最后的id),控制器会根据入参到业务模型中存取数据(上例中就是取出用户“宫本花藏”的文章、评论等信息),最后在用户界面上予以展示(如上上图所示)。
二、持久化框架与ORM
在面向对象中,有三层架构的思想。对于服务器来说,用户能够接触到的往往是最外的表现层,使用MVC模式来接收用户数据,或者向用户展示数据;服务层用户用户数据的逻辑处理;而最里面的持久层用于和数据库打交道,负责数据的增删改查。
人们都说mybatis是持久层框架,就是说mybatis这个框架,是专门用来让Java程序和数据库交互的。
如果说谁发明了一个服务层框架,就是说他设计的这个框架是专门用来处理业务逻辑的。emmm思考题:这么说来,那些算法的jar包看来都能算成服务层框架了???
众所周知,服务器能正常运行是要靠代码部署在上面的。Java是常用的服务器逻辑代码。如果想要通过Java访问数据库,就必须使用到Java中的JDBC接口。传统的JDBC连接数据库方式很繁琐,要先注册数据库驱动类,确认url,账号密码,再通过DriverManager打开数据库连接,将拼接好的SQL语句以字符串的形式给Statement,得到执行结果后手动关闭数据库链接。
为了解决该问题,ORM(Object Relation Mapping,对象-关系映射)框架应运而生。java代码可以根据映射配置文件,完成数据在对象模型(Java语言)与关系模型(SQL语言)之间的映射(比如java中的String类可以转换成SQL中的VARCHAR数据类型)。
另外,频繁的新建、关闭数据库连接会极大的消耗资源,成为系统的性能瓶颈。ORM框架的另外一个特色功能就是建立数据库连接池:专门创建若干给数据库连接,当某进程有连接请求时就将连接分配给该进程,用完了也不释放连接,而是将连接对象再放进池子里面来,供别的进程使用。
在书写代码的过程中,千千万万的程序员上演着可歌可泣的与bug斗智斗勇的故事,闻者流泪,听者伤心。代码既要处理用户输入,又要执行业务逻辑,存取数据;同时内要考虑负载均衡进程冲突资源抢占,外要预防非法输入网络攻击安全漏洞,忙不胜收。
于是,伟大的程序员先贤们针对代码混乱的问题,建立了各种框架。这些框架划分不同功能的代码,让它们各司其职。这些代码大多可以不依赖其他服务独立运行,并且提供某些特定服务。随着时代的发展,不同功能的代码还会划分层次,分出哪些用于公共服务,哪些用于应用实现。
比如上面这个,分成了基础支持层,核心处理层和接口层3层。每一层各个模块的功能目的都不相同。这个框架就是mybatis整体的框架,也是接下来我们的探索旅程的全景图。
不过由于本文是序文,所以本文只是给了一个最简单的mybatis使用的示例。
三、mybatis示例
所需:mysql数据库,集成开发环境IDEA(社区版即可),maven,预先装好的JDK1.8,一个能联网的环境
3.1 MySQL
既然mybatis是与数据库打交道的框架,就必须安装数据库。以免费的mysql为例,需要先注册ORACLE账户,登录进行下载。没有ORACLE账户的需要注册一个,是免费的。
mysql向导链接:https://www.mysql.com/cn/why-mysql/white-papers/visual-guide-to-installing-mysql-windows-zh/
安装的时候需要配置用户名和密码,按它给的pdf文档来即可。安装完成后,打开的界面如下所示:
点击 Local instance 字样的方框,就可以进入到页面中
进入数据库后,需要先创建schema。schema可以理解为是一个用户,一个用户可以拥有多张数据表。不同用户之间的数据表不同,并且有访问权限控制。输入以下语句创建用户:
create schema sjjdata default character set utf8 collate utf8_general_ci;
选择该用户,创建一个包含id,姓名,年龄3个字段的表,表的名字叫做student(是不是很俗)
use sjjdata;
create table student
(id int(11),
name varchar(25),
age int(11));
最终我们在mysql数据库中,创建了一个名为sjjdata的用户,在这个用户下新建了一张名为student的表。
3.2 Maven
程序语言发展至今,我们编程的时候再也不可能从零开始写。我们会依靠前人的编程成果,即调用代码库的形式,提升我们的编程效率。在Java中,我们通常以使用jar包的形式,调用前人写好的方法,而忽略实现该方法的细节(这种行为也被成为引用依赖)。jar包是一群java文件和配置文件的集合。除了jar包以外,随着规模的不同,还有tar包,war包。一个jar包举例:
发布jar包的团队或个人通常会每隔一段时间更新这个包, 修复一些bug或者新增一些功能。为了便于区分,他们为每一次发布的jar包取了一个版本号,比如上图中的mybatis包的版本是3.2.7。
其他程序员在编写程序引用jar包的时候,常常因为jar包的版本不对而引发bug。为了解决此类问题,有很多专门解决依赖的工具,maven就是其中一个。它通过xml文件(往往这个文件的名字叫做pom.xml)确定具体引用哪个版本的哪个包,就可以从它的maven仓库中取出此包,供程序员调用。这么做的好处是在程序移植的时候,只要把对应的xml文件也移植过去,其他机器上就算没有这个jar包,或者没有特定版本的jar包,也可以通过xml文件到maven仓库中找到它。
maven是apache的开源项目,可以在官网下载。https://maven.apache.org
下载过来的是一个zip文件,解压,随便放在某个目录就可以。为了避免不必要的字符编码问题,建议放在英文路径下。
当然,有时候IDEA自己在安装的时候可能已经自带了maven了……
3.3 Java
IDEA也有官网,也有免费使用的版本,奉上链接:
https://www.jetbrains.com/products.html#type=ide
笔者下载的是2019.3版本,打开界面如下所示:
进入后,点击file-->new-->project,选择maven
点击next,确定工程名,直接finish
在工程的出生地,就可以发现布局的很有规律的目录,以及一个pom.xml文件,这些都是maven规范自动生成的。
在xml文件中,贴入如下代码。其含义将在之后逐渐解释。
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
org.example
SecondMybatis
1.0-SNAPSHOT
org.mybatis
mybatis
3.2.7
mysql
mysql-connector-java
5.1.47
junit
junit
4.12
org.apache.logging.log4j
log4j-api
2.11.1
org.apache.logging.log4j
log4j-core
2.11.1
复制完之后,程序右下角会弹出这样的对话框:
点击Import Changes,就可以加载pom.xml指定的jar包
按照如下格式建立目录(直接右键文件夹,new,选择java或者package,这里不再赘述):
在UserBeanMapper中,写入如下代码:
package com.sjj.dao;
import com.sjj.entity.UserBeanVO;
import java.util.List;
public interface UserBeanMapper {
UserBeanVO queryUserByName(String name);
List
queryAll(); int insertUser(UserBeanVO userBean);
int deleteUserByName(String name);
int updateUserById(UserBeanVO userBean,int id);
}
在UserBeanVO中,写入如下代码:
package com.sjj.entity;
/**
* @author jun
* @date 2019/1/31
*/
public class UserBeanVO {
private int id;
private String name;
private int age;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
}
在UserBeanService中,写入如下代码:
package com.sjj.service;
public class UserBeanService {
}
在DBTools中,写入如下代码:
package com.sjj;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.Reader;
/**
* @author jun
* @date 2019/1/31
*/
public class DBtools {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
Reader reader = Resources.getResourceAsReader("config/SqlMapConfig.xml");
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
} catch (IOException e) {
e.printStackTrace();
}
}
public static SqlSessionFactory getSqlSessionFactory(){
return sqlSessionFactory;
}
}
在mysql.properties中,写入如下代码(注意,这里需要根据没人配置的不同有所更改):
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/sjjdata?useSSL=false
jdbc.username=你的账号,一般是root
jdbc.password=你的密码,安装的时候你自己配的
在SqlMapperConfig.xml中,写入如下代码:
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
在log4j2.xml中,写入如下代码:
filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">
%d{MM-dd-yyyy} %p %c{1.} [%t] -%M-%L- %m%n
在appTest中,写入如下代码:
package com.sjj.test;
import com.sjj.DBtools;
import com.sjj.dao.UserBeanMapper;
import com.sjj.entity.UserBeanVO;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
public class appTest {
public static void main(String[] args){
//1.创建sqlsessionFactory
SqlSessionFactory sqlSessionFactory = DBtools.getSqlSessionFactory();
//2.创建SqlSession
SqlSession session = sqlSessionFactory.openSession();
//3.session 中创建相应的接口代理类,即mapper对象
UserBeanMapper userBeanMapper = session.getMapper(UserBeanMapper.class);
System.out.println(userBeanMapper.queryAll());
try {
System.out.println(userBeanMapper.deleteUserByName("jun"));
UserBeanVO u1 = new UserBeanVO();
u1.setAge(16);
u1.setName("Nausicaa");
u1.setId(1);
System.out.println(userBeanMapper.insertUser(u1));
session.commit();//一定要提交,不然所有增删改操作不会生效的
System.out.println(userBeanMapper.queryAll());
}catch (Exception e){
session.rollback();//回滚
}
}
}
最终运行程序,跑出的结果如下所示:
去MySQL里面查,可以发现,上一篇推送《读书笔记6:<风之谷>》中的主角,娜乌西卡的信息,已经被登记到了数据库之中。
四、总结
谢特,怎么又有这么多字……