1. 程式人生 > 資訊 >蘋果 iOS 15.3.1/ iPadOS 15.3.1 正式版釋出:修復 Safari 網頁漏洞

蘋果 iOS 15.3.1/ iPadOS 15.3.1 正式版釋出:修復 Safari 網頁漏洞

MyBatis小結

MyBatis

環境:

  • JDK 1.8

  • Mysql 8.0.13

  • maven 3.6.2

  • IDEA 2021.1

回顧:

  • JDBC

  • mysql

  • Java基礎

  • maven

  • junit

框架:配置檔案,看官網

一、簡介

1.1 什麼是MyBatis?

MyBatis 是一款優秀的持久層框架,它支援自定義 SQL、儲存過程以及高階對映。

MyBatis 免除了幾乎所有的 JDBC 程式碼以及設定引數和獲取結果集的工作。

MyBatis 可以通過簡單的 XML註解來配置和對映原始型別、介面和 Java POJO(Plain Old Java Objects,普通老式 Java 物件)為資料庫中的記錄。

MyBatis的前身是apache的一個開源專案iBatis,2010年這個專案由apache software foundation遷移到了google code,並且改名為MyBatis。2013年11月遷移到Github

1.2 獲取MyBatis

  • maven
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.7</version>
</dependency>

1.3 持久化

資料持久化

  • 持久化就是將程式的資料在持久狀態和瞬時狀態轉化的過程

  • 記憶體:斷電即失

  • 資料庫,io檔案持久化

  • 生活:冷藏、罐頭

為什麼需要持久化?

  • 有一些物件,不能讓他丟掉

  • 記憶體太貴了

1.4 持久層

Dao層、service層、Controller層...

  • 完成持久化工作的程式碼塊

  • 層界限十分明顯

1.5 為什麼需要MyBatis?

  • 幫助開發人員將資料存入到資料庫中

  • 方便

  • 傳統的jdbc程式碼太複雜,簡化、框架、自動化

  • 更重要的一點:使用的人多

二、CRUD

  1. namespace: namespace是介面(dao/mapper)的全路徑名

  2. select

  • id : 就是介面中的方法名

  • resultType : sql語句執行的返回值

  • parameterType : 引數型別

  1. insert

  2. update

  3. delete

注意點:增刪改需要提交事務!

三、配置解析

3.1 核心配置檔案

MyBatis 的配置檔案包含了會深深影響 MyBatis 行為的設定和屬性資訊。 配置文件的頂層結構如下:

3.2 環境配置environments

  • 可以配置多套環境

  • 預設使用的環境 ID(比如:default="development")

  • 每個 environment 元素定義的環境 ID(比如:id="test")

  • 事務管理器的配置(比如:type="JDBC")

    • 事務管理器(transactionManager)

    • 在 MyBatis 中有兩種型別的事務管理器(也就是 type="[JDBC|MANAGED]"):

      • JDBC – 這個配置直接使用了 JDBC 的提交和回滾設施,它依賴從資料來源獲得的連線來管理事務作用域。

      • JDBC – 這個配置直接使用了 JDBC 的提交和回滾設施,它依賴從資料來源獲得的連線來管理事務作用域。

  • 資料來源的配置(比如:type="POOLED")

    • 資料來源(dataSource)

    • dataSource 元素使用標準的 JDBC 資料來源介面來配置 JDBC 連線物件的資源。

    • 有三種內建的資料來源型別(也就是 type="[UNPOOLED|POOLED|JNDI]")

      • UNPOOLED– 這個資料來源的實現會每次請求時開啟和關閉連線。雖然有點慢,但對那些資料庫連線可用性要求不高的簡單應用程式來說,是一個很好的選擇。 效能表現則依賴於使用的資料庫,對某些資料庫來說,使用連線池並不重要,這個配置就很適合這種情形。

      • POOLED– 這種資料來源的實現利用“池”的概念將 JDBC 連線物件組織起來,避免了建立新的連線例項時所必需的初始化和認證時間。 這種處理方式很流行,能使併發 Web 應用快速響應請求。

      • JNDI – 這個資料來源實現是為了能在如 EJB 或應用伺服器這類容器中使用,容器可以集中或在外部配置資料來源,然後放置一個 JNDI 上下文的資料來源引用。

