MyBatis學習大全(狂神秦疆版)
一、MyBatis
1.什麼是Mybaits
- 概念:MyBatis 是一款優秀的持久層框架
- 它支援自定義 SQL、儲存過程以及高階對映。
- MyBatis 免除了幾乎所有的 JDBC 程式碼以及設定引數和獲取結果集的工作。
- MyBatis 可以通過簡單的 XML 或註解來配置和對映原始型別、介面和 Java POJO(Plain Old Java Objects,普通老式 Java 物件)為資料庫中的記錄。
2.持久化
資料持久化
- 持久化就是將程式的資料在持久狀態和瞬時狀態轉化的過程
- 記憶體:斷電即失
- 資料庫(Jdbc),io檔案持久化。
- 生活:冷藏、罐頭。
為什麼需要需要持久化?
- 有一些物件,不能讓他丟掉。
- 記憶體太貴了
3.持久層
Dao層,Service層,Controller層....
- 完成持久化工作的程式碼塊
- 層界限十分明顯
4.為什麼需要Mybatis?
- 方便
- 傳統的JDBC程式碼太複雜了。簡化。框架。自動化。
- 不用Mybatis也可以。更容易上手。技術沒有高低之分·
- 優點:
- 簡單易學
- 靈活
- sql和程式碼的分離,提高了可維護性。
- 提供對映標籤,支援物件與資料庫的orm欄位關係對映。
- 提供物件關係對映標籤,支援物件關係組建維護
- 提供xml標籤,支援編寫動態sql。
二、第一個Mybatis程式
思路:搭建環境-->匯入Mybatis-->編寫程式-->測試!
1.搭建環境
搭建資料庫
新建專案
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.Aaron</groupId> <artifactId>MyBatis-Study</artifactId> <packaging>pom</packaging> <version>1.0-SNAPSHOT</version> <modules> <module>mybatis-01</module> </modules> <!--匯入依賴--> <dependencies> <!--資料庫驅動--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> </dependency> <!--MyBatis--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.9</version> </dependency> <!--junit--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> </project>
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?useSSL=trie&useUnicode=true&characterEncoding=UTF-8&serverTime=Asia/shanghai"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="org/mybatis/example/BlogMapper.xml"/>
</mappers>
</configuration>
- 編寫mybatis工具類
public class MyBatisUtils {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
//獲取sqlSessionFactory物件
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//既然有了 SqlSessionFactory,顧名思義,我們可以從中獲得 SqlSession 的例項。
// SqlSession 提供了在資料庫執行 SQL 命令所需的所有方法。
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession();
}
}
3.編寫程式碼
- 實體類
public class user {
private int id;
private String name;
private String password;
public user() {
}
public user(int id, String name, String password) {
this.id = id;
this.name = name;
this.password = password;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "user{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
- Dao介面
package com.Aaron.dao.User;
import java.util.List;
import com.Aaron.pojo.User;
/**
* @Author: Alskaboo
* @Date: 2022-04-16 15:08
**/
public interface UserDao {
List<User> getUserList();
}
- 介面實現類由原來的impl轉換為一個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">
<!--名稱空間=繫結一個對應的Dao/Mapper介面-->
<mapper namespace="com.Aaron.dao.User.UserDao">
<select id="getUserList" resultType="com.Aaron.pojo.User">
select * from mybatis.user;
</select>
</mapper>
4.測試
註冊配置檔案mapper
junit測試
public class UserMapperTest {
@Test
public void test(){
//第一步:獲取SqlSession物件
SqlSession sqlSession = MyBatisUtils.getSqlSession();
//方式一執行SQL
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();
//遍歷表格
for (User user : userList) {
System.out.println(user);
}
//關閉SqlSession
sqlSession.close();
}
}
遇到的問題:
- 配置檔案沒有註冊
- 繫結介面錯誤
- 方法名不對
- 返回型別不對
- Maven匯出資源
<!--在build中配置resources,來防止我們資源匯出失敗的問題-->
<build>
<!-- **.xml寫在src找不到問題解決方案 -->
<resources>
<resource>
<!-- directory:指定資原始檔的位置 -->
<directory>src/main/java</directory>
<includes>
<!-- “**” 表示任意級目錄 “*”表示任意任意檔案 -->
<!-- mvn resources:resources :對資源做出處理,先於compile階段 -->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<!-- filtering:開啟過濾,用指定的引數替換directory下的檔案中的引數(eg. ${name}) -->
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
三、CRUD(增刪改查)
1、namespace
namespace中的包名和Mapper介面名字一致!
2、select
選擇,查詢語句;
- id:就是對應的namespace中的方法名;
- resultType: Sql語句執行的返回值!
- parameterType :引數型別!
public interface UserMapper {
List<User> getUserList();
}
<select id="getUserList" resultType="com.Aaron.pojo.User">
- 編寫介面
/**
* 獲取全部資訊
* @return
*/
List<User> getUserList();
- 編寫對應的mapper中的SQL語句
<select id="getUserList" resultType="com.Aaron.pojo.User">
select *
from mybatis.user;
</select>
- 測試
public class UserMapperTest {
@Test
public void test() {
//第一步:獲取SqlSession物件
SqlSession sqlSession = MyBatisUtils.getSqlSession();
try{
//方式一:getMapper
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();
//方式二:
// List<User> userList = sqlSession.selectList("com.Aaron.dao.User.UserMapper.getUserList");
//遍歷表格
for (User user : userList) {
System.out.println(user);
}
}catch (Exception e){
e.printStackTrace();
}finally {
//關閉SqlSession
sqlSession.close();
}
}
3、Insert
- 編寫介面
/**
* insert一個使用者
* @param user
* @return
*/
int addUser(User user);
- 編寫對應的mapper中的SQL語句
<insert id="addUser" parameterType="com.Aaron.pojo.User">
insert into mybatis.user(id, name, password) VALUE (#{id}, #{name}, #{password});
</insert>
- 測試
@Test
public void addUser(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int res = mapper.addUser(new User(4, "小黃", "1314"));
if (res>0){
System.out.println("插入成功");
}else {
System.out.println("插入失敗");
}
//提交事務
sqlSession.commit();
sqlSession.close();
}
4、update
- 編寫介面
/**
* 修改使用者
* @param user
* @return
*/
int updateUser(User user);
- 編寫對應的mapper中的SQL語句
<update id="updateUser" parameterType="com.Aaron.pojo.User">
update mybatis.user
set name=#{name},
password=#{password}
where id = #{id};
</update>
- 測試
@Test
public void updateUser(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.updateUser(new User(4,"小紅","2020211891"));
sqlSession.commit();
sqlSession.close();
}
5、Delete
- 編寫介面
/**
*
* @param id
* @return
*/
int deleteUser(int id);
- 編寫對應的mapper中的SQL語句
<delete id="deleteUser" parameterType="int">
delete
from mybatis.user
where id = #{id};
</delete>
- 測試
@Test
public void deleteUser(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.deleteUser(4);
sqlSession.commit();
sqlSession.close();
}
注意點:
- 增刪改需要提交事務!
6、分析錯誤
- 標籤不要匹配錯
- resource繫結mapper,需要使用路徑!
- 程式配置檔案必須符合規範!
- NullPointerException,沒有註冊到資源!
- 輸出的xml檔案中存在中文亂碼問題!
- maven資源沒有匯出問題!
7、萬能Map
假設,我們的實體類,或者資料庫中的表,欄位或者引數過多,我們應當考慮使用Map!
//萬能Map
int addUser2(Map<String, Object> map);
<insert id="addUser2" parameterType="map">
insert into mybatis.user(id, name, password) VALUE (#{userid}, #{username}, #{password});
</insert>
@Test
public void addUser2(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put("userid",5);
map.put("username","Aaron");
map.put("password","171452");
mapper.addUser2(map);
sqlSession.commit();
sqlSession.close();
}
Map傳遞引數,直接在sql中取出key即可!【parameterType="map"】
物件傳遞引數,直接在sql中取物件的屬性即可!【parameterType="Object"】
只有一個基本型別引數的情況下,可以直接在sql中取到!
多個引數用Map,或者註解!
8、思考題
模糊查詢怎麼寫?
- Java程式碼執行的時候,傳遞萬用字元%%
List<User> userList = mapper.getUserLike("%李%");
- 在sql拼接中使用萬用字元!
select * from mybatis.user where name like "%"#{value}"%"
四、配置解析
1、核心配置檔案
- mybatis-config.xml
- MyBatis的配置檔案包含了會深深影響MyBatis行為的設定和屬性
configuration(配置)
properties(屬性)
settings(設定)
typeAliases(類型別名)
typeHandlers(型別處理器)
objectFactory(物件工廠)
plugins(外掛)
environments(環境配置)
environment(環境變數)
transactionManager(事務管理器)
dataSource(資料來源)
databaseIdProvider(資料庫廠商標識)
mappers(對映器)
2、環境配置(environments)
- MyBatis 可以配置成適應多種環境
- 不過要記住:儘管可以配置多個環境,但每個 SqlSessionFactory 例項只能選擇一種環境。
學會使用配置多套執行環境!
Mybatis預設的事務管理器就是JDBC ,連線池: POOLED
3、屬性(properties)
我們可以通過properties屬性來實現引用配置檔案
這些屬性可以在外部進行配置,並可以進行動態替換。你既可以在典型的 Java 屬性檔案中配置這些屬性,也可以在 properties 元素的子元素中設定。【db.properties】
編寫一個配置檔案
db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8&serverTime=Asia/shanghai
username=root
password=root
在核心配置檔案中映入
<!--引入外部配置檔案-->
<properties resource="db.properties ">
<property name="username" value="root" />
<property name="password" value="root"/>
</properties>
- 可以直接引入外部檔案
- 可以在其中增加一些屬性配置
- 如果兩個檔案有同一個欄位,優先使用外部配置檔案的!
4、類型別名(typeAliases)
- 類型別名可為 Java 型別設定一個縮寫名字。
- 它僅用於 XML 配置,意在降低冗餘的全限定類名書寫。
<!--給實體類起別名-->
<typeAliases>
<typeAlias type="com.Aaron.pojo.User" alias="User"/>
</typeAliases>
也可以指定一個包名,MyBatis 會在包名下面搜尋需要的 Java Bean
在沒有註解的情況下,會使用 Bean 的首字母小寫的非限定類名來作為它的別名
<typeAliases>
<package name="com.Aaron.pojo"/>
</typeAliases>
在實體類比較少的時候,使用第一種方式。
如果實體類十分多,建議使用第二種。
第一種可以DIY別名,第二種則不行,如果非要改,需要在實體上增加註解
@Alias("user")
pub1ic class user {
}
5、對映器(mappers)
MapperRegistry:註冊我們的Mapper檔案
<!--每一個Mapper均需要註冊-->
<mappers>
<mapper resource="com/Aaron/dao/UserMapper.xml"/>
<!-- <package name="com.Aaron.dao"/>-->
<!-- <mapper class="com.Aaron.dao"/>-->
</mappers>
6、生命週期和作用域
生命週期和作用域,是至關重要的,因為錯誤的使用會導致非常嚴重的併發問題。
sqlSessionFactoryBuilder:
- 一旦建立了SqlSessionFactory,就不再需要它了
- 區域性變數
sqlSessionFactory:
- 資料庫連線池
- sqlSessionFactory一旦被建立就應該在應用的執行期間一直存在,沒有任何理由丟棄它或重新建立另一個例項。
- 因此 SqlSessionFactory的最佳作用域是應用作用域
- 單例模式或靜態單例模式
sqlSession:
- 連線到連線池的一個請求!
- sqlSession的例項不是執行緒安全的,因此是不能被共享的,所以它的最佳的作用域是請求或方法作用域。
- 用完之後需要趕緊關閉,否則資源被佔用!
這裡面的每一個Mapper,代表每一個業務!
五、ResultMap
1、資料庫中的欄位
新建一個專案,但是實體類欄位不一樣
public class User {
private int id;
private String name;
private String pwd;
}
測試出現問題
select * from mybatis.user where id = #{id};
//型別處理器
select id,name,password from mybatis.user where id = #{id};
解決方法:
- 起別名
<select id="getUserById" parameterType="int" resultType="User">
select id, name, password as pwd
from mybatis.user
where id = #{id};
</select>
2、ResultMap
結果集對映
id name password
id name pwd
<!--結果集對映-->
<resultMap id="UserMap" type="User">
<!--column資料庫中的欄位,property實體類中的欄位名-->
<result column="password" property="pwd"/>
</resultMap>
<select id="getUserById" parameterType="int" resultMap="UserMap">
select *
from mybatis.user
where id = #{id};
</select>
- resultMap元素是MyBatis 中最重要最強大的元素
- ResultMap的設計思想是,對於簡單的語句根本不需要配置顯式的結果對映,而對於複雜一點的語句只需要描述它們的關係就行了。
六、日誌
1、日誌工廠
如果資料庫出現了異常,我們需要排除,日誌就是最好的助手!
曾經:sout debug
現在:日誌工廠!
- SLF4J
- LOG4J(重點)
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING(重點)
- NO_LoGGING
在Mybatis中具體使用哪個,在設定中實現!
STDOUT_LOGGING(重點)
在mybatis核心配種檔案中,配置我們的日誌!
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
2、LOG4J
什麼是LOG4J?
- Log4j是Apache的一個開源專案,通過使用Log4j,我們可以控制日誌資訊輸送的目的地是控制檯、檔案、GUI元件
- 我們也可以控制每一條日誌的輸出格式;
- 通過定義每一條日誌資訊的級別,我們能夠更加細緻地控制日誌的生成過程。
- 通過一個配置檔案來靈活地進行配置,而不需要修改應用的程式碼。
- 匯入LOG4J的Jar包
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
-
log4j的properties
### 配置根 ### 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=[%c] - %m%n ### 配置輸出到檔案,且大小到達指定尺寸的時候產生一個新的檔案 ### log4j.appender.file=org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/Aaron.log log4j.appender.file.MaxFileSize=10KB 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.Statement=DEBUG log4j.logger.java.ResultSet=DEBUG log4j.logger.java.PreparedStatement=DEBUG
-
log4j的配置實現
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
- log4j的使用
- 簡單測試
- 匯入包import org.apache.log4j.Logger;
- 日誌物件,引數為當前類的class
static Logger logger = Logger.getLogger(UserMapperTest.class);
- 日誌級別
logger.info("info:進入了testLog4j");
logger.debug("debug:進入了testLog4j");
logger.error("error:進入了testLog4j");
七、分頁
為什麼要分頁:減少資料的處理量
1、使用Limit分頁
語法:SELECT * from user limit startIndex,pageSize;
SELECT * from user limit 4; #[0,n]
使用Mybatis實現分頁,核心SQL
- 介面
List<User> getUserByLimit(HashMap<String, Integer> map);
- Mapper.XML
<select id="getUserByLimit" parameterType="map" resultMap="UserMap">
select * from mybatis.user limit #{startIndex},#{pageSize}
</select>
- 測試
static Logger logger = Logger.getLogger(UserMapperTest.class);
@Test
public void getUserByLimit(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("startIndex",0);
map.put("pageSize",2);
List<User> userList = mapper.getUserByLimit(map);
for (User user : userList) {
System.out.println(user);
}
}
2、RowBounds分頁
不在使用SQL實現分頁
- 介面
List<User> getUserByRowBounds();
- mapper.xml
<select id="getUserByRowBounds" resultMap="UserMap">
select *
from mybatis.user
</select>
- 測試
3、分頁外掛
Mybatis外掛
八、使用註解開發
1、面向介面程式設計
- 註解在介面上實現
@Select("select * from user")
List<User> getUserList();
- 需要核心配置檔案中繫結介面!
<!--繫結介面-->
<mappers>
<mapper class="com.Aaron.dao.UserMapper"/>
</mappers>
- 測試
本質:反射機制
實現底層:動態代理!
2、Mybatis詳細的執行流程
狂神說Mybatis16集,底層原理
3、CRUD
我們可以在工具類建立的時候實現自動提交事務!
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession(true);//true
}
編寫介面
@Select("select * from user")
List<User> getUserList();
@Select("select * from user where id = #{id}")
User getUserById(@Param("id")int id);
@Insert("insert into user(id,name,password) values (#{id}, #{name}, #{password})")
int addUser(User user);
@Update("update user set name=#{name},password=#{password} where id=#{id}")
int updateUser(User user);
@Delete("delete from user where id = #{id}")
int deleteUser(@Param("id") int id);
測試
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
// User userById = mapper.getUserById(1);
// System.out.println(userById);
// mapper.addUser(new User(12,"小周","1235"));
// mapper.updateUser(new User(5,"小洋","10086"));
// mapper.deleteUser(1);
sqlSession.close();
}
【注意:我們必須要講介面註冊繫結到我們的核心配置檔案中!】
<!--繫結介面-->
<mappers>
<mapper class="com.Aaron.dao.UserMapper"/>
</mappers>
4、關於@Param()註解
- 基本型別的引數或者String型別,需要加上
- 引用型別不需要加
- 如果只有一個基本型別的話,可以忽略,但是建議大家都加上!
- 我們在SQL中引用的就是我們這裡的@Param()中設定的屬性名!
#{} 和 ${}的區別
九、Lombok外掛
使用步驟:
-
在IDEA中安裝Lombok外掛!
-
在專案中匯入lombok的jar包
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency
- 使用註解
@Getter and @Setter
@FieldNameConstants
@ToString
@EqualsAndHashCode
@AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
@Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
@Data
@Builder
@SuperBuilder
@Singular
@Delegate
@Value
@Accessors
@Wither
@With
@SneakyThrows
@val
@var
experimental @var
@UtilityClass
Lombok config system
解釋:
@Data 無參構造,get、set、tostring.hashcode,equals
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode
@ToString
十、多對一處理
1、測試環境搭建
-
匯入lombok
-
匯入實體類Teacher(id,name)、Student(id, name,tid)
-
建立Mapper介面
-
建立Mapper.xml檔案
-
在核心配置檔案中繫結註冊我們的Mapper介面或者檔案!
-
測試是否成功
2、按照查詢巢狀處理
<!--
思路:
1.查詢所有的學生資訊
2.根據查詢出來的學生的tid.尋找對應的老師!
map
-->
<resultMap id="StudentTeacher" type="Student">
<!--複雜的屬性,單獨處理
物件(老師):association
集合(學生):collection
-->
<association property="teacher" column="tid" javaType="Teacher" select="getTeacherById"/>
</resultMap>
<select id="getStudent" resultMap="StudentTeacher">
select *
from mybatis.student;
</select>
<select id="getTeacherById" resultType="Teacher">
select *
from mybatis.teacher
where id = #{id}
</select>
@Test
public void testStudent(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> studentList = mapper.getStudent();
for (Student student : studentList) {
System.out.println(student);
}
sqlSession.close();
}
3、按照查詢巢狀處理
<!--按照結果巢狀處理-->
<select id="getStudent2" resultMap="StudentTeacher2">
select s.id sId, s.name sName, t.id tId,t.name tName
from mybatis.student s,
mybatis.teacher t
where s.tid = t.id;
</select>
<resultMap id="StudentTeacher2" type="Student">
<result property="id" column="sId"/>
<result property="name" column="sName"/>
<association property="teacher" javaType="Teacher">
<result property="name" column="tName"/>
<result property="id" column="tId"/>
</association>
</resultMap>
@Test
public void testStudent2(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> studentList = mapper.getStudent2();
for (Student student : studentList) {
System.out.println(student);
}
sqlSession.close();
}
十一、一對多處理
比如:一個老師擁有多個學生!
1、環境搭建
實體類
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
private int id;
private String name;
//學生需要關聯一個老師
private int tid;
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
private int id;
private String name;
private List<Student> students;
}
2、按照結果巢狀處理
<select id="getTeacher" resultMap="TeacherStudent">
select s.id sid, s.name sname, t.name tname, t.id tid
from mybatis.teacher t,
mybatis.student s
where s.tid = t.id
and t.id = #{tid}
</select>
<resultMap id="TeacherStudent" type="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<!--複雜的屬性,我們需要單獨處理
物件:association 集合:collection
javaType="”指定屬性的型別!
集合中的泛型資訊,我們使用ofType獲取
-->
<collection property="students" ofType="Student">
<result property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
3、按照查詢巢狀處理
<select id="getTeacher2" resultMap="TeacherStudent2">
select * from mybatis.teacher where id = #{tid}
</select>
<resultMap id="TeacherStudent2" type="Teacher">
<collection property="students" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId" column="id"/>
</resultMap>
<select id="getStudentByTeacherId" resultType="Student">
select * from mybatis.student where id = #{tid}
</select>
小結
-
關聯– association【多對一】
-
集合- collection【一對多】
-
javaType & ofType
-
JavaType 用來指定實體類中屬性的型別
-
ofType 用來指定對映到List或者集合中的pojo型別,泛型中的約束型別!
注意點:
- 保證SQL的可讀性,儘量保證通俗易懂
- 注意一對多和多對一中,屬性名和欄位的問題!
- 如果問題不好排查錯誤,可以使用日誌,建議使用Log4j
面試高頻
- Mysql引擎:Innodb MyISAM MEMORY MERGE
- lnnoDB底層原理
- 索引
- 索引優化!
十二、動態SQL
什麼是動態SQL:動態SQL就是指根據不同的條件生成不同的SQL語句
利用動態SQL這一特性可以徹底擺脫這種痛苦。
動態sQL元素和JSTL或基於類似 XML的文字處理器相似。在MyBatis之前的版本中,有很多元素需要花時間瞭解。MyBatis 3大大精簡了元素種類,現在只需學習原來一半的元素便可。MyBatis採用功能強大的基於OGNL的表示式來淘汰其它大部分元素。
if
choose (when,otherwise)
trim (where,set)
foreach
1、搭建環境
CREATE TABLE `blog`(
`id` VARCHAR(50) NOT NULL COMMENT '部落格id',
`title` VARCHAR(100) NOT NULL COMMENT '部落格標題',
`author` VARCHAR(30) NOT NULL COMMENT '部落格作者',
`create_time` datetime NOT NULL COMMENT '建立時間',
`views` INT(30) NOT NULL COMMENT '瀏覽量'
)ENGINE=INNODB DEFAULT CHARSET=UTF8
建立一個基礎工程
- 導包
- 編寫配置檔案
- 編寫實體類
@Data
@AllArgsConstructor
@NoArgsConstructor
public class blog {
private int id;
private String title;
private String author;
private Date createTime;
private int views;
}
- 編寫實體類對應Mapper介面和Mapper.XML檔案
插入資料
//插入資料
int addBolg(Blog blog);
<insert id="addBolg" parameterType="blog">
insert into mybatis.blog(id, title, author, create_time, views)
VALUE (#{id}, #{title}, #{author}, #{createTime}, #{views});
</insert>
@Test
public void addBlogTest(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Blog blog = new Blog();
blog.setId(IDUtils.getId());
blog.setTitle("Mybatis如此簡單");
blog.setAuthor("小菜");
blog.setCreateTime(new Date());
blog.setViews(2020211891);
mapper.addBolg(blog);
blog.setId(IDUtils.getId());
blog.setTitle("Java如此簡單");
mapper.addBolg(blog);
blog.setId(IDUtils.getId());
blog.setTitle("Spring如此簡單");
mapper.addBolg(blog);
blog.setId(IDUtils.getId());
blog.setTitle("微服務如此簡單");
mapper.addBolg(blog);
sqlSession.close();
}
2、If
List<Blog> queryBlogIf(Map map);
<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from mybatis.blog where 1=1
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and title = #{author}
</if>
</select>
test
@Test
public void queryBlogIf(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
map.put("title","Java如此簡單");
map.put("author","小菜");
List<Blog> blogs = mapper.queryBlogIf(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
3、choose (when,otherwise)
<select id="queryBlogChoose" resultType="blog" parameterType="map">
select * from mybatis.blog
<where>
<choose>
<when test="title != null">
title = #{title}
</when>
<when test="author != null">
and author = #{author}
</when>
<otherwise>
and views = #{views}
</otherwise>
</choose>
</where>
</select>
@Test
public void queryBlogChoose(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
map.put("title","Java如此簡單");
map.put("views",2020211890);
List<Blog> blogs = mapper.queryBlogChoose(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
4、trim (where,set)
where
<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
</select>
@Test
public void queryBlogIf(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
map.put("title","Java如此簡單");
map.put("author","小菜");
List<Blog> blogs = mapper.queryBlogIf(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
set
//修改
int updateBlog(Map map);
<update id="updateBlog" 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>
@Test
public void updateBlog(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
//map.put("title","微服務如此簡單");
map.put("author","小賀");
map.put("id", "53f0d40c77ca4b679711a0950d910591");
//map.put("views",2020211890);
mapper.updateBlog(map);
sqlSession.close();
}
5、sql語句
有的時候,我們可能會將一些功能的部分抽取出來,方便複用!
- 使用SQL標籤抽取公共的部分
<sql id="queryBlogIf_Head">
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>
- 在需要使用的地方使用Include標籤引用即可
<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<include refid="queryBlogIf_Head"></include>
</where>
</select>
注意事項:
- 最好基於單表來定義SQL片段!
- 不要存在where標籤
所謂的動態SQL,本質還是SQL語句,只是我們可以在SQL層面,去執行一個邏輯程式碼
6、foreach
List<Blog> queryBlogForeach(Map map);
<!--
select * from mybatis.blog where 1=1 and (id=1 or id=2 or id=3)
-->
<select id="queryBlogForeach" parameterType="map" resultType="blog">
select * from mybatis.blog
<where>
<foreach collection="ids" item="id" open="and (" close=")" separator="or">
id = #{id}
</foreach>
</where>
</select>
@Test
public void queryBlogForeach(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
ArrayList<Integer> ids = new ArrayList<>();
ids.add(1);
ids.add(2);
ids.add(3);
map.put("ids",ids);
List<Blog> blogs = mapper.queryBlogForeach(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
動態SQL就是在拼接SQL語句,我們只要保證SQL的正確性,按照SQL的格式,去排列組合就可以了
十三、快取
1、簡介
-
什麼是快取[Cache]?
- 存在記憶體中的臨時資料。
- 將使用者經常查詢的資料放在快取(記憶體)中,使用者去查詢資料就不用從磁碟上(關係型資料庫資料檔案)查詢,從快取中查詢,從而提高查詢效率,解決了高併發(高併發,高可用,高效能)系統的效能問題。
-
為什麼使用快取?
- 減少和資料庫的互動次數,減少系統開銷,提高系統效率。
-
什麼樣的資料能使用快取?
- 經常查詢並且不經常改變的資料。【使用快取】
2、Mybatis快取
- MyBatis包含一個非常強大的查詢快取特性,它可以非常方便地定製和配置快取。快取可以極大的提升查詢效率。
- MyBatis系統中預設定義了兩級快取:一級快取和二級快取
- 預設情況下,只有一級快取開啟。(SqlSession級別的快取,也稱為本地快取)。
- 二級快取需要手動開啟和配置,他是基於namespace級別的快取。
- 為了提高擴充套件性,MyBatis定義了快取介面Cache。我們可以通過實現Cache介面來自定義二級快取
3、一級快取
- 一級快取也叫本地快取:
- 與資料庫同一次會話期間查詢到的資料會放在本地快取中。
- 以後如果需要獲取相同的資料,直接從快取中拿,沒必須再去查詢資料庫;
測試步驟:
- 開啟日誌!
- 測試在一個Sesion中查詢兩次相同記錄
- 檢視日誌輸出
4、二級快取
- 二級快取也叫全域性快取,一級快取作用域太低了,所以誕生了二級快取
- 基於namespace級別的快取,一個名稱空間,對應一個二級快取;
- 工作機制
- 一個會話查詢一條資料,這個資料就會被放在當前會話的一級快取中;
- 如果當前會話關閉了,這個會話對應的一級快取就沒了;但是我們想要的是,會話關閉了,一級快取中的資料被儲存到二級快取中;
- 新的會話查詢資訊,就可以從二級快取中獲取內容;
- 不同的mapper查出的資料會放在自己對應的快取(map)中;
步驟:
- 開啟全域性快取
<!--顯示開啟的全域性快取-->
<setting name="cacheEnabled" value="true"/>
- 在要使用二級快取的Mapper中開啟
<!--在當前的Mapper中開啟二級快取-->
<cache eviction="FIFO"
flushInterval="6_0000"
size="512"
readOnly="true"/>
-
測試
-
問題:我們需要將實體類序列化!否則就會報錯!
caused by: java.io.Notserializab1eException: com.kuang.pojo.user
-
小結:
- 只要開啟了二級快取,在同一個Mapper下就有效
- 所有的資料都會先放在一級快取中;
- 只有當會話提交,或者關閉的時候,才會提交到二級快取中!
5、快取原理
6、自定義快取-ehcache
導包!
<!--快取-->
<dependency>
<groupId>org.mybatis.caches</groupId>
<artifactId>mybatis-ehcache</artifactId>
<version>1.1.0</version>
</dependency>
在mapper中指定我們的快取實現
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
resources檔案下ehcache.xml
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
updateCheck="false">
<!--
diskStore:為快取路徑,ehcache分為記憶體和磁碟兩級,此屬性定義磁碟的快取位置。引數解釋如下:
user.home – 使用者主目錄
user.dir – 使用者當前工作目錄
java.io.tmpdir – 預設臨時檔案路徑
-->
<diskStore path="./tmpdir/Tmp_EhCache"/>
<!--
defaultCache:預設快取策略,當ehcache找不到定義的快取時,則使用這個快取策略。只能定義一個。
-->
<defaultCache
eternal="false"
maxElementsInMemory="10000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="259200"
memoryStoreEvictionPolicy="LRU"/>
<!--
name:快取名稱。
maxElementsInMemory:快取最大數目
maxElementsOnDisk:硬碟最大快取個數。
eternal:物件是否永久有效,一但設定了,timeout將不起作用。
overflowToDisk:是否儲存到磁碟,當系統宕機時
timeToIdleSeconds:設定物件在失效前的允許閒置時間(單位:秒)。僅當eternal=false物件不是永久有效時使用,可選屬性,預設值是0,也就是可閒置時間無窮大。
timeToLiveSeconds:設定物件在失效前允許存活時間(單位:秒)。最大時間介於建立時間和失效時間之間。僅當eternal=false物件不是永久有效時使用,預設是0.,也就是物件存活時間無窮大。
diskPersistent:是否快取虛擬機器重啟期資料 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
diskSpoolBufferSizeMB:這個引數設定DiskStore(磁碟快取)的快取區大小。預設是30MB。每個Cache都應該有自己的一個緩衝區。
diskExpiryThreadIntervalSeconds:磁碟失效執行緒執行時間間隔,預設是120秒。
memoryStoreEvictionPolicy:當達到maxElementsInMemory限制時,Ehcache將會根據指定的策略去清理記憶體。預設策略是LRU(最近最少使用)。你可以設定為FIFO(先進先出)或是LFU(較少使用)。
clearOnFlush:記憶體數量最大時是否清除。
memoryStoreEvictionPolicy:可選策略有:LRU(最近最少使用,預設策略)、FIFO(先進先出)、LFU(最少訪問次數)。
FIFO,first in first out,這個是大家最熟的,先進先出。
LFU, Less Frequently Used,就是上面例子中使用的策略,直白一點就是講一直以來最少被使用的。如上面所講,快取的元素有一個hit屬性,hit值最小的將會被清出快取。
LRU,Least Recently Used,最近最少使用的,快取的元素有一個時間戳,當快取容量滿了,而又需要騰出地方來快取新的元素的時候,那麼現有快取元素中時間戳離當前時間最遠的元素將被清出快取。
-->
<cache
name="cloud_user"
eternal="false"
maxElementsInMemory="5000"
overflowToDisk="false"
diskPersistent="false"
timeToIdleSeconds="1800"
timeToLiveSeconds="1800"
memoryStoreEvictionPolicy="LRU"/>
</ehcache>
Redis資料庫來做快取!