1、簡介
1.1、什么是 MyBatis?
MyBatis 是一款優秀的持久層框架,它支持自定義 SQL、存盤程序以及高級映射,MyBatis 免除了幾乎所有的 JDBC 代碼以及設定引數和獲取結果集的作業,MyBatis 可以通過簡單的 XML 或注解來配置和映射原始型別、介面和 Java POJO(Plain Old Java Objects,普通老式 Java 物件)為資料庫中的記錄,
MyBatis本是apache的一個開源專案iBatis,2010年這個專案由apache software foundation遷移到了google code,并且改名為MyBatis,2013年11月遷移到Github,
1.2、持久化
資料持久化
- 持久化就是將程式的資料在持久狀態和瞬時狀態轉化的程序
- 記憶體:斷電即失
- 資料庫(Jdbc), io檔案持久化,
- 生活:冷藏.罐頭,
為什么需要需要持久化?
- 有一些物件,不能讓他丟掉,
- 記憶體太貴了
1.3、持久層
Dao層,Service層,Controller層...
- 完成持久化作業的代碼塊
- 層界限十分明顯,
1.4、為什么需要Mybatis
- 幫助程式猿將資料存入到資料庫中,
- 方便
- 傳統的JDBC代碼太復雜了,簡化,框架,自動化,
- 不用Mybatis也可以, 更容易上手,技術沒有高低之分
- 優點:簡單
- 易學
- 靈活
- sq|和代碼的分離,提高了可維護性,
- 提供映射標簽,支持物件與資料庫的orm欄位關系映射
- 提供物件關系映射標簽,支持物件關系組建維護
- 提供xml標簽,支持撰寫動態sql,
重要的一點:使用的人多!
2、第一個Mybatis程式
思路:搭建環境-->匯入Mybatis-->撰寫代碼-->測驗
2.1、搭建環境
創建SQL表:
CREATE TABLE `user` (
`id` int(0) NOT NULL AUTO_INCREMENT,
`name` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'null',
`pwd` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;
INSERT INTO `user` VALUES (1, '老大', '123456');
INSERT INTO `user` VALUES (2, '李二', '123456');
INSERT INTO `user` VALUES (3, '張三', '123456');
INSERT INTO `user` VALUES (4, '李四', '123456');
新建一個maven專案
匯入依賴
<dependencies>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.29</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
</dependency>
</dependencies>
撰寫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="https://www.cnblogs.com/Upzhh/p/com.mysql.cj.jdbc.Driver"/>
<property name="url" value="https://www.cnblogs.com/Upzhh/p/jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8&serverTimezone=UTC&useSSL=false"/>
<property name="username" value="https://www.cnblogs.com/Upzhh/p/root"/>
<property name="password" value="https://www.cnblogs.com/Upzhh/p/123456"/>
</dataSource> </environment> </environments>
</configuration>
2.2、第一個mybati程式
撰寫Mybatis工具類
public class MybatisUtil {
private static SqlSessionFactory sqlSessionFactory;
static {
try{
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}catch (IOException e){
e.printStackTrace();
}
}
public SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
撰寫代碼
@Test
public void t(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> userList = mapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
2.3、比較容易出現的問題
1、全域組態檔沒有注冊mapper,mapper配置路徑要用 / 隔開
<?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="https://www.cnblogs.com/Upzhh/p/com.mysql.cj.jdbc.Driver"/>
<property name="url" value="https://www.cnblogs.com/Upzhh/p/jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8&serverTimezone=UTC&useSSL=true"/>
<property name="username" value="https://www.cnblogs.com/Upzhh/p/root"/>
<property name="password" value="https://www.cnblogs.com/Upzhh/p/123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/zh/mapper/UserMapper.xml"></mapper>
</mappers>
</configuration>
2、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">
<mapper namespace="com.zh.mapper.UserMapper"> 一個命名空間對應一個dao類
<select id="getUserList" resultType="com.zh.pojo.User"> //id對應方法,回傳結果對物體類物件
select * from mybatis.user
</select>
</mapper>
3、資源檔案匯出問題,由于maven專案的約定大于配置,默認只會匯出resources目錄的檔案,但是在mybatis專案中,mapper推薦把映射組態檔放在和dao類同一級目錄,會導致加載不到組態檔,所以要進行設定
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
</build>
3、CRUD
namespace:namespace中的包名要和Dao/mapper介面的包名一致!
資料的增刪改需要手動提交資料!!!
- **#{}是預編譯處理,mybatis在處理#{}時,會將其替換成"?",再呼叫PreparedStatement的set方法來賦值,
- ${}是拼接字串,將接收到的引數的內容不加任何修飾的拼接在SQL陳述句中,會引發SQL注入問題
select
- id:就是對應的namespace中的方法名;
- resultType: Sq|陳述句執行的回傳值
- parameterType :引數型別(可以是物件)
1.撰寫介面
User getUserById(int id);
2.撰寫對應的mapper中的sq|語,屬性可以直接呼叫
<select id="getUserById" resultType="com.zh.pojo.User">
select * from user where id=#{id}
</select>
3.測驗
public void t2(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
System.out.println(user);
sqlSession.commit();
sqlSession.close();
}
Insert
int addUser(User user);
<insert id="addUser" parameterType="com.zh.pojo.User" >
insert into user(id,name,pwd) value (#{id},#{name},#{pwd})
</insert>
@Test
public void t3(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int num = mapper.addUser(new User(5, "張三", "123321"));
System.out.println(num);
sqlSession.commit();
sqlSession.close();
}
update
int updateUser(User user);
<update id="updateUser" parameterType="com.zh.pojo.User">
update user set name = #{name},pwd=#{pwd}where id=#{id};
</update>
@Test
public void t4(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int num = mapper.updateUser(new User(5, "哈哈哈哈", "123321"));
System.out.println(num);
sqlSession.commit();
sqlSession.close();
}
Delete
int delUser(int id);
<delete id="delUser" parameterType="int">
delete from user where id=#{id}
</delete>
@Test
public void t5(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int i = mapper.delUser(5);
System.out.println(i);
sqlSession.commit();
sqlSession.close();
}
4、配置決議
configuration(配置)
- properties(屬性)
- settings(設定)
- typeAliases(型別別名)
- typeHandlers(型別處理器)
- objectFactory(物件工廠)
- plugins(插件)
- environments(環境配置)
- environment(環境變數)
- transactionManager(事務管理器)
- dataSource(資料源)
- environment(環境變數)
- databaseIdProvider(資料庫廠商標識)
- mappers(映射器)
configuration 組態檔中,對標簽的順序有規定:
The content of element type "configuration" must match "(properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?)".
4.1、properties(屬性)
這些屬性可以在外部進行配置,并可以進行動態替換,你既可以在典型的 Java 屬性檔案中配置這些屬性,也可以在 properties 元素的子元素中設定,例如:
<properties resource="org/mybatis/example/config.properties">
<property name="username" value="https://www.cnblogs.com/Upzhh/p/dev_user"/>
<property name="password" value="https://www.cnblogs.com/Upzhh/p/F2Fa3!33TYyg"/>
</properties>
設定好的屬性可以在整個組態檔中用來替換需要動態配置的屬性值,比如:
<dataSource type="POOLED">
<property name="driver" value="https://www.cnblogs.com/Upzhh/p/${driver}"/>
<property name="url" value="https://www.cnblogs.com/Upzhh/p/${url}"/>
<property name="username" value="https://www.cnblogs.com/Upzhh/p/${username}"/>
<property name="password" value="https://www.cnblogs.com/Upzhh/p/${password}"/>
</dataSource>
這個例子中的 username 和 password 將會由 properties 元素中設定的相應值來替換, driver 和 url 屬性將會由 config.properties 檔案中對應的值來替換,這樣就為配置提供了諸多靈活選擇,
也可以在 SqlSessionFactoryBuilder.build() 方法中傳入屬性值,例如:
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, props);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, props);
如果一個屬性在不只一個地方進行了配置,那么,MyBatis 將按照下面的順序來加載:
- 首先讀取在 properties 元素體內指定的屬性,
- 然后根據 properties 元素中的 resource 屬性讀取類路徑下屬性檔案,或根據 url 屬性指定的路徑讀取屬性檔案,并覆寫之前讀取過的同名屬性,
- 最后讀取作為方法引數傳遞的屬性,并覆寫之前讀取過的同名屬性,
因此,通過方法引數傳遞的屬性具有最高優先級,resource/url 屬性中指定的組態檔次之,最低優先級的則是 properties 元素中指定的屬性,
4.2、settings(設定)
| 設定名 | 描述 | 有效值 | 默認值 |
|---|---|---|---|
| cacheEnabled | 全域性地開啟或關閉所有映射器組態檔中已配置的任何快取 | true / false | true |
| lazyLoadingEnabled | 延遲加載的全域開關,當開啟時,所有關聯物件都會延遲加載, 特定關聯關系中可通過設定 fetchType 屬性來覆寫該項的開關狀態, | true / false | false |
| logImpl | 指定 MyBatis 所用日志的具體實作,未指定時將自動查找, | SLF4J / LOG4J / LOG4J2 / JDK_LOGGING / COMMONS_LOGGING / STDOUT_LOGGING / NO_LOGGING | 未設定 |
<settings>
<setting name="cacheEnabled" value="https://www.cnblogs.com/Upzhh/p/true"/>
<setting name="lazyLoadingEnabled" value="https://www.cnblogs.com/Upzhh/p/true"/>
<setting name="multipleResultSetsEnabled" value="https://www.cnblogs.com/Upzhh/p/true"/>
<setting name="useColumnLabel" value="https://www.cnblogs.com/Upzhh/p/true"/>
<setting name="useGeneratedKeys" value="https://www.cnblogs.com/Upzhh/p/false"/>
<setting name="autoMappingBehavior" value="https://www.cnblogs.com/Upzhh/p/PARTIAL"/>
<setting name="autoMappingUnknownColumnBehavior" value="https://www.cnblogs.com/Upzhh/p/WARNING"/>
<setting name="defaultExecutorType" value="https://www.cnblogs.com/Upzhh/p/SIMPLE"/>
<setting name="defaultStatementTimeout" value="https://www.cnblogs.com/Upzhh/p/25"/>
<setting name="defaultFetchSize" value="https://www.cnblogs.com/Upzhh/p/100"/>
<setting name="safeRowBoundsEnabled" value="https://www.cnblogs.com/Upzhh/p/false"/>
<setting name="mapUnderscoreToCamelCase" value="https://www.cnblogs.com/Upzhh/p/false"/>
<setting name="localCacheScope" value="https://www.cnblogs.com/Upzhh/p/SESSION"/>
<setting name="jdbcTypeForNull" value="https://www.cnblogs.com/Upzhh/p/OTHER"/>
<setting name="lazyLoadTriggerMethods" value="https://www.cnblogs.com/Upzhh/p/equals,clone,hashCode,toString"/>
</settings>
4.3、typeAliases(型別別名)
型別別名可為 Java 型別設定一個縮寫名字, 它僅用于 XML 配置,意在降低冗余的全限定類名書寫,例如
<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
<typeAlias alias="Blog" type="domain.blog.Blog"/>
<typeAlias alias="Comment" type="domain.blog.Comment"/>
<typeAlias alias="Post" type="domain.blog.Post"/>
<typeAlias alias="Section" type="domain.blog.Section"/>
<typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>
當這樣配置時,Blog 可以用在任何使用 domain.blog.Blog 的地方,
也可以指定一個包名,MyBatis 會在包名下面搜索需要的 Java Bean,比如:
<typeAliases>
<package name="domain.blog"/>
</typeAliases>
每一個在包 domain.blog 中的 Java Bean,在沒有注解的情況下,會使用 Bean 的首字母小寫的非限定類名來作為它的別名, 比如 domain.blog.Author 的別名為 author;若有注解,則別名為其注解值,見下面的例子:
@Alias("author")
public class Author {
...
}
4.4、環境配置(environments)
MyBatis可以配置成適應多種環境
通過 default="development" 來選擇環境
<environments default="development"> //通過選擇環境id選擇環境
<environment id="development">
<transactionManager type="JDBC"/> //默認事務處理
<dataSource type="POOLED"> //默認使用連接池
<property name="driver" value="https://www.cnblogs.com/Upzhh/p/${driver}"/>
<property name="url" value="https://www.cnblogs.com/Upzhh/p/${url}"/>
<property name="username" value="https://www.cnblogs.com/Upzhh/p/${username}"/>
<property name="password" value="https://www.cnblogs.com/Upzhh/p/${password}"/>
</dataSource>
</environment>
<environment id="Test">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="https://www.cnblogs.com/Upzhh/p/${driver}"/>
<property name="url" value="https://www.cnblogs.com/Upzhh/p/${url}"/>
<property name="username" value="https://www.cnblogs.com/Upzhh/p/${username}"/>
<property name="password" value="https://www.cnblogs.com/Upzhh/p/${password}"/>
</dataSource>
</environment></environments>
注意一些關鍵點:
- 默認使用的環境 ID(比如:default="development"),
- 每個 environment 元素定義的環境 ID(比如:id="development"),
- 事務管理器的配置(比如:type="JDBC"),
- 資料源的配置(比如:type="POOLED"),
默認環境和環境 ID 顧名思義, 環境可以隨意命名,但務必保證默認的環境 ID 要匹配其中一個環境 ID,
事務管理器(transactionManager)
在 MyBatis 中有兩種型別的事務管理器(也就是 type="JDBC|MANAGED"):
- JDBC – 這個配置直接使用了 JDBC 的提交和回滾設施,它依賴從資料源獲得的連接來管理事務作用域,
- MANAGED – 這個配置幾乎沒做什么,它從不提交或回滾一個連接,而是讓容器來管理事務的整個生命周期(比如 JEE 應用服務器的背景關系), 默認情況下它會關閉連接,然而一些容器并不希望連接被關閉,因此需要將 closeConnection 屬性設定為 false 來阻止默認的關閉行為,例如:
提示 如果你正在使用 Spring + MyBatis,則沒有必要配置事務管理器,因為 Spring 模塊會使用自帶的管理器來覆寫前面的配置,
這兩種事務管理器型別都不需要設定任何屬性,它們其實是型別別名,換句話說,你可以用 TransactionFactory 介面實作類的全限定名或型別別名代替它們
盡管可以配置多個環境,但每個SqlSessionFactory實體只能選擇-種環境,
學會使用配置多套運行環境!
Mybatis默認的事務管理器就是JDBC,連接池: POOLED
4.5、mappers(映射器)
既然 MyBatis 的行為已經由上述元素配置完了,我們現在就要來定義 SQL 映射陳述句了, 但首先,我們需要告訴 MyBatis 到哪里去找到這些陳述句, 在自動查找資源方面,Java 并沒有提供一個很好的解決方案,所以最好的辦法是直接告訴 MyBatis 到哪里去找映射檔案, 你可以使用相對于類路徑的資源參考,或完全限定資源定位符(包括 file:/// 形式的 URL),或類名和包名等,例如:
<!-- 使用相對于類路徑的資源參考 -->
<mappers>
<mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
<mapper resource="org/mybatis/builder/BlogMapper.xml"/>
<mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用完全限定資源定位符(URL) -->
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
<mapper url="file:///var/mappers/BlogMapper.xml"/>
<mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
<!-- 使用映射器介面實作類的完全限定類名 -->
<mappers>
<mapper />
<mapper />
<mapper />
</mappers>
<!-- 將包內的映射器介面實作全部注冊為映射器 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
4.6、其他配置
- typeHandlers (型別處理器)
- objectFactory (物件工廠)
- plugins插件
- mybatis-generator-core
- mybatis-plus
- 通用mappe
4.7、生命周期和作用域
生命周期,和作用域,是至關重要的,因為錯誤的使用會導致非常嚴重的并發問題,
SqlSessionFactoryBuilder:
- 一旦創建了SqlSessionFactory, 就不再需要它了
- 區域變數
SqlSessionFactor
- 說白了就是可以想象為:資料庫連接池
- SqlSessionFactory 一-旦被創建就應該在應用的運行期間一直存在,沒有任何理由丟棄它或重新創建另一個實體,
- 因此SqISessionFactory的最佳作用域是應用作用域,
- 最簡單的就是使用單例模式或者靜態單例模式,
SqISession
- 連接到連接池的一個請求!
- SqISession 的實體不是執行緒安全的,因此是不能被共享的,所以它的最佳的作用域是請求或方法作用** 域,
- 用完之后需要趕緊關閉,否則資源被占用
5、處理欄位與屬性名不一樣的問題
物體類中的欄位:id name password
資料庫中的欄位:id name pwd
執行查詢陳述句
User{id=1, name='老大', password='null'}
User{id=2, name='李二', password='null'}
User{id=3, name='張三', password='null'}
會發現,欄位名不一樣,導致查詢到的欄位值為空
原始解決辦法:select id,name,pwd as password from user
User{id=1, name='老大', password='123456'}
User{id=2, name='李二', password='123456'}
User{id=3, name='張三', password='123456'}
顯示使用resultMap
<mapper namespace="com.zh.mapper.UserMapper">
<select id="getUserList" resultMap="resUser">
select * from user
</select>
<resultMap id="resUser" type="User">
<result column="id" property="id"/>
<result column="name" property="name"/>
<result column="pwd" property="password"/>
</resultMap>
</mapper>
結果
User{id=1, name='老大', password='123456'}
User{id=2, name='李二', password='123456'}
User{id=3, name='張三', password='123456'}
官方檔案:
<!-- SQL 映射 XML 中 -->
<select id="selectUsers" resultType="User">
select id, username, hashedPassword
from some_table
where id = #{id}
</select>
在這些情況下,MyBatis 會在幕后自動創建一個 ResultMap,再根據屬性名來映射列到 JavaBean 的屬性上,如果列名和屬性名不能匹配上,可以在 SELECT 陳述句中設定列別名(這是一個基本的 SQL 特性)來完成匹配,比如:
<select id="selectUsers" resultType="User">
select
user_id as "id",
user_name as "userName",
hashed_password as "hashedPassword"
from some_table
where id = #{id}
</select>
在學習了上面的知識后,你會發現上面的例子沒有一個需要顯式配置 ResultMap,這就是 ResultMap 的優秀之處——你完全可以不用顯式地配置它們, 雖然上面的例子不用顯式配置 ResultMap, 但為了講解,我們來看看如果在剛剛的示例中,顯式使用外部的 resultMap 會怎樣,這也是解決列名不匹配的另外一種方式,
<resultMap id="userResultMap" type="User">
<id property="id" column="user_id" />
<result property="username" column="user_name"/>
<result property="password" column="hashed_password"/>
</resultMap>
然后在參考它的陳述句中設定 resultMap 屬性就行了(注意我們去掉了 resultType 屬性),比如:
<select id="selectUsers" resultMap="userResultMap">
select user_id, user_name, hashed_password
from some_table
where id = #{id}
</select>
6、日志
6.1、日志工廠
1、標準日志工廠
<settings>
<setting name="logImpl" value="https://www.cnblogs.com/Upzhh/p/STDOUT_LOGGING"/>
</settings>
Logging initialized using 'class org.apache.ibatis.logging.stdout.StdOutImpl' adapter.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
PooledDataSource forcefully closed/removed all connections.
Opening JDBC Connection
Created connection 359742806.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@15713d56]
==> Preparing: select * from user
==> Parameters:
<== Columns: id, name, pwd
<== Row: 1, 老大, 123456
<== Row: 2, 李二, 123456
<== Row: 3, 張三, 123456
<== Row: 5, 張三, 123321
<== Row: 6, 哈哈哈哈, 123321
<== Row: 7, 張三, 123321
<== Total: 6
User{id=1, name='老大', password='123456'}
User{id=2, name='李二', password='123456'}
User{id=3, name='張三', password='123456'}
User{id=5, name='張三', password='123321'}
User{id=6, name='哈哈哈哈', password='123321'}
User{id=7, name='張三', password='123321'}
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@15713d56]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@15713d56]
Returned connection 359742806 to pool.
4.2 Log4j
什么是Log4j?
- Log4j是Apache的一一個開源專案,通過使用Log4j,我們可以控制日志資訊輸送的目的地是控制臺、檔案、GUl組件
- 我們也可以控制每-條日志的輸出格式;
- 通過定義每一條日志資訊的級別,我們能夠更加細致地控制日志的生成程序,
- 通過一個組態檔來靈活地進行配置,而不需要修改應用的代碼,
1、匯入log4j包
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2、組態檔
#將等級為DEBUG的日志資訊輸出到console和file這兩個目的地,console和file的定義在下面的代碼
log4j.rootLogger=DEBUG,console,file
#控制臺輸出的相關設定
log4j.appender.console = org.apache.log4j.ConsoleAppender
log4j.appender.console.Target = System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout = org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n
#檔案輸出的相關設定
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/logFile.log
log4j.appender.file.MaxFileSize=10mb
log4j.appender.file.Threshold=DEBUG
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n
#日志輸出級別
log4j.logger.org.mybatis=DEBUG
log4j.logger.java.sql=DEBUG
log4j.logger.java.sql.Statement=DEBUG
log4j.logger.java.sql.ResultSet=DEBUG
log4j.logger.java.sql.PreparedStatement=DEBUG
3、代碼中運用
private static Logger logger=Logger.getLogger(MapperTest.class);
@Test
public void test01(){
logger.debug("debug");
logger.info("info");
logger.warn("warn");
logger.error("error");
}
檔案中:
[DEBUG][22-08-19][com.zh.mapper.MapperTest]debug
[INFO][22-08-19][com.zh.mapper.MapperTest]info
[WARN][22-08-19][com.zh.mapper.MapperTest]warn
[ERROR][22-08-19][com.zh.mapper.MapperTest]error
7、分頁
1、SQL陳述句 limit
2、RowBounds
@Test
public void Test01(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
List<User> userList = sqlSession.selectList("com.zh.mapper.UserMapper.getUserList", null, new RowBounds(0, 2));
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
3、MyBatis 分頁插件 PageHelper
8、使用注解開發
1、注解執行SQL
介面方法上注解
public interface UserMapper {
@Select("select * from user")
List<User> getUserList();
}
全域組態檔中去配置mapper
<mappers>
<mapper />
</mappers>
2、多引數SQL陳述句
多引數SQL陳述句會出現問題
@Select("select * from user where id=#{id} and name=#{name}")
List<User> getUserList(int id,String name);
@Test
public void t(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map map = new HashMap();
List<User> userList = mapper.getUserList(1,"老大");
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
報錯
Cause: org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are [arg1, arg0, param1, param2]
解決辦法:
1、map傳參
嚴格來說,map適用幾乎所有場景,但是我們用得不多,原因有兩個:首先,map是一個鍵值對應的集合,使用者要通過閱讀它的鍵,才能明了其作用;其次,使用map不能限定其傳遞的資料型別,因此業務性質不強,可讀性差,使用者要讀懂代碼才能知道需要傳遞什么引數給它,所以不推薦用這種方式傳遞多個引數,
@Select("select * from user where id=#{id} and name=#{name}")
List<User> getUserList(Map<String,Object> map);
@Test
public void t(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String,Object> map = new HashMap();
map.put("id","1");
map.put("name","老大");
List<User> userList = mapper.getUserList(map);
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
User{id=1, name='老大', password='null'}
2、使用注解傳遞多個引數
MyBatis為開發者提供了一個注解@Param(org.apache.ibatis.annotations.Param),可以通過它去定義映射器的引數名稱,使用它可以得到更好的可讀性 這個時候需要修改映射檔案的代碼,此時并不需要給出parameterType屬性,讓MyBatis自動探索便可以了
@Select("select * from user where id=#{id} and name=#{name}")
List<User> getUserList(@Param("id") int id,@Param("name") String name);
@Test
public void t(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String,Object> map = new HashMap();
List<User> userList = mapper.getUserList(1,"老大");
for (User user : userList) {
System.out.println(user);
}
sqlSession.close();
}
Preparing: select * from user where id=? and name=?
==> Parameters: 1(Integer), 老大(String)
<== Columns: id, name, pwd
<== Row: 1, 老大, 123456
3、使用JavaBean傳參
在 mapperx.xml 配置中知道引數型別即可,物件屬性可以直接參考
9、多表查詢
多對一本質上還是處理欄位名和列名不一致的問題
9.1、多對一
9.1.1、通過子查詢的方式
介面
//子查詢
List<Student> getStuList1();
mapper
<!--子查詢-->
<select id="getStuList1" resultMap="StudentTeacher01">
select * from student
</select>
<resultMap id="StudentTeacher01" type="Student">
<result property="id" column="id"/>
<result property="name" column="name"/>
<association property="teacher" column="tid" javaType="Teacher" select="getTeacher"></association>
</resultMap>
<select id="getTeacher" resultType="Teacher">
select * from teacher where id=#{id}
</select>
查詢結果
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@22f31dec]
==> Preparing: select * from student
==> Parameters:
<== Columns: id, name, tid
<== Row: 1, 張三, 1
====> Preparing: select * from teacher where id=?
====> Parameters: 1(Integer)
<==== Columns: id, name
<==== Row: 1, 李老師
<==== Total: 1
<== Row: 2, 李四, 1
<== Row: 3, 王五, 2
====> Preparing: select * from teacher where id=?
====> Parameters: 2(Integer)
<==== Columns: id, name
<==== Row: 2, 王老師
<==== Total: 1
<== Total: 3
Student{id=1, name='張三', teacher=Teachar{id=1, name='李老師'}}
Student{id=2, name='李四', teacher=Teachar{id=1, name='李老師'}}
Student{id=3, name='王五', teacher=Teachar{id=2, name='王老師'}}
9.1.2、通過連表查詢
介面
//聯表查詢
List<Student> getStuList2();
mapper
<!--聯表查詢-->
<select id="getStuList2" resultMap="StudentTeacher02">
select s.id as sid,s.name as sname,t.id as tid,t.name as tname from student as s join teacher t on t.id = s.tid
</select>
<resultMap id="StudentTeacher02" 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>
查詢結果
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@22f31dec]
==> Preparing: select s.id as sid,s.name as sname,t.id as tid,t.name as tname from student as s join teacher t on t.id = s.tid
==> Parameters:
<== Columns: sid, sname, tid, tname
<== Row: 1, 張三, 1, 李老師
<== Row: 2, 李四, 1, 李老師
<== Row: 3, 王五, 2, 王老師
<== Total: 3
Student{id=1, name='張三', teacher=Teachar{id=1, name='李老師'}}
Student{id=2, name='李四', teacher=Teachar{id=1, name='李老師'}}
Student{id=3, name='王五', teacher=Teachar{id=2, name='王老師'}}
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@22f31dec]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@22f31dec]
Returned connection 586358252 to pool.
9.2、一對多
9.2.1、子查詢
介面
//子查詢
Teacher getTeacher2(@Param("tid") int id);
mapper
<select id="getTeacher2" resultMap="TeacherStudent02">
select * from teacher where id=#{tid}
</select>
<resultMap id="TeacherStudent02" type="Teacher">
<result property="id" column="id"/>
<result property="name" column="name"/>
<collection property="students" column="id" select="getStudents" ofType="Student">
</collection></resultMap>
<select id="getStudents" resultType="Student">
select * from student where tid=#{id}
</select>
結果
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@76a2ddf3]
==> Preparing: select * from teacher where id=?
==> Parameters: 1(Integer)
<== Columns: id, name
<== Row: 1, 李老師
====> Preparing: select * from student where tid=?
====> Parameters: 1(Integer)
<==== Columns: id, name, tid
<==== Row: 1, 張三, 1
<==== Row: 2, 李四, 1
<==== Total: 2
<== Total: 1
Teacher{id=1, name='李老師', students=[Student{id=1, name='張三', teacher=null}, Student{id=2, name='李四', teacher=null}]}
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@76a2ddf3]
9.2.2、連接查詢
介面
//查詢一個老師的所有資訊包括老師所有學生
//連接查詢
Teacher getTeacher1(@Param("tid") int id);
mapper
<select id="getTeacher1" resultMap="TeacherStudent01">
select t.id as tid,t.name as tname,s.name as sname from teacher t join student s on t.id = s.tid where tid=#{tid}
</select>
<resultMap id="TeacherStudent01" type="Teacher">
<result property="id" column="tid"/>
<result property="name" column="tname"/>
<collection property="students" ofType="Student">
<result property="name" column="sname"/>
</collection></resultMap>
結果:
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@76a2ddf3]
==> Preparing: select t.id as tid,t.name as tname,s.name as sname from teacher t join student s on t.id = s.tid where tid=?
==> Parameters: 1(Integer)
<== Columns: tid, tname, sname
<== Row: 1, 李老師, 張三
<== Row: 1, 李老師, 李四
<== Total: 2
Teacher{id=1, name='李老師', students=[Student{id=0, name='張三', teacher=null}, Student{id=0, name='李四', teacher=null}]}
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@76a2ddf3]
總結:
1.關聯- association 多對一
2.集合- collection 一對多
3. javaType & ofType
1. JavaType用來指定物體類中屬性的型別
2. ofType用來指定映射到List或者集合中的pojo型別,泛型中的約束型別!
注意點:
- 保證SQL的可讀性,盡量保證通俗易懂
- 注意一對多和多對一-中,屬性名和欄位的問題!
- 如果問題不好排查錯誤,可以使用日志,建議使用Log4j
面試高頻 - Mysql 引擎
- InnoDB 底層原理
- 索引
- 索引優化
10、動態SQL
10.1、if
List<Blog> getBlogsIF(Map<String,Object> map);
<select id="getBlogsIF" resultType="Blog">
select * from Blog where 1=1
<if test="title != null">
and title=#{title}
</if>
<if test="author != null">
and author=#{author}
</if>
</select>
@org.junit.Test
public void T(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map<String,Object> map = new HashMap<>();
//map.put("title","水滸傳");
map.put("author","羅貫中");
List<Blog> blogs = mapper.getBlogsIF(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
上例的where1=1在開發中不用,因為不安全
那么問題來了,去掉where1=1之后SQL陳述句會變成這樣
<select id="getBlogsIF" resultType="Blog">
select * from Blog where
<if test="title != null">
title=#{title}
</if>
<if test="author != null">
and author=#{author}
</if>
</select>
存在一個問題,如果只查詢第二個條件,SQL陳述句會拼接成:select * from Blog where and author=#{author}
這顯然是錯誤的
10.2、 trim、where、set
where 元素只會在子元素回傳任何內容的情況下才插入 “WHERE” 子句,而且,若子句的開頭為 “AND” 或 “OR”,where 元素也會將它們去除,
<select id="getBlogsIF" resultType="Blog">
select * from Blog
<where>
<if test="title != null">
title=#{title}
</if>
<if test="author != null">
and author=#{author}
</if>
</where>
</select>
SqlSession sqlSession = MybatisUtil.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map<String,Object> map = new HashMap<>();
//map.put("title","水滸傳");
map.put("author","羅貫中");
List<Blog> blogs = mapper.getBlogsIF(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
Preparing: select * from Blog WHERE author=?
==> Parameters: 羅貫中(String)
<== Columns: id, title, author, createTime, views
<== Row: db33c88052974b58b26001467597133a, 三國演義, 羅貫中, 2022-08-04 18:55:31, 300
<== Total: 1
set元素會動態地在行首插入 SET 關鍵字,并會刪掉額外的逗號(這些逗號是在使用條件陳述句給列賦值時引入的)
int updateBlog(Map<String,Object> map);
<update id="updateBlog" parameterType="map">
update blog
<set>
<if test="author != null">
author=#{author},
</if>
<if test="title != null">
title=#{title}
</if>
</set>
where id=#{id}
</update>
@org.junit.Test
public void T2(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map<String,Object> map = new HashMap<>();
//map.put("title","水滸傳");
map.put("author","羅貫中");
map.put("id","04f9dc6b550e4c7983ee77d66dd838f1");
int i = mapper.updateBlog(map);
sqlSession.close();
}
Preparing: update blog SET author=? where id=?
==> Parameters: 羅貫中(String), 04f9dc6b550e4c7983ee77d66dd838f1(String)
可以看到,set標簽自動加上了set關鍵字和去除了 author=#{author}, 的逗號以連接后面的where
Mybatis具有實作動態SQL的能力,但是在拼湊SQL陳述句的時候,稍有不注意則會畫蛇添足,此時可以通過 trim 標簽來進行修剪,trim 是“修剪”的意思,其基本格式如下:
<trim prefix="" suffix="" prefixOverrides="" suffixOverrides="">
......
</trim>
| 屬性名 | 作用 |
|---|---|
| prefix | 給sql增加前綴 |
| suffix | 給sql增加后綴 |
| prefixOverrides | 去掉sql前面多余的關鍵字或者字符 |
| suffixOverrides | 去掉sql后面多余的關鍵字或者字符 |
where 元素等價的自定義 trim 元素為:
<trim prefix="WHERE" prefixOverrides="AND |OR ">
...
</trim>
來看看與 set 元素等價的自定義 trim 元素吧:
<trim prefix="SET" suffixOverrides=",">
...
</trim>
10.3、choose,when,otherwise
List<Blog> getBlogsChoose(Map<String,Object> map);
<select id="getBlogsChoose" resultType="Blog">
select * from blog
<where>
<choose>
<when test="title != null">
title=#{title}
</when>
<when test="author != null">
author=#{author}
</when>
<otherwise>
</otherwise>
</choose>
</where>
</select>
@org.junit.Test
public void T3(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Map<String,Object> map = new HashMap<>();
map.put("title","水滸傳");
//map.put("author","羅貫中");
List<Blog> blogsChoose = mapper.getBlogsChoose(map);
sqlSession.close();
}
Preparing: select * from blog WHERE title=?
==> Parameters: 水滸傳(String)
<== Columns: id, title, author, createTime, views
<== Row: 04f9dc6b550e4c7983ee77d66dd838f1, 水滸傳, 施耐庵, 2022-08-10 18:56:18, 10
choose 只選擇一個條件
10.4、foreach
List<Blog> getBlogsForeach(@Param("titles") List<String> titleList);
<select id="getBlogsForeach" parameterType="arraylist" resultType="Blog">
select * from blog
<where>
<foreach collection="titles" open="(" close=")" item="title" separator=" or ">
title=#{title}
</foreach>
</where>
</select>
@org.junit.Test
public void T4(){
SqlSession sqlSession = MybatisUtil.getSqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
List list = new ArrayList();
list.add("紅樓夢");
list.add("西游記");
List blogsForeach = mapper.getBlogsForeach(list);
for (Object foreach : blogsForeach) {
System.out.println(foreach);
}
sqlSession.close();
}
Preparing: select * from blog WHERE ( title=? or title=? )
==> Parameters: 紅樓夢(String), 西游記(String)
<== Columns: id, title, author, createTime, views
<== Row: 3762dbf8b25847458e4417b5c9787b62, 紅樓夢, 曹雪芹, 2022-07-30 18:54:57, 100
<== Row: a064b30ced414ee78b1b1523c6342f88, 西游記, 吳承恩, 2022-08-11 18:54:27, 30
<== Total: 2
11、快取
11.1、簡介
1.什么是快取 Cache ?
- 存在記憶體中的臨時資料,
- 將用戶經常查詢的資料放在快取(記憶體)中,用戶去查詢資料就不用從磁盤上(關系型資料庫資料檔案)查詢,從快取中查詢,從而提高查詢效率,解決了高并發系統的性能問題,
2.為什么使用快取?
- 減少和資料庫的互動次數,減少系統開銷,提高系統效率,
3.什么樣的資料能使用快取?
- 經常查詢并且不經常改變的資料,
11.2、Mybatis快取
- MyBatis包含-個非常強大的查詢快取特性,它可以非常方便地定制和配置快取,快取可以極大的提升查詢效率
- MyBatis系統中默認定義了兩級快取: 一級快取和二級快取
- 默認情況下,只有一級快取開啟,(SqlSession級別的快取, 也稱為本地快取)
- 二級快取需要手動開啟和配置, 他是基于namespace級別的快取,
- 為了提高擴展性,MyBatis定義了快取介面Cache,我們可以通過實作Cache介面來自定義二級快取
11.3、測驗一級快取
一級快取僅僅對一個會話中的資料進行快取(即sqlsession打開和關閉期間)
map.put("title","水滸傳");
//map.put("author","羅貫中");
List<Blog> blogsChoose1 = mapper.getBlogsChoose(map);
List<Blog> blogsChoose2 = mapper.getBlogsChoose(map);
System.out.println(blogsChoose1.hashCode());
System.out.println(blogsChoose2.hashCode());
sqlSession.close();
Created connection 962944318.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@39655d3e]
==> Preparing: select * from blog WHERE title=?
==> Parameters: 水滸傳(String)
<== Columns: id, title, author, createTime, views
<== Row: 04f9dc6b550e4c7983ee77d66dd838f1, 水滸傳, 施耐庵, 2022-08-10 18:56:18, 10
<== Total: 1
264394960
264394960
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@39655d3e]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@39655d3e]
Returned connection 962944318 to pool.
可以通過日志看到,輸出的hashcode一樣,證明是同一個物件,并且只執行了一次SQL
改變引數看看:
Map<String,Object> map1 = new HashMap<>();
map1.put("title","水滸傳");
List<Blog> blogsChoose1 = mapper.getBlogsChoose(map1);
Map<String,Object> map2 = new HashMap<>();
map2.put("title","西游記");
List<Blog> blogsChoose2 = mapper.getBlogsChoose(map2);
System.out.println(blogsChoose1.hashCode());
System.out.println(blogsChoose2.hashCode());
sqlSession.close();
Created connection 962944318.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@39655d3e]
==> Preparing: select * from blog WHERE title=?
==> Parameters: 水滸傳(String)
<== Columns: id, title, author, createTime, views
<== Row: 04f9dc6b550e4c7983ee77d66dd838f1, 水滸傳, 施耐庵, 2022-08-10 18:56:18, 10
<== Total: 1
==> Preparing: select * from blog WHERE title=?
==> Parameters: 西游記(String)
<== Columns: id, title, author, createTime, views
<== Row: a064b30ced414ee78b1b1523c6342f88, 西游記, 吳承恩, 2022-08-11 18:54:27, 30
<== Total: 1
1878413745
769132178
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@39655d3e]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@39655d3e]
Returned connection 962944318 to pool.
執行了兩次SQL陳述句,hashcode也不一樣
對表的任何資料進行增刪改都會導致清除快取,即使是不相關的資料
小結: 一級快取默認是開啟的,只在一次SqISession中有效, 也就是拿到連接到關閉連接這個區間段!
11.4、二級快取
- 二級快取也叫全域快取,- -級快取作用域太低了,所以誕生了二級快取
- 基于namespace級別的快取,-個名稱空間,對應一個二級快取;
- 作業機制
- 一個會話查詢一條資料,這個資料就會被放在當前會話的一級快取中;
- 如果當前會話關閉了,這個會話對應的一級快取就沒了;但是我們想要的是,會話關閉了,一級快取中的資料被保存到* 級快取中;
- 新的會話查詢資訊,就可以從二級快取中獲取內容;
- 不同的mapper查出的資料會放在自己對應的快取(map) 中
開啟步驟
1、開啟全域快取
<!-- 顯示的開啟全域快取 -->
<setting name="cacheEnabled" value="https://www.cnblogs.com/Upzhh/p/true"/>
2、在要使用二級快取的mapper中開啟
<!--在當前mapper配置中開啟快取-->
<cache/>
基本上就是這樣,這個簡單陳述句的效果如下:
- 映射陳述句檔案中的所有 select 陳述句的結果將會被快取,
- 映射陳述句檔案中的所有 insert、update 和 delete 陳述句會重繪快取,
- 快取會使用最近最少使用演算法(LRU, Least Recently Used)演算法來清除不需要的快取,
- 快取不會定時進行重繪(也就是說,沒有重繪間隔),
- 快取會保存串列或物件(無論查詢方法回傳哪種)的 1024 個參考,
- 快取會被視為讀/寫快取,這意味著獲取到的物件并不是共享的,可以安全地被呼叫者修改,而不干擾其他呼叫者或執行緒所做的潛在修改,
提示 快取只作用于 cache 標簽所在的映射檔案中的陳述句,如果你混合使用 Java API 和 XML 映射檔案你需要使用 @CacheNamespaceRef 注解指定快取作用域,
這些屬性可以自定義修改:
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
可用的清除策略有:
- LRU – 最近最少使用:移除最長時間不被使用的物件,
- FIFO – 先進先出:按物件進入快取的順序來移除它們,
- SOFT – 軟參考:基于垃圾回收器狀態和軟參考規則移除物件,
- WEAK – 弱參考:更積極地基于垃圾收集器狀態和弱參考規則移除物件,
默認的清除策略是 LRU,
readOnly(只讀)屬性可以被設定為 true 或 false,只讀的快取會給所有呼叫者回傳快取物件的相同實體, 因此這些物件不能被修改,這就提供了可觀的性能提升,
而可讀寫的快取會(通過序列化)回傳快取物件的拷貝, 速度上會慢一些,但是更安全,因此默認值是 false,
提示 二級快取是事務性的,這意味著,當 SqlSession 完成并提交時,或是完成并回滾,但沒有執行 flushCache=true 的 insert/delete/update 陳述句時,快取會獲得更新,
3、測驗
Map<String,Object> map = new HashMap<>();
map.put("title","水滸傳");
SqlSession sqlSession1 = MybatisUtil.getSqlSession();
BlogMapper mapper1 = sqlSession1.getMapper(BlogMapper.class);
List<Blog> blogsChoose1 = mapper1.getBlogsChoose(map);
System.out.println(blogsChoose1.hashCode());
sqlSession1.close();
System.out.println("====================================");
SqlSession sqlSession2 = MybatisUtil.getSqlSession();
BlogMapper mapper2 = sqlSession2.getMapper(BlogMapper.class);
List<Blog> blogsChoose2 = mapper2.getBlogsChoose(map);
System.out.println(blogsChoose2.hashCode());
sqlSession2.close();
Opening JDBC Connection
Created connection 1444440224.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@561868a0]
==> Preparing: select * from blog WHERE title=?
==> Parameters: 水滸傳(String)
<== Columns: id, title, author, createTime, views
<== Row: 04f9dc6b550e4c7983ee77d66dd838f1, 水滸傳, 施耐庵, 2022-08-10 18:56:18, 10
<== Total: 1
790094636
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@561868a0]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@561868a0]
Returned connection 1444440224 to pool.
====================================
Cache Hit Ratio [com.zh.dao.BlogMapper]: 0.5
790094636
11.5、自定義快取
略
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/514992.html
標籤:Java
下一篇:day48-JDBC和連接池04
