什么是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&useUnicode=true&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ù)庫中找。