<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=true&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8"/>
            <property name="username" value="root"/>
            <property name="password" value="root"/>
        </dataSource>
    </environment>

    <environment id="test">
        <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=true&useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8"/>
            <property name="username" value="root"/>
            <property name="password" value="root"/>
        </dataSource>
    </environment>
</environments>

3.3 屬性properties

  1. 編寫一個配置檔案:jdbc.properties
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/test
jdbc.username=root
jdbc.password=root
  1. 在核心配置檔案中引入jdbc.properties
<!--引入外部配置檔案-->
<properties resource="jdbc.properties"/>
    <property name="username" value="root"/>
    <property name="password" value="root"/>
</properties>
  • 可以直接引入外部檔案

  • 可以在其中增加一些屬性配置

  • 如果兩個檔案有同一個欄位,優先載入使用外部配置檔案的!

注意點: properties中還可以再寫property屬性,如果jdbc.properties和mybatis-config.xml都有username欄位值,優先載入jdbc.properties檔案中的值!

3.4 類型別名typeAliases

類型別名可為 Java 型別設定一個縮寫名字。 它僅用於 XML 配置,意在降低冗餘的全限定類名書寫。

  • 方式一:為每一個實體類設定別名

mybatis-config.xml

<typeAliases>
     <typeAlias type="cn.com.longer.entity.User" alias="userAlias"></typeAlias>
</typeAliases>

UserMapper.xml

<update id="updateUser" parameterType="userAlias">
        update user set pwd = #{pwd} where id = #{id}
</update>
  • 方式二:為實體類所在的包設定

mybatis-config.xml

<typeAliases>
<!--        <typeAlias type="cn.com.longer.entity.User" alias="userAlias"></typeAlias>-->
        <package name="cn.com.longer.entity"/>
    </typeAliases>

UserMapper.xml

<update id="updateUser" parameterType="user">
        update user set pwd = #{pwd} where id = #{id}
 </update>

注: 為實體包設定別名,xml檔案中paramterType應該為實體類類名首字母小寫!

  • 方式三:在實體類上添加註解

    @Alias("userAlias")
    public class User {}
    

3.5 設定settings

這是mybatis中極為重要的調整設定,它會改變MyBatis的執行時行為。




一個配置完整的 settings 元素的示例如下:

<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
</settings>

3.6 對映器mapper

MapperRegistry : 註冊繫結我們的mapper檔案!

org.apache.ibatis.binding.BindingException: Type interface cn.com.longer.dao.UserMapper is not known to the MapperRegistry.
  • 方式一:使用mapper.xml檔案進行繫結【推薦使用】
<!--  每一個mapper.xml都需要在mybatis核心配置檔案中註冊!  -->
    <mappers>
        <mapper resource="cn/com/longer/dao/UserMapper.xml"/>
    </mappers>
  • 方式二:使用class檔案進行繫結
<mappers>
    <mapper class="cn.com.longer.dao.UserMapper"></mapper>
</mappers>

注意點:

  • 介面和它的mapper配置檔案必須同名!

  • 介面和它的mapper配置檔案必須在同一個包下!

  • 方式三:使用掃描package進行註冊

<mappers>
    <package name="cn.com.longer.dao"/>
</mappers>

注意點:

  • 介面和它的mapper配置檔案必須同名!

  • 介面和它的mapper配置檔案必須在同一個包下!

3.7 生命週期和作用域

作用域生命週期類別是至關重要的,因為錯誤的使用會導致非常嚴重的併發問題。

