Mybatis核心技術
介紹
MyBatis 本是apache的一個開源專案iBatis, 2010年這個專案由apache software foundation遷移到了google code,並且改名為MyBatis 。2013年11月遷移到Github。 MyBatis是一個優秀的持久層框架,它對jdbc的操作資料庫的過程進行封裝,使開發者只需要關注 SQL 本身,而不需要花費精力去處理例如註冊驅動、建立connection、建立statement、手動設定引數、結果集檢索等jdbc繁雜的過程程式碼。
Mybatis通過xml或註解的方式將要執行的各種statement(statement、preparedStatement、CallableStatement)配置起來,並通過java物件和statement中的sql進行對映生成最終執行的sql語句,最後由mybatis框架執行sql並將結果對映成java物件並返回。
使用原生jdbc出現的問題
1、 資料庫連結建立、釋放頻繁造成系統資源浪費從而影響系統性能,如果使用資料庫連結池可解決此問題。
2、 Sql語句在程式碼中硬編碼,造成程式碼不易維護,實際應用sql變化的可能較大,sql變動需要改變java程式碼。
3、 使用preparedStatement向佔有位符號傳引數存在硬編碼,因為sql語句的where條件不一定,可能多也可能少,修改sql還要修改程式碼,系統不易維護。
4、 對結果集解析存在硬編碼(查詢列名),sql變化導致解析程式碼變化,系統不易維護,如果能將資料庫記錄封裝成pojo物件解析比較方便。
Mybatis架構
Mybatis配置
Mapper.xml: sql對映檔案,檔案中配置了操作資料庫的sql語句。此檔案需要在sqlMapConfig.xml中載入。
#{}佔位符:佔位
如果傳入的是基本型別,那麼#{}中的變數名稱可以隨意寫
如果傳入的引數是pojo型別,那麼#{}中的變數名稱必須是pojo中的屬性.屬性名(user.username)
${}拼接符:字串原樣拼接(有sql注入的風險)
如果傳入的是基本型別,那麼${}中的變數名必須是value
如果傳入的引數是pojo型別,那麼${}中的變數名稱必須是pojo中的屬性.屬性名(user.username)
注意:使用拼接符有可能造成sql注入,在頁面輸入的時候可以加入校驗,不可輸入sql關鍵字,不可輸入空格
注意:如果是取簡單數量型別的引數,括號中的值必須為value
例: select * from user where username like '%${value}%'
可以這樣寫來解決sql注入:select * from user where username like '%’ #{name}’%'(開發經常使用)
入門
SqlMapConfig.xml配置:
(並不固定,可以寫別的名稱)mybatis基本配置<?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事務管理-->
<transactionManager type="JDBC" />
<!-- 資料庫連線池-->
<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="root" />
</dataSource>
</environment>
</environments>
<!—關聯對映-->
<mappers>
<mapper resource="sqlmap/User.xml"/>
</mappers>
</configuration>
經測驗對映檔案最好和核心配置檔案在一個資料夾下
properties(屬性)
settings(全域性配置引數)
typeAliases(類型別名)
typeHandlers(型別處理器)
objectFactory(物件工廠)
plugins(外掛)
environments(環境集合屬性物件)
environment(環境子屬性物件)
transactionManager(事務管理)
dataSource(資料來源)
mappers(對映器)
Mapper配置檔案
配表與pojo關係,以及sql<!-- 根據id獲取使用者資訊 -->
<select id="findUserById" parameterType="int " resultType="cn.itcast.mybatis.po.User" >
select * from user where id = #{id}
</select>
id: sql語句的唯一標識,與dao介面中的方法名一致
parameterType:定義輸入到sql中的對映型別(請求引數),#{id}表示使用preparedstatement設定佔位符號並將輸入變數id傳到sql。id 可以用任意字元代替並不固定resultType:定義結果對映型別(返回值)。 注意:當返回值是List集合時他的型別就是泛型的型別
mybatis支援別名:
別名對映的型別_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
mapMap
動態Sql
If
通過mybatis提供的各種標籤方法實現動態拼接sql原始sql語句: SELECT * FROM USER WHERE username LIKE '%王%' AND sex= ’2’
Mybatis中簡單的sql語句:
<select id="findUserByNameAndSex" parameterType="user" resultType="user">
SELECT * FROM USER WHERE username LIKE '%${username}%' AND sex=#{sex}
</select>
trim
<trim prefix="(" suffix=")" prefixOverrides="and" ></trim>
字首'and' 被'(' 替換
prefix:字首覆蓋並增加其內容 不寫的話預設替換為空
suffix:字尾覆蓋並增加其內容 不寫的話預設替換為空
prefixOverrides:字首判斷的條件
suffixOverrides:字尾判斷的條件
使用動態sql語句:
where
<select id="findUserByNameAndSex" parameterType="user" resultType="user">
SELECT * FROM USER
<where><!-- 他能自動判斷將where關鍵字後的and去掉 -->
<if test="username!=null and username!=''">
AND username LIKE '%${username}%'
</if>
<if test="sex!=null and sex!=''">
AND sex=#{sex}
</if>
</where>
</select>
<where> 可以自動去除sql語句where關鍵字後的and關鍵字
Foreach
向sql傳遞陣列或List, mybatis使用foreach解析,可以做批量處理Sql語句: SELECT * FROM USER WHERE id IN (1,10,16)
使用foreach賦值:
<select id="findUserByIds" parameterType="queryVo" resultType="user">
SELECT * FROM USER
<where>
<foreach collection="ids" open="id IN (" close=")" item="id" separator=",">
#{id}
</foreach>
</where>
</select>
foreach:迴圈傳入的集合引數collection:傳入的集合的變數名稱(要遍歷的值)
item:每次迴圈將迴圈出的資料放入這個變數中
open:迴圈開始拼接的字串
close:迴圈結束拼接的字串
separator:迴圈中拼接的分隔符
批量修改例項:
sql: UPDATE rc_notify_queue SET queue_status = 'E',execute_time=NOW() WHERE notify_queue_Id IN (3,22,11)
<!-- 批量修改 -->
<update id="updateByList" parameterType="java.util.List" >
UPDATE rc_notify_queue SET queue_status = 'E',execute_time=NOW()
<where>
<foreach collection="list" open="notify_queue_Id IN (" close=")" item="notify" separator=",">
#{notify.notifyQueueId}
</foreach>
</where>
</update>
一個大神朋友寫的批量修改:
if標籤是與(and)的關係,而 choose 是或(or)的關係。有時候我們並不想應用所有的條件,而只是想從多個選項中選擇一個。而使用if標籤時,只要test中的表示式為 true,就會執行 if 標籤中的條件。MyBatis 提供了 choose 元素。
sql片段
Sql中可將重複的sql提取出來,使用時用include引用即可,最終達到sql重用的目的將where條件抽取出來:
<sql id="user_where">
<where>
<if test="username!=null and username!=''">
AND username LIKE '%${username}%'
</if>
</where>
</sql>
引用:
<select id="findUserByNameAndSex" parameterType="user" resultType="user">
SELECT * FROM USER
<include refid="user_where"/>
</select>
注意:也可在sql標籤中也可以只放if標籤
<if test="_parameter != null" >
_parameter : 引數的引用
關聯查詢:
多表查詢返回的資料是兩個表中的資料這樣就出現了資料封裝的問題解決方案一:建立一個有兩個表的資料類
<!-- 一對一:自動對映 -->
<!-- 一對一:自動對映 -->
<select id="findOrdersAndUser" resultType="customOrders">
SELECT * FROM USER a , orders b WHERE a.id = b.user_id
</select>
和普通方法是一樣的,只是要多建立一個po類
解決方案二:不需要新建po類只要在對映檔案中配置:<resultMap type="orders" id="orderAndUserResultMap">
<id column="id" property="id"/>
<result column="userId" property="user_id"/>
<result column="number" property="number"/>
<result column="createtime" property="createtime"/>
<result column="note" property="note"/>
<!-- 封裝物件屬性 -->
<association property="user" javaType="com.itheima.pojo.User">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="sex" property="sex"/>
<result column="address" property="address"/>
<result column="birthday" property="birthday"/>
</association>
</resultMap>
使用:
<select id="findOrdersAndUser2" resultMap="orderAndUserResultMap">
SELECT * FROM USER a , orders b WHERE a.id = b.user_id
</select>
Type:po類的許可權定名,這裡使用的是別名
Id:主鍵
Result: 普通屬性
Column: po類屬性
Property: 表字段名
Association: 引用資料型別
Property: 在po類中的名稱
JavaType: 全限定名
分兩塊,第一塊使用resultMap標籤封裝資料,第二塊在select中使用resultMap引用封裝好的資料; 這種放法優於第一種方法,第一種方法再寫po類增加了工作量,也不便於以後對資料的使用,也不能表現表與表之間的關係
封裝map集合:
返回結果集型別: resultType="java.util.Map"查詢後返回的結果:List<Map>
將資料庫中每條資料:欄位和值按照 key(欄位名),value(欄位值) 封裝到hashMap集合中
然後在把Map集合封裝到Llist集合中
將一條資料封裝成map集合:
使用resultMap封裝資料
<resultMap type="java.util.Map" id="orderAndUserResultMap"> <id column="cid" property="cid"/> <result column="cust_name" property="cust_name"/> <result column="cust_type" property="cust_type"/> <result column="cust_phone" property="cust_phone"/> <result column="cust_address" property="cust_address"/> <result column="custlink_user" property="custlink_user"/> </resultMap> <select id="findCustList" parameterType="cn.itheima.pojo.QueryVo" resultMap="orderAndUserResultMap"> SELECT * FROM s_cust </select>
Column就是map集合的key Property資料庫傳來的資料注意:property值對應資料庫中的欄位值這種方式只能獲取一條資料
Spring整合mybatis
SqlMapConfig.xml配置:<typeAliases>
<!-- 包掃描 -->
<package name="com.mybatis.pojo"/>
</typeAliases>
<!-- 關聯對映 -->
<mappers>
<mapper resource="User.xml"/>
<!-- 包掃描 -->
<package name="com.mybatis.dao"/>
</mappers>
其餘交個spring管理
ApplicationContext.xml
配置dataSource
配置sqlSessionFactory
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 指定mybatis核心配置檔案 -->
<property name="configLocation" value="classpath:SqlMapConfig.xml"></property>
<!-- 指定會話工廠使用的資料來源 -->
<property name="dataSource" ref="dataSource"></property>
</bean>
原生Dao實現:原生Dao實現和普通類的配置一樣
Mapper介面代理:
方式一:
<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
<!-- 配置mapper介面的全路徑名稱 -->
<property name="mapperInterface" value="com.mybatis.dao.UserMapper"></property>
<property name="sqlSessionFactory" ref="sqlSessionFactory"></property>
</bean>
注意:
Bean標籤中class屬性是mybatis提供的介面代理物件所以是固定的
name="mapperInterface"和name="sqlSessionFactory" 代理物件中的屬性也是固定的
方式二:包掃描
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="cn.mybatis.dao"></property>
</bean>
注意:
使用包掃描的方式批量引入Mapper掃描後引用的時候可以使用類名,首字母小寫.
指定要掃描的包的全路徑名稱,如果有多個包用英文狀態下的逗號分隔