Mybatis之旅第一篇-初識Mybatis

一、JDBC的問題

為什么我們要使用Mybatis,是因為JDBC存在以下問題

1、 數(shù)據(jù)庫連接創(chuàng)建、釋放頻繁造成系統(tǒng)資源浪費,從而影響系統(tǒng)性能。如果使用數(shù)據(jù)庫連接池可解決此問題。

2、 Sql語句在代碼中硬編碼,造成代碼不易維護,實際應(yīng)用中sql變化的可能較大,sql變動需要改變java代碼。

3、 使用preparedStatement向占有位符號傳參數(shù)存在硬編碼,因為sql語句的where條件不一定,可能多也可能少,修改sql還要修改代碼,系統(tǒng)不易維護。

4、 對結(jié)果集解析存在硬編碼(查詢列名),sql變化導(dǎo)致解析代碼變化,系統(tǒng)不易維護,如果能將數(shù)據(jù)庫記錄封裝成pojo對象解析比較方便。

二、mybatis介紹

MyBatis 本是apache的一個開源項目iBatis, 2010年這個項目由apache software foundation 遷移到了google code,并且改名為MyBatis 。2013年11月遷移到Github。

MyBatis是一個優(yōu)秀的持久層框架,它對jdbc的操作數(shù)據(jù)庫的過程進行封裝,使開發(fā)者只需要關(guān)注 SQL 本身,而不需要花費精力去處理例如注冊驅(qū)動、創(chuàng)建connection、創(chuàng)建statement、手動設(shè)置參數(shù)、結(jié)果集檢索等jdbc繁雜的過程代碼。

Mybatis通過xml或注解的方式將要執(zhí)行的各種statement(statement、preparedStatemnt、CallableStatement)配置起來,并通過java對象和statement中的sql進行映射生成最終執(zhí)行的sql語句,最后由mybatis框架執(zhí)行sql并將結(jié)果映射成java對象并返回。?

三、Mybatis架構(gòu)

mybatis配置

? ? ? ? ? SqlMapConfig.xml,此文件作為mybatis的全局配置文件,配置了mybatis的運行環(huán)境等信息。mapper.xml 文件即sql映射文件,文件中配置了操作數(shù)據(jù)庫的sql語句。此文件需要在SqlMapConfig.xml中加載。

通過mybatis環(huán)境等配置信息構(gòu)造SqlSessionFactory即會話工廠

由會話工廠創(chuàng)建sqlSession即會話,操作數(shù)據(jù)庫需要通過sqlSession進行。

mybatis底層自定義了Executor執(zhí)行器接口操作數(shù)據(jù)庫,Executor接口有兩個實現(xiàn),一個是基本執(zhí)行器、一個是緩存執(zhí)行器。

Mapped Statement也是mybatis一個底層封裝對象,它包裝了mybatis配置信息及sql映射信息等。mapper.xml文件中一個sql對應(yīng)一個Mapped Statement對象,sql的id即是Mapped statement的id

Mapped Statement對sql執(zhí)行輸入?yún)?shù)進行定義,包括HashMap、基本類型、pojo,Executor通過Mapped Statement在執(zhí)行sql前將輸入的java對象映射至sql中,輸入?yún)?shù)映射就是jdbc編程中對preparedStatement設(shè)置參數(shù)。

Mapped Statement對sql執(zhí)行輸出結(jié)果進行定義,包括HashMap、基本類型、pojo,Executor通過Mapped Statement在執(zhí)行sql后將輸出結(jié)果映射至java對象中,輸出結(jié)果映射過程相當(dāng)于jdbc編程中對結(jié)果的解析處理過程。

四、入門程序

使用mybatis進行簡單的增刪改查能夠讓我們先有個大體感受,話不多說,開始擼代碼

第一步:新建一個maven項目

增加依賴,POM文件內(nèi)容:

