Mybatis的使用與Spring的整合

以下是我自己個人學習Mybatis框架的一些學習筆記,所以記錄的都是自己個人的東西,可能沒有那麼全面。主要是爲了方便日後自己查看,歡迎大家留言討論,一起學習一起進步!!


1.mybatis和hibernate

前者半自動,後者全自動 --->ORM(對象關係映射)

2.mybatis底層都是對jdbc的封裝

3.mybatis數據處理層的流程:

參數映射-->SQL解析-->SQL執行-->結果映射

4.mybatis工作原理圖:


5.屬性和成員變量的區別

屬性是指實體類的set方法,去掉set,然後首字母小寫,這個就是屬性!!例如setName,name就是屬性
而private String name --->是成員變量
對外提供set的纔是屬性!因爲可以修改。而成員變量是私有的不能直接修改。
可以結合spring的配置文件設置來理解,並不是所以的成員變量都能修改,只有對外提供了set方法的纔算屬性,才能修改!

6.mybatis要配置兩個xml文件。

一個是mybatis-config.xml名字可以隨意,配置的是mybatis的一些配置

一個是mybatis-mapper.xml文件,一般是一個dao對應一個mapper文件。

7.mybatis的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">

8.idea編譯器複製類的全路徑

在idea中,如果選中一個文件,然後右擊,copy path的話是全限定性路勁,包含電腦盤符的,一般不用這種。
我們用copy Reference 引用!!直接鼠標放的光標在一個類上就可以了,不管有沒有打開這個類。

9.什麼時候要寫classpath

一般location屬性的要加classpath: 而resource直接右擊copy Reference 快捷鍵就是shift+ctril+alt+c就可以了

10.mybatis用xml配置的流程

  • 配置mybatis-config文件,要配置properties,environments,mappers
  • 配置mapper文件,一般是一個實體類對應一個mapper,這裏注意,namaspace自定義一般用實體類的全限定名,下面的每一個sql標籤都有一個id值,也可以自定義,一般跟dao層的方法名稱相同
  • 配置mapper文件,一般是一個實體類對應一個mapper,這裏注意,namaspace自定義一般用實體類的全限定名,下面的每一個sql標籤都有一個id值,也可以自定義,一般跟dao層的方法名稱相同
  • 一般寫個工具類,直接獲取到sqlSession對象。
  • sqlSession對象是單實例線程安全的!
  • 擴展:Servlet也是單實例的,如果存在可以修改的參數,則會存在線程安全問題!

11.爲什麼sqlSessionFactoryBuider不需要關閉輸入流

sqlSessionFactoryBuilder.build--->源碼會自動關閉輸入流,所有不用我們手動關

12.typeAliases設置類的別名

在mybatis-config中配置,沒卵用,一般不配,因爲項目中的類非常多,寫全限定性命好維護。

13.插入對象後如何返回對象的id主鍵

mybatis中,插入對象後,返回該對象的id,需要加這個:
<selectKey keyProperty="id" order="AFTER" resultType="java.lang.Long">
    SELECT LAST_INSERT_ID()
</selectKey>
直接加載select標籤內部就可以了!!mysql的是after,oracle的是before

14.mybatis中自帶的常見的基本類型、封裝類型、集合類型的別名:

已經爲許多常見的 Java 類型內建了相應的類型別名。它們都是大小寫不敏感的,需要注意的是由基本類型名稱重複導致的特殊處理。

基本類型:



常用包裝類型:



最好還是寫全限定性名吧!!!好維護!!不會錯!!不過也不用擔心,後面有mybatis逆向工程,很多東西都是直接根據數據庫表自動生成的!!

15.框架項目搭建命名規範:


dao包裏:xxxMapper接口,比如student表,就建一個StudentMapper接口,接口裏有增刪改查操作方法

mapper包裏:xxxMapper.xml,namaspace就寫對應的mapper接口的全限定性名,下面對應的sql語句的id就寫接口對應的方法名

dao包的實現類:xxxMapperImpl,實現類裏面是通過sqlSession對象進行增刪改查的操作,一般這個後期直接由spring整合,進行動態映射處理

配置文件:mabatis-config.xml

