MyBatis原始碼分析(二)

螞蟻style發表於2020-07-08

MyBatis的xml配置(核心配置)

configuration(配置)
   properties(屬性)
   settings(設定)  
   typeAliases(型別別名)
   typeHandlers(型別處理器)
   objectFactory(物件工廠)
   plugins(外掛)
   environments(環境配置)
        environment(環境變數)
            transactionManager(事務管理器)
            dataSource(資料來源)
  databaseIdProvider(資料庫廠商標識)
  mappers(對映器)                

MyBatis的Mapper對映

Mapper對映檔案只有幾個頂級元素(按照應被定義的順序如下):
cache – 對給定名稱空間的快取配置。
cache-ref – 對其他名稱空間快取配置的引用。
resultMap – 是最複雜也是最強大的元素,用來描述如何從資料庫結果集中來載入物件。
sql – 可被其他語句引用的可重用語句塊。
insert – 對映插入語句
update – 對映更新語句
delete – 對映刪除語句
select – 對映查詢語句

MyBatis也可以使用註解開發

生成程式碼時候,配置:
<javaClientGenerator type="ANNOTATEDMAPPER"...
生成註解程式碼,不生成xml程式碼了

MyBatis開發使用註解還是xml?

方式 優點 缺點
Xml Xml與介面分離,方便管理,複雜的SQL不影響程式碼的可讀性 過多的Xml配置檔案
Annotation 介面就能看到SQL語句,可讀性強,不需要去找xml檔案,方便 程式碼和SQL混雜,複雜一點過於混亂

 

MyBatis批量插入

1 普通for迴圈(此種方式對於資料庫的I/O過於頻繁,不適合大資料量的操作)

for (int i = 0; i < 500; i++) {
    user = new User();
    user.setId("id" + i);
    user.setName("name" + i);
    userMapper.insert(user);
}

2 ExeutorType.BATCH(設定批量模式),把SQL語句發個資料庫,資料庫預編譯好,然後資料庫等待需要執行的引數,接收到引數一次性執行

session = sqlSessionFactory.openSession(ExecutorType.BATCH, true);
for (int i=0; i<500; i++) {
    UUserInfo userInfo = new UUserInfo();
    userInfo.setPhone("1346262122" + i);
    userInfo.setUserName("" + i);
    uUserInfoMapper.insert(userInfo);
}

3 傳入一個陣列或集合,標籤<foreach>插入

 <foreach item="item" collection="strs.split(',')" separator="," open="(" close=")">
            #{item}
  </foreach>
方式 效能 說明
For迴圈 效能低,IO高  
ExeutorType.BATCH 效能居中  
<foreach>標籤拼SQL 效能最高

有SQL長度限制,mysql預設接收SQl的長度為10486576(1M),該方式若超過1M

會丟擲異常

 

MyBatis聯合查詢

  一對一關係

  在一對一關係中,A表中的一行最多隻能匹配B表的一行,反之亦然.這種關係並不常見,因為一般來說,這種關係的資訊會儲存在一張表中,當然也可以利用一對一關係來儲存,比如分割具有多列的表.(如:一個人只有一個身份證) 

<association>標籤

<resultMap id="OneToOneBaseResultMap" type="com.test.mybatis.model.Person">
  <id column="id" jdbcType="INTEGER" property="id" />
  <result column="nick" jdbcType="VARCHAR" property="nick" />
  <result column="phone" jdbcType="VARCHAR" property="phone" />
  <result column="sex" jdbcType="INTEGER" property="sex" />
  <association property="idCard" javaType="com.test.mybatis.model.IdCard">
    <id column="id" jdbcType="INTEGER" property="id" />
    <result column="personId" jdbcType="INTEGER" property="personid" />
    <result column="realName" jdbcType="VARCHAR" property="realname" />
    <result column="idCard" jdbcType="VARCHAR" property="idcard" />
  </association>
</resultMap>

  一對多關係

  一對多是最普通的一種關係,在這種關係中,A表的一行可以匹配B表中的多行,但是B表中的一行只能匹配A表中的一行.舉例:(一個部門可以有多個員工)

  <collection>標籤

<resultMap type="com.mybatis.bean.Department" id="MyDept">
        <id column="did" property="id"/>
        <result column="dept_name" property="departmentName"/>
        <!-- 
            collection定義關聯集合型別的屬性的封裝規則 
            ofType:指定集合裡面元素的型別
        -->
        <collection property="emps" ofType="com.mybatis.bean.Employee">
            <!-- 定義這個集合中元素的封裝規則 -->
            <id column="eid" property="id"/>
            <result column="last_name" property="lastName"/>
            <result column="email" property="email"/>
            <result column="gender" property="gender"/>
        </collection>
    </resultMap>

  多對多關係

  在多對多的關係中,A表的一行可以匹配B表的多行.反之亦然.要建立這種關係,需要定義第三張表,也可以稱之為結合表或者關係表.它的主鍵是由A表和B表的外部鍵組成.  舉例(一個使用者有多個角色,一個角色可以對應多個使用者)  

 <collection>標籤

