版權聲明:本文為部落客原創文章,遵循 CC 4.0 BY-SA 版權協定,轉載請附上原文出處連結和本聲明。
本文連結:https://blog.csdn.net/zhao1299002788/article/details/102136068
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容器中。
複制