1、mybatis概述
mybatis 是支持普通 sql 查詢(相比較于hibernate的封裝,mybatis是半自動化的jdbc封裝,一個特點就是mybatis執行的sql查詢語句需要自己在配置文件中寫),存儲過程和高級映射的優秀持久層框架。mybatis 消除了幾乎所有的 jdbc 代碼和參數的手工設置以及對結果集的檢索。mybatis 可以使用簡單的xml 或注解用于配置和原始映射,將接口和 java 的 pojo(plain old java objects,普通的java 對象)映射成數據庫中的記錄。
2、mybatis原理解析
下面以mybatis簡單的執行流程
1、加載mybatis全局配置文件(數據源、mapper映射文件等),解析配置文件,mybatis基于xml配置文件生成configuration,和一個個mappedstatement(包括了參數映射配置、動態sql語句、結果映射配置),其對應著<select | update | delete | insert>標簽項。
2、sqlsessionfactorybuilder通過configuration對象生成sqlsessionfactory,用來開啟sqlsession。
3、sqlsession對象完成和數據庫的交互:
a、用戶程序調用mybatis接口層api(即mapper接口中的方法)
b、sqlsession通過調用api的statement id找到對應的mappedstatement對象
c、通過executor(負責動態sql的生成和查詢緩存的維護)將mappedstatement對象進行解析,sql參數轉化、動態sql拼接,生成jdbc statement對象
d、jdbc執行sql。
e、借助mappedstatement中的結果映射關系,將返回結果轉化成hashmap、javabean等存儲結構并返回。
下面是mybatis的框架原理圖
3、mybatis簡單實例
(1)導入相關jar包以及mybatis運行環境核心jar包和連接數據庫的包
(2)創建一張簡單的數據表
(3)創建java對象(po類型)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
|
package cn.mybatis.po; public class user { private int id; private string username; private string password; private string address; private string sex; public int getid() { return id; } public string getusername() { return username; } public string getpassword() { return password; } public string getaddress() { return address; } public string getsex() { return sex; } public void setid( int id) { this .id = id; } public void setusername(string username) { this .username = username; } public void setpassword(string password) { this .password = password; } public void setaddress(string address) { this .address = address; } public void setsex(string sex) { this .sex = sex; } @override public string tostring() { return "user{" + "id=" + id + ", username='" + username + '\ '' + ", password='" + password + '\ '' + ", address='" + address + '\ '' + ", sex='" + sex + '\ '' + '}' ; } } user實體類 |
(4)創建mybatis核心配置文件(sqlmapconfig.xml)
在核心配置文件配置連接數據庫的相關信息,(如果是和spring整合,則可以放在spring配置文件中進行對數據庫的配置)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
<?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= "jdbc.properties" ></properties> <!–settings配置log4j輸出日志 –> <settings> <setting name= "logimpl" value= "log4j" /> </settings>--> <!--typealiases配置包的別名--> <!--environments配置了數據庫連接,配置了driver、url、username、password屬性--> <environments default = "development" > <environment id= "development" > <transactionmanager type= "jdbc" > <!--<property name= "" value= "" />--> </transactionmanager> <datasource type= "pooled" > <property name= "driver" value= "com.mysql.jdbc.driver" /> <property name= "url" value= "jdbc:mysql:///mybatis01" /> <property name= "username" value= "root" /> <property name= "password" value= "123" /> </datasource> </environment> </environments> <!--配置一個sql語句和映射的配置文件--> <mappers> <mapper resource= "usermapper.xml" /> </mappers> </configuration> mybatis核心配置文件 |
(5)創建一個mapper.xml文件,對應編寫所需要的sql查詢操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<?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指定了命名空間--> <mapper namespace= "test" > <!--定義一個select查詢--> <!--parametertype:指定輸入參數的類型--> <!--#{}表示占位符--> <!--#{id}:其中的id表示的就是接受的輸入參數, 參數名稱就是id, 這里指出:如果輸入參數是簡單類型,#{}中的參數名可以任意設置(value或者其他名稱)--> <!--resulttype:指定輸出類型(即指定輸出結果所映射的java對象類型)--> <select id= "finduserbyid" parametertype= "int" resulttype= "cn.mybatis.po.user" > select * from t_user where id = #{id} </select> </mapper> usermapper配置文件 |
(7)創建測試程序,對剛剛編寫的select查詢進行測試
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
package cn.mybatis.first; import cn.mybatis.po.user; import org.apache.ibatis.io.resources; import org.apache.ibatis.session.sqlsession; import org.apache.ibatis.session.sqlsessionfactory; import org.apache.ibatis.session.sqlsessionfactorybuilder; import java.io.ioexception; import java.io.inputstream; public class test { public user finduserbyid() throws ioexception { //得到mybatis配置文件 string resource = "sqlmapconfig.xml" ; //得到配置文件的文件流信息 inputstream inputstream = resources.getresourceasstream(resource); //創建會話工廠 傳入mybatis的配置文件信息 sqlsessionfactory sqlsessionfactory = new sqlsessionfactorybuilder().build(inputstream); //通過會話工廠得到sqlsession sqlsession sqlsession = sqlsessionfactory.opensession(); //通過sqlsession來操作數據庫 //第一個參數就是映射文件中statment的id:namespace +statment的id //第二個參數就是制定映射文件中的parametertype類型的參數 user user = sqlsession.selectone( "test.finduserbyid" , 1 ); //system.out.println(user); //釋放會話資源 try { sqlsession.close(); } catch (exception e) { e.printstacktrace(); } return user; } public static void main(string[] args) { // todo auto-generated method stub test test = new test(); try { system.out.println(test.finduserbyid()); } catch (ioexception e) { e.printstacktrace(); } } } test測試程序 |
(8)加入log4j日志文件
1
2
3
4
5
6
7
8
9
|
### direct log messages to stdout ### log4j.rootlogger=debug, stdout log4j.appender.stdout=org.apache.log4j.consoleappender log4j.appender.stdout.layout=org.apache.log4j.patternlayout log4j.appender.stdout.layout.conversionpattern=%5p [%t] - %m%n log4j.properties |
(9)測試結果
4.其他crud操作
(1)insert操作
在mapper文件中添加響應的sql配置,以及使用mysql中的last_insert_id()函數得到增加的數據的主鍵值
1
2
3
4
5
6
7
8
9
10
11
12
13
|
<insert id= "adduser" parametertype= "cn.mybatis.po.user" > <!-- 現在需要得到剛剛插入的記錄中的主鍵值,只適用于自增主鍵的情況 last_insert_id() keyproperty:將查詢到的主鍵值設置到parametertype指定對象中的那個屬性 order:指定相對于insert的順序 resulttype:指定映射結果的結果類型 --> <selectkey keyproperty= "id" order= "after" resulttype= "java.lang.integer" > select last_insert_id() </selectkey> insert into t_user(id,username,password,address,sex) values(#{id},#{username},#{password},#{address}, #{sex}); </insert> |
插入數據的日志信息,沒有使用sqlsession.commit();之前的日志情況
從上面的圖中可以看出,沒有添加commit的時候,事務進行了回滾,所以要想添加數據,需要自己手動提交(在沒有整合spring之前)
附上insertuser的函數
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
public void inseruser() throws ioexception { //得到配置文件的文件流信息 inputstream inputstream = resources.getresourceasstream( "sqlmapconfig.xml" ); sqlsessionfactory sqlsessionfactory = new sqlsessionfactorybuilder().build(inputstream); sqlsession sqlsession = sqlsessionfactory.opensession(); user user = new user( "world" , "1234" , "武漢市" , "男" ); sqlsession.insert( "test.adduser" ,user); system.out.println(user.getid()); sqlsession.commit(); //釋放會話資源 try { sqlsession.close(); } catch (exception e) { e.printstacktrace(); } } inseruser函數 |
(2)模糊查詢
首先配置mapper文件,${}和#{}的簡單區別如下:
1
2
3
4
5
6
7
8
9
10
|
<!-- 模糊查詢可能會查詢多條記錄 resulttype:指定的就是查詢結果對應的單條記錄類型 ${}:表示將輸入的參數不加任何的修飾,直接作為字符串拼接在sql中 但是這樣直接拼接,容易導致sql注入的隱患 ${value}中的value表示接受的輸入參數,注意如果輸入參數是簡單類型,其中的形參只能用value --> <select id= "finduserbyusername" parametertype= "java.lang.string" resulttype= "cn.mybatis.po.user" > select * from t_user where username like '%${value}%' </select> |
使用查詢的時候碰到一個小錯誤,由于之前測試的insert方法,其中在user實體類中添加了有參構造函數,所以出現了下面的錯誤,分析原因就是:使用mybatis查詢的時候需要在實體類中加入無參構造方法(當然如果實體類本身沒有構造函數,就會是默認的無參構造函數)
附上findbyusername的函數實現
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
public void finduserbyusername() throws ioexception { //得到配置文件的文件流信息 inputstream inputstream = resources.getresourceasstream( "sqlmapconfig.xml" ); sqlsessionfactory sqlsessionfactory = new sqlsessionfactorybuilder().build(inputstream); sqlsession sqlsession = sqlsessionfactory.opensession(); list<user> userlist = sqlsession.selectlist( "test.finduserbyusername" , "u" ); system.out.println(userlist); //釋放會話資源 try { sqlsession.close(); } catch (exception e) { e.printstacktrace(); } } findbyusername函數實現 |
(3)刪除操作
首先在mapper中配置刪除的操作
1
2
3
|
<delete id= "deleteuser" parametertype= "java.lang.integer" > delete from t_user where id = #{value} </delete> |
運行測試程序,同insert中一樣,需要手動提交事務,如下面所示
最終結果:
數據表中刪除了編號為10的數據記錄
5.細節整理
(1)關于示例程序中一些相關類的理解
a)sqlsessionfactorybuilder
用來創建sqlsessionfactory。因為sqlsessionfactory使用了單例模式,所以不需要使用單例模式來管理sqlsessionfactorybuilder,只需要在創建sqlsessionfactory時 候使用一次就可以
b)sqlsessionfactory
會話工廠,用來創建sqlsession。可以使用單例模式來管理sqlsessionfactory這個會話工廠,工廠創建之后,就一直使用一個實例。
c)sqlsession
面向程序員的接口,提供了操作數據庫的方法。sqlsession是線程不安全的(原因:在sqlsession實現類中除了接口中的操作數據庫的方法之外,還有數據域的屬性,比如說一些提交的數據等等,所以在多線程并發請求的時候,會導致線程不安全),所以我們可以將sqlsession使用在方法體里面,這樣每個線程都有自己的方法,就不會沖突
(2)mybatis中mapper映射文件
如同解釋mybatis執行原理的時候一樣,mapper映射文件中配置的sql語句,實際上在執行的時候都被封裝稱為一個個mapperstatment對象,即mapper映射文件是按照statment來管理不同的sql。在編寫程序的時候,我們在使用sqlsession其中的操作數據庫的方法(selectone,selectlist等等)的時候,傳入的參數除了實參(id,模糊查詢的字符串等等)之外,還需要傳入的就是相應的sql位置,而sql是被statment管理,所以就是傳入namespace+statmentid
(3)占位符
#{id}:其中的id表示的就是接受的輸入參數,參數名稱就是id,這里指出:如果輸入參數是簡單類型,#{}中的參數名可以任意設置(value或者其他名稱)
${value}:表示將輸入的參數不加任何的修飾,直接作為字符串拼接在sql中但是這樣直接拼接,容易導致sql注入的隱患${value}中的value表示接受的輸入參數,注意如果輸入參數是簡單類型,其中的形參只能用value
(4)別名定義
①單個別名的定義
1
2
3
4
|
<typealiases> <!--針對單個別名的定義--> <typealias type= "cn.mybatis.po.user" alias= "user" ></typealias> </typealiases> |
定義別名后的使用
1
2
3
|
<select id= "finduserbyidtest" parametertype= "int" resulttype= "user" > select * from t_user where id = #{id} </select> |
②批量別名的定義
1
2
3
4
|
<typealiases> <!--批量別名定義:mybatis在定義別名的時候會自動掃描包中的po類,自動的將別名定義為類名(首字母大寫或者小寫都可以)--> < package name= "cn.mybatis.po" ></ package > </typealiases> |
(5)在sqlmapconfig.xml中加載mapper映射文件的時候,除了通過resource的方式,還可以使用mapper接口加載的方式來實現
①首先先注意一點:
在配置mybatis-config.xml時,其中的節點是有順序的,配置順序依次為:
properties/settings/typealiases/typehandlers/objectfactory/objectwrapperfactory/plugins/environments/databaseidprovider/mappers
②使用mapper加載的方式,要將mapper接口和mapper配置文件放在同一目錄下面,并且文件名稱一致,而且要遵循mapper代理的方式進行開發
1
2
3
|
<mappers> <mapper class = "cn.mybatis.mapper.usermapper" ></mapper> </mappers> |
6.mybatis開發dao方法簡介
(1)使用dao接口+實現類的方式
a)首先編寫接口,如同一般編寫模式方式進行編寫
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
package cn.mybatis.dao; import cn.mybatis.po.user; /** * 原始dao方式開發:dao接口+dao實現類的方式 */ public interface userdao { //根據id查詢信息 public user finduserbyid( int id) throws exception; //添加信息 public void insertuser(user user) throws exception; //刪除信息 public void deleteuser( int id) throws exception; } dao接口 |
b)然后編寫接口實現
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
|
package cn.mybatis.dao.daoimpl; import cn.mybatis.dao.userdao; import cn.mybatis.po.user; import org.apache.ibatis.session.sqlsession; import org.apache.ibatis.session.sqlsessionfactory; import org.junit.test; public class userdaoimpl implements userdao { //使用構造方法注入sqlsessionfactory private sqlsessionfactory sqlsessionfactory; public userdaoimpl(sqlsessionfactory sqlsessionfactory) { this .sqlsessionfactory = sqlsessionfactory; } @override @test public user finduserbyid( int id) throws exception { sqlsession sqlsession = sqlsessionfactory.opensession(); user user = sqlsession.selectone( "test.finduserbyid" ,id); sqlsession.close(); return user; } @override public void insertuser(user user) throws exception { sqlsession sqlsession = sqlsessionfactory.opensession(); // user user1 = new user("test1","123","洪山區","男"); sqlsession.insert( "test.finduserbyid" ,user); sqlsession.commit(); sqlsession.close(); } @override public void deleteuser( int id) throws exception { sqlsession sqlsession = sqlsessionfactory.opensession(); sqlsession.delete( "test.finduserbyid" ,id); sqlsession.commit(); sqlsession.close(); } } dao接口實現類 |
c)mapper配置文件和sqlconfig配置文件不變
d)使用junit進行測試
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
|
package cn.mybatis.testdao; import cn.mybatis.dao.userdao; import cn.mybatis.dao.daoimpl.userdaoimpl; import cn.mybatis.po.user; import org.apache.ibatis.io.resources; import org.apache.ibatis.session.sqlsessionfactory; import org.apache.ibatis.session.sqlsessionfactorybuilder; import org.junit.after; import org.junit.before; import org.junit.test; import java.io.inputstream; public class userdaoimpltest { private sqlsessionfactory sqlsessionfactory; @before public void setup() throws exception { inputstream inputstream = resources.getresourceasstream( "sqlmapconfig.xml" ); sqlsessionfactory = new sqlsessionfactorybuilder().build(inputstream); } @test public void testfinduserbyid() throws exception{ //創建userdao的對象 userdao userdao = new userdaoimpl(sqlsessionfactory); //調用userdao的方法 user user = userdao.finduserbyid( 1 ); system.out.println(user ); } } junit測試 |
e)測試結果
f)原始dao方法的問題
①dao接口實現中存在大量的模板方法(即很多重復性的代碼 )
②調用sqlsession方法的時候將statmentid硬編碼了
③條用sqlsession方法的時候傳入的參數,由于使用泛型,所以在編譯階段不會報錯(即使傳入參數錯誤)
(2)使用mapper代理的方法(即只需要mapper接口)
(a)使用mapper方式的規范
①在使用mapper代理的方式中,namespace的值應該是mapper接口的路徑
②在mapper.java接口文件中的接口方法名稱和mapper.xml中的statment的id一致
③在mapper.java接口文件中的接口方法的輸入參數和mapper.xml中的statment的parametertype一致
④在mapper.java接口文件中的接口方法的返回值類型和mapper.xml中的statment的resulttype一致
(b)查詢、刪除操作實例
①編寫mapper.xml配置文件,其中包含select和delete的sql配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
<?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指定了命名空間--> <!--在使用mapper代理的方式中,namespace的值應該是mapper接口的路徑--> <mapper namespace= "cn.mybatis.mapper.usermapper" > <select id= "finduserbyid" parametertype= "int" resulttype= "cn.mybatis.po.user" > select * from t_user where id = #{id} </select> <delete id= "deleteuser" parametertype= "java.lang.integer" > delete from t_user where id = #{value} </delete> </mapper> mapper.xml配置文件 |
②編寫mapper接口,按照mapper代理的方式開發規范來編寫mapper的接口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
|
package cn.mybatis.testmapper; import cn.mybatis.mapper.usermapper; import cn.mybatis.po.user; import org.apache.ibatis.io.resources; import org.apache.ibatis.session.sqlsession; import org.apache.ibatis.session.sqlsessionfactory; import org.apache.ibatis.session.sqlsessionfactorybuilder; import org.junit.after; import org.junit.before; import org.junit.test; import java.io.inputstream; public class usermappertest { private sqlsessionfactory sqlsessionfactory; @before public void setup() throws exception { inputstream inputstream = resources.getresourceasstream( "sqlmapconfig.xml" ); sqlsessionfactory = new sqlsessionfactorybuilder().build(inputstream); } @test public void testfinduserbyid() throws exception{ sqlsession sqlsession = sqlsessionfactory.opensession(); //得到usermapper的代理對象 usermapper usermapper = sqlsession.getmapper(usermapper. class ); user user = usermapper.finduserbyid( 9 ); system.out.println(user); } @test public void testdeleteuser() throws exception { sqlsession sqlsession = sqlsessionfactory.opensession(); usermapper usermapper = sqlsession.getmapper(usermapper. class ); usermapper.deleteuser( 9 ); sqlsession.commit(); } @after public void teardown() throws exception { } } mapper接口 |
③junit測試
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
|
package cn.mybatis.testmapper; import cn.mybatis.mapper.usermapper; import cn.mybatis.po.user; import org.apache.ibatis.io.resources; import org.apache.ibatis.session.sqlsession; import org.apache.ibatis.session.sqlsessionfactory; import org.apache.ibatis.session.sqlsessionfactorybuilder; import org.junit.after; import org.junit.before; import org.junit.test; import java.io.inputstream; public class usermappertest { private sqlsessionfactory sqlsessionfactory; @before public void setup() throws exception { inputstream inputstream = resources.getresourceasstream( "sqlmapconfig.xml" ); sqlsessionfactory = new sqlsessionfactorybuilder().build(inputstream); } @test public void testfinduserbyid() throws exception{ sqlsession sqlsession = sqlsessionfactory.opensession(); //得到usermapper的代理對象 usermapper usermapper = sqlsession.getmapper(usermapper. class ); user user = usermapper.finduserbyid( 8 ); system.out.println(user); } @test public void testdeleteuser() throws exception { sqlsession sqlsession = sqlsessionfactory.opensession(); usermapper usermapper = sqlsession.getmapper(usermapper. class ); usermapper.deleteuser( 8 ); } @after public void teardown() throws exception { } } junit測試 |
④查詢結果展示
⑤刪除結果展示
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。
原文鏈接:https://www.cnblogs.com/fsmly/p/10324491.html