mybatis框架知識梳理

mybatis框架知識梳理

Mybatis框架概述:

​ mybatis 經過 簡單的xml 或註解的方式將要執行的各類 statement 配置起來,並經過 java 對象(POJO類)和 statement 中sql 的動態參數進行映射生成最終執行的 sql 語句,最後由 mybatis 框架執行 sql 並將結果映射爲 java 對象並返回。java

爲何使用Mybatis:

傳統jdbc訪問數據庫:

​ 1.未經封裝,使用jdbc原始方法(加載驅動,獲取鏈接)進行對數據庫數據的crud操做mysql

​ 2.數據庫鏈接頻繁建立、釋放,形成系統資源的浪費,影響系統性能,web

解決:可以使用數據庫鏈接池技術

​ 3.Sql語句在代碼中屬於硬編碼,而實際應用sql語句常常變化,需改變java代碼,不利於代碼維護sql

解決:將 Sql 語句配置在 XXXXmapper.xml 文件中與 java 代碼分離。

​ 4.使用prepareStatement向佔位符傳參存在硬編碼,由於參數不定,條件可多可少,需修改代碼不易維護。數據庫

解決:Mybatis 自動將 java 對象映射至 sql 語句,經過 statement 中的 parameterType 定義輸入參數的類型。

​ 5.結果集解析存在硬編碼(查詢列名),若sql變化(參數),致使解析代碼變化,系統不易維護,可將數據庫記錄封裝爲POJO類對象。windows

解決:Mybatis 自動將 sql 執行結果映射至 java 對象,經過 statement 中的 resultType 定義輸出結果的類型

Mybatis入門準備:

環境搭建:

1.建立maven工程,導入依賴座標。(mybatis,mysql,log4j,junit)
2.編寫實體類Usr
3.編寫持久層接口UserDao
4.獲取log4j.properties到resource目錄下
5.編寫持久層接口的映射文件UserDao.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屬性爲dao接口的全限定類名-->
<mapper namespace="com.itheima.dao.IUserDao">
  
<!--配置查詢全部-->
<select id="findAll" resultType="com.itheima.domain.User">
    select * from user
</select>
  
</mapper>
6.編寫SqlMapConfig.xml(Mybatis運行環境的配置信息,dom4j解析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">
<!-- mybatis的主配置文件 -->
<configuration>
    <!-- 配置環境 -->
    <environments default="mysql">
        <!-- 配置mysql的環境-->
        <environment id="mysql">
            <!-- 配置事務的類型-->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置數據源(鏈接池) -->
            <dataSource type="POOLED">
                <!-- 配置鏈接數據庫的4個基本信息,建立鏈接對象 -->
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/eesy_mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="1234"/>
            </dataSource>
        </environment>
    </environments>

    <!-- 指定映射配置文件的位置,映射配置文件指的是每一個dao獨立的配置文件 -->
    <mappers>
      <!-- 基於xml -->
        <mapper resource="com/itheima/dao/IUserDao.xml"/>
      <!-- 基於註解 -->
      <mapper class="com.itheima.dao.IUserDao.xml"/>
    </mappers>
</configuration>
7.編寫測試類
public class MybatisTest {

    /**
     * 入門案例
     * @param args
     */
    public static void main(String[] args)throws Exception {
        //1.讀取配置文件
        InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
        //2.建立SqlSessionFactory工廠(不是本身建立的,構建者模式)
        SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
        SqlSessionFactory factory = builder.build(in);
        //3.使用工廠生產SqlSession對象
        SqlSession session = factory.openSession();
        //4.使用SqlSession建立Dao接口的代理對象
        IUserDao userDao = session.getMapper(IUserDao.class);
        //5.使用代理對象執行方法
        List<User> users = userDao.findAll();
        for(User user : users){
            System.out.println(user);
        }
        //6.釋放資源
        session.close();
        in.close();
    }
}

tips:

1.讀取配置文件

第一個:使用類加載器,只能讀取類路徑的配置文件設計模式

第二個:使用servletContext對象的getRealPath()緩存

2.建立工廠mybatis:構建者模式

優點:把對象的建立細節隱藏,使使用者直接調用方法便可拿到對象安全

3.生產SqlSession:工廠模式

優點:解耦,下降類之間的依賴。使用工廠生產對象,解決類之間的依賴,web開發每次修改改動源碼,影響開發效率,使用工廠生產對象,不須要從新編譯,部署,啓動服務器。服務器

4.建立Dao接口實現類:代理模式

優點:在不修改源碼基礎上對已有方法進行加強

加強對象的功能:

設計模式:一些通用的解決固定問題的方式

1)裝飾模式