SqlSessionFactoryBuilder

  • 一旦用SqlSessionFactoryBuilder建立完SqlSessionFactory後,就不再需要它了

  • 它的最佳作用域是方法作用域(也就是區域性方法變數

SqlSessionFactory

  • 可以把它想象為:資料庫連線池

  • SqlSessionFactory 一旦被建立就應該在應用的執行期間一直存在沒有任何理由丟棄它或重新建立另一個例項。

  • 因此 SqlSessionFactory 的最佳作用域是應用作用域。使用單例模式或者靜態單例模式。

SqlSession

  • 連線到請求池的一個請求

  • 每個執行緒都應該有它自己的 SqlSession 例項。

  • SqlSession 的例項不是執行緒安全的,因此是不能被共享的,所以它的最佳的作用域是請求或方法作用域。

  • 每次收到 HTTP 請求,就可以開啟一個 SqlSession,返回一個響應後,就關閉它。用完之後趕緊關閉,否則資源被佔用!把這個關閉操作放到 finally 塊中。

Mapper對映器例項

  • 對映器介面的例項是從 SqlSession 中獲得的。

  • 對映器例項應該在呼叫它們的方法中被獲取,使用完畢之後即可丟棄。

四、結果集對映

問題:Java實體類中的屬性名和資料庫表中的欄位名不一致!

User.java | id | name | password
user表    | id | name | pwd
  • 方法一:sql別名

    <select id="getUserList" resultType="cn.com.longer.entity.User">
       select id, name, pwd as password from user
    </select>
    
  • 方法二:resultMap

    結果集對映:

    <resultMap id="BaseUserMap" type="cn.com.longer.entity.User">
        <result column="id" property="id"></result>
        <result column="name" property="name"></result>
        <result column="pwd" property="password"></result>
    </resultMap>
    <select id="getUserList" resultMap="BaseUserMap">
        select id, name, pwd from user
    </select>
    
  • 注:如果column和property一致時,不需要配置它們,只需要配置二者不一樣的即可!

  • resultMap 元素是 MyBatis 中最重要最強大的元素。

  • ResultMap 的優秀之處——你完全可以不用顯式地配置它們。

  • 如果這個世界總是這麼簡單就好了。

五、日誌

5.1 日誌工廠

settings設定中有此設定

logImpl:

  • SLF4J

  • LOG4J(deprecated since 3.5.9) 【掌握】

  • LOG4J2

  • JDK_LOGGING

  • COMMONS_LOGGING

  • STDOUT_LOGGING 【掌握:標準日誌工廠實現】

  • NO_LOGGING

<setting name="logImpl" value="STDOUT_LOGGING" />

5.2 Log4j

什麼是Log4j?

  • Log4j是Apache的一個開源專案,通過使用Log4j,我們可以控制日誌資訊輸送的目的地是控制檯、檔案、GUI元件

  • 也可以控制每一條日誌的輸出格式

  • 通過定義每一條日誌資訊的級別(info、debug、error),我們能夠更加細緻地控制日誌的生成過程

  • 這些可以通過一個配置檔案來靈活地進行配置,而不需要修改應用的程式碼

使用步驟:

  1. 導包
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>
  1. mybatis-config.xml中設定日誌的具體實現
<settings>
    <setting name="logImpl" value="LOG4J"/>
</settings>
  1. 建立log4j.properties配置檔案
# 將等級為DEBUG的日誌資訊輸出到console和file這兩個目的地,console和file的定義在下面的程式碼中
log4j.rootLogger=DEBUG,console,file

# 控制檯輸出的相關設定
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.Target=System.out
log4j.appender.console.Threshold=DEBUG
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=[%c]-%m%n

# 檔案輸出的相關設定
log4j.appender.file=org.apache.log4j.RollingFileAppender
log4j.appender.file.File=./log/longer.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
  1. 在要輸出日誌的類中加入相關語句
public class UserMapperTest {
    // 定義屬性
    static Logger logger = Logger.getLogger(UserMapperTest.class);

    @Test
    public void getUserList(){
        logger.info("logger.info");
        logger.debug("logger.debug");
        logger.error("logger.error");
    }
}

六、分頁

為什麼要分頁?

減少資料的處理量

  1. 方式一:limit
-- 語法:SELECT * FROM table_name LIMIT startIndex, pageSize;
SELECT * FROM user LIMIT 3; [0, n]
  1. 方式二:使用MyBatis實現分頁,核心SQL

  2. 介面

List<User> getUserListByLimit(HashMap<String, Integer> limitMap);
  1. mapper.xml
<select id="getUserListByLimit" parameterType="map" resultMap="BaseUserMap">
    select * from user limit #{startIndex}, #{pageSize}
</select>

3.測試

@Test
    public void getUserListByLimit(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        HashMap<String, Integer> map = new HashMap<>();
        map.put("startIndex", 1);
        map.put("pageSize", 2);
        List<User> userList = mapper.getUserListByLimit(map);
        for (User user : userList) {
            logger.info("getUserListByLimit:" + user);
        }
        sqlSession.close();
    }
  1. 分頁外掛

pageHelper外掛: https://pagehelper.github.io/

七、使用註解開發

MyBatis的sql也可以使用註解的形式接在介面方法上面!

使用步驟:

  1. mybatis-config.xml進行介面註冊
<mappers>
    <mapper class="cn.com.longer.dao.UserMapper"></mapper>
</mappers>
  1. 在介面中的方法寫sql
@Select("select * from user")
List<User> selectUserList();

@Insert("insert into user(id, name, pwd) values(#{id}, #{name}, #{password})")
int insertUser(User user);

@Delete("delete from user where id = #{uid}")
int deleteUser(@Param("uid") int id);

@Update("update user set name = #{name} and pwd = #{password} where id = #{id}")
int updateUser(User user);

本質: 反射機制實現

底層: 動態代理

註解的侷限性:

  • 只適用於簡單的sql

  • 如果是複雜的sql,資料庫表字段與實體類屬性不一致,則力不從心

關於@Param()註解:

  • 基本型別的引數或者String型別,需要加上

  • 引用型別不需要加

  • 如果只有一個基本型別的引數時,可以不加,但建議加上

  • sql語句中的運用就是這裡@Param()中設定的屬性名

八、Lombok

Lombok是作用在POJO的,極大的簡化了程式碼量,避免程式設計師寫許多重複的程式碼!

使用步驟:

1.IDEA中安裝Lombok外掛

2.引入lombok的pom依賴

<dependencies>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.22</version>
    </dependency>
</dependencies>
  1. 實體類中使用註解
@Data : NoArgsConstructor, Getter,Setter,equals,hashCode,toString
@Getter and @Setter
@FieldNameConstants
@ToString
@EqualsAndHashCode
@AllArgsConstructor, 
@RequiredArgsConstructor  
@NoArgsConstructor

九、MyBatis詳細的執行流程

  1. Resource獲取載入全域性配置檔案(mybatis-config.xml)

  2. 例項化SqlSessionFactoryBuilder構造器

  3. 解析配置檔案流XMLConfigBuilder

  4. Configuration所有的配置資訊

  5. SqlSessionFactory例項化

  6. transaction事務管理

  7. 建立executor執行器

  8. 建立SqlSession

  9. 實現CRUD

  10. 檢視是否執行成功

  11. 提交事務

  12. 關閉

十、多對一的處理 :

多個學生對應一個老師:

10.1 根據查詢巢狀處理

資料庫表:

-- student表
id name tid
-- teacher表
id name

Java實體類:

public class Student{
    private int id;
    private String name;
    private Teacher teacher;   
}
public class Teacher{
    private int id;
    private String name;
}

1.介面

List<Student> getStudent();

2.Mapper.xml

<resultMap id="StudentTeacher" type="cn.com.longer.entity.Student"> 
    <result property="id" column="id" />
    <result property="name" column="name" />
    <!--複雜的屬性,我們需要單獨處理。 物件:association 集合:collection-->
    <association property="teacher" column="tid" javaType="cn.com.longer.entity.Teacher" select="getTeacher" />
</resultMap>
<select id="getStudent" resultMap="StudentTeacher">
    select * from student
</select>

<select id="getTeacher" resultType="cn.com.longer.entity.Teacher">
    select * from teacher where id = #{id}
</select>

10.2 按照結果巢狀處理

  1. 介面
List<Student> getStudent2();
  1. Mapper.xml
<resultlMap id="StudentTeacher2" type="cn.com.longer.entity.Student">
    <result property="id" column="sid" />
    <result property="name" column="sname" />
    <association property="teacher" javaType="cn.com.longer.entity.Teahcer">
        <result property="id" column="tid" />
        <result property="name" column="tname" />
    </association>
</resultMap>

<select id="getStudent2" resultMap="StudentTeacher2">
    select s.id sid, s.name sname, t.id tid, t.name tname
    from student s, teacher t
    where s.tid = t.id
</select>

十一、一對多處理

比如:老師擁有多個學生,對於老師而言,就是一對多的關係!

獲取指定老師下的所有學生及老師的資訊:

11.1 按照結果巢狀處理

Java實體類

public class Student{
    private int id;
    private String name;
}
public class Teacher{
    private int id;
    private String name;
    private List<Student> students;
}
  1. 介面:
Teacher getTeacherById(@Prarm("tid") int id);
  1. Mapper.xml
<resultMap id="teachetStudentList" type="cn.com.longer.entity.Teacher">
    <result property="id" column="tid" />
    <result property="name" column="tname" />
    <!--複雜的屬性,我們需要單獨處理: 物件:association 集合:collection
        javaType 指定屬性的型別
        集合中的泛型資訊,我們使用ofType獲取
    -->
    <collection property="students" ofType="cn.com.longer.entity.Student">
        <result property="id" column="sid" />
        <result property="name" column="sname" />
    </association>
</resultMap>

<select id="getTeacherById" resultMap="teachetStudentList">
    select s.id sid, s.name sname, t.id tid, t.name tname
    from student s, teacher t
    where s.tid = t.id and t.id = #{tid}
</select>

11.2 按照查詢巢狀處理

1.介面:

Teacher getTeacherById2(@Param("tid") int id);

2.Mapper.xml

<resultMap id="teachetStudentList2" type="cn.com.longer.entity.Teacher">
    <!--屬性和欄位相同的可以不寫-->
    <collection property="students" column="id" javaType="ArrayList" ofType="cn.com.longer.entity.Student" select="getStudentByTeacherId" />
</resultMap>

<select id="getTeacherById2" resultMap="teachetStudentList2">
    select id, name from teacher where id = #{tid}
</select>
<select id="getStudentByTeacherId" resultType="cn.com.longer.entity.Student">
    select * from student where tid=#{tid}
</select>

小結:

  1. 關聯 - association [多對一]

  2. 結合 - collection [一對多]

  3. javaType & ofType

  4. javaType : 用來指定實體類中屬性的型別

  5. ofType : 用來指定對映到List或者集合中的pojo型別,泛型中的約束型別

注意點:

  • 保證SQL的可讀性,計量保證通俗易懂
  • 注意一對多和多對一中,屬性名和欄位的問題
  • 如果問題不好排查錯誤,可以使用日誌,建議使用Log4j

面試高頻:

  • MySQL引擎
  • InnoDB底層原理
  • 索引
  • 索引優化

十二、動態SQL

什麼是動態SQL? 根據不同的條件,生產不同的SQL

12.1 SQL片段

有些時候,我們需要將一些功能的sql抽取出來,方便複用!

  1. 使用sql標籤抽取公共部分:
<sql id="if-title-author">
    <if test="title !=nulll">
        title = #{title}
    </if>
    <if test="author !=nulll">
        and author = #{author}
    </if>
</sql>
  1. 在需要使用的地方使用include 標籤引用即可:
<select id="queryBlogIf" parameterType="map" resultType="blog">
    select * from blog
    <where>
        <include refid="if-title-author" />
    </where>
</select>

注意事項:

  • 最好基於單表來定義SQL片段

  • 不要存在where標籤

12.2 if

使用動態 SQL 最常見情景是根據條件包含 where 子句的一部分。

<select id="findActiveBlogLike" resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <if test="title != null">
    AND title like #{title}
  </if>
  <if test="author != null and author.name != null">
    AND author_name like #{author.name}
  </if>
</select>

12.3 choose (when, otherwise)

  • 有時候,我們不想使用所有的條件,而只是想從多個條件中選擇一個使用。針對這種情況,MyBatis 提供了 choose 元素,它有點像 Java 中的 switch 語句。

  • 還是上面的例子,但是策略變為:傳入了 “title” 就按 “title” 查詢,傳入了 “author” 就按 “author” 查詢的情形。若兩者都沒有傳入,就返回標記為 featured 的 BLOG

<select id="findActiveBlogLike" resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <choose>
    <when test="title != null">
      AND title like #{title}
    </when>
    <when test="author != null and author.name != null">
      AND author_name like #{author.name}
    </when>
    <otherwise>
      AND featured = 1
    </otherwise>
  </choose>
</select>

12.4 trim (where, set)

  • where 元素只會在子元素返回任何內容的情況下才插入 “WHERE” 子句。而且,若子句的開頭為 “AND” 或 “OR”,where 元素也會將它們去除。
<select id="findActiveBlogLike" resultType="Blog">
  SELECT * FROM BLOG
  <where>
    <if test="state != null">
         state = #{state}
    </if>
    <if test="title != null">
        AND title like #{title}
    </if>
    <if test="author != null and author.name != null">
        AND author_name like #{author.name}
    </if>
  </where>
</select>
  • 如果 where 元素與你期望的不太一樣,你也可以通過自定義 trim 元素來定製 where 元素的功能。比如,和 where 元素等價的自定義 trim 元素為:
<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>

prefixOverrides: 屬性會忽略通過管道符分隔的文字序列(注意此例中的空格是必要的)。上述例子會移除所有 prefixOverrides 屬性中指定的內容,並且插入 prefix 屬性中指定的內容。

  • 用於動態更新語句的類似解決方案叫做 setset 元素可以用於動態包含需要更新的列,忽略其它不更新的列。
<update id="updateAuthorIfNecessary">
  update Author
    <set>
      <if test="username != null">username=#{username},</if>
      <if test="password != null">password=#{password},</if>
      <if test="email != null">email=#{email},</if>
      <if test="bio != null">bio=#{bio}</if>
    </set>
  where id=#{id}
</update>

set 元素會動態地在行首插入 SET 關鍵字,並會刪掉額外的逗號(這些逗號是在使用條件語句給列賦值時引入的)。

  • 來看看與 set 元素等價的自定義 trim 元素吧:
<trim prefix="SET" suffixOverrides=",">
  ...
</trim>

這樣:我們覆蓋了字尾值設定,並且自定義了字首值。

3.5 Foreach

動態 SQL 的另一個常見使用場景是對集合進行遍歷(尤其是在構建 IN 條件語句的時候)。

foreach 元素的功能非常強大,它允許你指定一個集合,宣告可以在元素體內使用的集合項(item)和索引(index)變數。它也允許你指定開頭與結尾的字串以及集合項迭代之間的分隔符。這個元素也不會錯誤地新增多餘的分隔符,看它多智慧!

  • 你可以將任何可迭代物件(如 List、Set 等)、Map 物件或者陣列物件作為集合引數傳遞給 foreach

  • 當使用可迭代物件或者陣列時,index 是當前迭代的序號,item 的值是本次迭代獲取到的元素。

  • 當使用 Map 物件(或者 Map.Entry 物件的集合)時,index 是鍵,item 是值。

使用陣列:

  1. 介面
List<Post> selectPostIn(List<Integer> ids);

2.Mapper.xml

<select id="selectPostIn" resultType="domain.blog.Post">
  SELECT *
  FROM POST P
  WHERE ID in
  <foreach item="item" index="index" collection="list"
      open="(" separator="," close=")">
        #{item}
  </foreach>
</select>

使用map:

  1. 介面
List<Blog> queryBlogForeach(Map map);
  1. Mapper.xml
<!--傳遞一個萬能的Map,map中可以存在一個集合!-->
<select>
    select * from blog
    <where>
        <foreach collection="ids" item="id" open="and (" close=")" separator="or">
            id=#{id}
        </foreach>
    </where>
</select>
  1. 測試
Map map = new HashMap();
List ids = new ArrayList<Integer>();
ids.add(1);
ids.add(2);
map.put("ids", ids);
List<Blog> blogList = mapper.queryBlogForeach(map);

十三、快取

13.1 簡介:

查詢 : 連線資料庫,耗資源!
    一次查詢的結果,給他暫存在一個可以直接取到的地方!---> 記憶體:快取
    我們再次查詢相同資料的時候,直接走快取,就不用走資料庫了!
  1. 什麼是快取[cache]?
  • 存在記憶體中的臨時資料

  • 將使用者經常查詢的資料放在快取(記憶體)中,使用者去查詢資料就不用從磁碟上(關係型資料庫資料檔案)查詢,從快取中查詢,從而提高查詢效率,解決了高併發系統的效能問題。

  1. 為什麼使用快取?
  • 減少和資料庫的互動次數,減少系統開銷,提高系統效率。

  • 快取資料在記憶體,記憶體的讀取速度 > 硬碟

  1. 什麼樣的資料能使用快取?
  • 經常查詢並且不經常改變的資料。【可以使用快取】

13.2 MyBatis快取

  • MyBatis包含了一個非常強大的查詢快取特性,它可以非常方便地制定和配置快取。快取可以極大的提升查詢效率。

  • MyBatis系統中預設定義了兩級快取:一級快取二級快取

    • 一級快取:SqlSession級別的快取,也稱本地快取。預設情況下,只有一級快取開啟

    • 二級快取需要手動開啟和配置,它是基於namespace級別的快取(mapper.xml檔案)

    • 為了提高擴充套件性,MyBatis定義了快取介面Cacha。我們可以通過實現Cache介面來自定義二級快取。

13.3 一級快取

  • 一級快取也叫本地快取:

    • 與資料庫同一次會話期間查詢到的資料會放在本地快取中

    • 以後如果需要獲取相同的資料,直接從快取中拿,沒必要再去查詢資料庫

測試步驟:

  1. 開啟日誌

  2. 測試在一個SqlSession中查詢兩次相同的記錄

  3. 檢視日誌輸出

快取失效:

  1. 查詢不同的資料

  2. 增刪改操作,可能會改變原來的資料,所以必定會重新整理快取

  3. 查詢不同的Mapper.xml

  4. 手動清除快取:

sqlSession.clearCache(); // 手動清理快取

小結:

  • 一級快取預設是開啟的,只在一次SqlSession中有效,也就是拿到連線到關閉連線這個區間段

  • 一級快取相當於一個Map

13.4 二級快取

  • 二級快取也叫全域性快取,一級快取作用域太低了,所以誕生了二級快取

  • 基於namespace基本的快取,一個名稱空間,對應一個二級快取

  • 被快取的實體類需要序列化 User implements Serializable

  • 若某個SQL語句不想使用快取 useCache="false" or flushCache='true'

  • 工作機制:

    • 一個會話查詢一條資料,這個資料就會被放在當前會話的一級快取中

    • 如果當前會話關閉了,這個會話對應的一級快取就沒了;但是我們想要的是,會話關閉了,一級快取中的資料被儲存到二級快取中

    • 新的會話查詢資訊,就可以從二級快取中獲取內容

    • 不同的mapper查出的資料會放在自己對應的快取(map)中

使用步驟:

  1. mybatis-config.xml中開啟全域性快取
<!--顯示的開啟全域性快取-->
<setting name="cacheEnabled" value="true" />
  1. 在要使用二級快取的Mapper.xml檔案中,新增標籤
<!--在當前mapper.xml中使用二級快取-->
<cache 
    eviction="FIFO"
    flushInterval="60000"
    size="512"
    readOnly="true"
/>

小結:

  • 只要開啟了二級快取,在同一個Mapper下就有效

  • 所有的資料都會先放在一級快取中

  • 只有當會話提交,或者關閉時,才會提交到二級快取中

13.5 快取原理

快取順序:

  1. 先看二級快取中有沒有

  2. 再看一級快取中有沒有

  3. 查詢資料庫

13.6 自定義快取-ehcache

ehcache是一種廣泛使用的開源Java分散式快取。主要面向通用快取!

使用步驟:

  1. 導包
<dependency>
    <groupId>org.mybatis.caches</groupId>
    <artifactId>mybatis-ehcache</artifactId>
    <version>1.1.0</version>
</dependency>
  1. 在Mapper.xml中指定使用我們的ehcache,實現快取
<cache type="org.mybatis.caches.ehcache.EhcacheCache" />

Redis資料庫來做快取!K-V

13.7 引用快取 cache-ref

對某一名稱空間的語句,只會使用該名稱空間的快取進行快取或重新整理。 但你可能會想要在多個名稱空間中共享相同的快取配置和例項。要實現這種需求,你可以使用 cache-ref 元素來引用另一個快取。

<cache-ref namespace="com.someone.application.data.SomeMapper"/>