Mybatis自學之路(狂神說)
MyBatis自學之路
1 Mybatis簡介
1.1 mybatis是什麼?
- MyBatis 是一款優秀的持久層框架,它支援自定義 SQL、儲存過程以及高階對映。MyBatis 免除了幾乎所有的 JDBC 程式碼以及設定引數和獲取結果集的工作。MyBatis 可以通過簡單的 XML 或註解來配置和對映原始型別、介面和 Java POJO(Plain Old Java Objects,普通老式 Java 物件)為資料庫中的記錄。
- MyBatis 本是apache的一個開源專案iBatis, 2010年這個專案由apache software foundation 遷移到了google code,並且改名為MyBatis 。2013年11月遷移到Github。
1.2 myBatis優點
- 簡單易學:本身就很小且簡單。沒有任何第三方依賴,最簡單安裝只要兩個jar檔案+配置幾個sql對映檔案易於學習,易於使用,通過文件和原始碼,可以比較完全的掌握它的設計思路和實現。
- 靈活:mybatis不會對應用程式或者資料庫的現有設計強加任何影響。 sql寫在xml裡,便於統一管理和優化。通過sql語句可以滿足操作資料庫的所有需求。
- 解除sql與程式程式碼的耦合:通過提供DAO層,將業務邏輯和資料訪問邏輯分離,使系統的設計更清晰,更易維護,更易單元測試。sql和程式碼的分離,提高了可維護性。
- 提供對映標籤,支援物件與資料庫的orm欄位關係對映
- 提供物件關係對映標籤,支援物件關係組建維護
- 提供xml標籤,支援編寫動態sql
1.3 持久化
資料持久化
- 持久化就是將程式的資料在持久狀態和瞬時狀態轉化的過程
- 記憶體:斷電即失
- 資料庫(Jdbc),io檔案持久化。
1.4 持久層
Dao層、Service層、Controller層
- 完成持久化工作的程式碼塊
- 層界限十分明顯
技術沒有高低之分,只有使用技術的人有高低之分
2 Mybatis第一個程式
2.1 環境搭建
-
準備資料庫
create database mybatis; use mybatis; create table user( id int(10) primary key, `name` varchar(30), `password` varchar(30) )engine=INNODB default charset = utf8; insert into user values (1,'zhangsan','123'), (2,'lisi','123'), (3,'wangwu','123');
-
新增環境依賴並建立子模組
<dependencies> <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.6</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> <scope>test</scope> </dependency> </dependencies>
-
從 XML 中構建 SqlSessionFactory
-
每個基於 MyBatis 的應用都是以一個 SqlSessionFactory 的例項為核心的。SqlSessionFactory 的例項可以通過 SqlSessionFactoryBuilder 獲得。而 SqlSessionFactoryBuilder 則可以從 XML 配置檔案或一個預先配置的 Configuration 例項來構建出 SqlSessionFactory 例項。
-
建立mybatis-configuration.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.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&characterEncoding=utf-8&useUnicode=true"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <mappers> <mapper resource="org/mybatis/example/BlogMapper.xml"/> </mappers> </configuration>
-
建立MybatisUtil工具類
public class MybatisUtil { //建立SqlSessionFactory例項 private static SqlSessionFactory sqlSessionFactory = null; static { String resource = "mybatis-configuration.xml"; SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder(); try { sqlSessionFactory = factoryBuilder.build(Resources.getResourceAsStream(resource)); } catch (IOException e) { e.printStackTrace(); } } //既然有了 SqlSessionFactory,顧名思義,我們可以從中獲得 SqlSession 的例項. // SqlSession 提供了在資料庫執行 SQL 命令所需的所有方法。 public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(); } }
-
2.2 編寫查詢語句程式碼
-
準備實體類User
-
編寫dao介面
public interface UserDao { List<User> getUserList(); }
-
編寫dao介面對應的mapper.xml配置檔案
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace 對應的 dao介面 id 方法名, 返回值的型別:填寫list中傳入的泛型--> <mapper namespace="com.iandf.dao.UserDao"> <select id="getUserList" resultType="com.iandf.pojo.User"> select * from mybatis.user </select> </mapper>
-
新增pom.xml新增配置,防止java目錄下的資源配置檔案匯出失敗
<build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> <include>**/*.properties</include> </includes> <filtering>true</filtering> </resource> </resources> </build>
-
使用junit測試
@org.junit.Test public void Test(){ SqlSession sqlSession = MybatisUtil.getSqlSession(); UserDao mapper = sqlSession.getMapper(UserDao.class); List<User> userList = mapper.getUserList(); for (User user : userList) { System.out.println(user.toString()); } }
2.3 遇到的錯誤
1. 類找不到,在終端執行 mvn idea:idea指令之後就能解決
2. 配置檔案mybatis-configuration.xml找不到,沒有配置mapper 或者 resources資料夾沒有被標記為資原始檔夾
2.4 CRUD
-
UserMapper介面類
public interface UserMapper { List<User> getUserList(); //insert int addUser(User user); //delete int deleteUserById(int id); //update int updateUserById(User user); //select User queryUserById(int id); }
-
mapper.xml配置檔案
<mapper namespace="com.iandf.dao.UserMapper"> <select id="getUserList" resultType="com.iandf.pojo.User"> select * from mybatis.user </select> <insert id="addUser" parameterType="com.iandf.pojo.User"> # #{user.id}也可以省去物件名稱 insert into mybatis.user(id, name, password) VALUES (#{user.id},#{name},#{password}) </insert> <delete id="deleteUserById" parameterType="int"> delete from mybatis.user where id = #{id} </delete> <update id="updateUserById" parameterType="com.iandf.pojo.User"> update mybatis.user set name = #{name},password = #{password} where id = #{id} </update> <select id="queryUserById" parameterType="int" resultType="com.iandf.pojo.User"> select * from mybatis.user where id = #{id} </select> </mapper>
-
測試檔案
@Test public void deleteUserByIdTest(){ SqlSession sqlSession = MybatisUtil.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); int nums = mapper.deleteUserById(4); System.out.println(nums); sqlSession.commit(); } @Test public void queryUserByIdTest(){ try(SqlSession sqlSession = MybatisUtil.getSqlSession()) { UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.queryUserById(4); System.out.println(user.toString()); } }
note:
1. 增刪改需要提交事務
2. sqlSession相當於connection mapper相當於statement
3. #{xxx} xxx必須與編寫的欄位和引數名相同
2.5 使用萬能的map傳遞引數
使用map插入一條記錄
-
dao介面
int addUserByMap(Map<String,Object> map);
-
mapper.xml
<insert id="addUserByMap" parameterType="map"> insert into mybatis.user(id, name, password) VALUES (#{userID},#{userName},#{UserPassword}) </insert>
-
測試案例
@Test public void addUserTestByMap(){ try(SqlSession sqlSession = MybatisUtil.getSqlSession()){ UserMapper mapper = sqlSession.getMapper(UserMapper.class); HashMap<String, Object> map = new HashMap<>(); map.put("userID",5); map.put("userName","王五"); map.put("UserPassword","123456"); mapper.addUserByMap(map); sqlSession.commit(); } }
NOTE:
- 傳遞多個引數可以使用物件,也可以使用map
- 傳遞一個引數,直接使用方法形參即可
2.6 模糊查詢
-
在傳遞引數時使用萬用字元,在sql語句中寫死
-
mapper.xml
<select id="queryUserByLikeName" resultType="com.iandf.pojo.User"> select * from mybatis.user where name like #{value} </select>
-
dao介面和測試程式碼
//select List<User> queryUserByLikeName(String value); @Test public void queryUserByLikeNameTest(){ try(SqlSession sqlSession = MybatisUtil.getSqlSession()) { UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.queryUserByLikeName("%n%"); for (User user : users) { System.out.println(user.toString()); } } }
-
-
在sql語句中進行拼接,傳遞引數時只需要傳使用者輸入部分,這樣sql在進行拼接,容易導致sql注入
-
mapper.xml
<select id="queryUserByLikeName2" parameterType="String" resultType="com.iandf.pojo.User"> select * from mybatis.user where name like "%"#{value}"%" </select>
-
dao介面和測試程式碼
List<User> queryUserByLikeName2(String value); @Test public void queryUserByLikeName2Test(){ try(SqlSession sqlSession = MybatisUtil.getSqlSession()) { UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.queryUserByLikeName2("n"); for (User user : users) { System.out.println(user.toString()); } } }
-
3 配置解析
3.1 核心配置檔案
Mybatis的配置檔案包含了會深深影響MyBatis行為的設定和屬性資訊。
configuration(配置)
properties(屬性)
settings(設定)
typeAliases(類型別名)
typeHandlers(型別處理器)
objectFactory(物件工廠)
plugins(外掛)
environments(環境配置)
environment(環境變數)
transactionManager(事務管理器)
dataSource(資料來源)
databaseIdProvider(資料庫廠商標識)
mappers(對映器)
3.2 環境配置 environments
MyBatis 可以配置成適應多種環境
不過要記住:儘管可以配置多個環境,但每個 SqlSessionFactory 例項只能選擇一種環境
MyBatis預設的事務管理器就是JDBC ,連線池:POOLED
3.3 屬性 properties
我們可以通過properties屬性來實現引用配置檔案
這些屬性可以在外部進行配置,並可以進行動態替換。你既可以在典型的 Java 屬性檔案中配置這些屬性,也可以在 properties 元素的子元素中設定。【db.poperties】
-
編寫一個配置檔案
db.properties
driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?userSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTimezone=UTC username=root password=root 1234
-
在核心配置檔案中引入
<!--引用外部配置檔案--> <properties resource="db.properties"> <property name="username" value="root"/> <property name="password" value="root"/> </properties> 12345
- 可以直接引入外部檔案
- 可以在其中增加一些屬性配置
- 如果兩個檔案有同一個欄位,優先使用外部配置檔案的
3.4 類型別名 typeAliases
- 類型別名可為 Java 型別設定一個縮寫名字。 它僅用於 XML 配置.
- 意在降低冗餘的全限定類名書寫。
<!--可以給實體類起別名-->
<typeAliases>
<typeAlias type="com.kuang.pojo.User" alias="User"/>
</typeAliases>
也可以指定一個包,每一個在包 domain.blog
中的 Java Bean,在沒有註解的情況下,會使用 Bean 的首字母小寫的非限定類名來作為它的別名。 比如 domain.blog.Author
的別名為 author
,;若有註解,則別名為其註解值。見下面的例子:
<typeAliases>
<package name="com.kuang.pojo"/>
</typeAliases>
在實體類比較少的時候,使用第一種方式。如果實體類十分多,建議用第二種掃描包的方式。
第一種可以DIY別名,第二種不行,如果非要改,需要在實體上增加註解。
@Alias("author")
public class Author {
...
}
3.5 對映器 mappers
MapperRegistry:註冊繫結我們的Mapper檔案;
方式一:【推薦使用】
<!--每一個Mapper.xml都需要在MyBatis核心配置檔案中註冊-->
<mappers>
<mapper resource="com/kuang/dao/UserMapper.xml"/>
</mappers>
1234
方式二:使用class檔案繫結註冊
<!--每一個Mapper.xml都需要在MyBatis核心配置檔案中註冊-->
<mappers>
<mapper class="com.kuang.dao.UserMapper"/>
</mappers>
方式三:使用包掃描進行注入
<mappers>
<package name="com.kuang.dao"/>
</mappers>
方法二和方法三的注意點:
- 介面和他的Mapper配置檔案必須同名
- 介面和他的Mapper配置檔案必須在同一個包下
3.6 作用域和生命週期
宣告週期和作用域是至關重要的,因為錯誤的使用會導致非常嚴重的併發問題。
SqlSessionFactoryBuilder:
- 一旦建立了SqlSessionFactory,就不再需要它了
- 區域性變數
SqlSessionFactory:
- 說白了就可以想象為:資料庫連線池
- SqlSessionFactory一旦被建立就應該在應用的執行期間一直存在,沒有任何理由丟棄它或重新建立一個例項。
- 因此SqlSessionFactory的最佳作用域是應用作用域(ApplocationContext)。
- 最簡單的就是使用單例模式或靜態單例模式。
SqlSession:
- 連線到連線池的一個請求
- SqlSession 的例項不是執行緒安全的,因此是不能被共享的,所以它的最佳的作用域是請求或方法作用域。
- 用完之後需要趕緊關閉,否則資源被佔用!
4、解決屬性名和欄位名不一致的問題
1. 問題
資料庫中的欄位
實體類欄位
public class User {
private int id;
private String name;
private String pwd;
}
<select id="queryUserById" parameterType="int" resultType="user">
select * from mybatis.user where id = #{user.id}
</select>
上面這個類有 3 個屬性:id,name 和 pwd。這些屬性會對應到 select 語句中的列名。這些屬性會對應到 select 語句中的列名。但是類的名字和資料庫的欄位要一致。這樣的一個 JavaBean 可以被對映到 ResultSet
上面案例中,pwd和password不同所以測試結果顯示pwd為null
解決方法:
-
起別名
<select id="getUserById" resultType="com.kuang.pojo.User"> select id,name,password as pwd from USER where id = #{id} </select>
MyBatis 會在幕後自動建立一個
ResultMap
,再根據屬性名來對映列到 JavaBean 的屬性上。如果列名和屬性名不能匹配上,可以在 SELECT 語句中設定列別名(這是一個基本的 SQL 特性)來完成匹配
2. resultMap
結果集對映
<resultMap id="resultMap" type="user">
<result column="password" property="pwd"/>
</resultMap>
<select id="queryUserById" parameterType="int" resultMap="resultMap">
#也可以省去物件名稱
select * from mybatis.user where id = #{user.id}
</select>
resultMap
元素是 MyBatis 中最重要最強大的元素。- ResultMap 的設計思想是,對簡單的語句做到零配置,對於複雜一點的語句,只需要描述語句之間的關係就行了。
ResultMap
的優秀之處——你完全可以不用顯式地配置它們。
5. 日誌
掌握 stdout_logging,log4j
5.1 STDOUT_LOGGING
在mybatis_config.xml檔案中新增設定
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
測試結果
5.2 LOG4J
簡介
Log4j是Apache的一個開源專案,通過使用Log4j,我們可以控制日誌資訊輸送的目的地是控制檯、檔案、GUI元件,甚至是套介面伺服器、NT的事件記錄器、UNIX Syslog守護程序等;我們也可以控制每一條日誌的輸出格式;通過定義每一條日誌資訊的級別,我們能夠更加細緻地控制日誌的生成過程。最令人感興趣的就是,這些可以通過一個配置檔案來靈活地進行配置,而不需要修改應用的程式碼。
程式碼測試
-
新增設定
<settings> <setting name="logImpl" value="LOG4J"/> </settings>
-
匯入log4j的依賴包
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency>
-
編寫log4j.properties
#將等級為DEBUG的日誌資訊輸出到console和file這兩個目的地,console和file的定義在下面的程式碼 log4j.rootLogger=DEBUG,console,file #控制檯輸出的相關設定 log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.Threshold=DEBUG log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=[%c]-%m%n #檔案輸出的相關設定 log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/rzp.log log4j.appender.file.MaxFileSize=10mb log4j.appender.file.Threshold=DEBUG log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n #日誌輸出級別 log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sq1.PreparedStatement=DEBUG
-
測試結果
-
簡單使用
Logger logger = Logger.getLogger(UserDaoTest.class); logger.debug("debug"); logger.error("error"); logger.info("info");
6. Mybatis的執行流程
-
建立SqlSessionFactoryBuilder
-
使用建造者模式構建SqlSessionFactory
-
原始碼解析,使用配置檔案的輸入流構建一個XMLConfigBuilder物件,使用XMLConfigBuilder為引數構建DefaultSqlSessionFactory
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { SqlSessionFactory var5; try { XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); var5 = this.build(parser.parse()); } catch (Exception var14) { .... } return var5; } public SqlSessionFactory build(Configuration config) { return new DefaultSqlSessionFactory(config); }
-
執行結果
-
使用SqlSessionFactory建立sqlSession物件
-
原始碼分析,使用使用transaction作為引數生成了executor,configuration,executor生成了sqlSession
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; DefaultSqlSession var8; try { Environment environment = this.configuration.getEnvironment(); TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment); tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit); Executor executor = this.configuration.newExecutor(tx, execType); var8 = new DefaultSqlSession(this.configuration, executor, autoCommit); } catch (Exception var12) { ... } return var8; }
-
執行結果
-
-
實現CRUD
-
NOTE:
- mybatis執行流程使用了大量的反射,例如它使用反射知道我們所返回的型別是列表
- 使用了建造者和工廠設計模式
流程圖如下:
7. 使用註解執行CRUD
-
mapper介面
-
//login @Select("select * from mybatis.user where name=#{name} and password = #{pwd}") User login(@Param("name") String name, @Param("pwd") String password);
-
-
使用class=xxx編寫mybatis_configuration.xml檔案
<mappers> <!--<mapper resource="com/iandf/dao/UserDaoMapper.xml"/>--> <mapper class="com.iandf.dao.UserMapper"/> </mappers>
-
測試
關於@param("xxx")註解:
- 基本型別和String都要加上,引用型別沒必要加
- 在sql引用上使用的就是param的屬性名
- 基本型別只有一個的時候可以加也可以不加,建議都加上
8. 負責查詢語句
8.1 多對一連表查詢
查詢語句 (查詢學生和學生對應的老師的資訊)
select s.id, s.name ,t.name from student s,teacher t where s.tid = t.id;
程式實現
1.按照查詢巢狀實現
-
mapper介面
-
mapper.xml
<select id="getStudentList" resultMap="studentMap"> select * from mybatis.student </select> <resultMap id="studentMap" type="com.iandf.pojo.Student"> <association property="teacher" column="tid" javaType="com.iandf.pojo.Teacher" select="getTeacher"/> </resultMap> <select id="getTeacher" resultType="teacher"> #\#{id}中的名字可以隨便取,他的值就是列名為column="tid"的值 select * from mybatis.teacher where id = #{id} </select>
-
測試程式碼
- 按照結果巢狀的方式實現
-
mapper介面
-
mapper.xml
<select id="getStudentList2" resultMap="studentMap2"> select s.id sId, s.name sName, t.name tName, t.id tId from mybatis.student s, mybatis.teacher t where s.tid = t.id; </select> <resultMap id="studentMap2" type="student"> <result property="id" column="sID"/> <result property="name" column="sName"/> <association property="teacher" javaType="Teacher"> <result property="id" column="tID"/> <result property="name" column="tName"/> </association> </resultMap>
-
測試程式碼
8.2 一對多連表查詢
查詢老師和老師交的所有學生
select t.id tId ,t.name tName,s.id sId, s.name sName from mybatis.student s,mybatis.teacher t where s.tid = t.id and t.id = 1;
按照結果巢狀方式實現
-
mapper介面
-
mapper.xml
<select id="getTeacherInfoById" resultMap="teacherMap" parameterType="int"> select t.id tId ,t.name tName,s.id sId, s.name sName from mybatis.student s,mybatis.teacher t where s.tid = t.id and t.id = #{id}; </select> <resultMap id="teacherMap" type="teacher"> <result column="tId" property="id"/> <result column="tName" property="name"/> <collection property="students" ofType="student"> <result property="id" column="sId"/> <result property="name" column="sName"/> </collection> </resultMap>
-
測試程式碼
按照查詢巢狀方式實現
mapper.xml
<select id="getTeacherInfoById2" resultMap="teacherMap2" parameterType="int">
select * from mybatis.teacher
</select>
<resultMap id="teacherMap2" type="teacher">
<collection property="students" column="id" ofType="Student" select="getStudents"/>
</resultMap>
<select id="getStudents" resultType="student">
select * from mybatis.student where tid = #{id}
</select>
8.3 總結
多對一使用association
association
– 一個複雜型別的關聯;許多結果將包裝成這種型別
- 巢狀結果對映 – 關聯可以是
resultMap
元素,或是對其它結果對映的引用
一對多使用collection
collection
– 一個複雜型別的集合
- 巢狀結果對映 – 集合可以是
resultMap
元素,或是對其它結果對映的引用 - ofType代表集合的泛型 javaType指的是實體類中的型別
9. 動態SQL
9.1 簡介
動態 SQL 是 MyBatis 的強大特性之一。如果你使用過 JDBC 或其它類似的框架,你應該能理解根據不同條件拼接 SQL 語句有多痛苦,例如拼接時要確保不能忘記新增必要的空格,還要注意去掉列表最後一個列名的逗號。利用動態 SQL,可以徹底擺脫這種痛苦。
使用動態 SQL 並非一件易事,但藉助可用於任何 SQL 對映語句中的強大的動態 SQL 語言,MyBatis 顯著地提升了這一特性的易用性。
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
9.2 IF
需求:根據作者名字和部落格名字來查詢部落格!如果作者名字為空,那麼只根據部落格名字查詢,反之,則根據作者名來查詢
-
mapper介面
List<Blog> queryBlogByIf(Map<String,String> map);
-
mapper.xml
<select id="queryBlogByIf" parameterType="map" resultType="Blog"> select * from mybatis.blog where 1=1 <if test="title != null"> and title = #{title} </if> <if test="author != null"> and author = #{author} </if> </select>
-
測試
1=1在專案中是不允許這樣使用的,mybatis為我們提供了where標籤來解決and或者or加不加的問題
改進後:
<select id="queryBlogByIf" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
</select>
這個“where”標籤會知道如果它包含的標籤中有返回值的話,它就插入一個‘where’。此外,如果標籤返回的內容是以AND 或OR 開頭的,則它會剔除掉。
9.3 set
同理,上面的對於查詢 SQL 語句包含 where 關鍵字,如果在進行更新操作的時候,含有 set 關鍵詞,我們怎麼處理呢?
需求:修改部落格的title或者author
-
mapper介面
void updateBlogBySet(Map<String,String> map);
-
mapper.xml
<update id="updateBlogBySet" parameterType="map"> update mybatis.blog <set> <if test="title != null"> title = #{title}, </if> <if test="author != null"> author = #{author} </if> </set> where id = #{id} </update>
-
測試
9.4 choose、when、otherwise
有時候,我們不想使用所有的條件,而只是想從多個條件中選擇一個使用。針對這種情況,MyBatis 提供了 choose 元素,它有點像 Java 中的 switch 語句。
需求:傳入了 “title” 就按 “title” 查詢,傳入了 “author” 就按 “author” 查詢的情形。若兩者都沒有傳入,就返回所有的 BLOG
-
mapper介面
List<Blog> queryBlogByChoose(Map<String,String> map);
-
mapper.xml
<select id="queryBlogByChoose" parameterType="map" resultType="blog"> select * from mybatis.blog <where> <choose> <when test="title != null"> title = #{title} </when> <when test="author != null"> and author = #{author} </when> </choose> </where> </select>
-
測試
9.5 sql片段
有時候可能某個 sql 語句我們用的特別多,為了增加程式碼的重用性,簡化程式碼,我們需要將這些程式碼抽取出來,然後使用時直接呼叫。
提取sql片段
<sql id="if-title-author">
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>
引用sql片段
<select id="queryBlogByIf" parameterType="map" resultType="Blog">
select * from mybatis.blog
<where>
<include refid="if-title-author"/>
<!-- 在這裡還可以引用其他的 sql 片段 -->
</where>
</select>
NOTE:
- 最好是基於單表查詢,可以提高片段的可重用性
- sql片段中不要包含where,最好只有幾個簡單的if
9.6 foreach
需求:我們需要查詢 blog 表中 id 分別為1,2,3的部落格資訊
sql語句
select * from blog where id in (1,2,3);
程式碼實現
-
mapper介面
List<Blog> queryBlogByForeach(Map<String,Object> map);
-
mapper.xml
<select id="queryBlogByForeach" resultType="blog" parameterType="map"> select * from mybatis.blog <where> <foreach collection="ids" item="id" open="id in (" close=")" separator=","> #{id} </foreach> </where> </select>
-
測試
public void queryBlogByForeach(){ try(SqlSession sqlSession = MybatisUtil.getSqlSession()){ BlogMapper mapper = sqlSession.getMapper(BlogMapper.class); HashMap<String, Object> map = new HashMap<>(); List<Integer> ids = new ArrayList<>(); ids.add(1); ids.add(2); ids.add(3); map.put("ids",ids); List<Blog> blogs = mapper.queryBlogByForeach(map); for (Blog blog : blogs) { System.out.println(blog.toString()); } } }
10. Mybatis快取
10.1 簡介
查詢 : 連線資料庫 ,耗資源!
一次查詢的結果,給他暫存在一個可以直接取到的地方!--> 記憶體 : 快取我們再次查詢相同資料的時候,直接走快取,就不用走資料庫了
-
什麼是快取 [ Cache ]?
- 存在記憶體中的臨時資料。
- 將使用者經常查詢的資料放在快取(記憶體)中,使用者去查詢資料就不用從磁碟上(關係型資料庫資料檔案)查詢,從快取中查詢,從而提高查詢效率,解決了高併發系統的效能問題。
-
為什麼使用快取?
- 減少和資料庫的互動次數,減少系統開銷,提高系統效率。
-
什麼樣的資料能使用快取?
- 經常查詢並且不經常改變的資料。【可以使用快取】
10.2、Mybatis快取
- MyBatis包含一個非常強大的查詢快取特性,它可以非常方便地定製和配置快取。快取可以極大的提升查詢效率。
- MyBatis系統中預設定義了兩級快取:一級快取和二級快取
- 預設情況下,只有一級快取開啟。(SqlSession級別的快取,也稱為本地快取)
- 二級快取需要手動開啟和配置,他是基於namespace級別的快取。
- 為了提高擴充套件性,MyBatis定義了快取介面Cache。我們可以通過實現Cache介面來自定義二級快取
10.3、一級快取
- 一級快取也叫本地快取: SqlSession
- 與資料庫同一次會話期間查詢到的資料會放在本地快取中。
- 以後如果需要獲取相同的資料,直接從快取中拿,沒必須再去查詢資料庫;
測試步驟:
- 開啟日誌!
- 測試在一個Sesion中查詢兩次相同記錄
- 檢視日誌輸出
快取失效的情況:
-
查詢不同的東西
-
增刪改操作,可能會改變原來的資料,所以必定會重新整理快取!
-
查詢不同的Mapper.xml
-
手動清理快取!
sqlsession.clearCache();
小結:一級快取預設是開啟的,只在一次SqlSession中有效,也就是拿到連線到關閉連線這個區間段!
一級快取就是一個Map。
10.4、二級快取
- 二級快取也叫全域性快取,一級快取作用域太低了,所以誕生了二級快取
- 基於namespace級別的快取,一個名稱空間,對應一個二級快取;
- 工作機制
- 一個會話查詢一條資料,這個資料就會被放在當前會話的一級快取中;
- 如果當前會話關閉了,這個會話對應的一級快取就沒了;但是我們想要的是,會話關閉了,一級快取中的資料被儲存到二級快取中;
- 新的會話查詢資訊,就可以從二級快取中獲取內容;
- 不同的mapper查出的資料會放在自己對應的快取(map)中;
步驟:
-
開啟全域性快取
<!--顯示的開啟全域性快取--> <setting name="cacheEnabled" value="true"/>
-
在要使用二級快取的Mapper中開啟
<!--在當前Mapper.xml中使用二級快取--> <cache/>
也可以自定義引數
<!--在當前Mapper.xml中使用二級快取--> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
-
測試
-
問題:我們需要將實體類序列化!否則就會報錯!
Caused by: java.io.NotSerializableException: com.kuang.pojo.User
-
小結:
- 只要開啟了二級快取,在同一個Mapper下就有效
- 所有的資料都會先放在一級快取中;
- 只有當會話提交,或者關閉的時候,才會提交到二級快取中!