2)代理模式

靜態代理:有一個類文件描述代理模式

動態代理:在內存中造成代理類

5.prepareStament用法

tips:使用jdbc鏈接數據庫執行sql語句時(執行許多SQL語句的JDBC程序產生大量的Statement和PreparedStatement對象。)

1.當sql語句含有多個參數,屢次使用時,使用preparestament將sql進行初始化,提到數據庫中進行預編譯,提升效率;

2.能夠替換變量,sql語句能夠包含佔位符?,把?替換成變量,ps.setString()....

3.安全性,有效防止SQL注入的問題:傳遞給PreparedStatement對象的參數能夠被強制進行類型轉換,使開發人員能夠確保在插入或查詢數據時與底層的數據庫格式匹配。(不太懂)

總結:

​ PreparedStatement: 數據庫會對sql語句進行預編譯,下次執行相同的sql語句時,數據庫端不會再進行預編譯了,而直接用數據庫的緩衝區,提升數據訪問的效率(但儘可能採用使用?號的方式傳遞參數),若是sql語句只執行一次,之後再也不復用。 從安全性上來看,PreparedStatement是經過?來傳遞參數的,避免了拼sql而出現sql注入的問題,因此安全性較好。

在開發中,推薦使用 PreparedStatement

6.什麼是SQL注入,怎麼防止SQL注入?

 所謂SQL注入,就是經過把SQL命令插入到Web表單提交或輸入域名或頁面請求的查詢字符串,最終達到欺騙服務器執行惡意的SQL命令。具體來講,它是利用現有應用程序,將(惡意)的SQL命令注入到後臺數據庫引擎執行的能力,它能夠經過在Web表單中輸入(惡意)SQL語句獲得一個存在安全漏洞的網站上的數據庫,而不是按照設計者意圖去執行SQL語句。

怎麼防止SQL注入,使用存儲過程來執行全部的查詢;檢查用戶輸入的合法性;將用戶的登陸名、密碼等數據加密保存。

映射配置文件

crud標籤:

1.sql語句使用#{}字符

至關於佔位符,裏面內容爲基本類型時隨意寫,若參數爲對象,則#{}裏使用ognl(對象圖導航語言)表達式,語法格式爲#{對象.對象}的方式

{user.username}它會先去找 user 對象,而後在 user 對象中找到 username 屬性,並調用getUsername()方法把值取出來。可是咱們在 parameterType 屬性上指定了實體類名稱,因此能夠省略 user.而直接寫 username。

2.Mysql中獲取id

添加記錄,使用insert標籤。只有參數,沒有返回值。
若是Mysql數據想獲取當前記錄的ID,須要配置:
selectKey
當運行了添加的方法後,mybatis會自動將返回的ID,設置給參數對象parameterType配置的對象