<?xml version="1.0" encoding="UTF-8"?><project xmlns="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.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.yuanqinnan</groupId><artifactId>mybatis-first</artifactId><version>1.0-SNAPSHOT</version><name>mybatis-first</name><!-- FIXME change it to the project's website --><url>http://www.example.com</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.7</maven.compiler.source><maven.compiler.target>1.7</maven.compiler.target></properties><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><scope>test</scope></dependency><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.4.1</version></dependency><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>6.0.6</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>1.7.25</version></dependency></dependencies></project>

第二步:添加配置文件

創(chuàng)建資源文件夾config,SqlMapConfig.xml配置文件,暫且不管在config下創(chuàng)建SqlMapConfig.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><!-- 和spring整合后 environments配置將廢除 --><environments default="development"><environment id="development"><!-- 使用jdbc事務(wù)管理 --><transactionManager type="JDBC"/><!-- 數(shù)據(jù)庫連接池 --><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url"? ? ? ? ? ? ? ? ? ? ? ? ? value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8"/><property name="username" value="root"/><property name="password" value="123456"/></dataSource></environment></environments></configuration>

第三步:創(chuàng)建數(shù)據(jù)庫并新建實體

創(chuàng)建腳本:

CREATETABLE`user` (

? `id` int(11)NOTNULL AUTO_INCREMENT,

? `username` varchar(32)NOTNULLCOMMENT'用戶名稱',

? `birthday` date DEFAULTNULLCOMMENT'生日',

? `sex` char(1)DEFAULTNULLCOMMENT'性別',

? `address` varchar(256)DEFAULTNULLCOMMENT'地址',

? PRIMARYKEY (`id`)

) ENGINE=InnoDB AUTO_INCREMENT=27DEFAULTCHARSET=utf8;-- ------------------------------ Records of user-- ----------------------------INSERTINTO`user`VALUES('1','王五',null,'2',null);INSERTINTO`user`VALUES('10','張三','2014-07-10','1','北京市');INSERTINTO`user`VALUES('16','張小明',null,'1','河南鄭州');INSERTINTO`user`VALUES('22','陳小明',null,'1','河南鄭州');INSERTINTO`user`VALUES('24','張三豐',null,'1','河南鄭州');INSERTINTO`user`VALUES('25','陳小明',null,'1','河南鄭州');INSERTINTO`user`VALUES('26','王五',null,null,null);

實體:

publicclassUserimplements Serializable {

? ? privatestaticfinallongserialVersionUID = 1L;

? ? private Integer id;

? ? privateString username;// 用戶姓名privateString sex;// 性別privateDate birthday;// 生日privateString address;// 地址public Integer getId() {

? ? ? ? return id;

? ? }

? ? publicvoid setId(Integer id) {

? ? ? ? this.id = id;

? ? }

? ? public String getUsername() {

? ? ? ? return username;

? ? }

? ? publicvoid setUsername(String username) {

? ? ? ? this.username = username;

? ? }

? ? public String getSex() {

? ? ? ? return sex;

? ? }

? ? publicvoid setSex(String sex) {

? ? ? ? this.sex = sex;

? ? }

? ? public Date getBirthday() {

? ? ? ? return birthday;

? ? }

? ? publicvoid setBirthday(Date birthday) {

? ? ? ? this.birthday = birthday;

? ? }

? ? public String getAddress() {

? ? ? ? return address;

? ? }

? ? publicvoid setAddress(String address) {

? ? ? ? this.address = address;

? ? }

? ? @Override

? ? public String toString() {

? ? ? ? return"User [id=" + id + ", username=" + username + ", sex=" + sex

? ? ? ? ? ? ? ? + ", birthday=" + birthday + ", address=" + address + "]";

? ? }

}

第四步:sql映射文件

先新增一個查詢方法

<?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"><!-- namespace:命名空間,用于隔離sql,還有一個很重要的作用,后面會講 --><mapper namespace="test"><select id="queryUserById" parameterType="int" resultType="com.yuanqinnan.model.User">? ? SELECT * FROM `user`where id=#{id}</select></mapper>

第五步:加載映射文件

在SqlMapConfig.xml中增加代碼段

<mappers><!-- 映射文件方式1,一個一個的配置--><mapper resource="config/sqlmap/User.xml"/></mappers>

整體結(jié)構(gòu)如下:

第六步:測試

publicclass CRUDTest {

? ? //定義 SqlSessionSqlSession session =null;

? ? @Before

? ? publicvoid init(){

? ? ? ? //定義mybatis全局配置文件String resource = "config/SqlMapConfig.xml";

? ? ? ? //加載 mybatis 全局配置文件InputStream inputStream = CRUDTest.class.getClassLoader()

? ? ? ? ? ? ? ? .getResourceAsStream(resource);

? ? ? ? //構(gòu)建sqlSession的工廠SqlSessionFactory sessionFactory =new SqlSessionFactoryBuilder().build(inputStream);

? ? ? ? //根據(jù) sqlSessionFactory 產(chǎn)生 sessionsession = sessionFactory.openSession();

? ? }

? ? //根據(jù)id查詢user表數(shù)據(jù)? ? @Test

? ? publicvoid testSelectUserById(){

? ? ? ? String statement = "queryUserById";

? ? ? ? User user = session.selectOne(statement, 1);

? ? ? ? System.out.println(user);

? ? ? ? session.close();

? ? }

}

測試結(jié)果:User [id=1, username=王五, sex=2, birthday=null, address=null]

至此,mybatis的功能已經(jīng)實現(xiàn),我們按照此例繼續(xù)其他的操作

五、其他操作

5.1查詢列表

user.xml 增加查詢

<!-- 查詢 user 表的所有數(shù)據(jù)--><select id="selectUserAll" resultType="com.yuanqinnan.model.User">? ? select * from user</select>

測試:

//查詢所有user表所有數(shù)據(jù)@Testpublicvoid testSelectUserAll(){

? ? String statement = "selectUserAll";

? ? List listUser = session.selectList(statement);

? ? for(User user : listUser){

? ? ? ? System.out.println(user);

? ? }

? ? session.close();

}

結(jié)果:

5.2模糊查詢(用${}實現(xiàn))

<!--? ? ? ? ? ? 1、${value}里面必須要寫value,不然會報錯

? ? ? ? ? ? 2、${}表示拼接 sql 字符串,將接收到的參數(shù)不加任何修飾拼接在sql語句中

? ? ? ? ? ? 3、使用${}會造成 sql 注入

? ? --><select id="selectLikeUserName" resultType="com.yuanqinnan.model.User" parameterType="String">? ? ? ? select * from user where username like '%${value}%'

? ? ? ? <!-- select * from user where username like #{username} --></select>

測試:

//模糊查詢:根據(jù) user 表的username字段(用${}實現(xiàn))@Testpublicvoid testSelectLikeUserName(){

? ? String statement = "selectLikeUserName";

? ? List listUser = session.selectList(statement, "三");

? ? for(User user : listUser){

? ? ? ? System.out.println(user);

? ? }

? ? session.close();

}

結(jié)果:

5.3 模糊查詢(用#{}實現(xiàn))

<!--#{}實現(xiàn)--><select id="selectLikeUserName2" resultType="com.yuanqinnan.model.User" parameterType="String">? ? select * from user where username like #{username}</select>

測試:

//模糊查詢:根據(jù) user 表的username字段(用#{}實現(xiàn))@Testpublicvoid testSelectLikeUserName2(){

? ? String statement = "selectLikeUserName2";

? ? List listUser = session.selectList(statement, "%三%");

? ? for(User user : listUser){

? ? ? ? System.out.println(user);

? ? }

? ? session.close();

}

結(jié)果與上面相同

5.4 新增用戶

<!-- 向 user 表插入一條數(shù)據(jù) --><insert id="insertUser" parameterType="com.yuanqinnan.model.User">? ? insert into user(id,username,sex,birthday,address)

? ? ? ? value(#{id},#{username},#{sex},#{birthday},#{address})</insert>

測試:

//向 user 表中插入一條數(shù)據(jù)@Testpublicvoid testInsertUser(){

? ? String statement = "insertUser";

? ? User user =new User();

? ? user.setUsername("袁帥");

? ? user.setSex("1");

? ? session.insert(statement, user);

? ? //提交插入的數(shù)據(jù)? ? session.commit();

? ? session.close();

}

