柚子快報(bào)激活碼778899分享:尚硅谷MyBatis 基礎(chǔ)筆記
MyBatis 筆記
文章目錄
MyBatis 筆記【1】配置文件【2】JDK Logging【3】Log4j【4】Log4j2【5】SLF4j【6】整合日志【7】SQL參數(shù)【8】${} / #{} 區(qū)別【9】MyBatis中DML操作【10】MyBatis中DQL操作【11】分頁(yè)查詢(xún)【12】模糊查詢(xún)【13】別名【14】結(jié)果填充【15】接口綁定【16】主鍵回填【17】動(dòng)態(tài)SQL【18】常用注解【19】多表查詢(xún)【20】延遲加載【21】緩存【23】四大核心接口介紹及執(zhí)行流程【24】自定義插件【25】執(zhí)行器類(lèi)型【26】執(zhí)行原理
【1】配置文件
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
【2】JDK Logging
package com.bjsxt;
import java.util.logging.*;
/**
* JDK自帶的日志 存在于java.util.logging包中
*/
public class TestJDKLogging {
public static void main(String[] args) throws Exception {
// 創(chuàng)建日志處理對(duì)象
Logger logger = Logger.getLogger("JDKLogger");
// 設(shè)置級(jí)別
logger.setLevel(Level.FINEST);
// 創(chuàng)建控制臺(tái)日志處理器
ConsoleHandler consoleHandler = new ConsoleHandler();
// 設(shè)置格式
consoleHandler.setFormatter(new SimpleFormatter());
// 設(shè)置日志級(jí)別
consoleHandler.setLevel(Level.FINEST);
// 增加到日志處理器
logger.addHandler(consoleHandler);
/*// 獲取控制臺(tái)日志處理器
Handler[] handlers = logger.getHandlers();
// 數(shù)組的0下標(biāo)就是控制器處理器
Handler handler = handlers[0];
// 設(shè)置控制臺(tái)日志級(jí)別
handler.setLevel(Level.FINEST);*/
// 創(chuàng)建一個(gè)文件日志處理器, 參數(shù)就是保存日志信息的文件名
FileHandler fileHandler = new FileHandler("jdk_log.log");
// 設(shè)置日志內(nèi)容的格式
fileHandler.setFormatter(new SimpleFormatter());
// 設(shè)置日志級(jí)別
fileHandler.setLevel(Level.FINEST);
// 把創(chuàng)建好的文件日志處理器,增加到日志處理對(duì)象中
logger.addHandler(fileHandler);
// 日志處理器,可以把日志信息輸出到控制臺(tái)和文件中
logger.finest("最詳細(xì)的日志");
logger.fine("詳細(xì)日志");
logger.warning("警告日志");
logger.info("標(biāo)準(zhǔn)消息日志");
logger.severe("嚴(yán)重錯(cuò)誤日志");
}
}
【3】Log4j
# log4j中定義的級(jí)別:fatal(致命錯(cuò)誤) > error(錯(cuò)誤) >warn(警告) >info(普通信息) >debug(調(diào)試信息)>trace(跟蹤信息)
# 定義Log4j中的Logger工具中有什么handler。 第一個(gè)DEBUG代表的是日志的級(jí)別。 后續(xù)每個(gè)單詞是一個(gè)handler名稱(chēng)
log4j.rootLogger = DEBUG , console , D
# log4j.logger是固定的,a.b.c是命名空間的名字可以只寫(xiě)一部分。
# 是mybatis中要使用日志的時(shí)候,必須提供的配置。
# 代表sql配置文件的namespace相應(yīng)的sql,執(zhí)行的時(shí)候,是否輸出日志,及日志的級(jí)別。
# 如:namespace="a.b" , log4j.logger.a.b=XXX級(jí)別。 那么 a.b.*所有的sql運(yùn)行的時(shí)候,都輸出日志。建議
# 如:namespace="a.b" , log4j.logger.a=XXX級(jí)別, 那么 a.*.*所有的sql運(yùn)行,都輸出日志。不建議
# 如果有多個(gè)namespace都需要輸出日志。定義若干行即可
log4j.logger.a=TRACE
log4j.logger.a.b=TRACE
log4j.logger.a.c=TRACE
### console ###
# 開(kāi)始定義console名稱(chēng)的handler
# console handler的類(lèi)型
log4j.appender.console = org.apache.log4j.ConsoleAppender
# console handler輸出的日志到哪里。 System.out 控制臺(tái)
log4j.appender.console.Target = System.out
# console輸出日志的時(shí)候,格式是什么樣的。是Pattern格式。 正則表達(dá)式格式
log4j.appender.console.layout = org.apache.log4j.PatternLayout
# 具體的格式正則規(guī)則。 %p 線(xiàn)程|進(jìn)程 %-d 時(shí)間 {yyyy-MM-dd HH\:mm\:ss} 具體的格式 %C 類(lèi)名輸出日志的類(lèi)型名 %m 輸出的日志文本 %n回車(chē)
log4j.appender.console.layout.ConversionPattern = [%p] [%-d{yyyy-MM-dd HH\:mm\:ss}] %C.%M(%L) | %m%n
### log file ###
log4j.appender.D = org.apache.log4j.DailyRollingFileAppender
# 文件日志handler,記錄日志到哪一個(gè)文件
log4j.appender.D.File = D:/log4j.log
# 是否是追加寫(xiě)入到日志文件
log4j.appender.D.Append = true
# 只能升級(jí)別,不能降
# 名字是D的handler,自定義日志級(jí)別是什么。
log4j.appender.D.Threshold = INFO
log4j.appender.D.layout = org.apache.log4j.PatternLayout
log4j.appender.D.layout.ConversionPattern = [%p] [%-d{yyyy-MM-dd HH\:mm\:ss}] %C.%M(%L) | %m%n
package com.bjsxt;
import org.apache.log4j.Logger;
/**
* 測(cè)試Log4j
* 要求classpath下,必須有配置文件log4j.properties。且必須在classpath下。不能有其他目錄。
*/
public class TestLog4j {
public static void main(String[] args) {
Logger logger = Logger.getLogger(TestLog4j.class);
// 輸出各種日志
logger.info("info日志");
logger.warn("waring日志");
logger.error("error日志");
logger.debug("debug日志");
}
}
【4】Log4j2
package com.bjsxt;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
/**
* 測(cè)試Log4j2
*/
public class TestLog4j2 {
public static void main(String[] args) {
Logger logger = LogManager.getLogger(TestLog4j2.class);
// 輸出日志
logger.debug("debug信息");
logger.info("info");
logger.warn("waring");
logger.error("error");
logger.fatal("fatal");
}
}
【5】SLF4j
SLF4j是日志的接口聲明,不參與日志的具體實(shí)現(xiàn)。需要配合其他日志具體實(shí)現(xiàn)工具包才能進(jìn)行使用。每次添加其他日志工具包時(shí),不要忘記SLF4j整合這個(gè)日志的依賴(lài)。
SLF4J支持多種日志實(shí)現(xiàn),但是在項(xiàng)目中整合依賴(lài)最好只有一個(gè),否則會(huì)出現(xiàn)警告
# Log4j配置
log4j.rootLogger=ERROR, stdout
# log4j.logger是固定的,a.b.c是命名空間的名字可以只寫(xiě)一部分。
log4j.logger.a.b.c=TRACE
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
public class Test {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(Test.class);
logger.trace("trace");
logger.debug("debug");
logger.info("info");
logger.warn("warn");
logger.error("error");
}
}
【6】整合日志
MyBatis框架內(nèi)置日志工廠(chǎng)。日志工廠(chǎng)負(fù)責(zé)自動(dòng)加載項(xiàng)目中配置的日志。MyBatis支持以下日志,當(dāng)存在多個(gè)日志工具時(shí),嚴(yán)格按照從上往下順序使用,且只會(huì)使用一個(gè)
- SLF4J
- Apache Commons Logging
- Log4j 2
- Log4j (deprecated since 3.5.9)
- JDK logging
【7】SQL參數(shù)
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
insert into people values(default,#{suiyixie})
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
insert into tb_user(id, name) values(#{u1.id}, #{u2.name})
【8】${} / #{} 區(qū)別
#{} 被解析為?,用在設(shè)置列的值或條件值時(shí),也可以使用在分頁(yè)等需要設(shè)置具體值的情況
${} 表示字符串拼接,用在動(dòng)態(tài)設(shè)置表名和動(dòng)態(tài)設(shè)置列名的情況下
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
insert into ${tableName} (${idColumn}, ${nameColumn}) values (#{id}, #{name})
【9】MyBatis中DML操作
操作mapper標(biāo)簽SqlSession方法名新增
在MyBatis中,增刪改底層使用的邏輯是同一個(gè)。那么在沒(méi)有特定需求的時(shí)候,可以使用update實(shí)現(xiàn)增刪改的所有功能。也就是標(biāo)簽用update, 方法用update。
雖然方便,但不推薦。語(yǔ)義不明確,后期維護(hù)的時(shí)候,成本高。
【10】MyBatis中DQL操作
SqlSession方法名解釋說(shuō)明selectOne()查詢(xún)一行數(shù)據(jù)時(shí),返回值為Object。如果沒(méi)有查詢(xún)到返回Null,但是不能查詢(xún)到多行會(huì)報(bào)錯(cuò)。selectList()當(dāng)查詢(xún)多行數(shù)據(jù)時(shí),返回值為L(zhǎng)ist。如果沒(méi)有查詢(xún)到返回長(zhǎng)度為零的List對(duì)象。selectMap()查詢(xún)多行數(shù)據(jù)時(shí),把其中某列結(jié)果當(dāng)做key,每行結(jié)果為ValueselectCursor()使用游標(biāo)查詢(xún)時(shí)使用,在大量數(shù)據(jù)時(shí)可以代替分頁(yè)select()萬(wàn)能方法,需要自己定義結(jié)果處理器
package com.bjsxt.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
/**
* 工具類(lèi)型
*/
public final class MyBatisUtils {
// 會(huì)話(huà)工廠(chǎng)
private static SqlSessionFactory sqlSessionFactory;
/**
* 初始化代碼塊,只允許一次,類(lèi)加載時(shí)運(yùn)行
*/
static {
// 創(chuàng)建會(huì)話(huà)工廠(chǎng)
try {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis.cfg.xml"));
} catch (Exception e) {
e.printStackTrace();
// 拋出異常,代表工廠(chǎng)創(chuàng)建失敗,后續(xù)的獲取會(huì)話(huà),訪(fǎng)問(wèn)數(shù)據(jù)庫(kù),都不可使用。
// 讓代碼終止
throw new ExceptionInInitializerError(e);
}
}
/**
* 私有構(gòu)造方法,禁止外部創(chuàng)建對(duì)象
*/
private MyBatisUtils() {
}
/**
* 獲取工廠(chǎng)的工具方法
*
* @return
*/
public static SqlSessionFactory getFactory() {
return sqlSessionFactory;
}
}
package com.bjsxt;
import com.bjsxt.pojo.User;
import com.bjsxt.utils.MyBatisUtils;
import org.apache.ibatis.cursor.Cursor;
import org.apache.ibatis.executor.result.DefaultResultHandler;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* 查詢(xún)
*/
public class TestSelect {
/**
* 查詢(xún)方法
*
* selectOne selectList 最多 。 必須掌握
* selectMap 次之。 建議掌握
* selectCursor 再次。 了解
* select 最少。 聽(tīng)說(shuō)
* 上述方法的使用頻率,就是定義順序。
*/
/**
* 查詢(xún)多行數(shù)據(jù)
*/
/**
* 使用select方法查詢(xún)多行數(shù)據(jù)
* void select(String statement[, Object parameter], ResultHandler handler);
* ResultHandler參數(shù) - 處理查詢(xún)結(jié)果的處理器。查詢(xún)的結(jié)果從處理器中獲取。
* 是一個(gè)接口。只有唯一的一個(gè)方法 void handleResult(ResultContext context)
* ResultContext中,有方法,可以獲取一個(gè)對(duì)象。ResultContext,就是查詢(xún)的一行數(shù)據(jù)。
* 獲取的對(duì)象,就是這行數(shù)據(jù)轉(zhuǎn)換后的Java對(duì)象。
* MyBatis給接口ResultHandler提供了若干實(shí)現(xiàn)類(lèi),select方法的底層實(shí)現(xiàn)就是selectList。
* select方法,只能查詢(xún)多行數(shù)據(jù),且返回List集合。
* 如果需要做特殊定制處理,可以給接口增加新的實(shí)現(xiàn)類(lèi)。
* 使用的ResultHandler實(shí)現(xiàn)類(lèi)型模式是DefaultResultHandler
*
* 使用頻率最低。幾乎不用。
*/
@Test
public void testSelect() {
SqlSession session = MyBatisUtils.getFactory().openSession();
DefaultResultHandler resultHandler = new DefaultResultHandler();
session.select("user.mapper.selectAll", resultHandler);
// 查詢(xún)結(jié)果后,從ResultHandler中獲取結(jié)果
List list = resultHandler.getResultList();
list.forEach(obj -> {
System.out.println(obj);
});
session.close();
}
/**
* 使用selectCursor查詢(xún)多行數(shù)據(jù)
*
* MyBatis 中 selectCursor方法返回的游標(biāo),就是JDBC查詢(xún)結(jié)果游標(biāo)。
*
* 游標(biāo)特性: 在MyBatis中,使用Cursor游標(biāo)獲取結(jié)果前,不能關(guān)閉SqlSession。否則拋出異常。
* 1. 必須保證連接未關(guān)閉。
* 2. 必須保證會(huì)話(huà)未結(jié)束。 會(huì)話(huà)是一個(gè)持續(xù)的,有狀態(tài)的,連接或數(shù)據(jù)對(duì)象。
* 如: 連接會(huì)話(huà), Connection, 是一個(gè)持續(xù)的,不關(guān)閉一直可以使用。 有狀態(tài)的, connection創(chuàng)建后,關(guān)閉前,操作都是有狀態(tài)的,
* connection是跟著操作一起變化的, 是可能有數(shù)據(jù)變動(dòng)的。
* 如: 數(shù)據(jù)會(huì)話(huà), HttpSession。請(qǐng)求到來(lái),創(chuàng)建會(huì)話(huà),相應(yīng)返回,連接斷開(kāi),關(guān)閉。連接關(guān)閉,會(huì)話(huà)還存在。
*
* 為什么不能關(guān)閉會(huì)話(huà)后,再迭代Cursor。
* Cursor中數(shù)據(jù),不再Java的管理內(nèi)存中,在數(shù)據(jù)庫(kù)的管理內(nèi)存中。
* 每次迭代Cursor中的一條數(shù)據(jù),必須連接數(shù)據(jù)庫(kù),從數(shù)據(jù)庫(kù)管理的內(nèi)存中,把數(shù)據(jù)讀取到Java管理的內(nèi)存中,再使用。
*
* Cursor - 游標(biāo)
* JDBC查詢(xún)數(shù)據(jù)庫(kù)時(shí),返回的結(jié)果ResultSet中,保存查詢(xún)結(jié)果數(shù)據(jù)的,就是游標(biāo)。
* 查詢(xún)的SQL沒(méi)有變化。
*
* 不常用
* 查詢(xún)的數(shù)據(jù)結(jié)果數(shù)量特別多,對(duì)Java代碼運(yùn)行的電腦內(nèi)存壓力非常大,且對(duì)查詢(xún)結(jié)果只做部分處理時(shí),使用。
*/
@Test
public void testSelectCursor() {
Map
params.put("start", 100);
params.put("end", 1000);
SqlSession session = MyBatisUtils.getFactory().openSession();
// 查詢(xún)?nèi)繑?shù)據(jù)
Cursor
// 條件查詢(xún)部分?jǐn)?shù)據(jù)
Cursor
session.close();
cursor.forEach(user -> {
System.out.println(user);
});
cursor1.forEach(user -> {
System.out.println(user);
});
}
/**
* 使用 selectMap 查詢(xún)多行數(shù)據(jù)
*
* keyColumnName 參數(shù) - 使用哪一個(gè)字段的值作為返回結(jié)果Map集合的key。
*/
@Test
public void testSelectMap() {
// 使用字段name的值作為key
SqlSession session = MyBatisUtils.getFactory().openSession();
// 查詢(xún)?nèi)?/p>
Map
Map
params.put("start", 100);
params.put("end", 1000);
// 根據(jù)主鍵范圍查詢(xún)
Map
session.close();
System.out.println(map1.size());
for (Map.Entry
System.out.println("key = " + entry.getKey() + " ; value = " + entry.getValue());
}
System.out.println(map2.size());
for (Map.Entry
System.out.println("key = " + entry.getKey() + " ; value = " + entry.getValue());
}
}
/**
* 使用 selectList查詢(xún)多行數(shù)據(jù)
*
* 特性:
* 1. 查詢(xún)無(wú)結(jié)果,返回size() = 0的 集合對(duì)象
* 2. 查詢(xún)有結(jié)果,返回size() = 查詢(xún)結(jié)果行數(shù)的 集合對(duì)象。
*/
@Test
public void testSelectAll() {
SqlSession session = MyBatisUtils.getFactory().openSession();
List
System.out.println(users.size());
users.forEach(user -> {
System.out.println(user);
});
}
@Test
public void testSelectRangeId() {
Map
params.put("start", 10);
params.put("end", 100);
SqlSession session = MyBatisUtils.getFactory().openSession();
List
System.out.println(list1.size());
list1.forEach(u -> {
System.out.println(u);
});
params.put("start", -10);
params.put("end", -1);
List
System.out.println(list2.size());
list2.forEach(user -> {
System.out.println(user);
});
}
/**
* 查詢(xún)一行數(shù)據(jù)
* 調(diào)用方法 :
* 方法特性:
* 1. 查詢(xún)結(jié)果不存在,返回null
* 2. 查詢(xún)結(jié)果只有一行,返回對(duì)象
* 3. 查詢(xún)結(jié)果多于一行,拋出異常
*/
@Test
public void testSelectById() {
SqlSession session = MyBatisUtils.getFactory().openSession();
User user = session.selectOne("user.mapper.selectById", 1000);
System.out.println(user);
session.close();
}
@Test
public void testSelectByName() {
String name = "劉備";
SqlSession session = MyBatisUtils.getFactory().openSession();
User u1 = session.selectOne("user.mapper.selectByName", name);
System.out.println(u1);
name = "大小姐";
User u2 = session.selectOne("user.mapper.selectByName", name);
System.out.println(u2);
name = "張三";
User u3 = session.selectOne("user.mapper.selectByName", name);
System.out.println(u3);
}
}
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
select id, name
from tb_user
select id, name
from tb_user
where id between #{start} and #{end}
select id, name
from tb_user
where id = #{id}
select id, name
from tb_user
where name = #{name}
select count(1) from tb_student
【11】分頁(yè)查詢(xún)
RowBounds是MyBatis中提供的一種"假分頁(yè)"實(shí)現(xiàn)方式。對(duì)從數(shù)據(jù)庫(kù)中查詢(xún)到的結(jié)果進(jìn)行截取。所以如果數(shù)據(jù)庫(kù)中數(shù)據(jù)量特別大的時(shí)候可能會(huì)出現(xiàn)OOM等問(wèn)題
但是由于RowBounds不需要考慮底層數(shù)據(jù)庫(kù)是什么,且使用簡(jiǎn)單,所以對(duì)于一些數(shù)據(jù)量不是特別大的應(yīng)用還是有人選擇使用的
在SqlSession中select、selectMap、selectList中通過(guò)方法重載都提供了一個(gè)帶有RowBounds
/**
* 分頁(yè)查詢(xún):
* 分頁(yè)查詢(xún)的種類(lèi)|方式(面試題):
* 1. 物理分頁(yè),使用分頁(yè)查詢(xún)語(yǔ)法實(shí)現(xiàn)分頁(yè)查詢(xún)。如:MySQL數(shù)據(jù)庫(kù)中的limit; SqlServer數(shù)據(jù)庫(kù)中的top; Oracle數(shù)據(jù)庫(kù)中的rownum+子查詢(xún)
* 可移植性差,和數(shù)據(jù)庫(kù)耦合,因?yàn)椴煌臄?shù)據(jù)庫(kù)軟件,分頁(yè)語(yǔ)法不同。
* 相對(duì)效率更好,因?yàn)閿?shù)據(jù)庫(kù)的分頁(yè)是有特殊優(yōu)化的,是數(shù)據(jù)庫(kù)廠(chǎng)商優(yōu)化的。且網(wǎng)絡(luò)傳輸?shù)臄?shù)據(jù)少,更可靠。
* 2. 邏輯分頁(yè),不使用分頁(yè)查詢(xún)語(yǔ)法,是查詢(xún)?nèi)繑?shù)據(jù),在內(nèi)存中,使用某種邏輯,分頁(yè)返回。
* 特點(diǎn)和物理分頁(yè)相反。
* MyBatis中的分頁(yè)實(shí)現(xiàn):
* 1. 物理分頁(yè),使用分頁(yè)語(yǔ)法實(shí)現(xiàn)分頁(yè)查詢(xún)。
* 2. RowBounds分頁(yè),是邏輯分頁(yè)的一種實(shí)現(xiàn)。底層基于Cursor實(shí)現(xiàn)。
* 查詢(xún)后,依次遍歷Cursor,按照分頁(yè)的需求,找到對(duì)應(yīng)的若干數(shù)據(jù),之后關(guān)閉Cursor,返回分頁(yè)結(jié)果。
*/
/**
* RowBounds分頁(yè)
* selectList * 不推薦使用,相對(duì)效率較低。 */ @Test public void testRowBounds() { SqlSession session = MyBatisUtils.getFactory().openSession(); int page = 1; int rows = 3; RowBounds rowBounds = new RowBounds((page - 1) * rows, rows); List users.forEach(user -> { System.out.println(user); }); System.out.println("===================================================="); page = 7; rowBounds = new RowBounds((page - 1) * rows, rows); users = session.selectList("user.mapper.selectAll", null, rowBounds); users.forEach(user -> { System.out.println(user); }); } 功能實(shí)現(xiàn) package com.bjsxt.result; import com.bjsxt.pojo.Student; import java.io.Serializable; import java.util.List; import java.util.Objects; /** * 分頁(yè)查詢(xún)結(jié)果類(lèi)型 */ public class PageResult implements Serializable { // 當(dāng)前是第幾頁(yè) private int currPage; // 一共多少行數(shù)據(jù) private int total; // 當(dāng)前頁(yè)要顯示的數(shù)據(jù)集合 private List // 每頁(yè)多少行數(shù)據(jù) private int pageSize; /** * 增加推導(dǎo)屬性,一共多少頁(yè)。 可選 * JavaScript 中,可以使用Math.ceil(total / pageSize) 向上取整計(jì)算總計(jì)頁(yè)數(shù) */ public void setPages(int pages) { } public int getPages() { return total % pageSize == 0 ? total / pageSize : (total / pageSize + 1); } public PageResult() { } @Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; PageResult that = (PageResult) o; return currPage == that.currPage && total == that.total && pageSize == that.pageSize && Objects.equals(list, that.list); } public int getPageSize() { return pageSize; } public void setPageSize(int pageSize) { this.pageSize = pageSize; } @Override public int hashCode() { return Objects.hash(currPage, total, list); } public int getCurrPage() { return currPage; } public void setCurrPage(int currPage) { this.currPage = currPage; } public int getTotal() { return total; } public void setTotal(int total) { this.total = total; } public List return list; } public void setList(List this.list = list; } } package com.bjsxt.service; import com.bjsxt.param.MyParam; import com.bjsxt.result.PageResult; import java.util.Map; public interface StudentService { /** * 分頁(yè)查詢(xún) * @param param 查詢(xún)條件,由Servlet處理后傳入 * @return 自定義一個(gè)結(jié)果類(lèi)型返回。 * 分頁(yè)結(jié)果需要內(nèi)容有: * 1. 第幾頁(yè) * 2. 總計(jì)多少行數(shù)據(jù) * 3. 一共多少頁(yè) * 4. 當(dāng)前頁(yè)要顯示的數(shù)據(jù)集合 */ PageResult selectByPage(MyParam param); } package com.bjsxt.service.impl; import com.bjsxt.dao.StudentDao; import com.bjsxt.dao.impl.StudentDaoImpl; import com.bjsxt.param.MyParam; import com.bjsxt.pojo.Student; import com.bjsxt.result.PageResult; import com.bjsxt.service.StudentService; import java.util.List; /** * 服務(wù)邏輯實(shí)現(xiàn)類(lèi)型 */ public class StudentServiceImpl implements StudentService { // 需要的數(shù)據(jù)庫(kù)訪(fǎng)問(wèn)對(duì)象 private StudentDao studentDao = new StudentDaoImpl(); /** * 分頁(yè)查詢(xún) * @param param 查詢(xún)條件,由Servlet處理后傳入 * @return */ @Override public PageResult selectByPage(MyParam param) { // 查詢(xún)當(dāng)前頁(yè)要顯示的數(shù)據(jù)集合 List // 查詢(xún)總計(jì)多少行數(shù)據(jù) int total = studentDao.selectCount(param); // 根據(jù)查詢(xún)結(jié)果,創(chuàng)建要返回的結(jié)果 PageResult pageResult = new PageResult(); pageResult.setCurrPage(param.getPageNum()); pageResult.setPageSize(param.getPageSize()); pageResult.setList(students); pageResult.setTotal(total); // 返回 return pageResult; } } package com.bjsxt.controller; import com.bjsxt.param.MyParam; import com.bjsxt.result.PageResult; import com.bjsxt.service.StudentService; import com.bjsxt.service.impl.StudentServiceImpl; import com.fasterxml.jackson.databind.ObjectMapper; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** * 分頁(yè)查詢(xún)學(xué)生的Servlet 控制器 */ @WebServlet("/getStudent") public class StudentPageServlet extends HttpServlet { private StudentService studentService = new StudentServiceImpl(); /** * servlet 服務(wù)方法 * 參數(shù)字符集問(wèn)題: * 1. 檢查JSP的字符集 * 2. 檢查Servlet中的請(qǐng)求字符集 * 3. 檢查請(qǐng)求方式是否是POST。request.setCharacterEncoding只對(duì)請(qǐng)求體中的數(shù)據(jù)生效 * * HTTP協(xié)議的請(qǐng)求頭,默認(rèn)字符集永遠(yuǎn)是ISO-8859-1。除非修改Tomcat中的server.xml配置文件,增加URIEncoding配置 * 請(qǐng)求頭參數(shù),絕大多數(shù)處理方案是:解碼再編碼 * * @param req * @param resp * @throws ServletException * @throws IOException */ @Override protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); // 處理請(qǐng)求參數(shù), // 如果未傳遞參數(shù) http://localhost:80/,返回結(jié)果是null // 如果傳遞參數(shù)未賦值 http://localhost:80/pageNum=&pageSize&name&address,返回結(jié)果是空字符串 "" // 分頁(yè)的頁(yè)碼 String pageNum = req.getParameter("pageNum"); // 分頁(yè)后的每頁(yè)行數(shù) String pageSize = req.getParameter("pageSize"); // 姓名模糊查詢(xún)條件 String name = req.getParameter("name"); byte[] nameBytes = name.getBytes("ISO-8859-1"); // 把字符串按照指定字符集,解碼成字節(jié)數(shù)組 name = new String(nameBytes, "UTF-8"); // 把字節(jié)數(shù)組,按照指定的字符集,編碼成字符串。 // 地址模糊查詢(xún)條件 String address = req.getParameter("address"); address = new String(address.getBytes("ISO-8859-1"), "UTF-8"); // 維護(hù)查詢(xún)條件對(duì)象 MyParam param = new MyParam(); param.setName((name == null || "".equals(name) ? null : name)); param.setAddress((address == null || "".equals(address)) ? null : address); param.setPageNum((pageNum == null || "".equals(pageNum)) ? 1 : Integer.parseInt(pageNum)); param.setPageSize((pageSize == null || "".equals(pageSize)) ? 2 : Integer.parseInt(pageSize)); // 查詢(xún) PageResult pageResult = studentService.selectByPage(param); // 向客戶(hù)端輸出的是一個(gè)對(duì)象,使用JSON格式的字符串描述這個(gè)對(duì)象。 // 設(shè)置響應(yīng)頭 resp.setContentType("application/json; charset=UTF-8"); // 設(shè)置相應(yīng)輸出流中的字符集 resp.setCharacterEncoding("UTF-8"); // 使用Jackson,把Java對(duì)象轉(zhuǎn)換成JSON格式的字符串。 // 創(chuàng)建Jackson中的對(duì)象和JSON互相轉(zhuǎn)換的工具對(duì)象。 ObjectMapper objectMapper = new ObjectMapper(); // 把java對(duì)象,轉(zhuǎn)換成JSON格式的字符串 String json = objectMapper.writeValueAsString(pageResult); // PrintWriter resp.getWriter() // 所有的IO輸出流的 PrintWriter & PrintStream,都有方法print和println resp.getWriter().println(json); // 刷新輸出流的緩沖區(qū) resp.getWriter().flush(); } } <%@ page contentType="text/html;charset=UTF-8" language="java" %>
序號(hào) | 姓名 | 地址 | 操作 |
---|
【12】模糊查詢(xún)
/**
* 模糊查詢(xún)
* SQL: select 字段 from 表格 where 字段 like ?
* 條件是: _xxx xxx_ _xx_ %xxx xxx% %xxx% _xxx% %xxx_
* _ : 是一個(gè)字符
* % : 是若干字符
*
* MyBatis中 #{}內(nèi)不可以寫(xiě) '' 。所以模糊查詢(xún)需要做特殊處理。
* #{'%' + name + '%'}
*/
@Test
public void testLike3() {
/*
* 借助數(shù)據(jù)庫(kù)的字符串拼接函數(shù),實(shí)現(xiàn)模糊查詢(xún)。
* 缺點(diǎn)是: 需要了解數(shù)據(jù)庫(kù)的函數(shù),且不同的數(shù)據(jù)庫(kù)函數(shù)未必相同。
* 優(yōu)點(diǎn): 更加符合開(kāi)發(fā)邏輯。且沒(méi)有SQL注入隱患
* 推薦的模糊查詢(xún)方式
*/
String arg = "d";
SqlSession session = MyBatisUtils.getFactory().openSession();
List
users.forEach(user -> {
System.out.println(user);
});
session.close();
}
@Test
public void testLike2() {
/*
* 使用MyBatis中的${}實(shí)現(xiàn)字符串拼接??梢员苊鈪?shù)傳遞過(guò)程中的拼接問(wèn)題。
* 但是可能有SQL注入隱患。 String arg = "d%' or 1 = 1 or name like '%n";
*/
String arg = "d";
SqlSession session = MyBatisUtils.getFactory().openSession();
List
users.forEach(user -> {
System.out.println(user);
});
session.close();
}
@Test
public void testLike1() {
/*
* 需要傳遞參數(shù)時(shí),拼接必要的占位符,也就是 % 或 _
*/
String arg = "d";
SqlSession session = MyBatisUtils.getFactory().openSession();
List
users.forEach(user -> {
System.out.println(user);
});
session.close();
}
select id, name
from tb_user
where name like #{name}
select id, name
from tb_user
where name like '%${name}%'
select id, name
from tb_user
where name like concat('%', #{name}, '%')
【13】別名
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
MyBatis框架中內(nèi)置了一些常見(jiàn)類(lèi)型的別名。這些別名不需要配置
別名映射的類(lèi)型別名映射的類(lèi)型別名映射的類(lèi)型_bytebytestringStringdateDate_longlongbyteBytedecimalBigDecimal_shortshortlongLongbigdecimalBigDecimal_intintshortShortobjectObject_integerintintIntegermapMap_doubledoubleintegerIntegerhashmapHashMap_floatfloatdoubleDoublelistList_booleanbooleanfloatFloatarraylistArrayListbooleanBooleancollectionCollectioniteratorIterator
【14】結(jié)果填充
在MyBatis框架中,查詢(xún)結(jié)果處理方案(結(jié)果填充|結(jié)果映射)有若干種:
1. 自動(dòng)映射 auto mapping : 當(dāng)
如:表格字段 id, name。類(lèi)型 com.bjsxt.pojo.Xxx 屬性(field和property)是 id name。自動(dòng)映射。
當(dāng)字段名和屬性名不同時(shí),可以通過(guò)查詢(xún)語(yǔ)法的別名實(shí)現(xiàn)自動(dòng)映射。
優(yōu)勢(shì): 不需要做任何特殊的處理配置
缺點(diǎn): 要求表格的字段名稱(chēng)和類(lèi)型的屬性名稱(chēng)必須一致。 Java中的命名規(guī)范 駝峰。 數(shù)據(jù)庫(kù)命名規(guī)范 多單詞之間下劃線(xiàn)分隔,一般所有單詞全小寫(xiě)。
2. 手工配置映射 ResultMap映射。 首選
需要在SQL配置文件中定義標(biāo)簽
優(yōu)勢(shì): 一切自定義,靈活。
缺點(diǎn): 配置增加
3. 駝峰對(duì)下劃線(xiàn) 很少使用
因?yàn)镴ava命名習(xí)慣 駝峰; 數(shù)據(jù)庫(kù)命名習(xí)慣 下劃線(xiàn)。因?yàn)檫@種常見(jiàn)的命名習(xí)慣很通用。所以MyBatis提供自動(dòng)的駝峰對(duì)下劃線(xiàn)映射。
需要在mybatis.cfg.xml中開(kāi)啟這種功能。
優(yōu)勢(shì): 按照常見(jiàn)命名習(xí)慣自動(dòng)映射
缺點(diǎn): 要求表格命名和類(lèi)型命名必須要個(gè)遵循常見(jiàn)命名習(xí)慣,并一一對(duì)應(yīng)。
【15】接口綁定
MyBatis提供自動(dòng)的接口實(shí)現(xiàn)能力。稱(chēng)為接口綁定。
命名習(xí)慣:
每個(gè)框架或技術(shù),都有各自的命名習(xí)慣。
MyBatis技術(shù)中,數(shù)據(jù)訪(fǎng)問(wèn)層(持久層)接口命名習(xí)慣是 XxxMapper。
正式工作開(kāi)發(fā)中,數(shù)據(jù)訪(fǎng)問(wèn)層(持久層)接口命名習(xí)慣是 XxxDao, XxxDAO
接口綁定的規(guī)則:
1. 必要規(guī)則: 必須遵守的規(guī)則,有兩條
1.1 SQL配置文件namespace,必須和對(duì)應(yīng)的要生成實(shí)現(xiàn)的接口的全命名完全相同,大小寫(xiě)敏感。
1.2 SQL配置文件中,編寫(xiě)SQL語(yǔ)句的標(biāo)簽id,必須和對(duì)應(yīng)的接口中的方法名完全相同,大小寫(xiě)敏感。
2. 可選規(guī)則: 可以遵守的規(guī)則,遵守可以簡(jiǎn)化mybatis.cfg.xml中的配置。 有兩條
2.1 SQL配置文件存放的位置,和對(duì)應(yīng)的接口所在的包完全相同。
2.2 SQL配置文件的文件名(不包含后綴),和對(duì)應(yīng)的接口的類(lèi)型名完全相同。
遵守這兩個(gè)可選規(guī)則,可以在mybatis.cfg.xml中,使用
接口綁定中的參數(shù)傳遞:
1. 一個(gè)參數(shù)
1.1 傳遞簡(jiǎn)單參數(shù)。 SQL配置文件中的占位變量命名隨意。框架不檢查。
1.2 傳遞Map參數(shù)。 SQL配置文件中的占位變量名必須是Map中的key。
1.3 傳遞自定義類(lèi)型對(duì)象(POJO實(shí)體)。 SQL配置文件中的占位變量名必須和類(lèi)型的屬性名(property或field)一致。
2. 多個(gè)參數(shù)
注意: 使用注解了argM的形式就不能使用了,需要通過(guò)注解中名稱(chēng)或paramN的方式調(diào)用
2.1 傳遞多個(gè)簡(jiǎn)單參數(shù)。 要求SQL配置文件中的占位變量必須按照框架要求定義。
2.1.1 如果接口的方法參數(shù),使用的注解@Param("")定義參數(shù)名,則占位變量名必須是注解的屬性值,或param1,param2,param3等。 推薦的方式
2.1.2 如果接口的方法參數(shù),不使用注解,則占位變量名必須是 param1,param2或 arg0, arg1 等。不推薦的方式。面試可能問(wèn)。
2.2 傳遞多個(gè)參數(shù),帶有Map或自定義類(lèi)型對(duì)象。 要求SQL配置文件的占位變量必須按照框架要求定義。
2.2.1 如果接口方法參數(shù),使用注解,可以使用 #{注解屬性.key}|#{注解屬性.屬性名} 或者 #{param1.key} | #{param1.屬性名}
2.2.2 如果接口方法參數(shù),不使用注解,可以使用 #{arg0.key} | #{arg0.屬性名} 或者 #{param1.key} | #{param1.屬性名}
多參數(shù)傳遞
int insert1(@Param("id") Integer id, @Param("name") String name);
int insert2(Integer id, String name);
int insert3(@Param("myMap") Map
int insert4(Map
insert into tb_people(id, name) values(#{param1}, #{name})
insert into tb_people(id, name) values(#{arg0}, #{param2})
insert into tb_people(id, name) values(#{param1.id}, #{peo.name})
insert into tb_people(id, name) values(#{arg0.id}, #{param2.name})
【16】主鍵回填
insert into tb_people(id, name) values(default, #{name})
【17】動(dòng)態(tài)SQL
動(dòng)態(tài)SQL在MyBatis中是基于標(biāo)簽實(shí)現(xiàn)的。
1. if標(biāo)簽。判斷標(biāo)簽。根據(jù)某boolean值,或結(jié)果為boolean的表達(dá)式(布爾表達(dá)式)動(dòng)態(tài)判斷。
在MyBatis的SQL配置文件中,可以寫(xiě)OGNL表達(dá)式。語(yǔ)法類(lèi)似JSP中的EL表達(dá)式。在各種標(biāo)簽中使用,得到需要的結(jié)果。
2. choose標(biāo)簽。選擇標(biāo)簽。類(lèi)似java語(yǔ)法中是switch case。根據(jù)多個(gè)不同的判斷邏輯,選擇某一個(gè)分支。
3. trim標(biāo)簽。為標(biāo)簽體中的文本去除前后綴或增加前后綴。先刪除前后綴,再增加前后綴
4. where標(biāo)簽。用在SQL語(yǔ)句的where子句動(dòng)態(tài)處理中。如果標(biāo)簽中存在文本,則增加where子句。如果標(biāo)簽中不存在文本,則不增加where子句。
where標(biāo)簽,有動(dòng)態(tài)識(shí)別能力,如果標(biāo)簽內(nèi)的文本以and或者or開(kāi)頭,自動(dòng)刪除前綴and或or。
5. set標(biāo)簽。用在update語(yǔ)句中的標(biāo)簽。動(dòng)態(tài)增加set子句。 有自動(dòng)識(shí)別能力,如果標(biāo)簽內(nèi)的文本以','結(jié)尾,自動(dòng)刪除后綴','。
6. forEach標(biāo)簽。用于循環(huán)集合的標(biāo)簽。常用于,批量新增,范圍in|not in查詢(xún)等。
要循環(huán)的變量: 命名方式有
1. 使用注解@Param("名字"), 推薦使用
2. arg0, collection, list 可用于List, Collection
3. arg0, array 可用于 []
close="循環(huán)內(nèi)容的結(jié)束字符串" item="循環(huán)過(guò)程的變量名" separator="每次循環(huán)時(shí)間隔符">
close=")" item="a" separator=","> #{a}
循環(huán)遍歷后的結(jié)果文本是: (?, ?, ?)
7. bind 標(biāo)簽,用于綁定修改后的特定數(shù)據(jù)的。常用于模糊查詢(xún)處理。
8. sql和include標(biāo)簽。 sql標(biāo)簽,定義SQL語(yǔ)句片段。 include標(biāo)簽,引用定義好的SQL語(yǔ)法片段。
常用于定義字段列表,或復(fù)雜的判斷邏輯。
如:
十幾個(gè)if,+不同的查詢(xún)條件
if
select * from people where 1=1
and name=#{name}
and address=#{address}
choose
select id, name from tb_people
where 1 = 1
and id < 10
and id between 10 and 20
and id > 20
trim
select * from people
and name=#{name}
and address=#{address}
where
select * from people
and name=#{name}
and address=#{address}
set
update people
name=#{name},
address=#{address},
id=#{id}
where id = #{id}
foreach
select id, name from tb_people where id in
#{id}
insert into tb_people(id, name)
#{p.id},#{p.name}
/**
* 根據(jù)參數(shù),in查詢(xún)部分?jǐn)?shù)據(jù)
*
* @param ids
* @return
*/
List
/**
* 批量新增
*
* @param list
* @return
*/
int insertBatch(List
bind
select * from people where name like #{n}
sql 和 include
select
id,name,address
【18】常用注解
注解功能@Insert新增@Delete刪除@Update修改@Select查詢(xún)@Results / @Result結(jié)果映射@SelectKey主鍵回填@InsertProvider調(diào)用SQL構(gòu)建器。新增專(zhuān)用@DeleteProvider調(diào)用SQL構(gòu)建器。刪除專(zhuān)用@UpdateProvider調(diào)用SQL構(gòu)建器。修改專(zhuān)用@SelectProvider調(diào)用SQL構(gòu)建器。查詢(xún)專(zhuān)用@Param定義參數(shù)的名稱(chēng)
常用注解:
1. 新增
@Insert("sql語(yǔ)句")
注解中處理參數(shù)的方式,和SQL配置文件中的方式完全相同。但是此注解沒(méi)有動(dòng)態(tài)SQL標(biāo)簽的功能。
1.1 主鍵回填
@SelectKey(statement="sql", before=真假, keyProperty="主鍵屬性名"[, keyColumn="查詢(xún)主鍵的字段名"], resultType=類(lèi)型)
2. 更新
@Update("sql"), 和SQL配置文件采用相同的參數(shù)處理方案。
3. 刪除
@Delete("sql")
4. 查詢(xún)
@Select("sql")
使用注解時(shí),返回結(jié)果類(lèi)型,默認(rèn)就是方法的返回值類(lèi)型或返回值集合的泛型或Map。 相當(dāng)于SQL配置文件中的
5. 動(dòng)態(tài)新增
@InsertProvider(type=Xxx.class, method="生成SQL語(yǔ)句的方法名")
6. 動(dòng)態(tài)更新
@UpdateProvider()
7. 動(dòng)態(tài)刪除
@DeleteProvider()
8. 動(dòng)態(tài)查詢(xún)
@SelectProvider()
9. 結(jié)果映射處理
@Results( //相當(dāng)于resultMap標(biāo)簽, 沒(méi)有強(qiáng)制id, 類(lèi)型就是方法返回類(lèi)型或返回集合的泛型
@Result(column="字段名" property="屬性名"), // 配置類(lèi)型中的一個(gè)屬性和一個(gè)字段的映射
@Result(column="字段名" property="屬性名")
)
使用注解:
1. 前提: 在mybatis.cfg.xml配置文件中,使用
2. 日志: 使用接口的包作為日志輸出的定位即可。更精確的方式是包名.接口名。
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
create table tb_dept(
dep_id int(11) primary key auto_increment comment '部門(mén)主鍵',
dep_name varchar(32) comment '部門(mén)名稱(chēng)',
dep_addr varchar(255) comment '部門(mén)地址'
) comment '部門(mén)表';
insert into tb_dept(dep_id, dep_name, dep_addr)
values (default, '教學(xué)部', '賽蒂工業(yè)園'),
(default, '行政部', '賽蒂工業(yè)園'),
(default, '財(cái)務(wù)部', '賽蒂工業(yè)園');
package com.bjsxt.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
/**
* 工具類(lèi)型
*/
public final class MyBatisUtils {
// 會(huì)話(huà)工廠(chǎng)
private static SqlSessionFactory sqlSessionFactory;
/**
* 初始化代碼塊,只允許一次,類(lèi)加載時(shí)運(yùn)行
*/
static {
// 創(chuàng)建會(huì)話(huà)工廠(chǎng)
try {
sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis.cfg.xml"));
} catch (Exception e) {
e.printStackTrace();
// 拋出異常,代表工廠(chǎng)創(chuàng)建失敗,后續(xù)的獲取會(huì)話(huà),訪(fǎng)問(wèn)數(shù)據(jù)庫(kù),都不可使用。
// 讓代碼終止
throw new ExceptionInInitializerError(e);
}
}
/**
* 私有構(gòu)造方法,禁止外部創(chuàng)建對(duì)象
*/
private MyBatisUtils() {
}
/**
* 獲取工廠(chǎng)的工具方法
*
* @return
*/
public static SqlSessionFactory getFactory() {
return sqlSessionFactory;
}
}
package com.bjsxt.mapper;
import com.bjsxt.pojo.Dept;
import com.bjsxt.provider.MyInsertSqlProvider;
import org.apache.ibatis.annotations.*;
import java.util.List;
import java.util.Map;
/**
* 數(shù)據(jù)訪(fǎng)問(wèn)接口 , 持久層接口
*/
public interface DeptMapper {
/**
* 查詢(xún)
* 當(dāng)id= null, 查詢(xún)?nèi)?/p>
* id < 10, 查詢(xún) id = 2的數(shù)據(jù)
* id == 10, 查詢(xún) id = 3的數(shù)據(jù)
* id > 10, 查詢(xún) id = 4的數(shù)據(jù)
*
* @param id
* @return
*/
@SelectProvider(type = MyInsertSqlProvider.class, method = "select")
List
/**
* 批量新增
*
* @param list
* @return
*/
@InsertProvider(type = MyInsertSqlProvider.class, method = "suibian")
int insertBatch(List
@Results(value = { // {} 代表數(shù)組
@Result(column = "dep_id", property = "depId"), @Result(column = "dep_name", property = "depName"), @Result(column = "dep_addr", property = "depAddr")})
@Select("select dep_id, dep_name, dep_addr from tb_dept where dep_id = #{depId}")
Dept selectByResults(Integer id);
/**
* 查詢(xún)所有,返回結(jié)果每行封裝一個(gè)Map集合。key是字段名,value是字段值
*
* @return
*/
@Select("select dep_id , dep_name , dep_addr from tb_dept")
List
/**
* 查詢(xún)所有
*
* @return
*/
@Select("select dep_id as depId, dep_name as depName, dep_addr as depAddr from tb_dept")
List
/**
* 根據(jù)主鍵查詢(xún)
*
* @param id
* @return
*/
@Select("select dep_id as depId, dep_name as depName, dep_addr as depAddr from tb_dept where dep_id = #{depId}")
Dept selectById(Integer id);
/**
* 根據(jù)主鍵刪除
*
* @param id
* @return
*/
@Delete("delete from tb_dept where dep_id = #{depId}")
int deleteById(Integer id);
/**
* 根據(jù)主鍵更新其他字段
*
* @param dept
* @return
*/
@Update("update tb_dept set dep_name = #{depName}, dep_addr = #{depAddr} where dep_id = #{depId}")
int updateById(Dept dept);
/**
* 新增
*
* @param dept
* @return
*/
@Insert("insert into tb_dept(dep_id, dep_name, dep_addr) values(#{depId}, #{depName}, #{depAddr})")
@SelectKey(statement = "select @@identity as id", keyProperty = "depId", before = false, resultType = Integer.class, keyColumn = "id")
int insertDept(Dept dept);
}
package com.bjsxt.provider;
import com.bjsxt.pojo.Dept;
import org.apache.ibatis.jdbc.SQL;
import java.util.List;
/**
* 自定義的新增SQL語(yǔ)句處理類(lèi)型
*/
public class MyInsertSqlProvider {
/**
* 動(dòng)態(tài)生成SQL
* MyBatis提供了一個(gè)類(lèi)型,可以通過(guò)調(diào)用方法的方式,逐漸拼接一個(gè)完整的SQL。
*
* @param id
* @return
*/
public String select(Integer id) {
// MyBatis提供的SQL生成類(lèi)型
SQL s = new SQL();
// 拼接select語(yǔ)句,參數(shù)是若干字段
s.SELECT("dep_id as depId", "dep_name as depName", "dep_addr as depAddr").FROM("tb_dept");
if (id == null) {
return s.toString();
}
if (id < 10) {
s.WHERE("dep_id = 2");
return s.toString();
}
if (id == 10) {
s.WHERE("dep_id = 3");
return s.toString();
}
if (id > 10) {
s.WHERE("dep_id = 4");
return s.toString();
}
// 返回生成的SQL語(yǔ)句。
return s.toString();
}
/**
* 動(dòng)態(tài)生成SQL的方法定義要求:
* 1. 公開(kāi)
* 2. 名稱(chēng)任意定義
* 3. 返回String
* 4. 如果需要參數(shù),接口方法如何定義,當(dāng)前方法如何定義
* 方法內(nèi)部,直接拼接完整的SQL,如果參數(shù)可以通過(guò)#{}或${}直接處理,可以設(shè)置占位變量。如果不可以直接處理,則在當(dāng)前方法中設(shè)置。
*/
public String suibian(List
StringBuilder sql = new StringBuilder("insert into tb_dept(dep_id, dep_name, dep_addr) values ");
for (Dept dept : list) {
sql.append("(").append(dept.getDepId()).append(", \"").append(dept.getDepName()).append("\", \"").append(dept.getDepAddr()).append("\"),");
}
// 刪除最后一個(gè)逗號(hào)
sql.deleteCharAt(sql.length() - 1);
System.out.println("動(dòng)態(tài)生成的SQL是:" + sql.toString());
return sql.toString();
}
}
package com.bjsxt.test;
import com.bjsxt.mapper.DeptMapper;
import com.bjsxt.pojo.Dept;
import com.bjsxt.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 測(cè)試注解
*/
public class TestAnnotation {
// 定義一個(gè)實(shí)例變量。
private DeptMapper deptMapper;
private SqlSession session;
/**
* 執(zhí)行測(cè)試方法前作什么
*/
@Before
public void before() {
session = MyBatisUtils.getFactory().openSession();
deptMapper = session.getMapper(DeptMapper.class);
System.out.println("before方法運(yùn)行,給SqlSession和DeptMapper賦值");
}
@Test
public void testSelectProvider() {
List
System.out.println(list);
list = deptMapper.selectByProvider(1);
System.out.println(list);
list = deptMapper.selectByProvider(10);
System.out.println(list);
list = deptMapper.selectByProvider(11);
System.out.println(list);
}
@Test
public void testInsertBatch() {
List
list.add(new Dept(null, "市場(chǎng)部", "賽蒂工業(yè)園"));
list.add(new Dept(null, "策劃部", "賽蒂工業(yè)園"));
list.add(new Dept(null, "開(kāi)發(fā)部", "賽蒂工業(yè)園"));
int rows = deptMapper.insertBatch(list);
System.out.println(rows);
session.commit();
session.close();
}
@Test
public void testSelectResults() {
Dept dept = deptMapper.selectByResults(2);
System.out.println(dept);
session.commit();
session.close();
}
@Test
public void testSelect() {
Dept dept = deptMapper.selectById(1);
System.out.println(dept);
System.out.println("================================================");
List
list.forEach(d -> {
System.out.println(d);
});
System.out.println("================================================");
List
maps.forEach(map -> {
System.out.println(map);
});
session.commit();
session.close();
}
@Test
public void testDelete() {
deptMapper.deleteById(5);
session.commit();
session.close();
}
@Test
public void testUpdate() {
Dept dept = new Dept(4, "總裁辦", "賽蒂工業(yè)園");
deptMapper.updateById(dept);
session.commit();
session.close();
}
@Test
public void testInsert() {
SqlSession session = MyBatisUtils.getFactory().openSession();
DeptMapper deptMapper = session.getMapper(DeptMapper.class);
Dept dept = new Dept(null, "測(cè)試新增注解", "賽蒂工業(yè)園");
System.out.println("新增前:" + dept);
int rows = deptMapper.insertDept(dept);
System.out.println(rows);
System.out.println("新增后:" + dept);
session.commit();
session.close();
}
}
【19】多表查詢(xún)
表格關(guān)系:
1. 一對(duì)一
1.1 唯一外鍵: A表格有字段id。 B表格有字段id和a_id,其中a_id是外鍵,引用A表格的id字段,且a_id字段約束是unique。常用
1.2 共享主鍵: A表格有字段id。 B表格有字段id,其中B表格的id字段是主鍵,且是一個(gè)外鍵,引用A表格的id字段。不常用
2. 一對(duì)多
A表格有字段id。 B表格有字段id和a_id,其中a_id是外鍵,引用A表格的id字段
3. 多對(duì)多
A表格有字段id。B表格有字段id。 C表格有字段a_id和b_id,分別引用A表格id和B表格的id。
Java中類(lèi)型的關(guān)系有:
依賴(lài):弱關(guān)系,有類(lèi)型A和B,只要A中有B,就是依賴(lài)。A依賴(lài)B。
聚合:中等強(qiáng)度的關(guān)系,有類(lèi)型A和B,A中有B類(lèi)型的或B泛型的實(shí)例變量。
組合:強(qiáng)關(guān)系,有類(lèi)型A和B,A中有B類(lèi)型或B泛型的實(shí)例變量;B中有A類(lèi)型的或A泛型的實(shí)例變量。且A和B不能獨(dú)立存在。
MyBatis如何處理多表關(guān)聯(lián)查詢(xún):
當(dāng)前類(lèi)型引用的其他類(lèi)型屬性是非集合: 在resultMap標(biāo)簽中,增加子標(biāo)簽
當(dāng)前類(lèi)型引用的其他類(lèi)型屬性是集合: 在resultMap標(biāo)簽中,增加子標(biāo)簽
MyBatis實(shí)現(xiàn)多表關(guān)聯(lián)數(shù)據(jù)查詢(xún)的方式有:
N+1次查詢(xún):如,A類(lèi)型對(duì)應(yīng)表格a,B類(lèi)型對(duì)應(yīng)表格b。A類(lèi)型中有List類(lèi)型的屬性。查詢(xún)A的同時(shí),要求查詢(xún)對(duì)應(yīng)的B集合。 1 是查詢(xún)A, N 是若干次查詢(xún)和A有關(guān)系的B
1次查詢(xún):如,A類(lèi)型對(duì)應(yīng)表格a,B類(lèi)型對(duì)應(yīng)表格b。A類(lèi)型中有List類(lèi)型的屬性。查詢(xún)A的同時(shí),要求查詢(xún)對(duì)應(yīng)的B集合。 使用多表聯(lián)合查詢(xún)的方式,一次性查詢(xún)A和B。
學(xué)習(xí)案例:
1. 查詢(xún)?nèi)康刂?,同時(shí)查詢(xún)地址對(duì)應(yīng)的客戶(hù)數(shù)據(jù)
1.1 N+1次
1.2 1次
2. 查詢(xún)?nèi)靠蛻?hù),同時(shí)查詢(xún)客戶(hù)對(duì)應(yīng)的地址集合
2.1 N+1次
2.2 1次
3. 業(yè)務(wù)裝配,就是單獨(dú)查詢(xún)每個(gè)表格的數(shù)據(jù),使用代碼,把數(shù)據(jù)直接的關(guān)系維護(hù)起來(lái)。
如:先查詢(xún)所有的客戶(hù),迭代分析,根據(jù)客戶(hù)主鍵,查詢(xún)每個(gè)客戶(hù)的地址集合,維護(hù)關(guān)系。
create table tb_customer(
id int(11) primary key auto_increment,
name varchar(32) comment '姓名',
username varchar(32) comment '登錄名',
password varchar(32) comment '登錄密碼'
) comment '客戶(hù)表格';
create table tb_address(
id int(11) primary key auto_increment,
province varchar(32) comment '省',
city varchar(32) comment '市',
address varchar(255) comment '具體地址',
customer_id int(11) references tb_customer(id)
);
insert into tb_customer(id, name, username, password) values
(default, '張三', 'zhangsan', '123'),
(default, '李四', 'lisi', '123'),
(default, '王五', 'wangwu', '123');
insert into tb_address(id, province, city, address, customer_id) values
(default, '北京', '北京', '西三旗', 1),
(default, '北京', '北京', '西二旗', 1),
(default, '北京', '北京', '西北旺', 1),
(default, '北京', '北京', '經(jīng)海路8號(hào)', 2),
(default, '北京', '北京', '經(jīng)海路9號(hào)', 2),
(default, '北京', '北京', '經(jīng)海路10號(hào)', 2),
(default, '北京', '北京', '天安門(mén)東', 3),
(default, '北京', '北京', '天安門(mén)西', 3),
(default, '北京', '北京', '前門(mén)大街', 3);
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
package com.bjsxt.pojo;
import java.io.Serializable;
import java.util.List;
import java.util.Objects;
/**
* 客戶(hù)實(shí)體
*/
public class Customer implements Serializable {
private Integer id;
private String name;
private String username;
private String password;
private List
addressList;public Customer() {
}
public Customer(Integer id, String name, String username, String password) {
this.id = id;
this.name = name;
this.username = username;
this.password = password;
}
public List
getAddressList() {return addressList;
}
public void setAddressList(List
addressList) {this.addressList = addressList;
}
@Override
public String toString() {
return "Customer{" +
"id=" + id +
", name='" + name + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Customer customer = (Customer) o;
return Objects.equals(id, customer.id) &&
Objects.equals(name, customer.name) &&
Objects.equals(username, customer.username) &&
Objects.equals(password, customer.password);
}
@Override
public int hashCode() {
return Objects.hash(id, name, username, password);
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
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;
}
}
package com.bjsxt.pojo;
import java.io.Serializable;
import java.util.Objects;
/**
* 地址
*/
public class Address implements Serializable {
private Integer id;
private String province;
private String city;
private String address;
private Customer customer;
private Integer customerId; // 外鍵字段
public Address() {
}
public Address(Integer id, String province, String city, String address) {
this.id = id;
this.province = province;
this.city = city;
this.address = address;
}
@Override
public String toString() {
return "Address{" +
"id=" + id +
", province='" + province + '\'' +
", city='" + city + '\'' +
", address='" + address + '\'' +
'}';
}
public Integer getCustomerId() {
return customerId;
}
public void setCustomerId(Integer customerId) {
this.customerId = customerId;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Address address1 = (Address) o;
return Objects.equals(id, address1.id) &&
Objects.equals(province, address1.province) &&
Objects.equals(city, address1.city) &&
Objects.equals(address, address1.address) &&
Objects.equals(customer, address1.customer);
}
@Override
public int hashCode() {
return Objects.hash(id, province, city, address, customer);
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getProvince() {
return province;
}
public void setProvince(String province) {
this.province = province;
}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
}
package com.bjsxt.mapper;
import com.bjsxt.pojo.Address;
import java.util.List;
/**
* 地址數(shù)據(jù)訪(fǎng)問(wèn)接口
*/
public interface AddressMapper {
List
selectAll();List
selectAll1();}
package com.bjsxt.mapper;
import com.bjsxt.pojo.Customer;
import java.util.List;
/**
* 客戶(hù)數(shù)據(jù)訪(fǎng)問(wèn)接口
*/
public interface CustomerMapper {
List
List
}
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
select addr.id, addr.province, addr.city, addr.address, cus.id as cusId, cus.name, cus.username, cus.password from
tb_address addr left join tb_customer cus on addr.customer_id = cus.id
select="com.bjsxt.mapper.CustomerMapper.selectById" column="{id = customer_id}">
select id, province, city, address, customer_id from tb_address
select id, province, city, address from tb_address where customer_id = #{customerId}
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
ofType="Address">
select cus.id as cusId, cus.name, cus.username, cus.password, addr.id, addr.province, addr.city, addr.address from
tb_customer as cus left join tb_address as addr on cus.id = addr.customer_id
ofType="Address" select="com.bjsxt.mapper.AddressMapper.selectAddrByCustomer" column="{customerId = id}">
select id, name, username, password from tb_customer
select id, name, username, password from tb_customer where id = #{id}
package com.bjsxt.test;
import com.bjsxt.mapper.AddressMapper;
import com.bjsxt.mapper.CustomerMapper;
import com.bjsxt.pojo.Address;
import com.bjsxt.pojo.Customer;
import com.bjsxt.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Before;
import org.junit.Test;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class TestAssociation {
private SqlSession session;
private AddressMapper addressMapper;
private CustomerMapper customerMapper;
@Before
public void before(){
session = MyBatisUtils.getFactory().openSession();
addressMapper = session.getMapper(AddressMapper.class);
customerMapper = session.getMapper(CustomerMapper.class);
}
@Test
public void testAssociation1(){
List
list = addressMapper.selectAll1();for (Address address : list){
System.out.println(address);
System.out.println(address.getCustomer());
System.out.println("====================================================");
}
}
@Test
public void testAssociation(){
List
list = addressMapper.selectAll();// for(Address address : list){
// System.out.println(address);
// //System.out.println(address.getCustomer());
// System.out.println("==========================================================");
// }
}
}
package com.bjsxt.test;
import com.bjsxt.mapper.CustomerMapper;
import com.bjsxt.pojo.Address;
import com.bjsxt.pojo.Customer;
import com.bjsxt.utils.MyBatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Before;
import org.junit.Test;
import java.util.List;
public class TestCollection {
private SqlSession session;
private CustomerMapper customerMapper;
@Before
public void before() {
session = MyBatisUtils.getFactory().openSession();
customerMapper = session.getMapper(CustomerMapper.class);
}
@Test
public void testCollection1() {
List
for (Customer customer : list) {
System.out.println(customer);
for (Address address : customer.getAddressList()) {
System.out.println(address);
}
System.out.println("=================================================");
}
}
@Test
public void testCollection() {
List
for (Customer customer : list) {
System.out.println(customer);
for (Address address : customer.getAddressList()) {
System.out.println(address);
}
System.out.println("==============================================================");
}
}
}
【20】延遲加載
延遲加載只能出現(xiàn)在多表聯(lián)合查詢(xún)的N+1方式中
表示當(dāng)執(zhí)行當(dāng)前方法時(shí),是否立即執(zhí)行關(guān)聯(lián)方法的SQL
全局配置從3.4.1版本開(kāi)始需要在MyBatis置文件里面配置lazyLoadingEnabled=true即可在當(dāng)前項(xiàng)目所有N+1的位置開(kāi)啟延遲加載
局部配置需要在collection或association標(biāo)簽中配置fetchType屬性。fetchType可取值:lazy(延遲加載)和eager(立即加載)
當(dāng)配置了fetchType屬性后,全局settings的配置被覆蓋,對(duì)于當(dāng)前標(biāo)簽以fetchType屬性值為準(zhǔn)
javaType="Dept" select="com.bjsxt.mapper.DeptMapper.selectById" column="e_d_id" fetchType="lazy">
select e_id,e_name,e_d_id from emp
屬性名解釋說(shuō)明可取值默認(rèn)值lazyLoadingEnabled延遲加載的全局開(kāi)關(guān)。當(dāng)開(kāi)啟時(shí),所有關(guān)聯(lián)對(duì)象都會(huì)延遲加載。 特定關(guān)聯(lián)關(guān)系中可通過(guò)設(shè)置 fetchType 屬性來(lái)覆蓋該項(xiàng)的開(kāi)關(guān)狀態(tài)。true | falsefalseaggressiveLazyLoading開(kāi)啟時(shí),任一方法的調(diào)用都會(huì)加載該對(duì)象的所有延遲加載屬性。 否則,每個(gè)延遲加載屬性會(huì)按需加載(參考 lazyLoadTriggerMethods)。true | falsefalse (在 3.4.1 及之前的版本中默認(rèn)為 true)
【21】緩存
[1] 一級(jí)緩存
會(huì)話(huà)級(jí)緩存、線(xiàn)程級(jí)緩存,默認(rèn)開(kāi)啟一級(jí)緩存
要求:必須使用同一個(gè)會(huì)話(huà)、執(zhí)行同一條SQL、使用完全相同的參數(shù)
一級(jí)緩存中的數(shù)據(jù)保存流程:
1. 執(zhí)行SQL,查詢(xún)數(shù)據(jù)
2. 查詢(xún)結(jié)果保存到一級(jí)緩存中
[2] 二級(jí)緩存
會(huì)話(huà)工廠(chǎng)緩存、進(jìn)程級(jí)緩存。
要求:必須使用同一個(gè)會(huì)話(huà)工廠(chǎng),執(zhí)行同一條SQL,使用完全相同的參數(shù),且在事務(wù)結(jié)束后,才能使用的緩存。實(shí)體必須實(shí)現(xiàn)接口Serializable
二級(jí)緩存可能保存在內(nèi)存,也可能保存在文件中。MyBatis使用Object輸出/輸入流,實(shí)現(xiàn)文件和內(nèi)存的緩存數(shù)據(jù)讀寫(xiě)。
二級(jí)緩存中的數(shù)據(jù)保存流程:
1. 執(zhí)行SQL,查詢(xún)數(shù)據(jù)
2. 查詢(xún)結(jié)果保存到一級(jí)緩存中
3. 提交/回滾/關(guān)閉會(huì)話(huà),刷新一級(jí)緩存到二級(jí)緩存
4. 查詢(xún)同樣的SQL,且使用同樣的參數(shù),使用二級(jí)緩存
[3] 注意
開(kāi)發(fā)時(shí),只用一級(jí)緩存。不用二級(jí)緩存。由于二級(jí)緩存,相對(duì)性能低,安全差。
二級(jí)緩存是以 namespace 為單位的,不同 namespace 下的操作互不影響
查詢(xún)數(shù)據(jù)順序 二級(jí)-->一級(jí)--->數(shù)據(jù)庫(kù)--->把數(shù)據(jù)保存到一級(jí),當(dāng)sqlsession關(guān)閉或者提交的時(shí)候,把數(shù)據(jù)刷入到二級(jí)緩存中
[4] 只讀緩存(readonly=true)
配置的目的是優(yōu)化查詢(xún)性能,確保緩存不被寫(xiě)入,但它不會(huì)影響數(shù)據(jù)庫(kù)的提交操作。數(shù)據(jù)庫(kù)中的數(shù)據(jù)會(huì)按照提交操作進(jìn)行實(shí)際的修改。
提交操作對(duì)數(shù)據(jù)庫(kù)的數(shù)據(jù)進(jìn)行實(shí)際更改,不管緩存是否配置為只讀。
MyBatis 通過(guò)自動(dòng)清除相關(guān)緩存來(lái)確保數(shù)據(jù)的一致性,使得提交操作后的數(shù)據(jù)在下次查詢(xún)時(shí)能夠得到正確的反映。
/**
* 緩存中存儲(chǔ)的JavaBean對(duì)象必須實(shí)現(xiàn)序列化接口
*/
public class Dept implements Serializable { ... }
cache標(biāo)簽屬性含義默認(rèn)值type自定義緩存類(lèi),要求實(shí)現(xiàn)org.apache.ibatis.cache.Cache接口nullreadOnly配置的目的是優(yōu)化查詢(xún)性能,確保緩存不被寫(xiě)入,但它不會(huì)影響數(shù)據(jù)庫(kù)的提交操作。數(shù)據(jù)庫(kù)中的數(shù)據(jù)會(huì)按照提交操作進(jìn)行實(shí)際的修改。提交操作對(duì)數(shù)據(jù)庫(kù)的數(shù)據(jù)進(jìn)行實(shí)際更改,不管緩存是否配置為只讀。MyBatis 通過(guò)自動(dòng)清除相關(guān)緩存來(lái)確保數(shù)據(jù)的一致性,使得提交操作后的數(shù)據(jù)在下次查詢(xún)時(shí)能夠得到正確的反映。是否只讀true:給所有調(diào)用者返回緩存對(duì)象的相同實(shí)例。因此這些對(duì)象不能被修改。這提供了很重要的性能優(yōu)勢(shì)。false:會(huì)返回緩存對(duì)象的拷貝(通過(guò)序列化) 。這會(huì)慢一些,但是安全。falseeviction緩存策略L(fǎng)RU(默認(rèn)) – 最近最少使用:移除最長(zhǎng)時(shí)間不被使用的對(duì)象。FIFO – 先進(jìn)先出:按對(duì)象進(jìn)入緩存的順序來(lái)移除它們。SOFT – 軟引用:基于垃圾回收器狀態(tài)和軟引用規(guī)則移除對(duì)象。WEAK – 弱引用:更積極地基于垃圾收集器狀態(tài)和弱引用規(guī)則移除對(duì)象。LRUflushInterval刷新間隔,毫秒為單位。默認(rèn)為null,也就是沒(méi)有刷新間隔,只有執(zhí)行update、insert、delete語(yǔ)句才會(huì)刷新nullsize緩存對(duì)象個(gè)數(shù)1024blocking是否使用阻塞性緩存BlockingCachetrue:在查詢(xún)緩存時(shí)鎖住對(duì)應(yīng)的Key,如果緩存命中了則會(huì)釋放對(duì)應(yīng)的鎖,否則會(huì)在查詢(xún)數(shù)據(jù)庫(kù)以后再釋放鎖,保證只有一個(gè)線(xiàn)程到數(shù)據(jù)庫(kù)中查找指定key對(duì)應(yīng)的數(shù)據(jù)false:不使用阻塞性緩存,性能更好false
【23】四大核心接口介紹及執(zhí)行流程
【24】自定義插件
MyBatis中支持?jǐn)U展插件,所有插件都必須實(shí)現(xiàn)org.apache.ibatis.plugin.Interceptor接口,可以對(duì)四大核心接口進(jìn)行攔截
List
select e_id,e_name,e_d_id from emp
public class MyPageHelper {
protected static Integer pageStart;// 分頁(yè)起始行
protected static Integer pageSize;// 查詢(xún)條數(shù)
public static void startPage(int pageStartArg,int pageSizeArg){
pageStart = pageStartArg;
pageSize = pageSizeArg;
}
}
package com.bjsxt.interceptor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import java.sql.Connection;
import java.util.Properties;
// 必須有的注解
/**
* @Intercepts 表示當(dāng)前是一個(gè)攔截器。
* @Signature 表示簽名。
* type:攔截器主要攔截的類(lèi)型.可以是四大核心接口。
* method:攔截type中的哪個(gè)方法
* args:method對(duì)應(yīng)方法的參數(shù)。這個(gè)很重要,因?yàn)镴ava支持方法重載,不設(shè)置參數(shù)可能無(wú)法精確到具體的方法
*/
@Intercepts(value = {@Signature(
type = StatementHandler.class,
method = "prepare",
args = {Connection.class,Integer.class}
)})
public class MyPageHelperInterceptor implements Interceptor {
// 這個(gè)方法的作用:實(shí)現(xiàn)攔截業(yè)務(wù)
// 對(duì)于自定義分頁(yè)插件來(lái)說(shuō),這個(gè)方法的作用就是在后面拼接limit x,y
@Override
public Object intercept(Invocation invocation) throws Throwable {
// 獲取攔截的對(duì)象
StatementHandler target = (StatementHandler) invocation.getTarget();
// 獲取SQL綁定器
BoundSql boundSql = target.getBoundSql();
// 獲取SQL語(yǔ)句
String sql = boundSql.getSql();
// 判斷是否已經(jīng)設(shè)置了分頁(yè)條件
if(MyPageHelper.pageStart!=null&&MyPageHelper.pageSize!=null) {
// 注意limit前面空格
sql += " limit " +MyPageHelper.pageStart+","+MyPageHelper.pageSize;
}
// 把修改后的SQL重新放回去
MetaObject metaObject = SystemMetaObject.forObject(target);
// 第一個(gè)參數(shù)為固定值,表示綁定的SQL
metaObject.setValue("parameterHandler.boundSql.sql",sql);
// 放行繼續(xù)執(zhí)行
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
// System.out.println(target.getClass().getName()); 通過(guò)輸出可以查詢(xún)執(zhí)行此方法時(shí)目標(biāo)對(duì)象
// 每次調(diào)用四大核心接口都會(huì)調(diào)用此方法,只需要對(duì)StatementHandler進(jìn)行處理
if(target instanceof StatementHandler){
return Plugin.wrap(target,this);
}
return target;
}
@Override
public void setProperties(Properties properties) {
// 獲取到后面配置插件時(shí)的屬性,設(shè)定屬性名為dialect(方言),這個(gè)屬性是自定義的。
System.out.println(properties.getProperty("dialect"));
}
}
public class Test {
public static void main(String[] args) throws IOException {
InputStream is = Resources.getResourceAsStream("mybatis.cfg.xml");
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
SqlSession session = factory.openSession();
EmpMapper empMapper = session.getMapper(EmpMapper.class);
// 設(shè)置分頁(yè)條件代碼必須放在調(diào)用SQL上面
MyPageHelper.startPage(0,2);
List
System.out.println(list);
session.close();
}
}
【25】執(zhí)行器類(lèi)型
【26】執(zhí)行原理
柚子快報(bào)激活碼778899分享:尚硅谷MyBatis 基礎(chǔ)筆記
好文推薦
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀(guān)點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。