以下是我自己個人學習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的是before14.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。
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.#{}的存放信息總結
- 參數對象的屬性,例如參數是student對象,那麼#{name},就是拿這個student對象的name屬性
- 隨意內容,一般是基本數據類型,且參數爲1個時,此時爲佔位符,建議與參數名稱相同
- 參數爲map時,#{key}
- 參數爲map中的對象時,#{key.field}
- 參數的索引,#{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的整合
註冊一個 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對應。
差不多就這些東西了。