MyBatis 本是apache的一個開源項目iBatis, 2010年這個項目由apache software foundation 遷移到了google code,而且更名爲MyBatis。2013年11月遷移到Github。 ●【GitHub】 GitHub就是一個互聯網上的超大SVN庫,裏面維護着許多開源項目的代碼。任何人均可以把本身好多項目代碼放上去共享,同時接受其餘人的共同開發。 2.2.什麼是MyBatis MyBatis是使用java語言編寫的一個優秀的持久層框架,是對JDBC操做數據庫的過程進行了全新的封裝。解決了JDBC中的問題。 Mybatis框架隱藏了jdbc繁雜的業務無關代碼: ·手動註冊驅動、建立connection、statement ·手動設置參數以及參數的順序 ·手動遍歷結果集 使開發者只需關注SQL怎麼寫。 3.JDBC的問題 JDBC是原生的數據庫開發。JDBC對於單機版的軟件或者一個小辦公室的小系統都仍是能夠應付的,畢竟業務簡單,數據量小,程序規模都不大。修改、編譯、發佈都很容易。但隨着業務發展這樣的IT技術不能知足要求了。逐漸的暴露出一些問題。
public
static
void
main(String[] args) { Connection connection =
null
; PreparedStatement preparedStatement =
null
; ResultSet resultSet =
null
;
try
{
//
加載數據庫驅動
Class.forName("com.mysql.jdbc.Driver");
//
經過驅動管理類獲取數據庫連接
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8", "root", "root");
//
定義sql語句 ?表示佔位符
String sql = "select * from user where username = ? and age = ?";
//
獲取預處理statement
preparedStatement = connection.prepareStatement(sql);
//
設置參數,第一個參數爲sql語句中參數的序號(從1開始),第二個參數爲設置的參數值
preparedStatement.setString(1, "王五"); preparedStatement.setInt(2, 18);
//
向數據庫發出sql執行查詢,查詢出結果集
resultSet = preparedStatement.executeQuery(); List<User> userList =
new
ArrayList<User>();
//
遍歷查詢結果集
while
(resultSet.next()){ User user =
new
User(); user.setUserId(resultSet.getInt("id")); user.setName(resultSet.getString("username")); userList.add(user); } }
catch
(Exception e) { e.printStackTrace(); }
finally
{
//
釋放資源
if
(resultSet!=
null
){
try
{ resultSet.close(); }
catch
(SQLException e) {
//
TODO Auto-generated catch block
e.printStackTrace(); } }
if
(preparedStatement!=
null
){
try
{ preparedStatement.close(); }
catch
(SQLException e) {
//
TODO Auto-generated catch block
e.printStackTrace(); } }
if
(connection!=
null
){
try
{ connection.close(); }
catch
(SQLException e) {
//
TODO Auto-generated catch block
e.printStackTrace(); } } } } 從上面的問題總結以下: 1.數據庫鏈接的建立、釋放頻繁形成系統資源浪費,缺少有效管理,很是影響系統性能。 若是使用數據庫鏈接池可解決此問題。 2.程序中存在硬編碼:(硬編碼就是寫死在程序中的固定值) 1)數據庫鏈接字符串:換數據庫就要改代碼,就要從新編譯發佈,維護壓力增大。 2)SQL定義字符串:SQL修改的可能性較大,修改SQL就要從新編譯發佈,維護壓力增大。 3)傳參數時的參數位置:參數必須按照前後順序設置到對應的SQL條件上,十分不靈活。 4)結果集中字段名字符串:字段名變化就要改代碼,就要從新編譯發佈,維護壓力增大。 3.1.如何解決JDBC的問題 框架發明的目的之一就是爲了解決jdbc問題,好比:Hibernate、MyBatis等。這些框架不只能夠解決問題還能夠大大簡化開發,讓開發人員更好的集中精力實現業務邏輯。 4.MyBatis主要的內容 MyBatis最重要的就是寫好SQL,包括寫好SQL語句和寫好SQL映射。 ·SQL語句就是標準的SQL語句(能夠在當前選用的數據庫產品下,根據需求使用該產品下的SQL函數) ·SQL映射包括:參數映射和返回值映射(返回值只針對查詢,增刪改是沒有返回值的) ●【參數映射】(也叫作【輸入映射】) MyBatis將java對象傳入給SQL語句參數的過程。 ●【返回值映射】(也叫作【輸出映射】) MyBatis將SQL查詢的結果集處理成一個java對象並返回給java程序的過程。 ●【java對象】 若是是單個參數映射,能夠是java簡單類型變量:
int
、
long
、
float
、String、Integer、Long、Boolean、Float等。參數值能夠映射給sql。 若是是多個參數映射,必須是用java bean,有一個名詞叫pojo(Plain Ordinary Java Object),裏面有許多屬性以及它們的getter/setter方法。將多個參數封裝到pojo對象裏面,一塊兒映射給sql。Java bean和pojo沒有區別,就是換種叫法而已。 ● SQL語句以及映射寫在xml或註解中。 5.MyBatis訪問數據庫的核心構成 咱們經過Hibernate與MyBatis訪問數據庫核心構成的對比來學習MyBatis如何訪問數據庫的。 從這個結構圖要明確知道MyBatis訪問數據庫的核心構成包括三個核心類和兩種配置文件。 三個核心類:SqlSessionFactoryBuilder、SqlSessionFactory、SqlSession; 兩種配置文件:MyBatis核心配置文件(一個)、MyBatis映射文件(多個)。 咱們在下面學習的入門程序主要學習的就是參數映射的規範和返回值映射的規範。 6.MyBatis開發環境搭建 這個開發環境僅僅是學習使用的,並非真正項目中使用的開發環境。 6.1.第一步:建立數據庫和表 1.建立數據庫【mybatis】,編碼utf-8 2.導入【資料\01.數據庫\initialDB.sql】腳本。 6.2.第二步:建立工程 建立一個普通java工程。 由於這裏咱們只是用來學習MyBatis,因此不是實際項目中真正的開發環境,所以創建一個普通java工程就夠用了。 等都後面SSM整合以後纔是咱們真正在實際項目中使用的開發環境。 6.3.第三步:導入jar包 這裏咱們用到【MyBatis的jar包】和【mysql的jar包】。 1.取得MyBatis的jar包 Mybatis的GitHub地址: https:
//
github.com/mybatis/mybatis-3/releases
2.導入MyBatis的jar包 1)Mybatis的jar包分爲: 核心jar包 依賴jar包 2)導入工程並關聯到工程 3. mysql的jar包也要導入工程並關聯,方法同上。 6.4.第四步:框架的配置文件 6.4.1.核心配置文件 建立Source Folder【config】,它做爲所有關於環境的配置文件的存放目錄。 在config下建立MyBatisConfig.xml(也有人叫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> <!-- 數據庫環境的配置(臨時配置) --> <!-- environments:表示多個數據庫環境的配置標籤
default
:當前默認使用的數據庫環境 --> <environments
default
="dev"> <!-- 開發數據庫環境的配置 --> <!-- environment:表示一個數據庫環境配置的標籤 id:表示惟一標識一個數據庫環境 --> <environment id="dev"> <!-- 事務管理的配置 --> <!-- transactionManager:表示事務管理的配置標籤 type:表示事務管理的類型,因爲MyBatis是對JDBC的封裝,因此一般使用JDBC的事務 --> <transactionManager type="JDBC"/> <!-- 數據源配置:driver, url, username, password --> <!-- dataSource:表示數據源的配置標籤 type:表示數據源的類型,標識是否支持數據庫鏈接池 POOLED:表示支持數據庫鏈接池的數據源 UNPOOLED:表示不支持數據庫鏈接池的數據源 --> <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="123"/> </dataSource> </environment> </environments> </configuration> 這個文件就是MyBatis的核心配置文件,裏面主要配置鏈接數據庫的數據源、事務管理以及MyBatis的映射文件有哪些。 其中整個【environments】部分不須要掌握,大概知道里面配置的是數據庫信息就能夠了,由於到實際項目中框架整合後就不用這麼配置了,因此它沒有實際用途,這裏只是臨時這麼用一下。 6.4.2.SQL映射文件 MyBatis的SQL映射文件就是用來寫SQL語句和配置SQL的參數映射和返回值映射的,能夠根據業務建立多個映射文件,好比關於用戶信息的映射文件:UserMapper.xml,關於訂單信息的映射文件:OrderMapper.xml等。 1.建立SQL映射文件(由於SQL映射文件是關於業務的,因此不要放到config裏面) 建立一個包【cn.baidu.mapper】,在其下建立UserMapper.xml映射文件: 把下面的內容拷貝到這個配置文件中:注意第一行,別重複了。 <?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映射文件的,是一個SQL映射文件的惟一標識 --> <mapper namespace="user"> <!-- SQL映射 --> </mapper> 2.配置SQL映射文件 在MyBatis核心配置文件中配置映射文件,目的是爲了讓MyBatis知道有這個映射文件。 <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> 。。。。。。 <!-- 配置映射文件 --> <mappers> <!-- 經過映射文件在編譯後類目錄下的相對路徑加載映射文件 resource:用來指定映射文件的相對路徑 --> <mapper resource="cn/baidu/mapper/UserMapper.xml" /> </mappers> </configuration> 6.5.第六步:測試開發環境 1.利用junit進行測試,eclipse自己包含junit的jar直接導入便可: 2.手動編寫客戶端測試程序 建立Source Folder【test】用於存放測試程序的,並建立一個普通的class:
package
mybatis;
import
java.io.InputStream;
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.Test;
public
class
MyTest { @Test
public
void
envTest()
throws
Exception { SqlSession sqlSession =
null
;
try
{
//
1. 讀取配置文件(MyBatis有專門讀取配置文件的工具類Resources)
InputStream inputStream = Resources.getResourceAsStream("MyBatisCofig.xml");
//
2. 根據主配置文件建立會話工廠
SqlSessionFactory sqlSessionFactory =
new
SqlSessionFactoryBuilder().build(inputStream);
//
3. 根據會話工廠建立會話對象
//
業務層經過SqlSession對象來訪問數據庫進行CRUD操做,每一個執行方法中會話對象要私有
sqlSession = sqlSessionFactory.openSession(); System.out.println(sqlSession); }
catch
(Exception e) { e.printStackTrace();
throw
e; }
finally
{
//
關閉會話
sqlSession.close(); } } } 注意:上面的核心類不是真實項目開發中須要寫的,都是臨時的寫法。這裏因爲只使用了MyBatis框架,因此只能臨時手動的加載核心配置文件、手動建立會話工廠以及會話對象,到真實項目中這些都不用寫。這裏只是作測試用的代碼。你們只要知道三個核心類是什麼就能夠。 3.可以打印出SqlSession對象的信息說明客戶端與數據庫鏈接的會話已經創建起來了。 6.6.第七步:給MyBatis加日誌輸出 把【資料\04.參考案例\config\log4j.properties】拷貝到工程的config目錄下。 6.7.小結 這一節主要是操做,所以記憶的東西不多,主要是課後多加練習開發環境的搭建。 搭建開發環境的目錄步驟能夠看目錄。 7.MyBatis入門程序(重點) MyBatis程序開發分爲兩步: 1. 在SQL映射文件中編寫SQL以及SQL映射,包括參數映射和返回值映射。 2. 在客戶端用SqlSession對象使用指定的方法調用SQL,包括兩個參數:第一個參數是某個配置好的SQL映射,第二個參數是要傳給SQL的參數。 7.1.查 7.1.1.根據id查詢用戶信息 1.【UserMapper.xml】映射文件中增長查詢SQL的映射配置: <說明>(須要掌握) 項目 解釋 <select> 用於查詢SQL的標籤。 id 在相同映射文件中SQL的惟一標識(名稱不容許包含點【.】) parameterType 傳入參數的類型(當沒有參數時能夠省略) resultType SQL返回給程序的java結果的類型 #{userId} 以#{xxx}做爲樣式,叫作佔位符。用來接收參數。xxx表示參數的變量名稱。 MyBatis都是按名稱進行參數映射的,若是隻寫#{}會報錯。有了名稱就不用考慮jdbc參數賦值的前後順序了,因此解決了jdbc傳值順序的硬編碼問題。 <SQL示例> 注意:要嚴格按照MyBatis的要求和映射規範去寫xml,不然MyBatis就沒法解析了。 <?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="user"> <!-- SQL --> <!-- 根據id查詢用戶信息 --> <select id="findUserById" parameterType="int" resultType="cn.baidu.pojo.User"> SELECT userId, name, mobile, sex, age, address FROM user WHERE userId = #{userId} </select> </mapper> <SQL映射規範> ·參數映射規範(一) 傳單個參數時,parameterType="java簡單類型",佔位符中的變量能夠任意名稱,但不能沒有。 ·返回值映射規範(一) 返回單條記錄時,resultType="pojo類型",結果集的列名必須等於pojo的屬性名。 (注意單條記錄中的多個值不能分散的返回,MyBatis不支持) 2.【MyTest.java】中增長一個測試方法: <說明>(須要掌握) 項目 解釋 selectOne 查詢單個記錄(單值或單條都使用它) 第一個參數 namespace屬性的值 + . + SQL id屬性的值(namespace肯定映射文件,id肯定SQL) 第二個參數 傳給SQL的參數,類型 = parameterType指定的類型(當沒有參數時可省略) 返回值 SQL查詢的結果,類型 = resultType指定的類型 <代碼示例>
//
測試根據id查詢用戶信息
@Test
public
void
test1()
throws
Exception {
//
讀取配置文件
InputStream inputStream = Resources.getResourceAsStream("MyBatisConfig.xml");
//
根據配置文件建立會話工廠
SqlSessionFactory sqlSessionFactory =
new
SqlSessionFactoryBuilder().build(inputStream);
//
根據會話工廠建立會話對象
SqlSession sqlSession = sqlSessionFactory.openSession();
//
根據id查詢用戶信息
User userInfo = sqlSession.selectOne("user.findUserById", 1001); System.out.println(userInfo); sqlSession.close(); } 7.2.根據用戶名查詢用戶信息 由於用戶名是一個不肯定的查詢條件,所以多半在SQL採用模糊查詢的方式進行條件匹配。 7.2.1.用佔位符接收參數映射 1.【UserMapper.xml】映射文件中增長查詢SQL配置 佔位符有一個特性:能夠作參數類型的判斷,若是是字符串類型的參數會自動添加單引號,不須要手動添加。 <!-- 根據用戶名查詢用戶信息(方式一:用佔位符接收參數映射) --> <select id="findUserByUserName" parameterType="String" resultType="cn.baidu.pojo.User"> SELECT userId, name,mobile,sex,age,address FROM user WHERE name LIKE #{userName} </select> <SQL映射規範> ·返回值映射規範(二) 返回多條記錄時,resultType="集合的pojo泛型的類型",結果集的列名必須等於pojo泛型的屬性名。 2.【MyTest.java】中增長一個測試方法: <說明>(須要掌握) 項目 解釋 selectList 查詢多條記錄(返回List<pojo>類型的java對象) 第一個參數 同上 第二個參數 同上 返回值 SQL查詢的List集合結果,List集合的泛型 = resultType指定的類型 MyBatis內部會經過返回值映射產生多個java對象,這些對象會放到一個List對象中,每一個對象的類型就是resultType配置的泛型,最終將List對象返回給java程序。 <代碼示例>
//
測試根據用戶名查詢用戶信息(方式一)
@Test
public
void
test1()
throws
Exception {
//
讀取配置文件
InputStream inputStream = Resources.getResourceAsStream("MyBatisConfig.xml");
//
根據配置文件建立會話工廠
SqlSessionFactory sqlSessionFactory =
new
SqlSessionFactoryBuilder().build(inputStream);
//
根據會話工廠建立會話對象
SqlSession sqlSession = sqlSessionFactory.openSession();
//
根據用戶名查詢用戶信息
List<User> userList = sqlSession.selectList("user.findUserByUserName", "%王%"); System.out.println(userList); sqlSession.close(); } 注意:這裏有一個問題就是若是SQL不是你寫的,你在調用的時候可能不知道里面是否用的是模糊查詢,因此也就很差判斷是否須要加百分號了,最好是能將百分號加在SQL上面,這樣外面無論是否是模糊查詢都須要傳姓名確定是沒有錯的。但這時就不能使用佔位符的,由於單引號須要本身加在SQL中。 這就須要使用MyBatis的另外一種用來接收參數映射的符號——字符串鏈接符,也叫字符串拼接符。 7.2.2.用字符串拼接符接收參數映射 1.【UserMapper.xml】映射文件中增長查詢SQL配置 <說明> 項目 解釋 ${value} 以${xxx}做爲樣式,叫作字符串拼接符。 拼接符讓MyBatis把SQL語句和參數值當成字符串進行字符串原樣拼接,所謂原樣拼接就是不作任何jdbc類型轉換,原來什麼樣就拼成什麼樣。因此SQL配置中必須人爲加單引號才行。 <SQL示例> <!-- 根據用戶名查詢用戶信息(方式二:用拼接符接收參數) --> <select id="findUserByUserName2" parameterType="String" resultType="cn.baidu.pojo.User"> SELECT userId, name,mobile,sex,age,address FROM user WHERE name LIKE '%${value}%' </select> <SQL映射規範> ·參數映射規範(二) 傳單個參數時,parameterType="java簡單類型",拼接符中的變量名必須是value,也不能沒有。 2.【MyTest.java】中增長一個測試方法:
//
測試根據用戶名查詢用戶信息(方式二)
@Test
public
void
test1()
throws
Exception {
//
讀取配置文件
InputStream inputStream = Resources.getResourceAsStream("MyBatisConfig.xml");
//
根據配置文件建立會話工廠
SqlSessionFactory sqlSessionFactory =
new
SqlSessionFactoryBuilder().build(inputStream);
//
根據會話工廠建立會話對象
SqlSession sqlSession = sqlSessionFactory.openSession();
//
根據用戶名查詢用戶信息
List<User> userList = sqlSession.selectList("user.findUserByUserName2", "王"); System.out.println(userList); sqlSession.close(); } 7.2.3.佔位符與拼接符區別 1.類型處理: 佔位符#{}傳遞參數時會作參數類型處理, 拼接符${}傳遞參數時不會作類型處理只進行字符串原樣拼接 2.安全性: ${}的原樣拼接致使它存在安全漏洞,容易產生SQL注入風險 #{}的類型處理會對參數中存在的SQL敏感字符先轉義而後再映射給SQL,這就不會影響原先的SQL,所以能夠有效防止SQL注入。 3.工做中的應用: 因爲拼接符${}存在安全隱患,所以在實際項目儘可能使用佔位符#{} 附:SQL注入的一個示例 1 映射文件中的配置 <!-- 用拼接符接收參數 --> <select id="selectUserByUserName3" parameterType="String" resultType="cn.baidu.pojo.User"> SELECT u.userId, u.name, u.age, u.address FROM user u WHERE u.name LIKE '${value}' </select> <!-- 用佔位符接收參數 --> <select id="selectUserByUserName4" parameterType="String" resultType="cn.baidu.pojo.User"> SELECT u.userId, u.name, u.age, u.address FROM user u WHERE u.name LIKE #{name} </select> 2 傳遞參數是一致的,左邊拼接符最外面的單引號已經在映射文件中寫上了;右邊佔位符按照預想因爲傳入的是String字符串類型的參數,因此會作類型處理自動的在參數外面加上一對單引號。但事情會想咱們想象的那樣進行嗎? List<User> userInfoList = sqlSession.selectList("user. selectUserByUserName3", "' OR 1=1 OR 1='"); List<User> userInfoList = sqlSession.selectList("user. selectUserByUserName4", "' OR 1=1 OR 1='"); 3 結果發現左邊確實發生了sql注入,右邊沒有發生: DEBUG [main] - Setting autocommit to
false
on JDBC Connection [com.mysql.jdbc.JDBC4Connection@462d5aee] DEBUG [main] - ==> Preparing: SELECT u.userId, u.name, u.age, u.address FROM user u WHERE u.name LIKE '' OR 1=1 OR 1='' DEBUG [main] - ==> Parameters: DEBUG [main] - <== Total: 14 [[1001, 王小一,
null
, 56,
null
, 南京], [1002, 王小二,
null
, 48,
null
, 青島], [1003, 王小三,
null
, 32,
null
, 大連], [1004, 張三,
null
, 23,
null
, 廣州], [1005, 王小五,
null
, 34,
null
, 重慶], [1006, 王小六,
null
, 31,
null
, 石家莊], [1007, 迎春,
null
, 28,
null
, 蘇州], [1008, 張三,
null
, 23,
null
, 廣州], [1009, 迎秋,
null
, 20,
null
, 長沙], [1010, 迎冬,
null
, 18,
null
, 佛山], [1011, 張三,
null
, 30,
null
, 廣州], [1013, 張三,
null
, 30,
null
, 廣州], [1014, 張三,
null
, 30,
null
, 廣州], [1015, 張三,
null
, 30,
null
, 廣州]] DEBUG [main] - Resetting autocommit to
true
on JDBC Connection [com.mysql.jdbc.JDBC4Connection@462d5aee] DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@462d5aee] DEBUG [main] - Returned connection 1177377518 to pool. DEBUG [main] - Setting autocommit to
false
on JDBC Connection [com.mysql.jdbc.JDBC4Connection@58c1670b] DEBUG [main] - ==> Preparing: SELECT u.userId, u.name, u.age, u.address FROM user u WHERE u.name LIKE ? DEBUG [main] - ==> Parameters: ' OR 1=1 OR 1='(String) DEBUG [main] - <== Total: 0 [] DEBUG [main] - Resetting autocommit to
true
on JDBC Connection [com.mysql.jdbc.JDBC4Connection@58c1670b] DEBUG [main] - Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@58c1670b] DEBUG [main] - Returned connection 1489069835 to pool. 左邊拼接是原樣拼接所以出現了漏洞,造成的SQL至關於mysql的工具中左邊的樣子: 右邊佔位符因爲作類型處理,首前後把校驗傳入的參數是否有敏感字符,這裏單引號就是一個敏感字符,其次若是有敏感字符須要進行轉義,上面的參數轉義爲:\' OR 1=1 OR 1=\',再次再把轉義完的參數映射給SQL並在參數外面加一對單引號,轉義後的參數就不會對原先的SQL產生影響,僅僅被看成普通參數值進行處理。造成的SQL至關於mysql的工具中右邊的樣子: 7.3.查詢用戶表記錄數 1.【UserMapper.xml】映射文件中增長查詢SQL配置 <說明> 項目 解釋 <select> 同上 id 同上 parameterType 沒有參數時能夠省略 resultType SQL返回給程序的java結果的類型 #{xxx}或${xxx} 沒有參數能夠省略 <SQL示例> <!-- 取得用戶表的記錄數 --> <select id="countUserRecord" resultType="int"> SELECT COUNT(userId) FROM user </select> 注意:不要使用count(*),由於count(*)效率低,能夠count(1)或count(字段名)均可以。 <SQL映射規範> ·返回值映射規範(三) 返回單值時,resultType="java簡單類型",值直接返回給java程序。 2.【MyTest.java】中增長一個測試方法:
//
selectOne也能夠返回單值結果
int
count = ss.selectOne("user.countUserRecord"); System.out.println(count); 7.4.增 7.4.1.插入單條記錄 1.【UserMapper.xml】映射文件中增長插入SQL配置 <說明> 項目 解釋 <insert> 用於插入SQL的標籤。 id 同查詢 parameterType 同查詢 resultType 插入SQL沒有返回值,因此沒有這個屬性 #{xxx} 同查詢,這裏能體現出名稱的價值。 <SQL示例> <!-- 插入用戶信息 --> <insert id="addUserInfo" parameterType="cn.baidu.pojo.User"> INSERT INTO USER (name,mobile,sex,age,address) VALUES (#{name},#{mobile},#{sex},#{age},#{address}) </insert> <SQL映射規範> ·參數映射規範(三) 傳多個參數時,parameterType="pojo類型",佔位符或拼接符的變量名必須等於pojo中的屬性名。 (在參數pojo中屬性是沒有順序的,因此很好的解決了jdbc參數順序硬編碼的問題) 2.【MyTest.java】中增長一個測試方法: <說明> 項目 解釋 insert 插入處理 第一個參數 同查詢 第二個參數 同查詢 返回值 SQL沒有返回值,可是方法自己有一個int的返回值,表示插入的記錄條數。 <代碼示例>
//
測試插入一條用戶信息
@Test
public
void
test1()
throws
Exception { SqlSession sqlSession =
null
;
try
{
//
根據會話工廠建立會話對象
sqlSession = sqlSessionFactory.openSession(); User user =
new
User(); user.setAge(18); user.setAddress("北京"); user.setMobile("13500099000"); user.setName("張三"); user.setSex("男");
//
插入用戶信息
int
count = sqlSession.insert("user.addUserInfo", user); System.out.println("count=" + count); sqlSession.commit(); }
catch
(Exception ex) { ex.printStackTrace(); sqlSession.rollback();
throw
ex; }
finally
{ sqlSession.close(); } } 7.5.改 7.5.1.根據id更新用戶信息 1.【UserMapper.xml】映射文件中增長插入SQL配置 <說明> 項目 解釋 <update> 用於更新SQL的標籤。 id 同查詢 parameterType 同查詢 resultType 更新SQL沒有返回值,因此沒有這個屬性 #{xxx} 同查詢,這裏能體現出名稱的價值。 <SQL示例> <!-- 根據id修改用戶信息 --> <update id="updateUserById" parameterType="cn.baidu.pojo.User"> UPDATE user SET name = #{name}, mobile = #{mobile}, sex = #{sex}, age = #{age}, address = #{address} WHERE userId = #{userId} </update> <SQL映射規範> 同插入的規範。 2.【MyTest.java】中增長一個測試方法: <說明> 項目 解釋 update 更新處理 第一個參數 同查詢 第二個參數 同查詢 返回值 SQL沒有返回值,可是方法自己有一個int的返回值,表示更新的記錄條數。 <代碼示例>
//
測試根據id修改用戶信息
@Test
public
void
test1()
throws
Exception { SqlSession sqlSession =
null
;
try
{
//
根據會話工廠建立會話對象
sqlSession = sqlSessionFactory.openSession(); User user =
new
User(); user.setAddress("天津"); user.setAge(28); user.setMobile("13600099000"); user.setName("李四"); user.setSex("女"); user.setUserId(1011);
//
更新用戶信息
int
count = sqlSession.update("user.updateUserById", user); System.out.println("count=" + count); sqlSession.commit(); }
catch
(Exception ex) { ex.printStackTrace(); sqlSession.rollback();
throw
ex; }
finally
{ sqlSession.close(); } } 7.6.刪 7.6.1.根據id刪除用戶信息 1.【UserMapper.xml】映射文件中增長插入SQL配置 <說明> 項目 解釋 <delete> 用於刪除SQL的標籤。 id 同查詢 parameterType 同查詢 resultType 刪除SQL沒有返回值,因此沒有這個屬性 #{xxx} 同查詢 <SQL示例> <!-- 根據id刪除用戶信息 --> <delete id="deleteUserById" parameterType="int"> DELETE FROM user WHERE userId = #{id} </delete> <SQL映射規範> 同查詢的規範。 2.【MyTest.java】中增長一個測試方法: <說明> 項目 解釋 delete 刪除處理 第一個參數 同查詢 第二個參數 同查詢 返回值 SQL沒有返回值,可是方法自己有一個int的返回值,表示刪除的記錄條數。 <代碼示例>
//
測試根據id刪除用戶信息
@Test
public
void
test1()
throws
Exception { SqlSession sqlSession =
null
;
try
{
//
根據會話工廠建立會話對象
sqlSession = sqlSessionFactory.openSession();
//
根據id刪除用戶信息
int
count = sqlSession.delete("user.deleteUserById", 1011); System.out.println("count=" + count); sqlSession.commit(); }
catch
(Exception ex) { ex.printStackTrace(); sqlSession.rollback();
throw
ex; }
finally
{ sqlSession.close(); } } 7.7.增刪改查小結 本小結下的內容都是重點須要重點記憶。 <SQL映射規範>(須要掌握) ·參數映射規範 傳單個參數時,parameterType="java簡單類型",佔位符中的變量能夠任意名稱,但不能沒有。 傳單個參數時,parameterType="java簡單類型",拼接符中的變量名必須是value,也不能沒有。 傳多個參數時,parameterType="pojo類型",佔位符或拼接符的變量名必須等於pojo中的屬性名。 ·返回值映射規範 返回單值時,resultType="java簡單類型",值直接返回給java程序。 返回單條記錄時,resultType="pojo類型",結果集的列名必須等於pojo的屬性名。 返回多條記錄時,resultType="集合的pojo泛型的類型",結果集列名必須等於pojo泛型的屬性名。 <增刪改查對應的標籤和java客戶端調用的方法>(須要掌握) 區分 標籤 客戶端調用方法 增 <insert> insert(namespace名+.+sql id, sql的參數變量) 刪 <delete> delete(參數同上) 改 <update> update(參數同上) 查 <select> 單值或單條selectOne(參數同上) 多條selectList(參數同上) 7.8.MyBatis對JDBC問題的解決 1.如何解決JDBC數據鏈接資源缺少管理的問題? 解決:在MyBatis配置文件中配置了數據庫鏈接池。 2.如何解決SQL的硬編碼 解決:將Sql語句配置在SQL映射文件中與java代碼分離。 3.如何解決SQL參數的順序硬編碼問題 解決:MyBatis的參數映射,能夠幫咱們把java對象自動的映射給SQL 4.如何解決結果集中字段名字符串的硬編碼 解決:MyBatis的返回值映射,能夠幫咱們把結果集自動的映射給java對象。 7.9.MyBatis與Hibernate對比總結 7.10.插入標籤中的子查詢(瞭解) 插入的子查詢主要是用來查詢數據表主鍵的做用。查詢出來的主鍵須要賦值給參數映射中傳入的pojo對象某屬性上。 7.10.1.功能一: 取得插入數據的自增主鍵 selectKey + LAST_INSERT_ID(),能夠解決如何查詢自增型主鍵的數據表中剛插入記錄的主鍵的問題。先插入後取得,取得後能夠和其餘表作外鍵關聯的操做。 1.【UserMapper.xml】映射文件中增長插入SQL配置 <說明> 項目 解釋 <selectKey> 用於<insert>操做的子查詢。 order 子查詢相對於insert SQL的執行順序(AFTER:在插入以後執行 BEFORE:在插入以前執行) keyProperty 傳入的java對象參數的某個屬性名,用於將子查詢結果賦值給參數對象的指定屬性。這樣在java程序中只要執行完insert()方法,就能夠從參數對象中指定的屬性上取得這個子查詢的結果。 resultType 子查詢的值按照什麼類型返回結果 LAST_INSERT_ID() mysql的函數,能夠返回最新插入記錄的主鍵,要和insert語句配合使用,不然單獨執行的值就是0 <SQL示例> <!-- 插入一條用戶信息並返回新插入記錄的主鍵 --> <insert id="addUserInfo" parameterType="cn.baidu.pojo.User"> <!-- 插入操做中的子查詢 --> <selectKey order="AFTER" keyProperty="userId" resultType="int"> SELECT LAST_INSERT_ID() </selectKey> INSERT INTO USER (name,mobile,sex,age,address) VALUES (#{name},#{mobile},#{sex},#{age},#{address}) </insert> 2.【MyTest.java】中增長一個測試方法:
//
測試插入一條用戶信息
@Test
public
void
test1()
throws
Exception { SqlSession sqlSession =
null
;
try
{
//
根據會話工廠建立會話對象
sqlSession = sqlSessionFactory.openSession(); User user =
new
User(); user.setAge(18); user.setAddress("北京"); user.setMobile("13500099000"); user.setName("張三"); user.setSex("男"); System.out.println("user.userId=" + user.getUserId());
//
插入用戶信息
int
count = sqlSession.insert("user.addUserInfo", user); System.out.println("count=" + count); System.out.println("user.userId=" + user.getUserId()); sqlSession.commit(); }
catch
(Exception ex) { ex.printStackTrace(); sqlSession.rollback();
throw
ex; }
finally
{ sqlSession.close(); } } 7.10.2.功能二: 使用UUID實現主鍵 selectKey + UUID(),能夠解決非自增型主鍵的數據表中在插入數據前先建立主鍵的問題。 <說明> 項目 解釋 <selectKey> 同上 order 同上(這裏指定爲BEFORE) keyProperty 同上 resultType 同上 UUID() mysql的函數,能夠返回隨機的UUID,能夠做爲主鍵用。 映射文件: <!-- 取得插入數據的主鍵後插入數據 --> <insert id="insertOrderData" parameterType="cn.baidu.pojo.Order"> <selectKey order="BEFORE" keyProperty="orderId" resultType="String"> SELECT UUID() </selectKey> INSERT INTO order1 (orderId, userId, orderStatus, goodsId, createDateTime) VALUES (#{orderId}, #{userId}, #{orderStatus}, #{goodsId}, now()); </insert> 客戶端程序:
//
數據庫操做...
//
insert:表示插入SQL的方法
Order order =
new
Order(); order.setGoodsId("123456789"); order.setOrderStatus("01"); order.setUserId(1008); System.out.println(order.getOrderId()); ss.insert("order.insertOrderData", order); System.out.println(order.getOrderId()); ss.commit(); 8.DAO開發方法 8.1.傳統DAO開發方式 傳統的DAO開發方式就是編寫DAO接口和DAO實現類來實現對數據庫的訪問。 8.1.1.編寫SQL 從【UserMapper.xml】中挑選兩個SQL <!-- 根據id查詢用戶信息 --> <select id="findUserById" parameterType="int" resultType="cn.baidu.pojo.User"> SELECT userId, name, mobile, sex, age, address FROM user WHERE userId = #{userId} </select> <!-- 根據用戶名查詢用戶信息 --> <select id="findUserByUserName2" parameterType="String" resultType="cn.baidu.pojo.User"> SELECT userId, name,mobile,sex,age,address FROM user WHERE name LIKE '%${value}%' </select> 8.1.2.編寫DAO接口 在【cn.baidu.dao】包下建立DAO接口【UserDao】
package
cn.baidu.dao;
import
java.util.List;
import
cn.baidu.pojo.User;
public
interface
UserDao {
//
根據id查詢用戶信息
public
User findUserById(Integer id)
throws
Exception;
//
根據姓名查詢用戶信息
public
List<User> findUserByName(String name)
throws
Exception; } 8.1.3.編寫DAO接口實現類 在【cn.baidu.dao】包下建立接口【UserDao】的實現類【UserDaoImpl】
package
cn.baidu.dao;
import
java.util.List;
import
org.apache.ibatis.session.SqlSession;
import
org.apache.ibatis.session.SqlSessionFactory;
import
cn.baidu.pojo.User;
public
class
UserDaoImpl
implements
UserDao {
private
SqlSessionFactory sqlSessionFactory;
public
void
setSqlSessionFactory(SqlSessionFactory sqlSf) {
this
.sqlSessionFactory = sqlSf; } @Override
public
User findUserById(Integer id)
throws
Exception { SqlSession sqlSession =
null
;
try
{ sqlSession = sqlSessionFactory.openSession();
//
根據id查詢
User userInfo = sqlSession.selectOne("user.findUserById", id);
return
userInfo; }
catch
(Exception ex) { ex.printStackTrace();
throw
ex; }
finally
{ sqlSession.close(); } } @Override
public
List<User> findUserByName(String name)
throws
Exception { SqlSession sqlSession =
null
;
try
{
//
根據會話工廠建立會話對象
sqlSession = sqlSessionFactory.openSession();
//
根據用戶名查詢
List<User> userInfoList = sqlSession.selectList("user.findUserByUserName2", name);
return
userInfoList; }
catch
(Exception ex) { ex.printStackTrace();
throw
ex; }
finally
{ sqlSession.close(); } } } 8.1.4.編寫客戶端測試程序 在【test】目錄下建立【MyTest2.java】測試程序
package
mybatis;
import
java.io.InputStream;
import
java.util.List;
import
org.apache.ibatis.io.Resources;
import
org.apache.ibatis.session.SqlSessionFactory;
import
org.apache.ibatis.session.SqlSessionFactoryBuilder;
import
org.junit.Before;
import
org.junit.Test;
import
cn.baidu.dao.UserDaoImpl;
import
cn.baidu.pojo.User;
/**
* DAO開發方式
*/
public
class
MyTest2 {
private
SqlSessionFactory sqlSessionFactory;
private
UserDaoImpl userDao;
//
測試初始化函數
@Before
public
void
init()
throws
Exception {
//
讀取配置文件
InputStream inputStream = Resources.getResourceAsStream("MyBatisConfig.xml");
//
根據主配置文件建立會話工廠
sqlSessionFactory =
new
SqlSessionFactoryBuilder().build(inputStream); userDao =
new
UserDaoImpl(); userDao.setSqlSessionFactory(sqlSessionFactory); }
//
測試根據id查詢用戶信息
@Test
public
void
test1()
throws
Exception { User user = userDao.findUserById(1001); System.out.println(user); }
//
測試根據用戶名查詢用戶信息
@Test
public
void
test2()
throws
Exception { List<User> userList = userDao.findUserListByName("迎"); System.out.println(userList); } } 8.1.5.傳統DAO開發方法的問題 正常的傳統DAO接口的實現類中各個方法的邏輯基本相同,代碼重複的部分較多。除非有特殊業務要求才會加入特殊的業務邏輯,不然實現類中的方法幾乎一致。 8.2.MyBatis動態代理DAO開發方式(重點) 8.2.1.什麼是MyBatis動態代理 MyBatis打破傳統DAO的開發方式,不須要程序員再寫DAO的實現類了,能夠直接用DAO接口的對象調用數據庫處理的方法,MyBatis在執行時由代理對象代替DAO接口的實現類執行數據庫處理。 要使用MyBatis動態代理就必須遵照動態代理的開發規範,即四個相等。 8.2.2.MyBatis動態代理開發規範 (須要掌握) 接口 映射文件 徹底限定名 = Namespace的值 方法名 = SQL的id的值 接口方法的參數類型 = parameterType的值 接口方法的返回值類型 = resultType的值 8.2.3.編寫SQL映射文件並添加配置 在【cn.baidu.mapper】包下建立新的映射文件【UserMapper2.xml】, 同時不要忘記配置到核心配置文件 <?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:整個MyBatis管理的映射文件中必須惟一 --> <mapper namespace="cn.baidu.dao.UserDao2"> <!-- SQL --> <!-- 根據id查詢用戶信息 --> <select id="findUserById" parameterType="int" resultType="cn.baidu.pojo.User"> SELECT userId, name, mobile, sex, age, address FROM user WHERE userId = #{userId} </select> <!-- 根據用戶名查詢用戶信息 --> <select id="findUserByUserName" parameterType="String" resultType="cn.baidu.pojo.User"> SELECT userId, name,mobile,sex,age,address FROM user WHERE name LIKE '%${value}%' </select> </mapper> 8.2.4.編寫DAO接口 在【cn.baidu.dao】包下建立DAO接口【UserDao2】
package
cn.baidu.dao;
import
java.util.List;
import
cn.baidu.pojo.User;
public
interface
UserDao2 {
//
根據id查詢用戶信息
public
User findUserById(Integer id)
throws
Exception;
//
根據姓名查詢用戶信息
public
List<User> findUserByUserName(String name)
throws
Exception; } 8.2.5.編寫客戶端測試程序 在【MyTest2.java】測試程序中增長兩個測試方法 <說明> 項目 解釋 getMapper 生成接口的代理對象。 參數 接口的類型描述。(Xxxx.
class
) 返回值 接口類型的代理對象。 <示例代碼>
//
測試根據id查詢用戶信息(動態代理DAO開發方式)
@Test
public
void
test3()
throws
Exception { SqlSession sqlSession = sqlSessionFactory.openSession();
//
用getMapper取得自動生成的DAO接口的實現類
UserDao2 userDao = sqlSession.getMapper(UserDao2.
class
); User userInfo = userDao.findUserById(1001); System.out.println(userInfo); sqlSession.close(); }
//
測試根據id查詢用戶信息(動態代理DAO開發方式)
@Test
public
void
test4()
throws
Exception { SqlSession sqlSession = sqlSessionFactory.openSession(); UserDao2 userDao = sqlSession.getMapper(UserDao2.
class
); List<User> userList = userDao.findUserByUserName("迎"); System.out.println(userList); sqlSession.close(); } 8.3.動態代理DAO開發方式的好處 Mybatis已經把開發中能簡化的都簡化了,對於咱們開發人員就能夠好好的集中精力寫SQL了。 8.4.傳統DAO開發方式與MyBatis動態代理的區別 傳統方式須要編寫DAO接口的實現類,並經過實例化實現類的對象來訪問數據庫。 動態代理不需編寫DAO接口的實現類,並經過接口類的代理對象來訪問數據庫。代理對象由MyBatis自動生成。 8.5.小結 本章的重點是MyBatis動態代理DAO開發方式,要掌握如何開發動態代理DAO,牢記動態代理四個開發規範。這也是今天課程中第二個重點。 9.MyBatis核心配置文件 9.1.核心配置文件配置項結構 官方說明Url:http:
//
www.mybatis.org/mybatis-3/zh/configuration.html
·注意:配置項必須按照上面的順序配置 紅框中的爲經常使用配置項,須要知道,其餘配置項不經常使用,從此工做中用到時再查資料瞭解。 9.2.properties(屬性)配置項 9.2.1.properties文件的好處 1.容易維護:相比於xml,properties文件更易於修改,下降修改時出錯的概率。在實際項目中常常把properties屬性文件與xml配置文件結合使用,把真正的值都放在properties屬性文件中,在xml中使用的時候直接引過來就可使用了,很是方便。 2.一處修改多處生效 9.2.2.properties標籤配置 <!-- 屬性文件的配置 --> <!-- properties:表示屬性文件配置的標籤 resource:表示類的相對路徑下的java屬性文件 url:表示文件的絕對路徑 --> <properties resource="jdbc.properties" /> 對應數據源配置的修改: <!-- 數據庫環境的配置 --> <environments
default
="dev"> <!-- 開發數據庫環境的配置 --> <environment id="dev"> <!-- 事務管理的配置 --> <transactionManager type="JDBC"/> <!-- 數據源配置:driver, url, username, password --> <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> properties屬性文件中key的命名方式: 文件名 + . + 具體的key名,這樣作的好處是不容易形成兩個properties文件中出現同名的key。key一旦同名,這兩個properties文件又都被同時引入到一個xml中就會出錯。 9.3.typeAliases(類型別名)配置項 9.3.1.MyBatis內建的java類型的別名 注意: 紅色框中的纔是java基本類型以及對應的別名,從中能夠看出java基本類型的別名都是如下劃線開頭的。 藍色框中的是java基本類型的包裝類以及對應的別名,這也是咱們前面一直在使用的。 沒有畫框的都是正常使用的別名。 爲何在MyBatis內建議使用java基本類型的包裝類型來傳遞參數? MyBatis在處理參數時,若是傳遞的是java基本類型:
int
、
long
、
char
、bool等,MyBatis都會將基本類型包裝成:Integer、Long、String、Boolean等,而後再進行傳遞,這樣作的好處是避免java基本類型的默認值問題。Java基本類型參數在沒有實際賦值時都會有默認值,若是你不主動給參數變量賦值就直接傳給SQL,就會把參數變量的默認值傳給SQL語句,這樣就可能形成破壞業務數據的風險,所以在MyBatis內部這樣的參數都會自動的被包裝成對象進行傳值。對象的好處是一旦沒有傳值,因爲是對象,它的默認值就是null,給SQL傳遞null時通常都不會執行成功。 所以, 爲了數據安全儘可能使用藍框的包裝類型來傳值賦值. 9.3.2.別名都是大小寫不敏感的 int和INT、iNt、Int等都是同樣的,其它的也同樣不區分大小寫 9.3.3.別名的目的 簡化映射文件的配置,縮短配置項的長度 9.3.4.POJO類型的自定義別名配置格式 MyBatisConfig.xml中(注意主配置文件中的項目是有順序的) <!-- 自定義別名 --> <typeAliases> <typeAlias type="cn.baidu.pojo.User" alias="User"/> </typeAliases> 映射文件: <!-- 根據id查詢用戶信息 --> <select id="findUserById" parameterType="int" resultType="User"> select userId, name,mobile,sex,age,address FROM user WHERE userId = #{userId} </select> 9.3.5.結論 對於java自己擁有的類型可使用別名,而自定義類型不要使用別名。 推薦使用java包裝類型或它們的別名(籃框中的),而不要使用java基本類型和它們的別名(紅框中的)。 9.4.mappers(映射文件)配置項 9.4.1.映射文件配置形式一 直接指定映射文件在類根目錄下的相對路徑 <說明> 項目 解釋 <mappers> 用於SQL映射文件的配置,下面能夠配置多個映射文件的掃描 <mapper> 是<mappers>的子標籤,用於一個映射文件的配置 resource 映射文件相對於類根目錄下的相對路徑 適用範圍 兩種DAO開發方式都適用這種配置,DAO的路徑和SQL映射文件的路徑之間沒有任何聯繫,隨意定義。 配置: <!-- 配置映射文件 --> <mappers> <mapper resource="cn/baidu/mapper/UserMapper.xml"/> <mapper resource="cn/baidu/mapper/UserMapper2.xml"/> </mappers> 9.4.2.映射文件配置形式二 從新建立一個包【cn.baidu.mapper2】,在其中建立兩個映射文件和兩個對應同名的DAO接口,而後進行試驗。 經過一個java接口的徹底限定名加載映射文件 <說明> 項目 解釋
class
接口的徹底限定名 要求 DAO接口文件與映射文件必須同名同目錄 適用範圍 只適用於MyBatis動態代理DAO開發方式 配置: <!-- 配置映射文件 --> <mappers> <mapper
class
="cn.baidu.mapper2.UserMapper" /> </mappers> 9.4.3.映射文件配置形式三 <說明> 項目 解釋 <
package
> 是第二種形式的批量加載形式 name 指定掃描的包路徑 要求 指定包下的DAO接口文件與映射文件必須同名同目錄 適用範圍 只適用於MyBatis動態代理DAO開發方式 配置: <!-- 配置映射文件 --> <mappers> <
package
name="cn.baidu.mapper2"/> </mappers> 9.4.4.三種形式的辨析 1.形式一: 兩種DAO開發方式都使用,但比較死板,一旦路徑寫錯了不容易發現。一次只能加載一個映射文件。 2.形式二: 只適用於動態代理方式,比較靈活,檢驗是否寫錯很容易(按住ctrl鍵,鼠標指針放上去有鏈接說明寫的沒有錯)。SM整合後能夠放到spring容器中。 3.形式三: 只適用於動態代理方式,方式更加實用,是形式二的批量掃描方式,比形式二好在能夠批量加載,SM整合後能夠放到spring容器中。
做者簡介:純潔的代碼,一個有故事的程序員。但願用文字,讓你讀懂代碼世界。微信號gzitcast,歡迎你們找我獲取各類資源。
html