16.關於sql操作,如果只有一個參數,且參數是基本類型的話,一般這樣操作,例如:

<!--一般來說參數是一個的,且參數類型是基本類型的話,#{}僅僅只是代表佔位符
        不過最好還是寫跟參數的名字一樣吧,這樣好看一些
    -->
    <delete id="deleteById" parameterType="integer">
        <!--delete from student where id=#{xxx}-->
        delete from student where id=#{id}
    </delete>

17.新增,修改,刪除,一般沒有resultType或者resultMap。

但是!查詢操作,一般都會返回各個字段,各個字段如何進行封裝成對象,即!!!orm的關係是怎麼樣的??這邊就需要我們制定resultType或者resultMapresultType一般用於數據庫查出來的字段和對象的字段,如果是對應的上的話,那麼就可以直接用resultType屬性resultMap一般用於數據庫的字段和對象的屬性不一致,例如數據庫是studnet_name,而java中是studentName此時就需要制定一個resultMap來解決數據庫字段和對象屬性之間的映射關係了!

18.selSession.seleceOne方法

如果是根據單一屬性,例如根據name啊,id啊,則應該用selectOne的方法!!不是select

19.#{}和${}的區別

#{}--->用的佔位符,底層用PreparedStatement,效率高${}--->用的是字符串拼接,底層用的Statement,效率低,有sql注入風險如果一個sql參數是字符串需要拼接的話,有兩中寫法,根據nama模糊查詢若用#{},則需像這樣寫:concat('%',#{name},'%') 這個值可以隨便寫,一般建議和參數名相同若用${},則需像這樣寫:'%${value}%' 這個值必須寫value!!建議使用#{}效率高,還沒有sql注入風險!!

20.jdbc:mysql:///databaseName 3個槓表示本機,默認端口3306的快捷寫法!!

21.mapper的動態代理! 兩個很重要的點

  • mapper文件的namaspace爲接口的全限定接口名
  • xml中的sql語句的id和接口的方法名相同

通過sqlSession.getMapper(Class對象) 返回的就是我們的mapper接口,底層使用的是jdk的proxy代理,例如:

private AnimalMapper animalMapper;
@Before
public void before(){
	SqlSession sqlSession = MybatisUtils.getSqlSession();
	animalMapper=sqlSession.getMapper(AnimalMapper.class);
}

下面的代碼只需要調用animalMapper就可以了。

就不需要像以前一樣再定義mapper的實現類,然後再獲取sqlSession,然後再調用sqlSession的方法來curd操作這麼麻煩了!!

22.多查詢條件的解決方法(Map方式解決)

其實也就是放在map中,再把map丟給接口去查。
Map<String,Object> map----->封裝多個參數,丟給mapper.xml文件就可以了
#{xxx}--->可以是佔位符,可以是對象的屬性,也可以是map中的key
如:map.put("name","張三");
#{name}---->"張三"
如果放的是對象,如 map.put("stu",student) 想要拿這個student裏面的東西,需要這麼寫#{stu.name}

22.多查詢條件的解決方法(索引方式解決)

例如mapper接口中有這個方法
void insert(String name,int age);
那麼在mapper.xml中可以這麼寫,#{0},#{1} 索引從0開始

23.#{}的存放信息總結

  1. 參數對象的屬性,例如參數是student對象,那麼#{name},就是拿這個student對象的name屬性
  2. 隨意內容,一般是基本數據類型,且參數爲1個時,此時爲佔位符,建議與參數名稱相同
  3. 參數爲map時,#{key}
  4. 參數爲map中的對象時,#{key.field}
  5. 參數的索引,#{0},#{1}......從0開始! 一般如果是多個參數的話,parameterType就不指定了

24.在xml文件中絕對不能出現的符號:



25.動態sql:


參考網址:http://www.mybatis.org/mybatis-3/zh/dynamic-sql.html

動態if標籤<if test="name!=null and name!=' ' ">

動態where標籤

用來解決where 1=1的問題!!因爲當數據量非常大的時候執行效率的問題。

雖然select * from student 和select * from student where 1=1 查詢結果相同。

但是當數據量較大時,後者的效率會大大下降

