Mybatis之旅(序)

序中序

    本系列旨在探讨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文件中,贴入如下代码。其含义将在之后逐渐解释。

<?xml version="1.0"encoding="UTF-8"?>

<projectxmlns="http://maven.apache.org/POM/4.0.0"

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">

<modelVersion>4.0.0</modelVersion>

<groupId>org.example</groupId>

<artifactId>SecondMybatis</artifactId>

<version>1.0-SNAPSHOT</version>

<dependencies>

<dependency>

<groupId>org.mybatis</groupId>

<artifactId>mybatis</artifactId>

<version>3.2.7</version>

</dependency>

<dependency>

<groupId>mysql</groupId>

<artifactId>mysql-connector-java</artifactId>

<version>5.1.47</version>

</dependency>

<dependency>

<groupId>junit</groupId>

<artifactId>junit</artifactId>

<version>4.12</version>

<!-- <scope>test</scope>-->

</dependency>

<dependency>

<groupId>org.apache.logging.log4j</groupId>

<artifactId>log4j-api</artifactId>

<version>2.11.1</version>

</dependency>

<dependency>

<groupId>org.apache.logging.log4j</groupId>

<artifactId>log4j-core</artifactId>

<version>2.11.1</version>

</dependency>

</dependencies>

</project>   

    复制完之后,程序右下角会弹出这样的对话框:

     点击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<UserBeanVO> 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中,写入如下代码:

<?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文件 -->

<properties resource="config/mysql.properties"></properties>

<!-- 全局配置 -->

<settings>

<!-- 打开延迟加载开关 -->

<setting name="lazyLoadingEnabled" value="true"/>

<!-- 将积极加载改为消极加载即按需加载 -->

<setting name="aggressiveLazyLoading" value="false"/>

</settings>

<!-- 定义别名 -->

<typeAliases>

<!-- 一个一个地定义 -->

<!-- <typeAlias type="com.gjh.domain.User" alias="user"/> -->

<!-- 指定包名定义,别名就是类名,首字母大小写都可以 -->

<package name="com.sjj.entity"/>

</typeAliases>

<!-- 和spring整合后 environments将废除 -->

<environments default="development">

<environment id="development">

<!-- 使用JDBC事务管理,事务控制由mybatis,另一种类型为MANAGED,此配置从来不提交或回滚事务 -->

<transactionManager type="JDBC"/>

<!-- 数据库连接池,由mybatis type的值有UNPOOLED、POOLED、JNDI三种数据源

不同的数据源有不同的配置属性 -->

<dataSource type="POOLED">

<property name="driver" value="${jdbc.driver}"/>

<property name="url" value="${jdbc.url}"/>

<property name="username" value="${jdbc.username}"/>

<property name="password" value="${jdbc.password}"/>

</dataSource>

</environment>

</environments>

<mappers>

<!-- 当映射文件mapper与接口类名一致且在同一个路径下时可以用class引入 -->

<!-- <mapper class="com.sjj.dao.UserBeanMapper"/>-->

<mapper resource="mapper/UserBeanMapper.xml"/>

<!-- 通过配置文件的位置加载——一次加载一个 -->

<!-- <mapper class="com.gjh.mapper.OrdersMapper"/>-->

<!-- 通过包名加载——加载包内的所有配置文件-->

<!-- <package name="mapper"/>-->

</mappers>

</configuration>

  在log4j2.xml中,写入如下代码:

<?xml version="1.0" encoding="UTF-8"?>

<Configuration>

<Appenders>

<Console name="STDOUT" target="SYSTEM_OUT">

<PatternLayout pattern="%d %-5p [%t] %C{2} (%F:%L) - %m%n"/>

</Console>

<RollingFile name="RollingFile" fileName="logs/strutslog1.log"

filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">

<PatternLayout>

<Pattern>%d{MM-dd-yyyy} %p %c{1.} [%t] -%M-%L- %m%n</Pattern>

</PatternLayout>

<Policies>

<TimeBasedTriggeringPolicy />

<SizeBasedTriggeringPolicy size="1 KB"/>

</Policies>

<DefaultRolloverStrategy fileIndex="max" max="2"/>

</RollingFile>

</Appenders>

<Loggers>

<Logger name="com.opensymphony.xwork2" level="WAN"/>

<Logger name="org.apache.struts2" level="WAN"/>

<Root level="warn">

<AppenderRef ref="STDOUT"/>

</Root>

</Loggers>

</Configuration>

  在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:<风之谷>》中的主角,娜乌西卡的信息,已经被登记到了数据库之中。

四、总结

    谢特,怎么又有这么多字……

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容