1、總體認識mybatis和mybatis的體系結構

1 myBatis 核心概念

1.1 基本概念

MyBatis 是一款優秀的持久層框架,它支持定製化 SQL、存儲過程以及高級映射。MyBatis 避免了幾乎全部的 JDBC 代碼和手動設置參數以及獲取結果集。MyBatis 可使用簡單的 XML 或註解來配置和映射原生類型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 對象)爲數據庫中的記錄.java

###1.2 核心對象的做用域與生命週期mysql

簡單示例:git

  • 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">
<mapper namespace="com.niuh.mybatis.dao.UserMapper">
    <select id="selectUser" resultType="com.niuh.mybatis.dao.User">
    select * from User where id = #{id}
  </select>
</mapper>
複製代碼
  • mybatis-config.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="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://192.168.0.147/niuhDB"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <!--<mapper resource="com/niuh/mybatis/dao/xml/UserMapper.xml"/>-->
        <mapper class="com.niuh.mybatis.dao.UserMapper"></mapper>
    </mappers>
</configuration>
複製代碼

示例:程序員

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession session = sqlSessionFactory.openSession();
User result = session.selectOne("com.niuh.mybatis.dao.UserMapper.selectUser", 1);
System.out.println(result.toString());
複製代碼
  • SqlSessionFactoryBuilder

用於構建會話工廠,基於 config.xml environment 、props 構建會話工廠,構建完成後便可丟棄。sql

  • SqlSessionFactory

用於生成會話的工廠,做用於整個應用運行期間,通常不須要構造多個工廠對像數據庫

  • SqlSession

做用於單次會話,如WEB一次請求期間,不能用做於某個對像屬性,也不能在多個線程間共享,由於它是線程不安全的。編程

1.3 接口式編程

因爲每次調用時都去找對應用 statement 以及拼裝參數,使用上不是特別友好,myBatis 引入了接口的機制,將接口與mapper.xml  的namespace 名稱綁定,MyBatis就能夠根據ASM工具動態構建該接口的實例。緩存

mapper 映射器接口實例 經過 session.getMapper(Class type) 就能夠獲取mapper 實例,該實例通常做用於方法域。安全

2 全局的configuration配置

2.1 屬性

properties 元素能夠經過 resource 或url 加載外部 properties文件中的屬性,也能夠直接設置property 屬性。而後在xml 中就能夠經過${屬性名}進行引用替換。bash

<properties resource="app.properties" url="">
    <property name="jdbc.driver" value="com.oracle.jdbc.Driver"/>
</properties>
複製代碼

resource= app.properties  從class path中加載 url=[file:///G:/git/niuh-mybatis/src/main/resources/app.properties](file:///G:/git/tuling-mybatis/src/main/resources/app.properties) 基於url加載

引用屬性方式: {jdbc.user} 
從 MyBatis 3.4.2 開始,位符指定一個默認值。例如:{jdbc.user:root}

2.2 環境配置

一個項目常常須要在例如開發壞境、測試環境、預演環境、生產環境中等不一樣環境中進行部署,每一個環境所對應的參數是不同的,myBatis 中能夠經過 environment 來設置不一樣環境的屬性。

<environments default="${default.environment}">
        <environment id="test">
            <!--type=JDBC|MANAGED-->
            <transactionManager type="JDBC"></transactionManager>
            <!-- type=UNPOOLED|POOLED|JNDI-->
            <dataSource type="UNPOOLED">
                <property name="driver" value="${jdbc.driver}"/>
            </dataSource>
        </environment>
        <environment id="dev">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driver}"/>
            </dataSource>
        </environment>
    </environments>
複製代碼

可經過 SqlSessionFactoryBuilder.build( environment) 來指定初始化哪套環境。

2.3 設置

設置MyBatis 全局參數,約定myBatis 的全局行爲

<settings>
<!-- 開啓二級緩存-->
  <setting name="cacheEnabled" value="true"/>
  <!-- 開啓駝峯命名適配-->
  <setting name="mapUnderscoreToCamelCase" value="true"/>
<settings>
複製代碼

示例駝峯命名開啓與關閉:嘗試開關 mapUnderscoreToCamelCase 屬性 來觀察Account 數據查詢狀況。

