Java基礎(chǔ)框架之Mybatis

什么是Mybatis?

  • Mybatis是一款優(yōu)秀的持久層框架,它支持定制化SQL,存儲過程以及高級映射。
  • Mybatis避免了幾乎所有的JDBC代碼和手動設(shè)置參數(shù)以及獲取結(jié)果集。
  • Mybatis可以使用簡單的XML或注解配置和映射原生類型、接口和Java的POJO(Plain Old Java Objects:普通老式Java對象)為數(shù)據(jù)庫中的記錄。

導(dǎo)入依賴

<!--Mybatis 依賴-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.2</version>
</dependency>

使用XML的方式配置Mybatis

<!--這是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.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/test?useSSL=true&amp;useUnicode=true&amp;serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
<!--    <mappers>-->
<!--        <mapper resource="org/mybatis/example/BlogMapper.xml"/>-->
<!--    </mappers>-->
</configuration>

獲得sqlSession對象

public class MybatsiUtils  {

    private static SqlSessionFactory sqlSessionFactory;
    static {

        try {
            //使用Mybatis第一步,獲取sqlSessionFactory對象
            String resource = "mybatis-config.xml";//這是配置Mybatis的文件名
            InputStream inputStream = Resources.getResourceAsStream(resource);//通過流的形式獲得xml文件的信息,傳給獲取sqlSessionFactory工廠
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
    //既然有了獲取sqlSessionFactory,顧名思義,我們就可以從中獲得sqlsession的實例了
    public static SqlSession getSqlSession(){
        //sqlSession 完全包含了面向數(shù)據(jù)庫執(zhí)行SQL命令所需的所有方法,這就有點像connection
        SqlSession sqlSession = sqlSessionFactory.openSession();
        return  sqlSession;
    }
}

開始第一個Mybatis程序

  • 創(chuàng)建實體類User
  • 創(chuàng)建dao層,創(chuàng)建一個接口,IUserMapper(不用IUserDao)
  • 不用寫UserMapperImpl,Mybatis會幫我們做連接數(shù)據(jù)庫的相關(guān)操作,直接寫Mapper配置文件mybatis-config.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:綁定對應(yīng)的IUserMapper接口-->
<mapper namespace="com.cwx.dao.IUserMapper">
<!--select 查詢語句,id:IUserMapper接口文件的方法名,resultType:返回結(jié)果的類型,必須寫全路徑,parameterType:參數(shù)的類型,若沒帶參數(shù)就不用寫-->
  <select id="getUserList" resultType="com.cwx.entity.User">
    select * from test
  </select>
  <select id="getUserListByID" parameterType="int" resultType="com.cwx.entity.User">
    select * from test where id = #{id}<!--這里用的id是mapper接口中的參數(shù)名,要一致-->
  </select>
    <!--插入一個數(shù)據(jù),參數(shù)名用實體類定義的私有變量名,沒有返回值-->
    <insert id="addUser" parameterType="com.cwx.entity.User">
        insert into test (name,age,pwd) values (#{name},#{age},#{pwd})
  </insert>
    <!--更新一個數(shù)據(jù),沒有返回值-->
  <update id="updateUser" parameterType="com.cwx.entity.User">
    update test set age=#{age},pwd=#{pwd} where name=#{name}
</update>
</mapper>
  • 在MapperRegister中注冊Mapper(別忘了!),在mybatis-config.xml文件中配置
 <mappers>
       <mapper resource="mapper/UserMapper.xml"/>
  </mappers>
  • 測試是否查詢成功
@Test
 public void mybatisTest(){
        //第一步,獲得sqlsession對象
        SqlSession sqlSession = MybatisUtils.getSqlSession;
        //第二步,利用sqlSession實現(xiàn)執(zhí)行語句
        UserMapper userMapper = sqlSession.getMapper(IUserMapper.class);//獲得dao層的Mapper接口,放入工廠中
        List<User> userList = userMapper.getUserList();//執(zhí)行配置文件里的方法,若方法里有參數(shù)就在這寫入
        for(User user : userList){
        System.out.println(user);
        sqlSession.close();//必須要關(guān)閉,sqlSession線程是不安全的
  }
  }

Tips:使用增刪改操作需要添加事務(wù),即測試時最后要提交事務(wù)sqlSession.commit()

Mybatis使用Map

當(dāng)方法的參數(shù)有多個時使用map或者當(dāng)我們實體類或數(shù)據(jù)庫中的字段或參數(shù)比較多時,當(dāng)我們進行修改時,只修改某個參數(shù)就可以使用map,使用map可以只用寫我們需要修改的值,其他的不用動或者。

 <update id="updateUser2" parameterType="map"><!--參數(shù)類型為map-->
    update test pwd=#{pwd} where name=#{name}<!--這里只用修改pwd-->
</update>

測試類:

@Test
public void testUpdate(){
     //第一步,獲得sqlsession對象
        SqlSession sqlSession = MybatisUtils.getSqlSession;
        //第二步,利用sqlSession實現(xiàn)執(zhí)行語句
        UserMapper userMapper = sqlSession.getMapper(IUserMapper.class);//獲得dao層的Mapper接口,放入工廠中
        Map<String,Object> map = new HashMap<String,Object>();
            map.put("name","cwx");
            map.put("pwd",123456);
        userMapper.updateUser2(map)
        sqlSession.commit();
        sqlSession.close();
}

環(huán)境配置(environments)

Tips:mybatis默認事務(wù)是JDBC(還有一個是MANAGED),默認數(shù)據(jù)源是POOLED,Mybatis可以配置多個環(huán)境,但每個Sqlsession實例只能選擇一種環(huán)境。

屬性(properties)

我們可以通過properties屬性來引用配置文件,例如:

<properties resource="db.properties">
  <property name ="username" value="root" />
  <property name ="password" value="111111" />
</properties>

【注意點】: 若db.properties文件中和這里的property屬性有一樣的字段,優(yōu)先使用db.properties里的,其它地方通過#{}符號引用屬性值。

Tips:${}#{}區(qū)別:兩者都可以獲得參數(shù),但是#{}能夠更大程度的防止SQL注入問題,而${}無法防止SQL注入。

類型別名(typeAliases)

類型別名可為 Java 類型設(shè)置一個縮寫名字。 它僅用于 XML 配置,意在降低冗余的全限定類名書寫。

<typeAliases>
  <typeAlias alias="User" type="com.cwx.entity.User"/>
</typeAliases>
<!--或者使用包名,當(dāng)實體類有很多時用這種-->
<!--MyBatis 會在包名下面搜索需要的 Java Bean,別名是開頭字母小寫,如:user-->
<typeAliases>
  <package name="com.cwx.entity"/>
</typeAliases>

Tips:MyBatis 允許你在映射語句執(zhí)行過程中的某一點進行攔截調(diào)用,此時Mybatis-plus應(yīng)運而生,更加簡化了操作。

映射器(mappers)

在MapperRegister中注冊mapper,一般就使用<mapper resource="mapper/UserMapper.xml"/>方式,其他方式和注意點到 Mybatis-3官方文檔里看映射器的方式 里面查看。

生命周期和作用域(Scope)

  • SqlSessionFactoryBuilder
    一旦創(chuàng)建了 SqlSessionFactory,就不再需要它了,類似于局部變量。
  • SqlSessionFactory
    一旦被創(chuàng)建就應(yīng)該在應(yīng)用的運行期間一直存在,沒有任何理由丟棄它或重新創(chuàng)建另一個實例,可以想象為數(shù)據(jù)庫連接池。
  • SqlSession
    SqlSession 的實例不是線程安全的,因此是不能被共享的,所以它的最佳的作用域是請求或方法作用域,可以把它想象為一個請求,請求完后需要關(guān)閉。

ResultMap結(jié)果集映射

  • 解決屬性名和字段名不一致的問題
    在UserMapper.xml文件中進行配置:
<!--實現(xiàn)結(jié)果集映射,保證實體類中的名和數(shù)據(jù)庫字段名對應(yīng)-->
<resultMap id="UserMap" type="com.cwx.entity.User">
<!--  column:是數(shù)據(jù)庫中的字段,property:實體類中的屬性名-->
  <result column="id" property="id" />  <!--名字相同可以不用寫-->
  <result column="name" property="name" />  <!--名字相同可以不用寫-->
  <result column="pwd" property="password" />  <!--這里的名字不一樣,這都是簡單版的結(jié)果映射-->
</resultMap>

<select id="getUserById" resultMap="UserMap">
      select * from test where id=#{id}
</select>
  • resultMap 元素是 MyBatis 中最重要最強大的元素,它的設(shè)計思想是,對簡單的語句做到零配置,對于復(fù)雜一點的語句,只需要描述語句之間的關(guān)系就行了。
  • 上面的只是一些簡單的結(jié)果映射,如果世界有這么簡單就好了。

分頁

  • limit分頁:語法:select * from test limit startIndex,pageSize;
  • 1.接口:List<User> getUserLimit(Map<String,Integer> map)(多個參數(shù)就使用map)
  • 2.配置UserMapper.xml文件
<select id="getUserLimit" paramType="map" resutlMap="userMap">
    select * from test limit #{startIndex},#{pageSize}
</select>
  • 3.測試:步驟和上面差不多,不再贅寫。
  • 分頁插件:PageHelper
    ,更加簡化了操作。

使用注解開發(fā)

  • 面向接口編程:在一個面向?qū)ο蟮南到y(tǒng)中,系統(tǒng)的各個功能是由許多不同的對象協(xié)作完成的,在這中情況下,各個對象的內(nèi)部是如何具體實現(xiàn)的對系統(tǒng)設(shè)計員來說就不那么重要了,按照這種思想編程就是面向接口編程,實際開發(fā)中,架構(gòu)師只會給定相應(yīng)的接口,具體實現(xiàn)就讓碼農(nóng)干了。
  • 為什么使用面向接口編程?
    根本原因:解耦,可拓展,提高可復(fù)用,分層開發(fā)中,上層不用管具體實現(xiàn),大家都遵守共同的標(biāo)準,使得開發(fā)變得容易,規(guī)范性更好。
  • 1.在接口上寫注解
@Select(select * form test)
List<User> getUserList();//如果方法中有多個參數(shù),就使用`@Param()`注解
  • 2.在mapper注冊中心綁定接口
<mappers>
  <mapper class="com.cwx.dao.UserMapper"/><!--這里用的是class,以前是用resources-->
</mappers>
  • 測試:步驟和上面差不多,不再贅寫。
  • 本質(zhì):反射機制,底層是動態(tài)代理。

Mybatis執(zhí)行流程(分析源碼)

  • 1.Resources獲得配置文件 (用流來獲取文件)
  • 2.實例化SqlSessionFactoryBuilder()構(gòu)造器,返回SQLSessionFactory
  • 3.解析配置文件流XMLConfigBuilder
  • 4.Configuration對象包含所有的配置信息
  • 5.SqlSessionFactory實例化,返回一個SqlSession
  • 6.Transaction事務(wù)管理
  • 7.創(chuàng)建execute執(zhí)行器
  • 8.創(chuàng)建一個SqlSession
  • 9.實現(xiàn)CRUD(若執(zhí)行失敗,則回滾)
  • 10.若執(zhí)行成功則commit

數(shù)據(jù)關(guān)系之多對一查詢

舉例:有個student實體類,包含id,name,Teacher(老師的對象)多個學(xué)生對應(yīng)一個老師,要求查出所有學(xué)生和(一個)老師

<!--方式一(常用): 按照結(jié)果嵌套處理 -->
<select id="getStudent" resultMap="StudentTeacher">
  select s.name sname,s.id sid,t.name tname 
  from student s ,teacher t 
  where s.tid=t.id;
</select>
<resultMap id="StudentTeacher" type="Studenet">
  <result property="id" column="sid">
  <result property="name" column="sname">
  <association property="teacher" javaType="Teacher">
   <result property="name" column="sname">
  </association>
</resultMap>
<!--方式二: 按照查詢方式處理 -->
<select id="getStudent" resultMap="StudentTeacher2">
  select * from student
</select>
<resultMap id="StudentTeacher2" type="Student">
  <association property="teacher" column="tid" javaType="Teacher" select="getTeacher">
</resultMap>
<select id="getTeacher" type="Teacehr">
  select * from Teacher where id =#{tid}
</select>

總結(jié):主要是利用了<association>,當(dāng)要查詢的東西屬于多的一方用<association>,當(dāng)要查詢的東西屬于一的一方用collection。

數(shù)據(jù)關(guān)系之一對多查詢

還是上面的例子,這次的角度站在老師這一方,就是一對多關(guān)系,Student類中的teacher對象,變?yōu)閠id,Teacher類加一個List<Student>對象。

<!--方式一:按照結(jié)果嵌套處理-->
<select id="getTeacher" resultMap="TeacherStudent">
  select s.name sname,s.tid stid,t.id tid, t.name tname
  from sutdent s,teacher t
  where t.id=s.tid and t.id=#{tid}
</select>
<resultMap id="TeacherStudent" type="Teacher">
  <result property="name" column="tname">
  <result property="id" column="tid">
  <collection property="student" ofType="Studnet"><!--這里區(qū)別是使用了ofType,為了識別集合中的類型-->
    <result property="name" cloumn="sname">
    <result property="id" cloumn="sid">
  </collection>
</resultMap>
<!--方式:按照查詢方式嵌套處理-->
<select id="getTeacehr" resultMap="TeacherStudent2">
  select * from teacher where id=#{tid}
</select>
<resultMap id="TeacherStudent2" type="Teacher"> 
 <!--這里的column=id是為了給下面的條件用的id傳值-->
  <collection property="student"  javaType="ArrayList" ofType="Student" select="getStudent" column="id">
</resultMap>
<select id="getStudent" type="Student">
  select * from student where id =#{tid}
</select>

數(shù)據(jù)關(guān)系之多對多查詢

思路:利用中間表
【面試高頻】