結(jié)果:

如果我們想要返回當(dāng)前新增的ID,則需要先獲取自增ID

<!-- 保存用戶 --><insert id="saveUser" parameterType="com.yuanqinnan.model.User"><!-- selectKey 標(biāo)簽實現(xiàn)主鍵返回 --><!-- keyColumn:主鍵對應(yīng)的表中的哪一列 --><!-- keyProperty:主鍵對應(yīng)的pojo中的哪一個屬性 --><!-- order:設(shè)置在執(zhí)行insert語句前執(zhí)行查詢id的sql,在執(zhí)行insert語句之后執(zhí)行查詢id的sql --><!-- resultType:設(shè)置返回的id的類型 --><selectKey keyColumn="id" keyProperty="id" order="AFTER"? ? ? ? ? ? ? resultType="int">? ? ? ? SELECT LAST_INSERT_ID()

? ? </selectKey>? ? INSERT INTO `user`

? ? (username,birthday,sex,address) VALUES

? ? (#{username},#{birthday},#{sex},#{address})</insert>

測試:

@Testpublicvoid testInsertUser2(){

? ? String statement = "saveUser";

? ? User user =new User();

? ? user.setUsername("袁大帥");

? ? user.setSex("1");

? ? session.insert(statement, user);

? ? System.out.println(user);

? ? //提交插入的數(shù)據(jù)? ? session.commit();

? ? session.close();

}

結(jié)果:User [id=29, username=袁大帥, sex=1, birthday=null, address=null]

5.5 更新用戶

<!-- 根據(jù) id 更新 user 表的數(shù)據(jù) --><update id="updateUserById" parameterType="com.yuanqinnan.model.User">? ? update user set username=#{username} where id=#{id}</update>

測試:

//根據(jù) id 更新 user 表的數(shù)據(jù)@Testpublicvoid testUpdateUserById(){

? ? String statement = "updateUserById";

? ? //如果設(shè)置的 id不存在,那么數(shù)據(jù)庫沒有數(shù)據(jù)更改User user =new User();

? ? user.setId(29);

? ? user.setUsername("袁不帥");

? ? session.update(statement, user);

? ? session.commit();

? ? session.close();

}

結(jié)果:

5.6 刪除用戶

<!-- 根據(jù) id 刪除 user 表的數(shù)據(jù) --><delete id="deleteUserById" parameterType="int">? ? delete from user where id=#{id}</delete>

測試:

//根據(jù) id 刪除 user 表的數(shù)據(jù)

@Test

public void testDeleteUserById(){

? ? String statement = "deleteUserById";

? ? session.delete(statement,29);

? ? session.commit();

? ? session.close();

}

結(jié)果:刪除成功

6 總結(jié)

#{}和${}

#{}表示一個占位符號,通過#{}可以實現(xiàn)preparedStatement向占位符中設(shè)置值,自動進行java類型和jdbc類型轉(zhuǎn)換。#{}可以有效防止sql注入。 #{}可以接收簡單類型值或pojo屬性值。 如果parameterType傳輸單個簡單類型值,#{}括號中可以是value或其它名稱。

表示拼接串,通過{}可以將parameterType 傳入的內(nèi)容拼接在sql中且不進行jdbc類型轉(zhuǎn)換,?可以接收簡單類型值或?qū)傩灾担绻麄鬏攩蝹€簡單類型值,{}括號中只能是value。

parameterType和resultType

parameterType:指定輸入?yún)?shù)類型,mybatis通過ognl從輸入對象中獲取參數(shù)值拼接在sql中。

resultType:指定輸出結(jié)果類型,mybatis將sql查詢結(jié)果的一行記錄數(shù)據(jù)映射為resultType指定類型的對象。如果有多條數(shù)據(jù),則分別進行映射,并把對象放到容器List中

selectOne和selectList

selectOne查詢一條記錄,如果使用selectOne查詢多條記錄則拋出異常:

org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 3

at org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(DefaultSqlSession.java:70)

selectList可以查詢一條或多條記錄。