2.4 別名

在myBatis 中常常會用到 java 中類型,如sql 塊中中 parameterType  參數引用中 javaType 結果集映射的javaType ,都要使用java 全路徑名,能夠經過

<typeAliases>
    <typeAlias type="com.niuh.mybatis.dao.Account" alias="account"/>
    <package name="com.niuh.mybatis.dao"  />
</typeAliases>
複製代碼

提示:建議不要設置。由於經常使用的類 mybatis 已經內置別名,而自定義的類設置別反而很差去找,影響閱讀。

2.5 類型處理器

持久層框架其中比較重要的工做就是處理數據的映射轉換,把java 類型轉換成jdbc 類型的參數,又須要把jdbc 類型的結果集轉換成java 類型。在mybatis 中是經過 TypeHandler 接口來實現的。

能夠看到 typeHandler 就是兩個做用 設置參數 與獲取結果。 你能夠設置自定義處理器

<typeHandlers>
  <typeHandler handler="org.mybatis.example.ExampleTypeHandler"  />
</typeHandlers>
複製代碼

能夠經過如下兩種方式指定處理的範圍

  • javaType="long", jdbcType="Date"
  • @MappedJdbcTypes( jdbc類型) @MappedTypes       java類型

示例: long 類型時間戳轉換成 日期類型 添加算定義處理類:

@MappedJdbcTypes(JdbcType.TIMESTAMP)
@MappedTypes(Long.class)
public class LongTimeHandler extends BaseTypeHandler<Long> {
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Long parameter, JdbcType jdbcType) throws SQLException {
        ps.setDate(i, new Date(parameter));
    }
 
    @Override
    public Long getNullableResult(ResultSet rs, String columnName) throws SQLException {
        return rs.getDate(columnName).getTime();
    }
 
    @Override
    public Long getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        return rs.getDate(columnIndex).getTime();
    }
 
    @Override
    public Long getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        return cs.getDate(columnIndex).getTime();
    }
}
複製代碼

在resultMap中指定 typeHandler:

<resultMap id="account2" type="com.niuh.mybatis.dao.Account">
   <result property="createTimestamp" column="createTimestamp" typeHandler="com.niuh.mybatis.dao.LongTimeHandler"/>
</resultMap>
<select id="selectById2" resultMap="account2">
  select a.*,a.createTime as createTimestamp from account a where id = #{id}
</select>
複製代碼

2.6 mappers映謝器

<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  <mapper  url="http://www.xxx.com/xml/BlogMapper.xml"/>
  <mapper class="org.mybatis.builder.BlogMapper"/>
 <package name="org.mybatis.builder"/>
</mappers>
複製代碼

加載方式:

  • resource 基於classPath 加載xml文件
  • url:基於資源定位加載xml 文件
  • class:基於接口加載
  • package :掃描包下全部class 而後進行加載
  • 約定規則:
  • mapper 中的 namespace必須與對應的接口名稱對應。
  • 經過 class 或package 中加載時 .xml 文件必須與接口在同一級目錄。

3 mapper 文件

3.1 sql語句塊statement

經過原生JDBC寫DAO的年代 ,程序員最怕莫過於 拼接SQL語句,拼接參數與設置返回結果集,Hibernate 將拼接SQL時代成爲過去,經過ORM映謝,徹底不須要處理任何SQL,但這又帶來了新的問題就是。沒法編寫自定義SQL從而喪失了靈活活及更好的性能。MyBatis 經過 mapper 映射SQL很好解決了這一點。它無需在JAVA代碼中拼接SQL,而是將其移至mapper 文件集中處理SQL節約了大量的開發時間。

Mapper中的元素:

  • cache – 對給定命名空間的緩存配置。
  • resultMap – 結果集映射。
  • sql – 可被其餘語句引用的可重用語句塊。
  • insert – 插入語句
  • update – 更新語句
  • delete –刪除語句
  • select – 查詢語句

select 用法及屬性

示例:

<select id="selectById" resultType="com.niuh.mybatis.dao.Account">
  select * from account where id = #{id}
</select>
複製代碼

屬性:

<select
  id="selectById"        <!-- 語句塊的惟一標識 與接口中方法名稱對應 -->
  parameterType="User"   <!--參數java類型-->
  resultType="hashmap"   <!--返回結果java類型-->
  resultMap="userResultMap" <!--返回結果映射-->
  flushCache="false"      <!--true 每次調用都會刷新 一二級緩存-->
  useCache="true"         <!--true 是否保存至二級緩存當中去-->
  timeout="10"
  statementType= PREPARED"> 複製代碼

insert&update&delete 用法

示例:

<insert id="addUser" keyColumn="id" keyProperty="id" useGeneratedKeys="true"
        parameterType="com.niuh.mybatis.dao.User">
    insert into  user (name,updateTime,createTime) values (#{name},#{updateTime},#{createTime})
</insert>
複製代碼

屬性:

<insert
  id="addUser"   <!-- 語句塊的惟一標識 與接口中方法名稱對應 -->
  parameterType="User"   <!--參數java類型-->
  flushCache="true"  <!--true 每次調用都會刷新 一二級緩存-->
  statementType="PREPARED" <執行類型>
  keyProperty=""      <!--主鍵對應的java 屬性,多個用 逗號分割-->
  keyColumn=""        <!--主鍵列,多個用 逗號分割-->
  useGeneratedKeys=""  <!--插入成功後將 將值回設至 原參數->
  timeout="20">
複製代碼

3.2 參數映射

參數映射引用

參數映射是最強大功能之一,基能夠經過如下方式進行引用

  • 單個簡單參數引用 :若是方法中只有一個參數可經過任意名稱 進行引用
  • 多個簡單參數引用:經過參數下標引用 #{arg0} #{arg1} 或 #{param1} ,#{param2}
  • 對像屬性引用: 直接經過對象屬性名稱引用,嵌套對像經過. 號進行引用
  • map key值引用:
  • 變量名稱引用(須要jdk1.8支持) :經過方法中參數名稱引用,須要jdk1.8支持,且在編譯時必須加上 -parameters 編譯命令

在idea 中添加 編譯參數

在maven中添加 編譯參數

注:一但可經過變量名稱引入不在支持arg0獲取! 參數引用 相關屬性 javaType=int, #參數java類型 jdbcType=NUMERIC,# jdbc類型 typeHandler=MyTypeHandler#  指定類型處理器

參數拼接${}

基於#的參數引用 其原理是經過 ?佔位其經過預處理能得到更好的性能 和安全性(防止SQL注入)但有些需求是經過?佔位沒法實現的,好比在一些分庫分表的場景中咱們須要 動態的拼接表結構。好比某系統日誌表是按年進行切割的 2018_systemlog,2019_systemlog這時就能夠經過

示例:

@Select("SELECT * FROM ${table} WHERE id = #{id}")
User selectByTable(String table, int id);
複製代碼

3.3 結果集映射

結果集映射是指 將resultSet 中內容封裝轉換成java對像,在純jdbc時代所有都是用調用resultSet的getXXX(columnName) 來獲取屬性並封裝。代碼量大,編程效率低尤爲當數據模型是1對多,或多對多這種複雜關係,這種封裝代碼將會變得很是複雜。結果集映射就是爲解決這個問題 經過resultMap 集中處理 結果集與JAVA對像的關係。

結果集自動映射

在select 中指定 resultType=「」 後無須要任何配置 myBatis 會基於 resultType中的JAV類型及屬性自動推斷生成 一個隱示的resultMap  從而完成結果映射

resultMap

但有時jdbc   並非與java Bean 徹底貼合這時就須要手動設置resultMap

<resultMap id="account2" type="com.niuh.mybatis.dao.Account">
    <id property="id"/>
    <result property="createTimestamp" column="createTimestamp"
            typeHandler="com.niuh.mybatis.dao.LongTimeHandler"/>
</resultMap>
複製代碼

這時在select元素中用 resultMap ="account2" 便可引用該map映射。

基本元素與屬性

  • ID:用於結果集中的惟一標識
  • result:設置一個某經過字段

property: jdbcType: javaType: column: typeHandler:

嵌套結果映射

關聯 association 示例:

<resultMap id="accountAndUser" type="com.niuh.mybatis.dao.Account">
    <id property="id" column="id"/>
    <association property="user" javaType="com.niuh.mybatis.dao.User">
        <id property="id" column="user_id"/>
        <result property="name" column="userName"/>
    </association>
</resultMap>
<select id="selectAccountAndUser" resultMap="accountAndUser">
    SELECT a.*, b.name userName from account a,user b where a.user_id=b.id
</select>
複製代碼

引入外部Select

<!--基於屢次查詢拼裝引入 -->
<resultMap id="accountAndUser2" type="com.niuh.mybatis.dao.Account">
    <id property="id" column="id"/>
    <association property="user" javaType="com.niuh.mybatis.dao.User" select="selectUser" column="user_id">
    </association>
</resultMap>

<select id="selectUser" resultType="com.niuh.mybatis.dao.User">
    select * from user  where id = #{id}
</select>
複製代碼

集合collection

一、直接將collection集合元素的屬性寫爲collection的字標籤
<resultMap type="com.niuh.mybatis.dao.User" id="userMap">
    <id property="id" column="uid"/>
    <result property="username" column="username"/>
    <result property="password" column="password"/>
    <collection property="roles" ofType="com.niuh.mybatis.dao.Role">
        <id property="id" column="rid"/>
        <result property="name" column="rname"/>
        <collection property="permissions" ofType="com.niuh.mybatis.dao.Permissions">
            <id property="id" column="pid"/>
            <result property="name" column="pname"/>
       </collection>
   </collection>
</resultMap>
複製代碼

它們的關係是這樣的: User裏有一個Set roles Role裏有一個Set permissions 經過這樣的配置,咱們在執行查詢User時,經過多表聯查,就能夠將這些級聯屬性所有關聯查出。

下面是查詢語句:

<select id="queryUserName" parameterType="string" resultMap="userMap">
        SELECT u.*,r.*,p.* FROM user u inner join user_role ur on ur.uid=u.uid
        inner join role r on r.rid=ur.rid
        inner join permissions_role pr on pr.rid=r.rid
        inner join permissions p on pr.pid=p.pid
        WHERE username=#{username};
</select>
複製代碼
二、經過在collection標籤中引用別的mapper的查詢方法
<resultMap id="BaseResultMap" type="com.niuh.mybatis.dao.SysUser" >
        <id column="user_id" property="id" jdbcType="BIGINT" />
        <result column="username" property="username" jdbcType="VARCHAR" />
        <result column="password" property="password" jdbcType="VARCHAR" />
        <collection property="sysRoles" column="user_id"
                    select="com.niuh.mybatis.dao.SysRoleMapper.selectRoleListByUserId">
        </collection>
</resultMap>
複製代碼

在SysUser中有Set sysRoles 咱們不須要再在collection中配置SysRole的屬性,只須要將SysRole中的selectRoleListByUserId方法引入就能夠了。

如下是查詢語句:咱們只須要查詢SysUser就好了

<select id="findByUsername" resultMap="BaseResultMap">
        SELECT
            us.id as user_id,
            us.username,
            us.password
        FROM t_sys_user us  WHERE us.username = #{username}
</select>
複製代碼

固然,在SysRole的mapper中,咱們是須要有selectRoleListByUserId方法的:

<resultMap id="roleResult" type="com.niuh.mybatis.dao.SysRole">
        <id property="id" column="role_id" jdbcType="BIGINT"/>
        <result property="name" column="name" jdbcType="VARCHAR"/>
        <result property="desc" column="desc" jdbcType="VARCHAR"/>
        <collection property="permissions" column="role_id"
                    select="com.niuh.mybatis.dao.SysPermissionMapper.selectPermissionByRoleId">
        </collection>
    </resultMap>
 
    <select id="selectRoleListByUserId" resultMap="roleResult">
        SELECT
            ro.id as role_id,
            ro.name,
            ro.desc
        FROM  t_sys_user_role ur
         LEFT JOIN t_sys_role  ro
        ON  ur.`role_id` = ro.`id` WHERE ur.user_id = #{userId}
    </select>
複製代碼

同理,SysRole中的permissions也是同樣的。 兩種方式的實現均可以,第二種方式不須要寫過於複雜的sql,同時,每一個mapper中的方法都是獨立可使用的,其適用性更強。

相關文章
相關標籤/搜索