1. 簡介 1.1什麼是Mybatis MyBatis 是一款優秀的持久層框架 它支持自定義 SQL、存儲過程以及高級映射。 MyBatis 免除了幾乎所有的 JDBC 代碼以及設置參數和獲取結果集的工作。 MyBatis 可以通過簡單的 XML 或註解來配置和映射原始類型、介面和 Java POJ ...
目錄
- 1. 簡介
- 2.初涉Mybatis
- 3.Mybatis的CRUD簡單實現
- 4.配置解析
- 5.拓展:當屬性名和列名不一致
- 6.日誌
- 7.分頁
- 8.註解開發
- 9.Lombok
- 10.複雜查詢
- 11.動態SQL
- 12.Mybatis緩存
1. 簡介
1.1什麼是Mybatis
- MyBatis 是一款優秀的持久層框架
- 它支持自定義 SQL、存儲過程以及高級映射。
- MyBatis 免除了幾乎所有的 JDBC 代碼以及設置參數和獲取結果集的工作。
- MyBatis 可以通過簡單的 XML 或註解來配置和映射原始類型、介面和 Java POJO(Plain Old Java Objects,普通老式 Java 對象)為資料庫中的記錄。
1.2 如何獲得Mybatis
-
獲得maven倉庫:
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.9</version> </dependency>
1.3 使用Mybatis的好處:
- 使用簡單,傳統的JDBC代碼太複雜
- 簡單易學:沒有任何第三方依賴,最簡單安裝只要兩個jar文件+配置幾個sql映射文件。
- 靈活
- 解除sql與程式代碼的耦合
- 提供映射標簽,支持對象與資料庫的orm欄位關係映射。
- 提供對象關係映射標簽,支持對象關係組建維護。
- 提供xml標簽,支持編寫動態sql。
2.初涉Mybatis
2.1環境搭建
-
使用navicat創建一個資料庫
-
新建項目
-
導入依賴
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.9</version> </dependency>
2.2、創建一個模塊(項目)
-
編寫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"> <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/mybatis?&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai"/> <property name="username" value="root"/> <property name="password" value="18227022334a"/> </dataSource> </environment> </environments> <mappers> <mapper resource="Mapper/UserMapper.xml"/> </mappers> </configuration> <!--註:xml文件中使用&符號需要改成使用 & 代替-->
-
編寫mybatis工具類(封裝):
public class MybatisUtils { private static SqlSessionFactory sqlSessionFactory; //第一步:獲取SqlSessionFactory對象 static { try { String resource = "mybatis-config.xml";//路徑寫對 InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { e.printStackTrace(); } } //通過SqlSessionFactory對象獲取SqlSession對象 //SqlSession:SqlSession 提供了在資料庫執行 SQL 命令所需的所有方法。你可以通過 SqlSession 實例來直接執行已映射的 SQL 語句。 public static SqlSession getSqlSession(){return sqlSessionFactory.openSession();}//當傳遞一個true參數時表示自動提交事務 }
-
編寫
-
編寫實體類
//註;實體類的屬性名必須與資料庫的列名一致,否則無法進行匹配從而出現數據為空的現象,當然也可以在Mapper.xml配置中通過編寫一個resultMap來進行映射,此時名字可以不相同 public class User { private int id; private String username; private String password; public User() { } public User(int id, String username, String password) { this.id = id; this.username = username; this.password = password; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
-
編寫Mapper介面(Dao)
public interface UserMapper { public List<User> getUser(); }
-
編寫Mapper配置文件
//註:每個Mapper文件都需要在mybatis-config.xml文件中去註冊 //<mapper resource="Mapper/UserMapper.xml"/> //相當於Mapper的介面實現類 <?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:命名空間,指定要實現的Mapper介面,路徑映射 <mapper namespace="com.study.dao.UserMapper"> //id:唯一匹配於Mapper介面的方法名 //resultType:返回類型,必須寫全限定名稱 <select id="getUser" resultType="com.study.pojo.User"> select * from user; </select> </mapper>
-
-
測試
public class UserMapperTest { @Test public void test(){ //獲得sqlSession對象 SqlSession sqlSession = MybatisUtils.getSqlSession(); //獲取UserMapper的對象以調用介面內的方法 UserMapper userMapper = sqlSession.getMapper(UserMapper.class); //調用方法 List<User> users = userMapper.getUser(); //遍歷結果集 for (User user : users) { System.out.println(user.getId() + " " + user.getUsername() + " " + user.getPassword()); } //關閉sqlSessoin sqlSession.close(); } }
2.3、使用Mybatis的三個重要類
-
SqlSessionFactoryBuilder:
可以重用 SqlSessionFactoryBuilder 來創建多個 SqlSessionFactory 實例,但最好還是不要一直保留著它,以保證所有的 XML 解析資源可以被釋放給更重要的事情
-
SqlSessionFactory:
SqlSessionFactory 一旦被創建就應該在應用的運行期間一直存在,沒有任何理由丟棄它或重新創建另一個實例,最簡單的就是使用單例模式或者靜態單例模式
-
SqlSession:
SqlSession 的實例不是線程安全的,因此是不能被共用的,每次打開一個 SqlSession,記得關閉它, 這個關閉操作很重要
3.Mybatis的CRUD簡單實現
3.1、通過id查找用戶:
-
介面:
public User getUserById(int id);
-
Mapper配置文件
<select id="getUserById" resultType="com.study.pojo.User"> select * from user where id=#{id}; </select>
-
測試
SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User user = userMapper.getUserById(1); sqlSession.close();
3.2、插入用戶:
-
介面:
public void insertUser(User user);
-
Mapper配置文件
<insert id="insertUser"> insert into user values (#{id},#{username},#{password}); </insert>
-
測試
SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); userMapper.insertUser(new User(5,"老六","1234")); sqlSession.commit(); sqlSession.close(); //註:在Mybatis中增刪改查找都需要使用事務,必須手動提交事務如果沒有配置自動管理事務的情況下
3.3、根據id更改用戶名字:
-
介面:
public void insertUser(User user);
-
Mapper配置文件
<insert id="insertUser"> insert into user values (#{id},#{username},#{password}); </insert>
-
測試
//當有多個參數的時候,可以使用@Param()註解指定名字 //public void updateName(@Param("id") int id,@Param("username") String username); SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); userMapper.updateName(3,"王五"); sqlSession.commit(); sqlSession.close();
3.4、 根據id刪除用戶:
-
介面:
public void insertUser(User user);
-
Mapper配置文件
<insert id="insertUser"> insert into user values (#{id},#{username},#{password}); </insert>
-
測試
SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); userMapper.deleteUserById(5); sqlSession.commit(); sqlSession.close();
3.5、好用的Map
當介面中傳遞的對象有多個的時候,除了可以使用·@Param()註解之外,我們常常使用一個不算規範但是特別好用的方式,那就是傳遞Map
-
介面:
public void updateNameById(Map<String,Object> map);
-
mapper配置文件:
<update id="updateNameById" > update user set username=#{username} where id=#{id}; </update>
-
調用:
SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); Map<String,Object> map=new HashMap(); map.put("username","戰三"); map.put("id",2); userMapper.updateNameById(map); sqlSession.commit(); sqlSession.close();
總結:
- 當介面只傳遞一個參數的時候:可以直接在mapper文件使用,且不需要與參數名字對應
- 當需要多個參數的時候:
- 如果有一個實體類正好與之對應,那麼可以傳遞一個對象,然後在mapper文件中通過#{對象.屬性}調用
- 通過使用註解的方式@Param()指定名字
- 通過傳遞一個Map
3.6、模糊查詢
-
方式一:在java代碼在傳遞“%”
userMapper.getUserLike("%張%");
-
方式二:在mapper文件的sql語句中實現
<select id="getUserLike" resultType="com.study.pojo.User"> select * from user where username like "%"#{name}"%" ; </select>
4.配置解析
MyBatis 的配置文件包含了會深深影響 MyBatis 行為的設置和屬性信息。 配置文檔的頂層結構如下:
- configuration(配置)
- properties(屬性)
- settings(設置)
- typeAliases(類型別名)
- typeHandlers(類型處理器)
- objectFactory(對象工廠)
- plugins(插件)
- environments(環境配置)
- environment(環境變數)
- transactionManager(事務管理器)
- dataSource(數據源)
- environment(環境變數)
- databaseIdProvider(資料庫廠商標識)
- mappers(映射器)
4.1、environments 環境配置
MyBatis 可以配置成適應多種環境,不過要記住:儘管可以配置多個環境,但每個 SqlSessionFactory 實例只能選擇一種環境。
environments 元素定義瞭如何配置環境。
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="..." value="..."/>
</transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
<!--如果要切換環境只需要更改環境default,改成需要使用的環境的id-->
<environment id="test">
</environment>
</environments>
註意點:
- 預設使用的環境 ID(比如:default="development")。
- 每個 environment 元素定義的環境 ID(比如:id="development")。
- 事務管理器的配置(比如:type="JDBC")。
- 數據源的配置(比如:type="POOLED")。
4.2、properties(屬性)
可以從外部的資源環境讀取,也可以在內部定義,可以動態的替換環境中的配置信息
-
外部資源文件引入:
-
db.properties
driver=com.mysql.cj.jdbc.Driver url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai username=root password=11111111
-
引入:
<properties resource="db.properties"> </properties>
-
-
內部使用property定義:
<properties resource="org/mybatis/example/config.properties"> <property name="username" value="dev_user"/> <property name="password" value="F2Fa3!33TYyg"/> </properties>
設置好的屬性可以動態替換:
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
註:如果同時使用了外部資源文件和內部定義的方式那麼會使用外部資源文件。
4.3、typeAliases(類型別名)
類型別名可為 Java 類型設置一個縮寫名字,降低冗餘的全限定類名書寫
-
方式一:使用具體的全限定類名
<typeAliases> <typeAlias alias="Author" type="domain.blog.Author"/> <typeAlias alias="Blog" type="domain.blog.Blog"/> </typeAliases>
註:設定之後在其他使用domain.blog.Author的地方就可以使用Author代替
-
方式二:使用實體類包名
<typeAliases> <package name="domain.blog"/> </typeAliases>
註:通過這種方式設定之後,在該包下所有的實體類的全限定類名可以使用該類名的首字母小寫來代替,比如User使用user代替
總結:兩種方式各有優劣,當類比較少的時候可以使用第一種方式,當類比較多的時候可以使用第二種方式,如果想在使用第二種方式的同時給特點的類設定指定名字,可以使用註解的方式,且如果同時使用了第一種方式和第二種方式那麼使用兩個別名都正確。
@Alias("author")
public class Author {}
4.4、settings(設置)
1.常用的一些設置:
設置名 | 描述 | 有效值 | 預設值 |
---|---|---|---|
cacheEnabled | 全局性地開啟或關閉所有映射器配置文件中已配置的任何緩存。 | true | false | true |
lazyLoadingEnabled | 延遲載入的全局開關。當開啟時,所有關聯對象都會延遲載入。 特定關聯關係中可通過設置 fetchType 屬性來覆蓋該項的開關狀態。 |
true | false | false |
useGeneratedKeys | 允許 JDBC 支持自動生成主鍵,需要資料庫驅動支持。如果設置為 true,將強制使用自動生成主鍵。儘管一些資料庫驅動不支持此特性,但仍可正常工作(如 Derby)。 | true | false | False |
mapUnderscoreToCamelCase | 是否開啟駝峰命名自動映射,即從經典資料庫列名 A_COLUMN 映射到經典 Java 屬性名 aColumn。 | true | false | False |
logImpl | 指定 MyBatis 所用日誌的具體實現,未指定時將自動查找。 | SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING | 未設置 |
日誌:
- STDOUT_LOGGING (mybatis預設)
- LOG4J (掌握)
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- SLF4J
- NO_LOGGING:
4.5、mappers(映射器)
-
作用:告訴 MyBatis 到哪裡去找映射文件
-
實現方式:
-
方式一:resource(推薦)
<!-- 使用相對於類路徑的資源引用 --> <mappers> <mapper resource="org/mybatis/builder/AuthorMapper.xml"/> <mapper resource="org/mybatis/builder/BlogMapper.xml"/> <mapper resource="org/mybatis/builder/PostMapper.xml"/> </mappers>
-
方式二:url(極不推薦)
<!-- 使用完全限定資源定位符(URL) --> <mappers> <mapper url="file:///var/mappers/AuthorMapper.xml"/> <mapper url="file:///var/mappers/BlogMapper.xml"/> <mapper url="file:///var/mappers/PostMapper.xml"/> </mappers>
-
方式三:class
<!-- 使用映射器介面實現類的完全限定類名 --> <mappers> <mapper class="org.mybatis.builder.AuthorMapper"/> <mapper class="org.mybatis.builder.BlogMapper"/> <mapper class="org.mybatis.builder.PostMapper"/> </mappers>
註:
- 介面必須和mapper配置文件在同一個包下
- 介面和mapper配置文件的名字必須相同
-
方式四:name
<!-- 將包內的映射器介面實現全部註冊為映射器 --> <mappers> <package name="org.mybatis.builder"/> </mappers>
註:
- 介面必須和mapper配置文件在同一個包下
- 介面和mapper配置文件的名字必須相同
-
4.6、其他(暫時瞭解)
- typeHandlers(類型處理器)
- objectFactory(對象工廠)
- plugins(插件)(有用)
- Mybatis PageHelper分頁插件
- Mybatis通用Mapper插件
- Mybatis Plus插件
- 代碼生成插件mybatis-generator
5.拓展:當屬性名和列名不一致
假設存在一個資料庫user表,欄位如下:
一個User實體類如下:
public class User {
private int id;
private String username;
private String password;
}
那麼當查詢結果集為User類型的時候,會出現姓名為空的問題。
解決方式:
-
方式一:在sql語句中使用別名
select id ,name as usernaem,password from user;
-
方式二:resultMap結果映射
<resultMap id="userMap" type="user"> <id property="id" column="id"/> <result property="username" column="name"></result> <result property="password" column="password"></result> </resultMap> <select id="getUserAll" resultMap="userMap"> select * from user ; </select>
-
resultMap
元素是 MyBatis 中最重要最強大的元素。 -
ResultMap 的設計思想是,對簡單的語句做到零配置,對於複雜一點的語句,只需要描述語句之間的關係就行了。
-
ResultMap
的優秀之處——你完全可以不用顯式地配置它們,如上面的resultMap可以改為
<resultMap id="userMap" type="user"> <result property="username" column="name"></result> </resultMap>
即只需要顯示的配置不匹配的情況即可。
註:其實第一種方法使用別名的本質上還是使用了resultMap的映射,因為在這些情況下,MyBatis 會在幕後自動創建一個
ResultMap
,再根據屬性名來映射列到 JavaBean 的屬性上。 -
6.日誌
6.1、日誌工廠
-
當我們操作資料庫出現錯誤時,需要藉助一些手段進行排錯
- 之前:sout,debug
- 現在:日誌
-
日誌類別:
- STDOUT_LOGGING(掌握)(預設的標準日誌工廠)
- LOG4J(掌握)
- LOG4J2
- SLF4J
- JDK_LOGGING
- COMMONS_LOGGING
- NO_LOGGING
-
配置日誌:
<settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
使用了日誌之後,我們就可以查看到非常多的信息
6.2、Log4j
-
介紹:
- Log4j是Apache的一個開源項目,通過使用Log4j,我們可以控制日誌信息輸送的目的地是控制台、文件、GUI組件等
- 我們也可以控制每一條日誌的輸出格式;
- 通過定義每一條日誌信息的級別,我們能夠更加細緻地控制日誌的生成過程。
- 可以通過一個配置文件來靈活地進行配置,而不需要修改應用的代碼。
-
log4j:
-
導入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.layout=org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=%p:%c%n%m%l%n%m #文件輸出的相關設置 log4j.appender.file=org.apache.log4j.FileAppender log4j.appender.file.File=d:/log4jFile/mybatis.log log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l %F %p %m%n log4j.appender.file.MaxFileSize=10mb #控制日誌的輸出鑒別 log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG
-
在mybatis-config中配置log4j的使用
<settings> <setting name="logImpl" value="LOG4J"/> </settings>
-
7.分頁
思考:為什麼要分頁
- 減少數據的處理量
7.1、 limit分頁:
select * from user limit startIndex,pagesize;
select *from user limit pagesize; -- 當只有一個參數的時候,從第一個數據開始
7.2、 RowBounds分頁(不推薦使用)
-
介面:
public List<User> getUser();
-
mapper.xml:
<select id="getUser" resultType="com.study.pojo.User"> select * from user ; </select>
-
實現:
SqlSession sqlSession = MybatisUtils.getSqlSession(); RowBounds rowBounds=new RowBounds(1,2); List<User> list = sqlSession.selectList("com.study.dao.UserMapper.getUser", null, rowBounds); for (Object user : list) { System.out.println(user.toString()); }
7.3、分頁插件
使用PageHelper分頁插件,https://pagehelper.github.io/,瞭解一下即可,如有需要再看使用文檔。
8.註解開發
- 使用註解來映射簡單語句會使代碼顯得更加簡潔
- 但對於稍微複雜一點的語句,Java 註解不僅力不從心,還會讓你本就複雜的 SQL 語句更加混亂不堪
- 如果需要做一些很複雜的操作,最好用 XML 來映射語句。
使用建議:除非是很簡單的操作,否則儘量建議使用xml方式完成,一般而言我們都會使用xml。
8.1、常用的註解
-
@Insert:
插入記錄的時候麻煩的一點是主鍵如何生成,對此基本上有三種方案,分別是手動指定(應用層)、自增主鍵(數據層單表)、選擇主鍵(數據層多表)。(如想瞭解可以查)
@Insert("insert into user values (#{id},#{username},null)")
-
@Delete:
@Delete("delete from user where id=#{id};")
-
@Updata:
@Update(" update user set username=#{username} where id=#{id};")
-
@select:
@Select("select * from user")
-
@Param:
在介面中傳遞多個參數的時候可以指定
public void updateName(@Param("id") int id,@Param("username") String username);
註:
- 基本類型或者String類型需要加上
- 引用類型不需要加
- 如果只有一個基本類型的參數可以不加
- 在mapper.xml中使用的就是它指定的名字
-
@Results, @Result:
當使用select標簽時,如果查詢的欄位與當前實體類不能進行很好的匹配那麼需要我們進行一個映射
@Results(id = "userMap", value = { @Result(id=true, column = "id", property = "id"), @Result(column = "username", property = "username"), @Result(column = "passwd", property = "passwd"), }) @Select("SELECT * FROM t_user WHERE id=#{id}") User loadByIdResultMap(Long id);
-
@ResultMap:
如果以及存在一個@Results,那麼可以通過@ResultMap指定id名字去引用它
@ResultMap("userMap") @Select("SELECT * FROM t_user WHERE id=#{id}") User loadByIdResultMapReference(Long id);
8.2、使用註解開發
-
介面:
public interface UserMapper { @Select("select * from user") public List<User> getUser(); @Update(" update user set username=#{username} where id=#{id};") public void updateName(@Param("id") int id,@Param("username") String username); @Insert("insert into user values (#{id},#{username},null)") public void insertUser(User user); @Delete("delete from user where id=#{id};") public void deleteUserById(int id); }
-
mybatis-config.xml註冊介面:
<mappers> <mapper class="com.study.dao.UserMapper"/> </mappers>
-
測試:
SqlSession sqlSession = MybatisUtils.getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> user = userMapper.getUser(); //userMapper.insertUser(new User(6,"隨便","123123")); //userMapper.updateName(2,"還行"); //userMapper.deleteUserById(1); for (User user1 : user) { System.out.println(user1); } sqlSession.commit(); sqlSession.close();
本質:反射機制實現
底層:動態代理
8.3、關於#{}和${}的比較
- 前者是占位,在使用時會將傳入的字元串加上引號當做一個整體,${}是拼接,在使用的時候會將傳入的字元串不做處理直接拼接
- 使用#{}時會進行預編譯可以防止sql註入,使用${}不會預編譯不可以防止sql註入
- 使用時#{}是編譯好SQL後語句再去取值,${}取值以後再去編譯SQL語句
- 建議:一般能用#{}就不用${}
8.4、當註解開發和xml開發同時使用
註:雖然註解和xml配置文件可以同時使用,但是如果在介面中的同一個方法上既使用了註解,有在xml文件中進行了配置,也就是對同一個方法同時使用了註解配置文件兩種方式,那麼程式會報錯。
9.Lombok
Lombok項目是一個java庫,它可以自動插入到編輯器和構建工具中,增強java的性能。不需要再寫getter、setter或equals方法,只要有一個註解,你的類就有一個功能齊全的構建器、自動記錄變數等等.
-
常用註解:
- Data:整合了Getter、Setter、ToString、EqualsAndHashCode、無參構造函數註解。
- Getter:快速構建Getter方法。
- Setter:快速構建Setter方法。
- ToString:快速將當前對象轉換成字元串類型,便於log
- EqualsAndHashCode:快速進行相等判斷
- NonNull:判斷變數(對象)是否為空。
- AllArgsConstructor:快速構建全部參數的構造函數
- NoArgsConstructor:快速構建無參構造函數
-
Lombok的使用:
-
在IDEA中安裝Lombok插件
-
在項目中導入lombok的jar包
<dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.16</version> </dependency>
-
使用:
@Data @AllArgsConstructor @NoArgsConstructor public class Persosn { private String name; private int age; }
註:由於使用@Data只含有無參構造函數,使用要結合@AllArgsConstructor使用,但是使用了@AllArgsConstructor之後@Data的無參構造函數就會消失,使用需要再搭配@NoArgsConstructor使用。
-
註:缺點:無法承載有各種參數的構造函數,但是我們可以手動的去添加。
10.複雜查詢
10.1、模擬場景環境搭建
- 新建項目,導入相關依賴
- 配置文件的編寫
- 新建實體類Student和Teacher
- 建立對應的Mapper介面
- 建立對應的Mapper配置文件
- 註冊配置文件
- 測試環境搭建是否成功
10.2、多對一實際應用:關聯
比如多個學生對應一個老師,就是多對一
實體類如下:
@Data
public class Student {
private int id;
private String name;
private Teacher teacher;//多個學生一個老師
}
@Data
public class Teacher {
private int id;
private String name;
}
實現關鍵:association
10.2.1、嵌套查詢
<resultMap id="Student_t" type="student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<association property="teacher" column="tid" javaType="teacher" select="getTeacher"/>
</resultMap>
<select id="getStudent" resultMap="Student_t">
select * from student ;
</select>
<select id="getTeacher" resultType="teacher">
select * from teacher where id=#{id}
</select
通過嵌套一個子查詢的方式,通過學生的tid去找到對應的老師
10.2.2、嵌套結果
<resultMap id="Student_t2" 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>
<select id="getStudent2" resultMap="Student_t2">
select s.id sid,s.name sname,t.id tid ,t.name tname from student s,teacher t where s.tid=t.id;
</select>
通過對結果集直接進行映射
10.3、一對多實際應用:集合
比如一個老師有多個學生,對於老師而言就是一對多
實體類如下:
@Data
public class Student {
private int id;
private String name;
private int tid;
}
@Data
public class Teacher {
private int id;
private String name;
private List<Student> students;
}
實現關鍵:collection
10.3.1、嵌套查詢
<resultMap id="teacher_s1" type="teacher">
<result property="id" column="id"/>
<result property="name" column="name"/>
<collection property="students" ofType="student" column="id" select="getStudent" javaType="ArrayList"/>
</resultMap>
<select id="getTeacher" resultMap="teacher_s1">
select * from teacher t;
</select>
<select id="getStudent" resultType="student">
select * from student where tid=#{id};
</select>
10.3.2、嵌套結果
<resultMap id="teacher_s2" type="teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="students" ofType="student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result column="tid" property="tid"/>
</collection>
</resultMap>
<select id="getTeacher1" resultMap="teacher_s2">
select s.id sid,s.name sname,t.id tid ,t.name tname from student s,teacher t where s.tid=t.id;
</select>
10.4、小結
- 關聯:association
- 集合:collection
- javaType:用來指定實體類中的屬性的類型
- ofType:用來指定映射到List或者集合中的pojo類型,泛型中的約束類型
附:面試高頻問題;
- MySql引擎
- InnoDB底層原理
- 索引
- 索引優化
11.動態SQL
11.1、簡單介紹
動態 SQL 是 MyBatis 的強大特性之一。如果你使用過 JDBC 或其它類似的框架,你應該能理解根據不同條件拼接 SQL 語句有多痛苦,例如拼接時要確保不能忘記添加必要的空格,還要註意去掉列表最後一個列名的逗號。利用動態 SQL,可以徹底擺脫這種痛苦。
種類:
- if
- choose (when, otherwise)
- trim (where, set)
- foreach
11.2、實例項目的搭建
-
在資料庫中根據以下實體類創建blog表
@Data public class Blog { private String id; private String title; private String author; private Date createTime; private int views; }
拓展:實體類中的id類型是String,在具體的一個項目中其實我們可以使用UUID這個類來隨機生成一個唯一的ID,工具類簡單封裝如下:
public class IDUtils { public static String getId(){ return UUID.randomUUID().toString().replace("-",""); } }
-
新建項目,導入相關依賴
-
配置文件的編寫
<settings> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings>
註:開啟這個設置可以自動將資料庫的下劃線映射到實體類的駝峰命名屬性
-
新建實體類Blog
-
建立對應的Mapper介面
-
建立對應的Mapper配置文件
-
註冊配置文件
-
測試環境搭建是否成功
11.3動態SQL的使用
11.3.1、if
- if提供了條件判斷的功能,可以根據情況決定是否要追加語句。
<select id="queryBlog" resultType="blog">
select * from blog where 1=1
<if test="id != null">
and id=#{id}
</if>
<if test="title != null">
and title =#{title}
</if>
<if test="author!='李四'">
and author =#{author}
</if>
<if test="views > 800 ">
and views>#{views}
</if>
</select>
註:第一個where條件後面加一個1=1是為了方便後面的每一個判斷條件追加sql的第一個能寫and
11.3.2、choose (when, otherwise)
- 有時候,我們不想使用所有的條件,而只是想從多個條件中選擇一個使用。針對這種情況,MyBatis 提供了 choose 元素,它有點像 Java 中的 switch 語句。
<select id="queryBlog" resultType="blog">
select * from blog
<where>
<choose>
<when test="id != null">
id=#{id}
</when>
<when test="title != null">
and title =#{title}
</when>
<when test="author!='李四'">
and author =#{author}
</when>
<otherwise>
and views>#{views}
</otherwise>
</choose>
</where>
</select>
- 註:當同時滿足多個when的時候,按照順序選擇拼接,比如第一個when滿足那麼即是後面的滿足了也不會拼接,然後都不滿足那麼拼接otherwise裡面的內容,另外otherwise不是必須的
11.3.3、trim (where, set)
-
where:where 元素只會在子元素返回任何內容的情況下才插入 “WHERE” 子句。而且,若子句的開頭為 “AND” 或 “OR”,where 元素也會將它們去除。
-
set:set 元素可以用於動態包含需要更新的列,忽略其它不更新的列
<update id="updateAuthorIfNecessary"> update Author <set> <if test="author != null">author=#{author},</if> <if test="title != null">title=#{title},</if> <if test="views >= 0">views=#{views}</if> </set> </update>
-
trim:
-
trim包含的屬性;
- prefix:自定義首碼
- prefixOverrides:需要被移除的首碼
- suffix:自定義尾碼
- suffixOverrides:需要被移除的尾碼
-
可以自定義標簽的替換方式,比如與where等同效果的格式如下:
<trim prefix="WHERE" prefixOverrides="AND |OR "> ... </trim>
prefixOverrides 屬性會忽略通過管道符分隔的文本序列(註意此例中的空格是必要的)。上述例子會移除所有 prefixOverrides 屬性中指定的內容,並且插入 prefix 屬性中指定的內容。
-
與set等同效果的格式如下:
<trim prefix="SET" suffixOverrides=","> ... </trim>
-
11.3.4、foreach
-
foreach 元素的功能非常強大,它允許你指定一個集合,聲明可以在元素體內使用的集合項(item)和索引(index)變數。它也允許你指定開頭與結尾的字元串以及集合項迭代之間的分隔符。
-
可以將任何可迭代對象(如 List、Set 等)、Map 對象或者數組對象作為集合參數傳遞給 foreach。當使用可迭代對象或者數組時,index 是當前迭代的序號,item 的值是本次迭代獲取到的元素。當使用 Map 對象(或者 Map.Entry 對象的集合)時,index 是鍵,item 是值。
<select id="queryBlog" resultType="blog"> select * from blog <where> <foreach collection="names" item="name" open="author in (" close=")" separator=","> #{name} </foreach> </where> </select>
測試:
@Test public void test() { SqlSession sqlSession = MybatisUtils.getSqlSession(); BlogMapper blogMapper = sqlSession.getMapper(BlogMapper.class); ArrayList<String> names = new ArrayList<>(); names.add("張三"); names.add("李四"); Map<String,List> map=new HashMap<>(); map.put("names",names); List<Blog> blogs = blogMapper.queryBlog(map); for (Blog blog : blogs) { System.out.println(blog); } sqlSession.close(); }
11.3.5、sql片段
sql片段的作用就是可以將一部分sql代碼抽取出來,然後可以對他進行一個復用。
<select id="queryBlog" resultType="blog">
select * from blog
<where>
<include refid="choose"></include>
</where>
</select>
<sql id="choose">
<choose>
<when test="id != null">
id=#{id}
</when>
<when test="title != null">
and title =#{title}
</when>
<when test="author!='李四'">
and author =#{author}
</when>
</choose>
</sql>
註意事項:
- 儘量不要使sql片段太過複雜
11.3.6、小結
動態sql本質上就是拼接sql語句
建議:
- 我們可以在mysql中去寫寫出我們需要的sql語句,然後再去拼接
12.Mybatis緩存
12.1、簡介
- 什麼是緩存
- 存儲在記憶體中的臨時數據
- 將經常查詢的信息從記憶體中複製一份到緩存,當再次查找時就直接從緩存中查找,從而提高效率,解決高併發的性能問題
- 使用緩存的好處
- 減少資料庫的交互次數,減小系統的開銷,提高效率
- 什麼樣的資料庫可以使用緩存
- 查詢比較頻繁並且不經常改動的資料庫
12.2、Mybatis緩存
mybatis本身有兩種緩存,分別為一級緩存和二級緩存
-
一級緩存:
- MyBatis預設開啟了一級緩存,一級緩存是在 SqlSession 層面進行緩存的。即,同一個 SqlSession ,多次調用同一個 Mapper 和同一個方法的同一個參數,只會進行一次資料庫查詢,然後把數據緩存到緩衝中,以後直接先從緩存中取出數據,不會直接去查資料庫。
-
二級緩存:
- 二級緩存需要手動開啟,他是基於namespce級別的緩存
- 為了提高拓展性,mybatis定義了緩存介面Cache,我們可以通過該介面自定義二級緩存
12.3、一級緩存
- 一級緩存也叫本地緩存:SqlSession
- 在同一個SqlSession之間查詢到的數據會放到本地的緩存中
- 之後如果想要獲取數據自己從本地緩存中取
測試步驟:
-
開啟日誌
<settings> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
-
測試在同一個SqlSession期間查詢相同的數據
-
查看日誌輸出
緩存失效的情況:
-
查詢不一樣的數據
-
增刪改操作,由於可能會改變原來的數據,所有緩存也會失效
-
查詢不同的Mapper.xml
-
手動清除緩存
sqlSession.clearCache();
-
緩存會使用最近最少使用演算法(LRU, Least Recently Used)演算法來清除不需要的緩存。
-
緩存不會定時進行刷新(也就是說,沒有刷新間隔)
小結:一級緩存是預設開啟的,也關閉不掉,只能手動清理或者設置,一級緩存其實就相當於一個Map
12.4、二級緩存
- 二級緩存也叫全局緩存,它的出現是為瞭解決一級緩存的作用域太低
- 它是基於namespace級別的緩存,一個命名空間,對應一個二級緩存
- 機制:
- 一個SqlSession查詢一條數據,這個數據會被放到一級緩存中;
- 當這個SqlSession斷開的時候,一級緩存就會消失,為了能夠繼續保存,我們就產生了二級緩存
- 當其他的SqlSession查詢數據時,就可以先從二級緩存中查詢
- 不同的mapper.xml文件查出的數據會放到自己的緩存(map)之中
測試步驟:
-
開啟二級緩存設置;
<setting name="cacheEnabled" value="true"/> <!--註:二級緩存是預設開啟的,但是我們一般會顯示的寫出來-->
-
在要使用二級緩存的mapper.xml配置文件中開啟緩存:
<!--直接開啟--> <cache/> <!--當然還可以給緩存設置一些屬性--> <cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/> <!--該緩存的 清除緩存策略:FIFO(最近最少使用) 刷新間隔:60s 最多存儲:結果對象或列表的 512 個引用 返回的對象被認為是只讀的 -->
-
測試
小結:
-
在使用二級緩存的時候可能會報錯:
Error serializing object. Cause: java.io.NotSerializableException
解決:原因是沒有將實體類序列化,所以直接將類序列化即可
-
在mapper.xml文件中,對於查找標簽都可以手動設置是否開啟緩存
useCache="true"
-
緩存清除的策略:
LRU
– 最近最少使用:移除最長時間不被使用的對象。FIFO
– 先進先出:按對象進入緩存的順序來移除它們。SOFT
– 軟引用:基於垃圾回收器狀態和軟引用規則移除對象。WEAK
– 弱引用:更積極地基於垃圾收集器狀態和弱引用規則移除對象。
-
所有的數據都會先存到一級緩存,只有當會話提交或者關閉才會提交到二級緩存
12.5、緩存原理
12.6、自定義緩存(瞭解)
註:一般可以採用redis來做緩存!!