MyBatis插入並返回主鍵

  方式一:(資料庫要設定主鍵自增長)

<insert id="insertSelective" useGeneratedKeys="true" keyProperty="id" keyColumn="id"
 parameterType="com.test.mybatis.model.UUserInfo">


useGeneratedKeys="true"表示使用主鍵自增  keyProperty="id"表示將主鍵自增後的主鍵值賦值給實體類中的id屬性
parameterType="com.test.mybatis.model.UUserInfo" 實體類,自增後的主鍵值將會賦值給該實體類的id屬性

  方式二:(一般用在<insert>標籤裡面)

<selectKey keyProperty="id" resultType="integer" order="AFTER">
  SELECT LAST_INSERT_ID()
</selectKey>
 SELECT LAST_INSERT_ID()表示查詢出剛剛插入的的記錄自增長id  order='AFTER' 表示先執行插入語句,之後再執行查詢語句

MyBatis的SQL隱碼攻擊
  注入攻擊的危害
  1:資料庫被拖庫(把資料從資料庫拉取出來,造成資料洩露)
  2.重要資訊被洩露
注入的本質是把使用者輸入的資料當做有效程式碼執行.
Mybatis的預編譯機制可以有效防止SQL隱碼攻擊.
  '#{}''${}'的區別:
'#{}':MyBaits會首先對其進行預編譯,將#{user_ids}替換成?佔位符,然後在執行時替換成實際傳入的user_id值,**並在兩邊加上單引號,以字串方式處理。
'${}':簡單的字串拼接
栗子:
uUserInfoMapper.selectByIn("select user_name from u_user_info");
where user_name in (${userName})
可使用MyBatis自帶迴圈標籤解決SQL語句動態拼接的問題:
select * from news where id in
<foreach collection="ids" item="item" open="("separator="," close=")">
  #{item} 
</foreach>

MyBatis的自定義型別轉換器(實現資料型別和資料庫資料型別的對映關係 如:String和VARCHAR的對應關係)  需求:實現對資料庫身份證儲存的加密
1、定義自己的HandlerType實現TypeHandler介面或者繼承BaseTypeHandler類
2、覆蓋其四個方法;
3、在核心配置檔案mybatis-config.xml中配置<typeHandlers>標籤或者在對映檔案的增、改、查位置單獨配置;

<typeHandlers>
    <typeHandler javaType="com.test.mybatis.typehandler.UserIdCard" handler="com.bjpowernode.mybatis.typehandler.CryptTypeHandler"/>
</typeHandlers>

<result column="idCard" jdbcType="VARCHAR" property="userIdCard" javaType="com.bjpowernode.mybatis.typehandler.UserIdCard" 
typeHandler="com.test.mybatis.typehandler.CryptTypeHandler"/> <if test="userIdCard != null"> #{userIdCard, jdbcType=VARCHAR, typeHandler=com.test.mybatis.typehandler.CryptTypeHandler}, </if>
系統自帶的型別轉換器StringTypeHandler 
public class StringTypeHandler extends BaseTypeHandler<String> {

  @Override
  public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
      throws SQLException {
    ps.setString(i, parameter);
  }

  @Override
  public String getNullableResult(ResultSet rs, String columnName)
      throws SQLException {
    return rs.getString(columnName);
  }

  @Override
  public String getNullableResult(ResultSet rs, int columnIndex)
      throws SQLException {
    return rs.getString(columnIndex);
  }

  @Override
  public String getNullableResult(CallableStatement cs, int columnIndex)
      throws SQLException {
    return cs.getString(columnIndex);
  }
}
MyBatis的動態SQL

  if   --判斷標籤

  choose (when, otherwise)  --多項選擇 ,與頁面的 jstl 標籤非常類似

  trim (where, set)  --修剪、格式化,新增前字尾的一個標籤

  foreach  --迴圈

<select id="dynamicChoose" parameterType="News" resultType="News">
    select * from news where 1 = 1 
    <choose>
        <when test="title != null">
            and title = #{title}
        </when>
        <when test="content != null">
            and content = #{content}
        </when>
        <otherwise>
            and owner = "zhangsan"
        </otherwise>
    </choose>
</select>

MyBatis的SQL片段使用

  <sql id="Base_Column_List">
    id, name
  </sql>
 <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
        select
        <include refid="Base_Column_List"/>
        from user
        where id = #{id,jdbcType=INTEGER}
    </select>

MyBatis的一級.二級快取

  一級快取是sqlSession級別,預設開啟

  二級快取是mapper級別,預設關閉,快取命中率低

MyBatis的事務管理:

<transactionManager type="JDBC">
      <property name="..." value="..."/>
</transactionManager>

如果你正在使用 Spring + MyBatis,則沒有必要配置事務管理器, 因為 Spring 模組會使用自帶的管理器來覆蓋前面的配置.

相關文章