序中序
? ? 本系列旨在探討mybatis底層邏輯,順便以源碼出發(fā),探究Java語言的若干機制禀挫。
? ? 同時,此系列采取工程代碼開發(fā)中敏捷的原則拓颓,不會像以往一樣動輒寫數(shù)千字语婴,而是以“小步快跑”的形式,快速對自己所學(xué)的知識作出反應(yīng)驶睦,總結(jié)砰左、分享出來。
? ? 本文的第一场航、二節(jié)講述一些網(wǎng)頁應(yīng)用缠导,ORM和持久化的基本概念,第三節(jié)會講一個最簡單的mybatis使用的實例(正如網(wǎng)上大部分博客那樣)旗闽。
一酬核、網(wǎng)頁應(yīng)用
? ? 以簡書一個不知名博主的主頁為例:http://www.reibang.com/u/6d6837a8715a
? ? 當(dāng)我們在瀏覽器輸入上面這個鏈接,按下回車的時候适室,瀏覽器就會根據(jù)這個鏈接向相關(guān)的服務(wù)器請求數(shù)據(jù)(詳情可見《無線世界》系列)嫡意。服務(wù)器會返回html,js和css文件給我們的電腦捣辆,我們電腦上的瀏覽器會解析這些文件蔬螟,并且將簡書的頁面展示給我們看。
? ? 這種模式被成為瀏覽器/服務(wù)器模式(B/S Browser/Server)汽畴。另外一種常見的叫做客戶端/服務(wù)器模式(C/S Client/Server)旧巾,咱也不懂,咱也不去說忍些。
? ? 但是簡書上的博主鲁猩、用戶這么多,簡書不可能為每一個人都單獨建立一套html/js/css文件罢坝,于是采取了比較討巧的做法:只用一份html/js/css文件來用作頁面的展示廓握,而將用戶名,文章嘁酿,動態(tài)等信息存進數(shù)據(jù)庫中隙券,為每一個用戶配置一個唯一id,用這個id來關(guān)聯(lián)用戶的數(shù)據(jù)闹司。
? ? 這就可以引申到MVC(Model-View-Control)模型的基本思想娱仔。M指業(yè)務(wù)模型,V指用戶界面游桩,C指控制器(用于業(yè)務(wù)模型和用戶界面的同步等)牲迫。控制器處理用戶的入?yún)ⅲū热缟侠泻啎溄幼詈蟮膇d)众弓,控制器會根據(jù)入?yún)⒌綐I(yè)務(wù)模型中存取數(shù)據(jù)(上例中就是取出用戶“宮本花藏”的文章恩溅、評論等信息),最后在用戶界面上予以展示(如上上圖所示)谓娃。
二脚乡、持久化框架與ORM
? ? 在面向?qū)ο笾校腥龑蛹軜?gòu)的思想滨达。對于服務(wù)器來說奶稠,用戶能夠接觸到的往往是最外的表現(xiàn)層,使用MVC模式來接收用戶數(shù)據(jù)捡遍,或者向用戶展示數(shù)據(jù)锌订;服務(wù)層用戶用戶數(shù)據(jù)的邏輯處理;而最里面的持久層用于和數(shù)據(jù)庫打交道画株,負(fù)責(zé)數(shù)據(jù)的增刪改查辆飘。
? ? 人們都說mybatis是持久層框架啦辐,就是說mybatis這個框架,是專門用來讓Java程序和數(shù)據(jù)庫交互的蜈项。
? ? 如果說誰發(fā)明了一個服務(wù)層框架芹关,就是說他設(shè)計的這個框架是專門用來處理業(yè)務(wù)邏輯的。emmm思考題:這么說來紧卒,那些算法的jar包看來都能算成服務(wù)層框架了侥衬??跑芳?
?? ?眾所周知轴总,服務(wù)器能正常運行是要靠代碼部署在上面的。Java是常用的服務(wù)器邏輯代碼博个。如果想要通過Java訪問數(shù)據(jù)庫怀樟,就必須使用到Java中的JDBC接口。傳統(tǒng)的JDBC連接數(shù)據(jù)庫方式很繁瑣盆佣,要先注冊數(shù)據(jù)庫驅(qū)動類漂佩,確認(rèn)url,賬號密碼罪塔,再通過DriverManager打開數(shù)據(jù)庫連接投蝉,將拼接好的SQL語句以字符串的形式給Statement,得到執(zhí)行結(jié)果后手動關(guān)閉數(shù)據(jù)庫鏈接征堪。
? ? 為了解決該問題瘩缆,ORM(Object Relation Mapping,對象-關(guān)系映射)框架應(yīng)運而生佃蚜。java代碼可以根據(jù)映射配置文件庸娱,完成數(shù)據(jù)在對象模型(Java語言)與關(guān)系模型(SQL語言)之間的映射(比如java中的String類可以轉(zhuǎn)換成SQL中的VARCHAR數(shù)據(jù)類型)。
? ? 另外谐算,頻繁的新建熟尉、關(guān)閉數(shù)據(jù)庫連接會極大的消耗資源,成為系統(tǒng)的性能瓶頸洲脂。ORM框架的另外一個特色功能就是建立數(shù)據(jù)庫連接池:專門創(chuàng)建若干給數(shù)據(jù)庫連接斤儿,當(dāng)某進程有連接請求時就將連接分配給該進程,用完了也不釋放連接恐锦,而是將連接對象再放進池子里面來往果,供別的進程使用。
? ? 在書寫代碼的過程中一铅,千千萬萬的程序員上演著可歌可泣的與bug斗智斗勇的故事陕贮,聞?wù)吡鳒I,聽者傷心潘飘。代碼既要處理用戶輸入肮之,又要執(zhí)行業(yè)務(wù)邏輯掉缺,存取數(shù)據(jù);同時內(nèi)要考慮負(fù)載均衡進程沖突資源搶占戈擒,外要預(yù)防非法輸入網(wǎng)絡(luò)攻擊安全漏洞攀圈,忙不勝收。
? ? 于是峦甩,偉大的程序員先賢們針對代碼混亂的問題,建立了各種框架现喳。這些框架劃分不同功能的代碼凯傲,讓它們各司其職。這些代碼大多可以不依賴其他服務(wù)獨立運行嗦篱,并且提供某些特定服務(wù)冰单。隨著時代的發(fā)展,不同功能的代碼還會劃分層次灸促,分出哪些用于公共服務(wù)诫欠,哪些用于應(yīng)用實現(xiàn)。
? ? 比如上面這個浴栽,分成了基礎(chǔ)支持層荒叼,核心處理層和接口層3層。每一層各個模塊的功能目的都不相同典鸡。這個框架就是mybatis整體的框架被廓,也是接下來我們的探索旅程的全景圖。
? ? 不過由于本文是序文萝玷,所以本文只是給了一個最簡單的mybatis使用的示例嫁乘。
三、mybatis示例
?? ?所需:mysql數(shù)據(jù)庫球碉,集成開發(fā)環(huán)境IDEA(社區(qū)版即可)蜓斧,maven,預(yù)先裝好的JDK1.8睁冬,一個能聯(lián)網(wǎng)的環(huán)境
3.1 MySQL
? ? 既然mybatis是與數(shù)據(jù)庫打交道的框架挎春,就必須安裝數(shù)據(jù)庫。以免費的mysql為例豆拨,需要先注冊O(shè)RACLE賬戶搂蜓,登錄進行下載。沒有ORACLE賬戶的需要注冊一個辽装,是免費的帮碰。
? ? mysql向?qū)ф溄樱?a target="_blank">https://www.mysql.com/cn/why-mysql/white-papers/visual-guide-to-installing-mysql-windows-zh/
? ? 安裝的時候需要配置用戶名和密碼,按它給的pdf文檔來即可拾积。安裝完成后殉挽,打開的界面如下所示:
????點擊 Local instance 字樣的方框丰涉,就可以進入到頁面中
? ? 進入數(shù)據(jù)庫后,需要先創(chuàng)建schema斯碌。schema可以理解為是一個用戶一死,一個用戶可以擁有多張數(shù)據(jù)表。不同用戶之間的數(shù)據(jù)表不同傻唾,并且有訪問權(quán)限控制投慈。輸入以下語句創(chuàng)建用戶:
?? ?create schema sjjdata default character set utf8 collate utf8_general_ci;
? ? 選擇該用戶,創(chuàng)建一個包含id冠骄,姓名伪煤,年齡3個字段的表,表的名字叫做student(是不是很俗)
use sjjdata;
?? ?create table student
?? ?(id int(11),
?? ?name varchar(25),
?? ?age int(11));
????最終我們在mysql數(shù)據(jù)庫中凛辣,創(chuàng)建了一個名為sjjdata的用戶抱既,在這個用戶下新建了一張名為student的表。
3.2 Maven
? ? 程序語言發(fā)展至今扁誓,我們編程的時候再也不可能從零開始寫防泵。我們會依靠前人的編程成果,即調(diào)用代碼庫的形式蝗敢,提升我們的編程效率捷泞。在Java中,我們通常以使用jar包的形式寿谴,調(diào)用前人寫好的方法肚邢,而忽略實現(xiàn)該方法的細(xì)節(jié)(這種行為也被成為引用依賴)。jar包是一群java文件和配置文件的集合拭卿。除了jar包以外骡湖,隨著規(guī)模的不同,還有tar包峻厚,war包响蕴。一個jar包舉例:
? ? 發(fā)布jar包的團隊或個人通常會每隔一段時間更新這個包, 修復(fù)一些bug或者新增一些功能惠桃。為了便于區(qū)分浦夷,他們?yōu)槊恳淮伟l(fā)布的jar包取了一個版本號,比如上圖中的mybatis包的版本是3.2.7辜王。
? ? 其他程序員在編寫程序引用jar包的時候劈狐,常常因為jar包的版本不對而引發(fā)bug。為了解決此類問題呐馆,有很多專門解決依賴的工具肥缔,maven就是其中一個。它通過xml文件(往往這個文件的名字叫做pom.xml)確定具體引用哪個版本的哪個包汹来,就可以從它的maven倉庫中取出此包续膳,供程序員調(diào)用改艇。這么做的好處是在程序移植的時候,只要把對應(yīng)的xml文件也移植過去坟岔,其他機器上就算沒有這個jar包谒兄,或者沒有特定版本的jar包,也可以通過xml文件到maven倉庫中找到它社付。
?? ?maven是apache的開源項目承疲,可以在官網(wǎng)下載。https://maven.apache.org
? ? 下載過來的是一個zip文件鸥咖,解壓燕鸽,隨便放在某個目錄就可以。為了避免不必要的字符編碼問題扛或,建議放在英文路徑下。
? ? 當(dāng)然碘饼,有時候IDEA自己在安裝的時候可能已經(jīng)自帶了maven了……
3.3 Java
? ? IDEA也有官網(wǎng)熙兔,也有免費使用的版本,奉上鏈接:
https://www.jetbrains.com/products.html#type=ide
? ? 筆者下載的是2019.3版本艾恼,打開界面如下所示:
? ? 進入后住涉,點擊file-->new-->project,選擇maven
? 點擊next钠绍,確定工程名舆声,直接finish
? ? 在工程的出生地,就可以發(fā)現(xiàn)布局的很有規(guī)律的目錄柳爽,以及一個pom.xml文件媳握,這些都是maven規(guī)范自動生成的。
????在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>???
????復(fù)制完之后赵誓,程序右下角會彈出這樣的對話框:
?????點擊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中蔓罚,寫入如下代碼(注意罗珍,這里需要根據(jù)沒人配置的不同有所更改):
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>
<!--?打開延遲加載開關(guān)?-->
<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事務(wù)管理扣唱,事務(wù)控制由mybatis藕坯,另一種類型為MANAGED,此配置從來不提交或回滾事務(wù)?-->
<transactionManager?type="JDBC"/>
<!--?數(shù)據(jù)庫連接池噪沙,由mybatis type的值有UNPOOLED炼彪、POOLED、JNDI三種數(shù)據(jù)源
不同的數(shù)據(jù)源有不同的配置屬性?-->
<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>
<!--?當(dāng)映射文件mapper與接口類名一致且在同一個路徑下時可以用class引入?-->
<!-- <mapper class="com.sjj.dao.UserBeanMapper"/>-->
<mapper?resource="mapper/UserBeanMapper.xml"/>
<!--?通過配置文件的位置加載——一次加載一個?-->
<!-- <mapper class="com.gjh.mapper.OrdersMapper"/>-->
<!--?通過包名加載——加載包內(nèi)的所有配置文件-->
<!-- <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.創(chuàng)建sqlsessionFactory
SqlSessionFactory sqlSessionFactory = DBtools.getSqlSessionFactory();
//2.創(chuàng)建SqlSession
SqlSession session = sqlSessionFactory.openSession();
//3.session?中創(chuàng)建相應(yīng)的接口代理類,即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();//回滾
}
}
}
? 最終運行程序喜爷,跑出的結(jié)果如下所示:
? ? 去MySQL里面查,可以發(fā)現(xiàn)萄唇,上一篇推送《讀書筆記6:<風(fēng)之谷>》中的主角檩帐,娜烏西卡的信息,已經(jīng)被登記到了數(shù)據(jù)庫之中另萤。
四湃密、總結(jié)
? ? 謝特,怎么又有這么多字……