  • MySQL引擎
  • InnoDB底層原理
  • 索引
  • 索引優(yōu)化

動態(tài)SQL

  • 動態(tài)SQL:指在不同的條件下生成不同的SQL語句。
  • IF
<select id="quryBlogIf" type="Blog" parameType="map"><!--這里的參數(shù)是map類型,有可能會傳多個值-->
  select * from Blog where state  ='ACTIVE'
  <if test="title !=null">
    and titile=#{title}
  </if>
  <if test="author!=null">
    and author=#{author}
  </if>
</select>
  • 其他標(biāo)簽可以看 官方文檔,超級詳細!
  • SQL片段:說白了就是講公共的部分抽取出來,利用<sql id="xxx">標(biāo)簽,引用是用<include refid="xxx">,這就就可以減少代碼重復(fù)。
  • Foreach標(biāo)簽:(當(dāng)條件需要傳一個集合時,直接用遍歷,如id in(1,2,3),查找id在這里面的博客 )
    • collection:傳值過來的集合名字。
    • item:從集合中取的單個的名字。
    • open:掃描集合開始的位置一般用"("
    • close:掃描集合結(jié)束的位置一般用")"
    • separator:集合里面的值的分隔符,一般是","

緩存

  • 什么是緩存?
    緩存是存在內(nèi)存中的臨時數(shù)據(jù),將經(jīng)常查詢的數(shù)據(jù)放在緩存中,用戶就不用再去磁盤上查詢,從而提高查詢效率,減少系統(tǒng)開銷,解決了高并發(fā)系統(tǒng)的性能問題。
  • 數(shù)據(jù)庫:當(dāng)流量多了,需要多個數(shù)據(jù)庫,需要實現(xiàn):讀寫分離,主從復(fù)制(保持一致性)。

Mybatis緩存