<insert id="saveUser" parameterType="com.itheima.domain.User">
    <!-- 配置插入操做後,獲取插入數據的id -->
    <selectKey keyProperty="id" keyColumn="id" resultType="int" order="AFTER">
        select last_insert_id();
    </selectKey>
    insert into user(username,address,sex,birthday)values(#{username},#{address},#{sex},#{birthday});
</insert>
3.模糊查詢

​ 咱們在配置文件中沒有加入%來做爲模糊查詢的條件,因此在傳入字符串實參時,就須要給定模糊查詢的標
識%。 配置文件中的#{username}也只是一個佔位符,因此 SQL 語句顯示爲「?」。

​ 模糊查詢另外一種查詢方式:'%${value}%' 代替#{}

4.{}與${}的區別
#{}表示一個佔位符號
經過#{}能夠實現 preparedStatement 向佔位符中設置值,自動進行 java 類型和 jdbc 類型轉換,
#{}能夠有效防止 sql 注入。 #{}能夠接收簡單類型值或 pojo 屬性值。 若是 parameterType 傳輸單個簡單類
型值, #{}括號中能夠是 value 或其它名稱。
${}表示拼接 sql 串
經過${}能夠將 parameterType 傳入的內容拼接在 sql 中且不進行 jdbc 類型轉換, ${}能夠接收簡
單類型值或 pojo 屬性值,若是 parameterType 傳輸單個簡單類型值, ${}括號中只能是 value。

Mybatis參數深刻:

parameterType:基本類型與string,採用包名.類名,如Java.Lang.String

​ 包裝類採用全限定分類名

特殊狀況:實體類屬性名稱與數據庫列名不一致,查詢結果爲null

​ MySql在windows系統中不區分大小寫,映射配置修改以下:

<!-- 配置查詢全部操做 -->
<select id="findAll" resultType="com.itheima.domain.User">
select * from user
</select>

改成:

使用別名查詢
<!-- 配置查詢全部操做 -->
<select id="findAll" resultType="com.itheima.domain.User">
select id as userId,username as userName,birthday as userBirthday,
sex as userSex,address as userAddress from user
</select>

思考:
若是咱們的查詢不少,都使用別名的話寫起來豈不是很麻煩,有沒有別的解決辦法呢? 以下

resultMap結果類型:

<!-- 創建 User 實體和數據庫表的對應關係
type 屬性:指定實體類的全限定類名
id 屬性:給定一個惟一標識,是給查詢 select 標籤引用用的。
-->
<resultMap type="com.itheima.domain.User" id="userMap">
<id column="id" property="userId"/>
<result column="username" property="userName"/>
<result column="sex" property="userSex"/>
<result column="address" property="userAddress"/>
<result column="birthday" property="userBirthday"/>
</resultMap>
id 標籤:用於指定主鍵字段
result 標籤:用於指定非主鍵字段
column 屬性:用於指定數據庫列名
property 屬性:用於指定實體類屬性名稱

對應映射配置

<!-- 配置查詢全部操做 -->
<select id="findAll" resultMap="userMap">
select * from user
</select>

SqlMapConfig.xml配置更新

properties標籤:需把jdbcConfig.properties提取出來,放到resources路徑下

typeAliases標籤:

pakage標籤:

<!-- mybatis的主配置文件 -->
<configuration>
  
    <properties resource="jdbcConfig.properties"></properties>
  <!--使用typeAliases配置別名,它只能配置domain中類的別名 -->
    <typeAliases>
       <!-- 用於指定要配置別名的包,當指定以後,該包下的實體類都會註冊別名,而且類名就是別名,再也不區分大小寫-->
        <package name="com.itheima.domain"></package>
    </typeAliases>
  
    <!-- 配置環境 -->
    <environments default="mysql">
        <!-- 配置mysql的環境-->
        <environment id="mysql">
            <!-- 配置事務的類型-->
            <transactionManager type="JDBC"></transactionManager>
            <!-- 配置數據源(鏈接池) -->
            <dataSource type="POOLED">
                <!-- 配置鏈接數據庫的4個基本信息 -->
                <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>

    <!-- 指定映射配置文件的位置,映射配置文件指的是每一個dao獨立的配置文件 -->
    <mappers>
       <!-- package標籤是用於指定dao接口所在的包,當指定了以後就不須要在寫mapper以及resource或者class了 -->
        <package name="com.itheima.dao"></package>
    </mappers>
</configuration>

動態Sql語句

動態SQL目的是爲了更好的重用SQL語句的片斷或者靈活的生成SQL語句。本質上,就是SQL語句字符串的拼接。

if進行判斷,test屬性爲true,就拼接上標籤中的SQL語句
test中就是OGNL表達式
    1. 不要加 #{}
    2. 邏輯運算,要使用  and,or
    3. 調用對象的方法:  list.size()

    <select id="findUserByCondition" resultMap="userMap" parameterType="user">
        select * from user
        <where>
            <if test="userName != null">
                and username = #{userName}
            </if>
            <if test="userSex != null">
                and sex = #{userSex}
            </if>
        </where>
    </select>
<update id="updateUser" parameterType="user">
          update user
          <set>
              <if test="username !=null and username !=''">
                username = #{username} ,
              </if>
              <if test="username !=null">
                sex = #{sex},
              </if>
              <if test="username !=null">
              password = #{password},
              </if>
          </set>
          where id=#{id}
      </update>
foreach 進行循環的集合,必須經過包裝的對象提供。
<select id="findInIds" resultType="user" parameterType="queryvo">
    <where>
      <if test="ids != null and ids.size() > 0">
          <foreach collection="ids" open="and id in (" close=")" item="uid" separator=",">
          #{uid}
          </foreach>
      </if>
    </where>
</select>
將SQL中,相同的內容提取出來,在多個地方進行引用。
 1. SQL語句中的字符串
 2. 動態SQL中的標籤

抽取SQL語句
    <sql id="defaultUser">
        select * from user
    </sql>
使用SQL
    <select id="findAll" resultMap="userMap">
        <include refid="defaultUser"></include>
    </select>

簡化sql片斷

<!-- 抽取重複的語句代碼片斷 -->
<sql id="defaultSql">
select * from user
</sql>

<!-- 配置查詢全部操做 -->
<select id="findAll" resultType="user">
<include refid="defaultSql"></include>
</select>

一對一查詢

使用 resultMap,定義專門的 resultMap 用於映射一對一查詢結果。
經過面向對象的(has a)關係能夠得知,咱們能夠在 Account 類中加入一個 User 類的對象來表明這個帳戶
是哪一個用戶的。

一對一映射配置

<resultMap id="accountUserMap" type="account">
    <id property="id" column="aid"></id>
    <result property="uid" column="uid"></result>
    <result property="money" column="money"></result>
    <!-- 一對一的關係映射:配置封裝user的內容-->
    <association property="user" column="uid" javaType="user">
        <id property="id" column="id"></id>
        <result column="username" property="username"></result>
        <result column="address" property="address"></result>
        <result column="sex" property="sex"></result>
        <result column="birthday" property="birthday"></result>
    </association>
</resultMap>

一對多查詢

<resultMap type="user" id="userMap">
<id column="id" property="id"></id>
<result column="username" property="username"/>
<result column="address" property="address"/>
<result column="sex" property="sex"/>
<result column="birthday" property="birthday"/>
<!-- collection 是用於創建一對多中集合屬性的對應關係
ofType 用於指定集合元素的數據類型
-->
<collection property="accounts" ofType="account">
<id column="aid" property="id"/>
<result column="uid" property="uid"/>
<result column="money" property="money"/>
  </collection>
</resultMap>
<!-- 配置查詢全部操做 -->
<select id="findAll" resultMap="userMap">
select u.*,a.id as aid ,a.uid,a.money from user u left outer join account
a on u.id =a.uid
</select>
</mapper>
collection
部分定義了用戶關聯的帳戶信息。表示關聯查詢結果集
property="accList":
關聯查詢的結果集存儲在 User 對象的上哪一個屬性。
ofType="account":
指定關聯查詢的結果集中的對象類型即 List中的對象類型。此處可使用別名,也可使用全限定名。

多對多查詢

<!--定義 role 表的 ResultMap-->
<resultMap id="roleMap" type="role">
<id property="roleId" column="rid"></id>
<result property="roleName" column="role_name"></result>
<result property="roleDesc" column="role_desc"></result>
<collection property="users" ofType="user">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="address" property="address"></result>
<result column="sex" property="sex"></result>
<result column="birthday" property="birthday"></result>
</collection>
</resultMap>
<!--查詢全部-->
<select id="findAll" resultMap="roleMap">
select u.*,r.id as rid,r.role_name,r.role_desc from role r
left outer join user_role ur on r.id = ur.rid
left outer join user u on u.id = ur.uid
</select>
</mapper>

延遲加載

  1. 什麼是延遲加載,爲何要進行延遲加載
    1. 在加載當前對象的時候,對於對象的關聯的屬性對象或者屬性集合,是否當即查詢?不當即查詢,就是延遲加載,也叫作懶加載。
  2. 在mybatis中如何設置延遲加載(association、 collection 具有延遲加載功能)。
    1. 在當前應用中,開啓延遲加載
//SqlMapConfig.xml
    <!--配置參數-->
    <settings>
        <!--開啓Mybatis支持延遲加載-->
        <setting name="lazyLoadingEnabled" value="true"/>  <!-- 默認就是true -->
        <setting name="aggressiveLazyLoading" value="false"></setting>
    </settings>

​ 2.一對一

<!-- 一對一的關係映射:配置封裝user的內容
        select屬性指定的內容:查詢用戶的惟一標識:當前mapper配置的sid(namespace+id)
        column屬性指定的內容:用戶根據id查詢時,所須要的參數的值
        -->
        <association property="user" column="uid" javaType="user" select="com.itheima.dao.IUserDao.findById"></association>

<select id="findAll" resultMap="accountMap">
select * from account
</select>
select: 填寫咱們要調用的 select 映射的 id
column : 填寫咱們要傳遞給 select 映射的參數

​ 3.一對多

<!-- collection 是用於創建一對多中集合屬性的對應關係
ofType 用於指定集合元素的數據類型
select 是用於指定查詢帳戶的惟一標識(帳戶的 dao 全限定類名加上方法名稱)
column 是用於指定使用哪一個字段的值做爲條件查詢
-->
<collection property="accounts" ofType="account"
select="com.itheima.dao.IAccountDao.findByUid"
column="id">
</collection>


<!-- 配置查詢全部操做 -->
<select id="findAll" resultMap="userMap">
select * from user
</select>
<collection>標籤:
主要用於加載關聯的集合對象
select 屬性:
用於指定查詢 account 列表的 sql 語句,因此填寫的是該 sql 映射的 id
column 屬性:
用於指定 select 屬性的 sql 語句的參數來源,上面的參數來自於 user 的 id 列,因此就寫成 id 這一
個字段名了

緩存

緩存的使用

  1. 緩存的做用和緩存使用的時機

    1. 緩存是爲了減小了數據庫的交互,提交系統的性能
    2. 適合使用緩存的狀況:
      1. 常常查詢的數據,而且不會常常發生變化。
      2. 容許數據出現不一樣步的狀況
  2. mybatis的緩存

    1. 一級緩存

      SqlSession範圍的緩存,同一個SQLSession對象,可使用同一個緩存。當使用session查詢相同的數據(同一方法,相同參數),查詢出同一個對象。

      當session關閉,調用clearCache()方法,執行增刪改操做時,會被清空。

    2. 二級緩存

      SQLSessionFactory範圍的緩存,通常應用只會建立一個SQLSessionFactory對象,當前應用中,全部的操做均可以使用二級緩存的數據。不一樣的session查詢相同的數據(同一方法,相同參數)能夠從二級緩存中獲取,二級緩存只緩存了數據的內容,每一個session會拿到不一樣的對象,擁有相同的屬性。

      二級緩存默認是不啓用的,須要手動開啓。

//SqlMapConfig.xml
    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>

//對應的mapper文件中
<cache />

//當前配置的mapper文件的對應須要緩存的SQL上
<!-- 根據id查詢用戶 -->
<select id="findById" parameterType="INT" resultType="user" useCache="true">
  select * from user where id = #{uid}
</select>

Mybatis經常使用註解

@Insert:實現新增
@Update:實現更新
@Delete:實現刪除
@Select:實現查詢
@Result:實現結果集封裝
@Results:能夠與@Result 一塊兒使用,封裝多個結果集
@ResultMap:實現引用@Results 定義的封裝
@One:實現一對一結果集封裝
@Many:實現一對多結果集封裝
@SelectProvider: 實現動態 SQL 映射
@CacheNamespace:實現註解二級緩存的使用

  1. 搭建開發環境

    若是使用了註解開發,就不能再使用xml配置。

  2. 單表增刪改查操做

  3. 對象屬性和表字段不一致的配置

    @Select("select * from user")
        @Results(id="userMap",value={
                @Result(id=true,column = "id",property = "userId"),
                @Result(column = "username",property = "userName"),
                @Result(column = "address",property = "userAddress"),
                @Result(column = "sex",property = "userSex"),
                @Result(column = "birthday",property = "userBirthday")
        })
        List<User> findAll();
    //重複使用resultmap
          @Select("select * from user  where id=#{id} ")
          @ResultMap("userMap")
          User findById(Integer userId);

    4.對象關係映射配置

    1. 一對一

      @Select("select * from account")
          @Results(id="accountMap",value = {
                  @Result(id=true,column = "id",property = "id"),
                  @Result(column = "uid",property = "uid"),
                  @Result(column = "money",property = "money"),
                  @Result(property = "user",column = "uid",one=@One(select="com.itheima.dao.IUserDao.findById",fetchType= FetchType.EAGER))
          })
      //property  對象屬性的名稱
      //column  後面配置的select查詢的參數
      //one 屬性是對象
        //  select 查詢的方法
        //   fetchType 是否延遲加載    FetchType.EAGER 當即加載
          List<Account> findAll();

      2.一對多

      @Select("select * from user")
          @Results(id="userMap",value={
                  @Result(id=true,column = "id",property = "userId"),
                  @Result(column = "username",property = "userName"),
                  @Result(column = "address",property = "userAddress"),
                  @Result(column = "sex",property = "userSex"),
                  @Result(column = "birthday",property = "userBirthday"),
                  @Result(property = "accounts",column = "id",
                          many = @Many(select = "com.itheima.dao.IAccountDao.findAccountByUid",
                                      fetchType = FetchType.LAZY))
          })
      // many 對象的屬性是集合  fetchType = FetchType.LAZY 延遲就加載
          List<User> findAll();

相關文章
相關標籤/搜索