mybatis第一天 mybatis基礎知識
教學思路:
整個教學思路以訂單商品的項目作爲驅動。
mybatis第一天:基礎知識
mybatis是一個java持久層框架,java中操作關係型 數據庫用的是jdbc,mybatis是對jdbc的一個封裝。
1、從一個jdbc程序開始,通過jdbc 程序找到使用原生態的jdbc開發程序,存在哪些問題??通過學習mybatis,mybatis是如何解決這些問題。
2、mybatis的架構(重點)
3、mybatis的入門程序(重點)
實現 用戶的查詢、添加、修改、刪除。
4、mybatis開發dao的兩種方法。(重點)
原始的dao開發方式(dao接口和dao實現都需要編寫)
mapper代理方式(只需要寫dao接口)
5、輸入映射類型和輸出映射類型。
6、動態sql
第二天:高級知識
高級映射查詢(一對一、一對多、多對多)(重點)
查詢緩存
延遲加載
mybatis和spring整合(重點)
mybatis逆向工程 。
1 開發環境
jdk1.7.0_72
eclipse:eclipse-3.7-indigo
mysql:mysql5.1
1.1 創建數據庫
先導入sql_table.sql,再導入sql_data.sql(記錄系統的初始化數據)
通常需要提供初始化數據的數據庫腳本。
2 jdbc編程中問題
企業開發中,根據項目大小、特點進行技術選型 ,jdbc操作數據庫時效率是很高的,jdbc也是技術選型的參考。
2.1 jdbc程序
參考教案
需要數據庫的驅動包:
上邊是mysql的驅動,下邊是oracle的驅動。
2.2 jdbc問題總結
1、數據庫連接頻繁的創建和關閉,缺點浪費數據庫的資源,影響操作效率
設想:使用數據庫連接池
2、sql語句是硬編碼,如果需求變更需要修改sql,就需要修改java代碼,需要重新編譯,系統不易維護。
設想:將sql語句 統一配置在文件中,修改sql不需要修改java代碼。
3、通過preparedStatement向佔位符設置參數,存在硬編碼( 參數位置,參數)問題。系統不易維護。
設想:將sql中的佔位符及對應的參數類型配置在配置文件中,能夠自動輸入 映射。
4、遍歷查詢結果集存在硬編碼(列名)。
設想:自動進行sql查詢結果向java對象的映射(輸出映射)。
3 mybatis架構(重點)
3.1 mybatis介紹
MyBatis 本是apache的一個開源項目iBatis, 2010年這個項目由apache software foundation 遷移到了google code,並且改名爲MyBatis,實質上Mybatis對ibatis進行一些改進。
目前mybatis在github上託管。git(分佈式版本控制,當前比較流程)
MyBatis是一個優秀的持久層框架,它對jdbc的操作數據庫的過程進行封裝,
使開發者只需要關注 SQL 本身,而不需要花費精力去處理例如註冊驅動、創建connection、創建statement、手動設置參數、結果集檢索等jdbc繁雜的過程代碼。
Mybatis通過xml或註解的方式將要執行的各種statement(statement、preparedStatemnt、CallableStatement)配置起來,
並通過java對象和statement中的sql進行映射生成最終執行的sql語句,最後由mybatis框架執行sql並將結果映射成java對象並返回。
3.2 mybatis架構
4 mybatis入門程序
4.1 需求
實現用戶查詢:
根據用戶id(主鍵)查詢用戶信息(單條記錄)
根據用戶名稱模糊查詢用戶信息(多條記錄)
用戶添加
用戶刪除
用戶修改
4.2 導入jar包
從mybatis管網下載(地址:https://github.com/mybatis/mybatis-3/releases)
mybatis-3.2.7.pdf---操作手冊
mybatis-3.2.7.jar--核心 jar包
依賴的jar包
4.3 工程結構
4.4 log4j.properties(公用文件)
# Global logging configuration,建議開發環境中要用debug
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
4.5 SqlMapConfig.xml(公用文件)
通過SqlMapConfig.xml加載mybatis運行環境。
4.6 根據id查詢用戶
4.6.1 pojo(User.java)
4.6.2 User.xml(重點)
建議命名規則:表名+mapper.xml
早期ibatis命名規則:表名.xml
4.6.3 編碼
創建SqlSessionFactory:
4.7 根據用戶名稱模糊查詢用戶信息
根據用戶名稱模糊查詢用戶信息可能返回多條記錄。
4.7.1 User.xml
建議命名規則:表名+mapper.xml
早期ibatis命名規則:表名.xml
4.7.2 編碼
4.7.3 使用${}接收參數
4.8 mybatis開發過程小結
1、編寫SqlMapConfig.xml
2、編寫mapper.xml
定義了statement
3、編程通過配置文件創建SqlSessionFactory
4、通過SqlSessionFactory獲取SqlSession
5、通過SqlSession操作數據庫
如果執行添加、更新、刪除需要調用SqlSession.commit()
6、SqlSesion使用完成要關閉
4.9 用戶添加
向用戶表插入一條記錄。
4.9.1 User.xml
4.9.2 編碼
4.9.3 主鍵返回
需求:user對象插入到數據庫後,新記錄的主鍵要通過user對象返回,通過user獲取主鍵值。
解決思路:
通過LAST_INSERT_ID()獲取剛插入記錄的自增主鍵值,在insert語句執行後,執行select LAST_INSERT_ID()就可以獲取自增主鍵。
User.xml修改:
使用mysql的uuid機制生成主鍵:
使用uuid生成主鍵的好處是不考慮數據庫移植後主鍵衝突問題。
實現思路:
先查詢uuid得到主鍵,將主鍵設置到user對象中,將user對象插入數據庫。
實現 oracle數據庫主鍵返回,如何做??
oracle沒有自增主鍵機制,使用序列完成主鍵生成。
實現思路:
先查詢序列得到主鍵,將主鍵設置到user對象中,將user對象插入數據庫。
4.10 用戶刪除和更新
4.10.1 編碼
// 測試根據id刪除用戶(得到單條記錄)
@Test
public void testDeleteUser() {
// 通過sqlSessionFactory創建sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 通過sqlSession操作數據庫
try {
sqlSession.delete("test.deleteUser", 35);
// 需要提交事務
sqlSession.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 關閉sqlSession
sqlSession.close();
}
}
// 測試根據id更新用戶(得到單條記錄)
@Test
public void testUpdateUser() {
// 通過sqlSessionFactory創建sqlSession
SqlSession sqlSession = sqlSessionFactory.openSession();
// 通過sqlSession操作數據庫
// 創建更新數據對象,要求必須包括 id
User user = new User();
user.setId(35);
user.setUsername("燕青");
user.setAddress("河南鄭州");
// user.setBirthday(new Date());
user.setSex("1");
try {
sqlSession.update("test.updateUser", user);
// 需要提交事務
sqlSession.commit();
} catch (Exception e) {
e.printStackTrace();
} finally {
// 關閉sqlSession
sqlSession.close();
}
System.out.println("用戶的id=" + user.getId());
}
4.11 Mybatis解決jdbc編程的問題
1、 數據庫鏈接創建、釋放頻繁造成系統資源浪費從而影響系統性能,如果使用數據庫鏈接池可解決此問題。
解決:在SqlMapConfig.xml中配置數據鏈接池,使用連接池管理數據庫鏈接。
2、 Sql語句寫在代碼中造成代碼不易維護,實際應用sql變化的可能較大,sql變動需要改變java代碼。
解決:將Sql語句配置在XXXXmapper.xml文件中與java代碼分離。
3、 向sql語句傳參數麻煩,因爲sql語句的where條件不一定,可能多也可能少,佔位符需要和參數一一對應。
解決:Mybatis自動將java對象映射至sql語句,通過statement中的parameterType定義輸入參數的類型。
4、 對結果集解析麻煩,sql變化導致解析代碼變化,且解析前需要遍歷,如果能將數據庫記錄封裝成pojo對象解析比較方便。
解決:Mybatis自動將sql執行結果映射至java對象,通過statement中的resultType定義輸出結果的類型。
4.12 mybatis與hibernate重要區別
企業開發進行技術選型 ,考慮mybatis與hibernate適用場景。
mybatis:入門簡單,程序容易上手開發,節省開發成本 。mybatis需要程序員自己編寫sql語句,是一個不完全 的ORM框架,對sql修改和優化非常容易實現 。
mybatis適合開發需求變更頻繁的系統,比如:互聯網項目。
hibernate:入門門檻高,如果用hibernate寫出高性能的程序不容易實現。hibernate不用寫sql語句,是一個 ORM框架。
hibernate適合需求固定,對象數據模型穩定,中小型項目,比如:企業OA系統。
總之,企業在技術選型時根據項目實際情況,以降低成本和提高系統 可維護性爲出發點進行技術選型。
4.13 總結
4.13.1 SqlMapConfig.xml
是mybatis全局配置文件,只有一個,名稱不固定的,主要mapper.xml,mapper.xml中配置 sql語句
4.13.2 mapper.xml
mapper.xml是以statement爲單位進行配置。(把一個sql稱爲一個statement),satatement中配置 sql語句、parameterType輸入參數類型(完成輸入映射)、resultType輸出結果類型(完成輸出映射)。
還提供了parameterMap配置輸入參數類型(過期了,不推薦使用了)
還提供resultMap配置輸出結果類型(完成輸出映射),明天重點講通過resultMap完成複雜數據類型的映射(一對多,多對多映射)
4.13.3 #{}
表示一個佔位符,向佔位符輸入參數,mybatis自動進行java類型和jdbc類型的轉換。
程序員不需要考慮參數的類型,比如:傳入字符串,mybatis最終拼接好的sql就是參數兩邊加單引號。
#{}接收pojo數據,可以使用OGNL解析出pojo的屬性值
4.13.4 ${}
表示sql的拼接,通過${}接收參數,將參數的內容不加任何修飾拼接在sql中。
${}也可以接收pojo數據,可以使用OGNL解析出pojo的屬性值
缺點:不能防止sql注入。
4.13.5 selectOne
用於查詢單條記錄,不能用於查詢多條記錄,否則異常:
org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 4
4.14 selectList
用於查詢多條記錄,可以用於查詢單條記錄的。
5 mybatis開發dao的方法
5.1 SqlSession作用範圍
是使用局部變量、成員變量。。。。???
5.1.1 SqlSessionFactoryBuilder
SqlSessionFactoryBuilder是以工具類方式來使用,需要創建sqlSessionFactory就new一個SqlSessionFactoryBuilder。
5.1.2 sqlSessionFactory
正常開發時,以單例方式管理sqlSessionFactory,整個系統運行過程中sqlSessionFactory只有一個實例,將來和spring整合後由spring以單例方式管理sqlSessionFactory。
5.1.3 SqlSession
sqlSession是一個面向用戶(程序員)的接口,程序員調用sqlSession的接口方法進行操作數據庫。
sqlSession能否以單例 方式使用??
由於sqlSession是線程不安全,所以sqlSession最佳應用範圍在方法體內,在方法體內定義局部變量使用sqlSession。
5.2 原始dao開發方式
程序員需要寫dao接口和dao 的實現 類
5.2.1 dao接口
5.2.2 dao接口實現
5.2.3 測試代碼
5.3 mapper代理的方式
程序員只需要寫dao接口,dao接口實現對象由mybatis自動生成代理對象。
本身dao在三層架構中就是一個通用的接口。
5.3.1 上邊原始dao開發方式的問題
1 dao的實現類中存在重複代碼,整個mybatis操作的過程代碼模板重複(先創建sqlsession、調用sqlsession的方法、關閉sqlsession)
2、dao的實現 類中存在硬編碼,調用sqlsession方法時將statement的id硬編碼。
5.3.2 mapper開發規範
要想讓mybatis自動創建dao接口實現類的代理對象,必須遵循一些規則:
1、mapper.xml中namespace指定爲mapper接口的全限定名
此步驟目的:通過mapper.xml和mapper.java進行關聯。
2、mapper.xml中statement的id就是mapper.java中方法名
3、mapper.xml中statement的parameterType和mapper.java中方法輸入參數類型一致
4、mapper.xml中statement的resultType和mapper.java中方法返回值類型一致.
5.3.3 mapper.xml(映射文件)
mapper映射文件的命名方式建議:表名Mapper.xml
namespace指定爲mapper接口的全限定名
5.3.4 mapper接口
mybatis提出了mapper接口,相當 於dao 接口。
mapper接口的命名方式建議:表名Mapper
5.3.5 將mapper.xml在SqlMapConfig.xml中加載
5.3.6 mapper接口返回單個對象和集合對象
不管查詢記錄是單條還是多條,在 statement中resultType定義一致,都是單條記錄映射的pojo類型。
mapper接口方法返回值,如果是返回的單個對象,返回值類型是pojo類型,生成的代理對象內部通過selectOne獲取記錄,如果返回值類型是集合對象,生成的代理對象內部通過selectList獲取記錄。
5.3.7 問題
5.3.7.1 返回值的問題
如果方法調用的statement,返回是多條記錄,而mapper.java方法的返回值爲pojo,此時代理對象通過selectOne調用,由於返回多條記錄,所以報錯:
org.apache.ibatis.exceptions.TooManyResultsException: Expected one result (or null) to be returned by selectOne(), but found: 4
5.3.7.2 輸入參數的問題
使用mapper代理的方式開發,mapper接口方法輸入 參數只有一個,可擴展性是否很差??
可擴展性沒有問題,因爲dao層就是通用的,可以通過擴展pojo(定義pojo包裝類型)將不同的參數(可以是pojo也可以簡單類型)傳入進去。
6 sqlMapConfig.xml
SqlMapConfig.xml中配置的內容和順序如下:
properties(屬性)
settings(全局配置參數)
typeAliases(類型別名)
typeHandlers(類型處理器)
objectFactory(對象工廠)
plugins(插件)
environments(環境集合屬性對象)
environment(環境子屬性對象)
transactionManager(事務管理)
dataSource(數據源)
mappers(映射器)
6.1 properties屬性定義
可以把一些通用的屬性值配置在屬性文件中,加載到mybatis運行環境內。
比如:創建db.properties配置數據庫連接參數。
注意: MyBatis 將按照下面的順序來加載屬性:
u 在 properties 元素體內定義的屬性首先被讀取。
u 然後會讀取properties 元素中resource或 url 加載的屬性,它會覆蓋已讀取的同名屬性。
u 最後讀取parameterType傳遞的屬性,它會覆蓋已讀取的同名屬性。
建議使用properties,不要在properties中定義屬性,只引用定義的properties文件中屬性,並且properties文件中定義的key要有一些特殊的規則。
6.2 settings全局參數配置
mybatis運行時可以調整一些全局參數(相當於軟件的運行參數),參考:mybatis-settings.xlsx
根據使用需求進行參數配置。
注意:小心配置,配置參數會影響mybatis的執行。
ibatis的全局配置參數中包括很多的性能參數(最大線程數,最大待時間。。。),通過調整這些性能參數使ibatis達到高性能的運行,mybatis沒有這些性能參數,由mybatis自動調節。
6.3 typeAliases(常用)
可以將parameterType、resultType中指定的類型 通過別名引用。
6.3.1 mybaits提供了很多別名
別名 |
映射的類型 |
_byte |
byte |
_long |
long |
_short |
short |
_int |
int |
_integer |
int |
_double |
double |
_float |
float |
_boolean |
boolean |
string |
String |
byte |
Byte |
long |
Long |
short |
Short |
int |
Integer |
integer |
Integer |
double |
Double |
float |
Float |
boolean |
Boolean |
date |
Date |
decimal |
BigDecimal |
bigdecimal |
BigDecimal |
6.3.2 自定義別名
6.3.3 使用別名
在parameterType、resultType中使用別名:
6.3.4 typeHandlers
類型處理器將java類型和jdbc類型進行映射。
mybatis默認提供很多類型處理器,一般情況下夠用了。
6.3.5 mappers
7 輸入和輸出映射
通過parameterType完成輸入映射,通過resultType和resultMap完成輸出映射。
7.1 parameterType傳遞pojo包裝對象
可以定義pojo包裝類型擴展mapper接口輸入參數的內容。
需求:
自定義查詢條件查詢用戶信息,需要向statement輸入查詢條件,查詢條件可以有user信息、商品信息。。。。
7.1.1 包裝類型
7.1.2 mapper.xml
7.1.3 mapper.java
7.1.4 測試
7.1.5 異常
如果parameterType中指定屬性錯誤,異常,找不到getter方法:
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'userCusto' in 'class cn.itcast.mybatis.po.UserQueryVo'
### Cause: org.apache.ibatis.reflection.ReflectionException: There is no getter for property named 'userCusto' in 'class cn.itcast.mybatis.po.UserQueryVo'
注意:如果將來和spring整合後,不是通過調用getter方法來獲取屬性值,通過反射強讀取pojo的屬性值。
7.2 resultType
指定輸出結果的類型(pojo、簡單類型、hashmap..),將sql查詢結果映射爲java對象 。
7.2.1 返回簡單類型
mapper.xml
mapper.java
注意:
如果查詢記錄結果集爲一條記錄且一列再使用返回簡單類型。
7.3 resultMap(入門)
resultType :指定輸出結果的類型(pojo、簡單類型、hashmap..),將sql查詢結果映射爲java對象 。
使用resultType注意:sql查詢的列名要和resultType指定pojo的屬性名相同,指定相同 屬性方可映射成功,如果sql查詢的列名要和resultType指定pojo的屬性名全部不相同,list中無法創建pojo對象的。
resultMap:將sql查詢結果映射爲java對象。
如果sql查詢列名和最終要映射的pojo的屬性名不一致,使用resultMap將列名和pojo的屬性名做一個對應關係 (列名和屬性名映射配置)
7.3.1 resultMap配置
7.3.2 使用resultMap
7.3.3 mapper.java
8 動態sql(重點)
mybatis重點是對sql的靈活解析和處理。
8.1 需求
將自定義查詢條件查詢用戶列表和查詢用戶列表總記錄數改爲動態sql
8.2 if和where
8.3 sql片段
通過sql片段可以將通用的sql語句抽取出來,單獨定義,在其它的statement中可以引用sql片段。
通用的sql語句,常用:where條件、查詢列
8.3.1 sql片段的定義
8.3.2 引用sql片段
8.4 foreach
在statement通過foreach遍歷parameterType中的集合類型。
需求:
根據多個用戶id查詢用戶信息。
8.4.1 在userQueryVo中定義list<Integer> ids
在userQueryvo中定義list<Integer> ids存儲多個id
8.4.2 修改where語句
使用foreach遍歷list:
8.4.3 測試代碼