  • Mybatis 包含了一個非常強大的查詢緩存特性,它可以非常方便的定制和配置緩存,Mybatis中默認定義了兩個緩存:一級緩存二級緩存。
    • 默認情況下,一級緩存自動開啟,屬于SqlSession級別的緩存,也稱為本地緩存(查詢完后數(shù)據(jù)會被緩存,關(guān)閉后緩存就消失)。
    • 二級緩存需要手動開啟和配置,它屬于namespace級別的緩存(一個Mapper接口中所有的數(shù)據(jù))。
    • 為了提高擴展性,Mybatis定義了緩存接口Cache,可以通過實行這個接口自定義二級緩存。

一級緩存

  • 只在SqlSession作用域中有緩存,一旦SqlSession.close()后緩存就沒了。
  • 緩存失效的情況:
    • 當(dāng)實現(xiàn)增刪改操作時,緩存會被刷新,即清空。
    • 當(dāng)手動清除緩存時,使用sqlSession.clearCache()方法。

二級緩存

二級緩存開啟前先要保證Mybatis是否開啟了全局緩存,全局緩存默認是開啟的。

  • 二級緩存也叫全局緩存,一級緩存的作用域太低了,一個nameSpace空間對應(yīng)一個二級緩存。
  • 工作機制
    • 一個會話查詢一條數(shù)據(jù),這個數(shù)據(jù)會被放在以及緩存中,我們要做的事將這個數(shù)據(jù)放入到二級緩存中,這樣一級緩存關(guān)閉后數(shù)據(jù)還存在二級緩存中。(注意!這里只有當(dāng)一級緩存關(guān)閉后才會給到二級緩存,若沒有關(guān)閉則二級緩存沒有數(shù)據(jù)?。?/li>
    • 新的會話查詢信息時,可以從二級緩存中獲取相同的內(nèi)容。
    • 不同的Mapper查出的數(shù)據(jù)會放在自己對應(yīng)的緩存(Map)中。
  • 開啟二級緩存:直接在Mapper.xml中配置<cache/>。

Tips:我們要將實體類序列化,實現(xiàn)Serializable接口,不然會報錯NoSerializableException

Mybatis緩存原理

  • 查找緩存的順序
    • 先從二級緩存查找,若沒有,在到一級緩存找,若還沒有則從數(shù)據(jù)庫中找。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容