效率低下的寫法:
<select id="selectAll" resultType="com.mybatisDynamicProxy.entity.Animal">
    select * from animal
    where 1=1
    <if test="name!=null and name!=''">
        and name like concat('%',#{name},'%')
    </if>
    <if test="age!=null and age !=0">
        and age>#{age}
    </if>
 </select>

高效的寫法: 每個if都加上and,第一個條件拼接會自動去掉and
<select id="selectAll" resultType="com.mybatisDynamicProxy.entity.Animal">
    select * from animal
    <where>
        <if test="name!=null and name!=''">
            and name like concat('%',#{name},'%')
        </if>
        <if test="age!=null and age !=0">
            and age>#{age}
        </if>
    </where>
 </select>

3.choose標籤,when標籤,otherwise標籤,就跟java中的switch一樣。when標籤時選擇中了後面的就不管了!if標籤時一個個都執行。

4.foreach標籤


就是如果遍歷的泛型是自定義類型,比如自定義對象,那麼item屬性就是代表一個對象,下面要用這個對象裏面的屬性,比如item="student",如果要用對象裏面的name屬性,就要用這樣的格式#{student.name}

<select id="selectByIds" parameterType="list" resultType="com.mybatisDynamicProxy.entity.Animal">
    select * from animal
    <where>
        <foreach collection="list" item="animal" open="id in (" close=")" separator=",">
            #{animal.id}
        </foreach>
    </where>
</select>

26.先插一句題外話:

resultMap中如果是collection屬性,也就是一對多的情況,裏面用ofType屬性

如果是association屬性,也就是多對一的情況,一個學生,每個學生屬性裏面都有一個學校的屬性。裏面用javaType

resultMap更詳細的解釋是resultMapping,結果映射。不要想成map集合,雖然來說map集合也可以理解,但是mapping更貼切吧!

27.多種關係如何封裝成resultMap,關聯查詢!!

1對多-->


有兩種解決方式,一種是通過多表關聯查詢,還有一種是通過多表單獨查詢。後者可以使用延遲加載!!下面直接貼xml文件。不在解釋

多表關聯查詢:
<resultMap id="baseMap" type="com.one2many.entity.School">
    <id column="schid" property="id"/>
    <result column="schname" property="name"/>
    <collection property="students" ofType="com.one2many.entity.Student">
        <id column="stuid" property="id"/>
        <result column="stuname" property="name"/>
    </collection>
</resultMap>

<select id="selectAll" resultType="com.one2many.entity.School" resultMap="baseMap">
    SELECT
        *
    FROM
        student,
        school
    WHERE
        student.schid=school.schid
</select>

也就是說只用一個查詢語句,在resultMap中設置好,就ok了,直接全部封裝好了!!這種方式不能延遲加載
多表單獨查詢:
<resultMap id="baseMap" type="com.one2many.entity.School">
    <id column="schid" property="id"/>
    <result column="schname" property="name"/>
    <collection property="students"
                ofType="com.one2many.entity.Student"
                select="selectStus" column="schid"/>
    <!--這裏的column就是根據哪個列,然後把那個列的值傳給引入的select-->
    <!--collection對應ofType,association對應javaType-->
</resultMap>

<resultMap id="StudnetMap" type="com.one2many.entity.Student">
    <id column="stuid" property="id"/>
    <result column="stuname" property="name"/>
</resultMap>

<select id="selectStus" resultMap="StudnetMap">
    select * from student where schid=#{schid}
</select>

<select id="selectAll" resultType="com.one2many.entity.School" resultMap="baseMap">
    select * from school
</select>

多對1-->


跟1對多差不多,就是多個學生,對應一個學校。每個學生屬性裏面都會有一個學校的屬性。

就是那個resultMap中寫association,然後用javaType就可以了!

自關聯--->


自己的理解是員工表,員工有上級領導。所有也是存在一對多,多對一的關係。

像parent_ids,leader,這種字段,自關聯的主鍵id。
1.自關聯,一對多(1)

查詢子孫的,不包含傳入的那個參數的數據!!比如我傳的是1,查出來的是所有老闆以下的所有員工的關係。但是沒有包含老闆。

<resultMap id="EmployeeMap1" type="com.mybatis.oneself.dao.Employee">
    <id column="id" property="id"/>
    <result column="name" property="name"/>
    <collection property="employeeList"
                ofType="com.mybatis.oneself.dao.Employee"
                column="id"
                select="selectChildrenByParent"/>
<!--這裏的select選擇非常關鍵,就相當於循環遞歸調用自己-->
</resultMap>

<select id="selectChildrenByParent" resultMap="EmployeeMap1">
    select id,name from employee where pid=#{pid}
</select>

2.自關聯,一對多(2)

查詢自身以及所有的子孫,比如傳了1,老闆也查出來,老闆以下的也都查出來!

<resultMap id="EmployeeMap" type="com.mybatis.oneself.dao.Employee">
    <id column="id" property="id"/>
    <result column="name" property="name"/>
    <collection property="employeeList"
                ofType="com.mybatis.oneself.dao.Employee"
                select="selectChildrenByParent"
                column="id"/>
    <!--這裏非常關鍵,就相當於循環遞歸調用自己-->
  <!--先把自己查出來,然後把自己的id當做pid傳給另外一個查詢,然後那個查詢就是上面遞歸調用的方法是一樣的!-->
</resultMap>

<select id="selectChildrenByParent" resultMap="EmployeeMap">
    select id,name from employee where pid=#{pid}
</select>

<select id="selectEmployeeById" resultMap="EmployeeMap">
    select * from employee where id=#{id}
</select>
3.自關聯,多對一。

一個員工對應一個上級!也就是resultMap改來改去,然後resultMap標籤裏面的select選擇以及column的選擇!!就是說,resultMap中的select語句要用哪個column的值作爲子查詢的值!!再次強調下ofType和javaType,一個集合類型的,一個單個類型的。


多對多--->

(就是兩個一對多構成的!!)中間關聯表是多方!!,學生表和課程表是一方

一個表只要有外鍵的,就是多方!反過來說,外鍵肯定是在多方的!

就是通過第三張表關聯起來,比如學生選課表

一個學生對應多個課程,一個課程也對應多個學生!!(現實模型)

程序就跟上面的差不多。



如果要延遲加載,則必須要用到子查詢。即在resultMap中的association或者collection中引入select標籤!

28.延遲加載,就是sql的執行時間


主查詢沒法設置,會直接加載。而從查詢可以配置延遲加載,加載時機。

(主表、關聯表)

關聯對象的加載時機(子查詢):

  • 直接加載:直接查
  • 侵入式延遲:關聯對象的詳情侵入到了主加載對象的詳情裏面了!!
  • 深度延遲:子查詢一開始不查,只有真正訪問關聯對象時纔回去查 

<settings>
    <!--日誌-->
    <setting name="logImpl" value="LOG4J"/>
    <!--是否開啓延遲加載,這個是總開關,false的話下面的也沒用!-->
    <setting name="lazyLoadingEnabled" value="true"/>
    <!--是否開啓侵入式延遲加載-->
    <setting name="aggressiveLazyLoading" value="false"/>
</settings>

在mybatis-config.xml配置文件中設置。


個人理解:

第一個直接加載不說了。

第二個,侵入式加載--->是指,一開始進行關聯對象的查詢,但是,如果主加載對象進行訪問,就算沒有訪問到關聯對象,那麼關聯對象的詳情也會進行查詢操作!!
比如主是School,從是Student,查詢School,如果訪問School的詳情,就算不訪問School裏面的Student屬性,Student的詳情在這個時候也會進行查詢!這就是侵入式加載!
第三個,延遲加載,只有訪問到School的Student的裏面的屬性,確確實實訪問到了Student的屬性,那麼纔開始查詢操作。


延遲加載只能適用在多表單獨查詢,對於多表連接查詢是沒有用的!!


29.緩存的處理


緩存有作用域和生命週期,作用域是根據namespace來區分的!

一級緩存:

一級緩存,默認開啓,且不能關閉,生命週期是單個sqlSession,sqlSession如果關閉了,那麼就沒有了。而二級緩存是整個應用環境的!緩存數據在多個sqlSession之間共享!


一級緩存的證明,以及查詢緩存的依據:







注意:如果進行了增刪改的操作,那麼緩存都會被清空,不管是不是一個namespace下的,因爲本身也可以是交叉業務。緩存會被清空哦!!




二級緩存:

內置二級緩存的開啓與驗證




注意:在二級緩存下如果進行了增刪改的操作,只會把那個namespace下的緩存的entry的value設爲了null,key還是存在的。所有,如果是其他namespace的二級緩存還是存在的,這一點跟一級緩存不一樣,一級緩存是全部清空。所有這邊二級緩存的設置有一些屬性,比如大小,滿了以後怎麼操作?刷新時間?
如果想要二級緩存不清楚,需要設置flushcache爲false!!
二級緩存的關閉:
局部關閉就是在sql標籤加上useCache屬性爲false
全局關閉需要在mybatis配置文件中的setting標籤中配置name=CacheEnabled 屬性爲false
二級緩存使用原則:
  • 多個namespace不要操作同一張表
  • 不要在關聯關係表上執行增刪改操作
  • 查詢多餘增刪改的時候

綜上,實際項目中的表都是千絲萬縷的關係,幾乎很少是單表的。所有幾乎用不到二級緩存,因爲用不好了就會產生數據不一致的情況!!一般來說,都是用局部的二級緩存!!比如,那個mapper接口想要用二級緩存就在哪個mapper接口對應的xml中,配置cache屬性!!

使用第三方二級緩存:
不多介紹了,百度mybatis如何使用第三方二級緩存即可。

30.mybatis的註解式開發

首先,官方並不推薦註解式開發,還是建議xml方式的開發。註解還是挺簡單的,具體有什麼註解,可以到mybatis的註解的包下看看,一查便知。
如果想要註解生效,需要再配置文件中的mappers標籤中用package標籤,引入mapper接口所在的包。以前是使用mapper標籤引入xml映射文件。


@MapKey這個註解

可以用用,其他的主鍵個人不建議。

返回的是以MapKey註解裏面額值爲key,以返回對象的map對象key-value的形式,注意不是直接返回對象哦!
mapper接口這樣寫:
@MapKey("name")
Map<String,Map<Object,Object>> selectAll();
xml文件這樣寫:
<select id="selectAll" resultType="java.util.Map">
    select * from employee
</select>

31.mybatis與spring的整合

註冊一個 SqlSessionFactoryBean
1.然後配置DataSource屬性
2.配置mapperLocation屬性,就是mapper.xml的文件,把xml文件全部加載進去。
3.配置configLocation屬性,就是mybatis的主配置文件,一般主配置文件裏面就一個日誌的設置,還有一些插件的設置,比如分頁插件等

註冊一個 MapperScannerConfigurer

1.配置basePacke屬性--->就是mapper接口,因爲在這邊已經配過了,所有mybatis的主配置文件那邊就不用再配置了!!

2。配置sqlSessionFactoryBeanName屬性,就是剛剛上面那個



這是我項目中的整合xml文件:

spring-mybatis.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns="http://www.springframework.org/schema/beans"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd">

    <context:property-placeholder location="classpath:properties/*.properties"/>

    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="username" value="${driver.username}"/>
        <property name="password" value="${driver.password}"/>
        <property name="url" value="${driver.url}"/>
        <property name="driverClassName" value="${driver.class}"/>
    </bean>

    <!--配置sqlSessionFactoryBean對象-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="configLocation" value="classpath:mybatis-config2.xml"/>
        <property name="mapperLocations" value="classpath:mapper/*.xml"/>
    </bean>

    <!--配置Mapper自動掃描器-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
        <property name="basePackage" value="com.mybatis.**.dao"/>
    </bean>
</beans>

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>

    <settings>
        <!--日誌-->
        <setting name="logImpl" value="LOG4J"/>
    </settings>

</configuration>
當然啦,這個config還可以配置很多其他的東西,像分頁的插件啊,緩存啊等等。


然後,就是寫實體類和Dao層接口的時候,名字對應上,還有Dao層的接口和mapper.xml中的namespace要對應,方法要和sql語句的id對應。




差不多就這些東西了。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章