Mybatis解決jdbc編程的問題

1、 數(shù)據(jù)庫連接創(chuàng)建、釋放頻繁造成系統(tǒng)資源浪費從而影響系統(tǒng)性能,如果使用數(shù)據(jù)庫連接池可解決此問題。

解決:在SqlMapConfig.xml中配置數(shù)據(jù)連接池,使用連接池管理數(shù)據(jù)庫鏈接。

2、 Sql語句寫在代碼中造成代碼不易維護,實際應(yīng)用sql變化的可能較大,sql變動需要改變java代碼。

解決:將Sql語句配置在XXXXmapper.xml文件中與java代碼分離。

3、 向sql語句傳參數(shù)麻煩,因為sql語句的where條件不一定,可能多也可能少,占位符需要和參數(shù)一一對應(yīng)。

解決:Mybatis自動將java對象映射至sql語句,通過statement中的parameterType定義輸入?yún)?shù)的類型。

4、 對結(jié)果集解析麻煩,sql變化導(dǎo)致解析代碼變化,且解析前需要遍歷,如果能將數(shù)據(jù)庫記錄封裝成pojo對象解析比較方便。

解決:Mybatis自動將sql執(zhí)行

mybatis與hibernate不同

Mybatis和hibernate不同,它不完全是一個ORM框架,因為MyBatis需要程序員自己編寫Sql語句。mybatis可以通過XML或注解方式靈活配置要運行的sql語句,并將java對象和sql語句映射生成最終執(zhí)行的sql,最后將sql執(zhí)行的結(jié)果再映射生成java對象。

Mybatis學(xué)習(xí)門檻低,簡單易學(xué),程序員直接編寫原生態(tài)sql,可嚴(yán)格控制sql執(zhí)行性能,靈活度高,非常適合對關(guān)系數(shù)據(jù)模型要求不高的軟件開發(fā),例如互聯(lián)網(wǎng)軟件、企業(yè)運營類軟件等,因為這類軟件需求變化頻繁,一但需求變化要求成果輸出迅速。但是靈活的前提是mybatis無法做到數(shù)據(jù)庫無關(guān)性,如果需要實現(xiàn)支持多種數(shù)據(jù)庫的軟件則需要自定義多套sql映射文件,工作量大。

Hibernate對象/關(guān)系映射能力強,數(shù)據(jù)庫無關(guān)性好,對于關(guān)系模型要求高的軟件(例如需求固定的定制化軟件)如果用hibernate開發(fā)可以節(jié)省很多代碼,提高效率。但是Hibernate的學(xué)習(xí)門檻高,要精通門檻更高,而且怎么設(shè)計O/R映射,在性能和對象模型之間如何權(quán)衡,以及怎樣用好Hibernate需要具有很強的經(jīng)驗和能力才行。

總之,按照用戶的需求在有限的資源環(huán)境下只要能做出維護性、擴展性良好的軟件架構(gòu)都是好架構(gòu),所以框架只有適合才是最好。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Mybatis介紹 MyBatis 本是apache的一個開源項目iBatis, 2010年這個項目由apache...
    day_Sunny閱讀 2,911評論 0 6
  • Spring 技術(shù)筆記Day 1 預(yù)熱知識一、 基本術(shù)語Blob類型,二進制對象Object Graph:對象圖...
    OchardBird閱讀 1,080評論 0 2
  • 1. 簡介 1.1 什么是 MyBatis ? MyBatis 是支持定制化 SQL、存儲過程以及高級映射的優(yōu)秀的...
    笨鳥慢飛閱讀 6,273評論 0 4
  • 九個小時的火車,帶著我們一行7大4小來到武漢。半夜三點多,一片細(xì)雨,在路燈下紛紛撒撒,似光似霧。伴著襲面而來...
    Always十月閱讀 281評論 0 0
  • 注意地方 判斷兩個浮點數(shù)a 和 b 是否相等應(yīng)該判斷二者之差的絕對值fabs(a-b)是否小于某個閾值,例如1e-...
    謨涵閱讀 229評論 0 0

友情鏈接更多精彩內(nèi)容