IK擴充套件詞和停用詞
技術標籤:MyBatis
1、什麼是MyBatis
- MyBatis是一款優秀的持久層框架,它支援自定義SQL、儲存過程以及高階對映。
- MyBatis 免除了幾乎所有的 JDBC 程式碼以及設定引數和獲取結果集的工作
- MyBatis 可以通過簡單的 XML 或註解來配置和對映原始型別、介面和 Java POJO(Plain Old Java Objects,普通老式 Java 物件)為資料庫中的記錄
- MyBatis 是一個半自動化的ORM框架 (Object Relationship Mapping) -->物件關係對映
- MyBatis官方文件地址:https://mybatis.org/mybatis-3/zh/index.html
- GitHub專案地址:https://github.com/mybatis/mybatis-3
1.1、什麼是持久化
持久化(Persistence),即把資料(如記憶體中的物件)儲存到可永久儲存的儲存裝置中(如磁碟)。持久化的主要應用是將記憶體中的物件儲存在資料庫中,或者儲存在磁碟檔案中、XML資料檔案中等等。
持久化是將程式資料在持久狀態和瞬時狀態間轉換的機制。
通俗的講,就是瞬時資料(比如記憶體中的資料,是不能永久儲存的)持久化為持久資料(比如持久化至資料庫中,能夠長久儲存)。
1.2、什麼是持久層
持久層用來固化資料,如常說的DAO層,操作資料庫將資料入庫。
持久層,就是把持久的動作封裝成一個獨立的層,這是為了降低功能程式碼之間的關聯。建立一個更清晰的抽象,提高程式碼的內聚力,降低程式碼的耦合度,從而增強程式碼的要勞動局生和可重用性。
MyBatis 是一款優秀的持久層框架。
2、MyBatis入門
-
建立maven專案
-
匯入MyBatis的jar包
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency>
-
匯入MySQL的jar包
<dependency>
-
匯入JUnit測試包
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency>
-
建立MyBatis工具類
import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; 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 static SqlSession getSession() { // 開啟事務自動提交 return sqlSessionFactory.openSession(true); // 事務不會自動提交 // return sqlSessionFactory.openSession(); } }
-
建立MyBatis核心配置檔案mybatis-config.xml
<?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>
<!--匯入外部配置檔案-->
<properties resource="properties/db.properties"/>
<settings>
<!--設定日誌-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--設定資料來源-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<!--設定對映器(mapper)-->
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
</mappers>
</configuration>
db.properties
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://192.168.50.129:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username=mybatis
password=123456
- 編寫實體類
import java.util.Date;
public class User {
private int id;
private String user_name;
private String password;
private String sex;
private Date create_time;
private String birthday;
public User() {
}
public User(int id, String user_name, String password, String sex, String birthday) {
this.id = id;
this.user_name = user_name;
this.password = password;
this.sex = sex;
this.birthday = birthday;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUser_name() {
return user_name;
}
public void setUser_name(String user_name) {
this.user_name = user_name;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getSex() {
return sex;
}
public void setSex(String sex) {
this.sex = sex;
}
public Date getCreate_time() {
return create_time;
}
public void setCreate_time(Date create_time) {
this.create_time = create_time;
}
public String getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", user_name='" + user_name + '\'' +
", password='" + password + '\'' +
", sex='" + sex + '\'' +
", create_time=" + create_time +
", birthday='" + birthday + '\'' +
'}';
}
}
- 編寫Mapper介面類
import com.ghost.mybatis.pojo.User;
import java.util.List;
public interface UserMapper {
List<User> selectUser();
User selectUserById(int id);
int addUser(User user);
int updateUser(User user);
int deleteUser(int id);
}
- 編寫Mapper.xml配置檔案
<?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.ghost.mybatis.mapper.UserMapper">
<select id="selectUser" resultType="com.ghost.mybatis.pojo.User">
select *
from user
</select>
<select id="selectUserById" parameterType="int" resultType="user">
select *
from user
where id = #{id};
</select>
<insert id="addUser" parameterType="com.ghost.mybatis.pojo.User">
insert into user(id, user_name, password, sex, birthday)
values (#{id}, #{user_name}, #{password}, #{sex}, #{birthday});
</insert>
<update id="updateUser" parameterType="com.ghost.mybatis.pojo.User">
update user
set user_name = #{user_name},
password = #{password},
sex = #{sex},
birthday = #{birthday}
where id = #{id};
</update>
<delete id="deleteUser" parameterType="int">
delete
from user
where id = #{id};
</delete>
</mapper>
- 編寫測試類
import com.ghost.mybatis.mapper.UserMapper;
import com.ghost.mybatis.pojo.User;
import com.ghost.mybatis.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class MyTest {
@Test
public void selectUser() {
// 獲取SqlSession
SqlSession session = MybatisUtil.getSession();
//方法一:
//List<User> users = session.selectList("com.ghost.mybatis.mapper.UserMapper.selectUser");
//方法二:
UserMapper mapper = session.getMapper(UserMapper.class);
List<User> users = mapper.selectUser();
for (User user: users){
System.out.println(user);
}
session.close();
}
@Test
public void selectUserById() {
SqlSession session = MybatisUtil.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.selectUserById(1);
System.out.println(user);
session.close();
}
@Test
public void addUser() {
SqlSession session = MybatisUtil.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.addUser(new User(6, "李四", "12334", "女", "2020-02"));
// session.commit();
session.close();
}
@Test
public void updateUser() {
SqlSession session = MybatisUtil.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.updateUser(new User(1, "張三", "8899993", "男", "1990-09"));
// session.commit();
session.close();
}
@Test
public void deleteUser() {
SqlSession session = MybatisUtil.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
mapper.deleteUser(3);
// session.commit();
session.close();
}
}
- 執行測試查詢,查詢出結果,OK!!!
注意
- 如果將Mapper.xml檔案寫入src/main/java下,但是在執行時,target資料夾下沒有該檔案則,則需要在pom.xml 檔案中加入如下配置(Maven靜態資源過濾問題)
<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>
- 出現如下錯誤,則需要在MyBatis核心配置檔案中設定對映器
org.apache.ibatis.binding.BindingException: Type interface com.ghost.mybatis.dao.UserMapper is not known to the MapperRegistry.
-
使用高版本MySQL時需要在URL中設定時區:serverTimezone=Asia/Shanghai
-
配置檔案中namespace中的名稱為對應Mapper介面或者Dao介面的完整包名,必須一致
-
標籤不能對應錯
-
resource繫結mapper需要使用路徑
-
程式配置檔案必須符合規範
3、作用域(Scope)和生命週期
不同作用域和生命週期類別是至關重要的,因為錯誤的使用會導致非常嚴重的併發問題。
建立順序:SqlSessionFactoryBuilder --> SqlSessionFactory --> SqlSession
3.1 SqlSessionFactoryBuilder
- 這個類可以被例項化、使用和丟棄,一旦建立了 SqlSessionFactory,就不再需要它了
- 最佳作用域:區域性變數
3.2 SqlSessionFactory
- 可以想象為資料庫連線池
- SqlSessionFactory 一旦被建立就應該在應用的執行期間一直存在,沒有任何理由丟棄它或重新建立另一個例項
- SqlSessionFactory 的最佳作用域是應用作用域
- 使用單例模式或者靜態單例模式
3.3 SqlSession
- 每個執行緒都應該有它自己的 SqlSession 例項
- SqlSession 的例項不是執行緒安全的,因此是不能被共享的,所以它的最佳的作用域是請求或方法作用域
- 每次使用後確保執行關閉操作,否則資源被佔用
3.4 從 SqlSessionFactory 中獲取 SqlSession方式
- 方式一:(不推薦,型別不安全)
try (SqlSession session = sqlSessionFactory.openSession()) {
Blog blog = (Blog) session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 101);
}
- 方式二:
try (SqlSession session = sqlSessionFactory.openSession()) {
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(101);
}
- 每個執行緒都應該有它自己的 SqlSession 例項。SqlSession 的例項不是執行緒安全的,因此是不能被共享的,所以它的最佳的作用域是請求或方法作用域。
- 確保每次都能執行關閉操作
3.5 對映器
- 對映器是一些繫結對映語句的介面(定義對映SQL語句檔案)
- 既然 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 class="org.mybatis.builder.AuthorMapper"/>
<mapper class="org.mybatis.builder.BlogMapper"/>
<mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
<!-- 將包內的對映器介面實現全部註冊為對映器 -->
<mappers>
<package name="org.mybatis.builder"/>
</mappers>
- MapperRegistry:註冊繫結我們的Mapper檔案
- 在使用“使用對映器介面實現類的完全限定類名”和“將包內的對映器介面實現全部註冊為對映器”引入對映器介面時,需要注意如下問題:
- 介面和他的Mapper配置檔案必須同名
- 介面和他的Mapper配置檔案必須在同一個包下
3.6 對映器例項
- 對映器介面的例項是從 SqlSession 中獲得的
- 方法作用域才是對映器例項的最合適的作用域,對映器例項應該在呼叫它們的方法中被獲取,使用完畢之後即可丟棄
- 最好將對映器放在方法作用域內
try (SqlSession session = sqlSessionFactory.openSession()) {
BlogMapper mapper = session.getMapper(BlogMapper.class);
// 你的應用邏輯程式碼
}
4、XML配置
MyBatis 的配置檔案包含了會深深影響 MyBatis 行為的設定和屬性資訊,核心配置檔案一般為:mybatis-config.xml
4.1 屬性(properties)
- 我們可以通過Properties屬性來實現應用配置檔案
- 這些屬性可以在外部進行配置,並可以進行動態替換。你既可以在典型的 Java 屬性檔案中配置這些屬性,也可以在 properties 元素的子元素中設定db.properties
- 在核心配置檔案中引入外部配置檔案
<!-- 可以直接引入外部檔案 -->
<!-- 可以在其中增加一些屬性配置 -->
<!-- 如果兩個檔案中有同一個欄位,優先使用配置檔案的 -->
<properties resource="properties/db.properties"/>
- 在XML中,所有的標籤都可以規定其順序
4.2 環境配置(environments)
<environments default="development">
<environment id="development">
<transactionManager type="JDBC">
<property name="..." value="..."/>
</transactionManager>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
- MyBatis 可以配置成適應多種環境
- 儘管可以配置多個環境,但每個 SqlSessionFactory 例項只能選擇一種環境
- Mybatis預設的事務管理器是JDBC,連線池:POOLED
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>
- 也可以指定一個包名,MyBatis 會在包名下面搜尋需要的 Java Bean
<!-- 在沒有註解的情況下,會使用 Bean 的首字母小寫的非限定類名來作為它的別名 -->
<!-- 若有註解,則別名為其註解值 -->
<typeAliases>
<package name="domain.blog"/>
</typeAliases>
- 第一種可以使用DIY原則,第二種則不行,如果要使用則需要在實體類上增加註解
- 在實體類比較少的時候,可以使用第一種方式,如果實體類十分多,建議使用第二種
4.4 設定(settings)
- cacheEnabled - 全域性性地開啟或關閉所有對映器配置檔案中已配置的任何快取
- lazyLoadingEnabled - 延遲載入的全域性開關。當開啟時,所有關聯物件都會延遲載入。 特定關聯關係中可通過設定 fetchType 屬性來覆蓋該項的開關狀態
- useGeneratedKeys - 允許 JDBC 支援自動生成主鍵,需要資料庫驅動支援。如果設定為 true,將強制使用自動生成主鍵。儘管一些資料庫驅動不支援此特性,但仍可正常工作(如 Derby)
- mapUnderscoreToCamelCase - 是否開啟駝峰命名自動對映,即從經典資料庫列名 A_COLUMN 對映到經典 Java 屬性名 aColumn
- logImpl - 指定 MyBatis 所用日誌的具體實現,未指定時將自動查詢(SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING(標準日誌輸出) | NO_LOGGING)
4.5 日誌工廠
- 如果一個數據庫操作出現了異常,我們需要排錯。日誌就是最好的助手。
- 在mybatis中具體使用那一個日誌實現,在設定中設定。
- 配置資訊
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
-
LOG4J
- 什麼是log4j
- Log4j是Apache的一個開源專案,通過使用Log4j,我們可以控制日誌資訊輸送的目的地是控制檯、檔案、GUI元件
- 可以控制每一條日誌的輸出格式
- 通過定義每一條日誌資訊的級別,我們能夠更加細緻地控制日誌的生成過程
- 可以通過一個配置檔案來靈活地進行配置,而不需要修改應用的程式碼
- maven專案中匯入jar包
- 設定日誌格式及其他配置資訊
#將等級為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/kuang.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
- 在要使用Log4j的類中,匯入包 import org.apache.log4j.Logger;
- 日誌物件,引數設定為當前類的class
- 日誌級別:INFO|DEBUG|ERROR
- 什麼是log4j
4.6 其他配置
- typeHandlers(型別處理器)
- objectFactory(物件工廠)
- plugins(外掛)
- mybatis-generator 自動生成MyBatis程式碼
- mybatis-plus 可以提高mybatis的效率
- PageHelper MyBatis分頁外掛
- 通用mapper
5、XML 對映器
MyBatis 的真正強大在於它的語句對映,這是它的魔力所在。
SQL 對映檔案只有很少的幾個頂級元素(按照應被定義的順序列出)
cache
– 該名稱空間的快取配置。cache-ref
– 引用其它名稱空間的快取配置。resultMap
– 描述如何從資料庫結果集中載入物件,是最複雜也是最強大的元素。sql
– 可被其它語句引用的可重用語句塊。insert
– 對映插入語句。update
– 對映更新語句。delete
– 對映刪除語句。select
– 對映查詢語句。
增刪改需要提交事務
5.1 模糊查詢
-
java程式碼執行的時候,傳遞萬用字元%
-
在SQL拼接中使用萬用字元
5.2 字串替換
- 預設情況下,使用
#{}
引數語法時,MyBatis 會建立PreparedStatement
引數佔位符,並通過佔位符安全地設定引數(就像使用 ? 一樣) - #{}可以防止SQL注入
- 使用${}引數語法時,MyBatis 就不會修改或轉義該字串了
- 用這種方式接受使用者的輸入,並用作語句引數是不安全的,會導致潛在的 SQL 注入攻擊。
- 因此,要麼不允許使用者輸入這些欄位,要麼自行轉義並檢驗這些引數。
5.3 解決屬性名和欄位名不一致的問題
- 別名:查詢SQL中使用別名
- 結果集對映:resultMap
- resultMap 元素是 MyBatis 中最重要最強大的元素
- ResultMap 的設計思想是,對簡單的語句做到零配置,對於複雜一點的語句,只需要描述語句之間的關係就行了
- ResultMap 的優秀之處——你完全可以不用顯式地配置它們
如果這個世界總是這麼簡單就好了
5.4 分頁
-
為什麼要分頁 - 減少資料的處理量
-
使用Limit分頁
select * from user limit startIndex,pageSize;
-
使用mybatis實現分頁,核心是SQL
-
使用RowBounds分頁,不在使用SQL實現分頁(瞭解即可,不推薦使用)
-
分頁外掛
- Mybatis-PageHelper
5.5 高階結果對映
5.5.1 多對一處理
- 物件:association - 關聯【多對一】
- 按照查詢巢狀處理
- 按結果巢狀處理
- MySQL多對一查詢方式
- 子查詢
- 聯表查詢
- JavaType:用來指定實體類中屬性的型別
- ofType:用來指定對映到List或者集合中的POJO型別,泛型中的約束型別
5.5.2 練習
- MyBatis工具類
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
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 static SqlSession getSession() {
// 開啟事務自動提交
return sqlSessionFactory.openSession(true);
// 事務不會自動提交
// return sqlSessionFactory.openSession();
}
}
- 配置檔案
<?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>
<!--匯入外部配置檔案-->
<properties resource="properties/db.properties"/>
<settings>
<!--設定日誌-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<typeAliases>
<typeAlias type="com.ghost.mybatis.pojo.Teacher" alias="Teacher" />
<typeAlias type="com.ghost.mybatis.pojo.Student" alias="Student" />
</typeAliases>
<!--設定資料連線-->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/StudentMapper.xml"/>
<mapper resource="mapper/TeacherMapper.xml"/>
</mappers>
</configuration>
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://192.168.50.129:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username=mybatis
password=123456
- 實體類
import lombok.Data;
@Data
public class Student {
private int id;
private String name;
private Teacher teacher;
}
import lombok.Data;
@Data
public class Teacher {
private int id;
private String name;
}
- Mapper介面
import com.ghost.mybatis.pojo.Student;
import java.util.List;
public interface StudentMapper {
List<Student> selectStudentInNestedQuery();
List<Student> selectStudentInResultsTheNested();
}
import com.ghost.mybatis.pojo.Teacher;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
public interface TeacherMapper {
}
- Mapper.xml檔案
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ghost.mybatis.mapper.StudentMapper">
<!-- 子查詢方式 -->
<select id="selectStudentInNestedQuery" resultMap="StudentInNestedQuery">
select *
from mybatis.student
</select>
<!-- 子查詢方式結果集 -->
<resultMap id="StudentInNestedQuery" type="Student">
<association property="teacher" column="tid" javaType="Teacher" select="selectTeacher"/>
</resultMap>
<!-- 子查詢方式 - 子查詢方法 -->
<select id="selectTeacher" parameterType="int" resultType="Teacher">
select *
from mybatis.teacher
where id = #{tid}
</select>
<!-- 結果集查詢方式 -->
<select id="selectStudentInResultsTheNested" resultMap="StudentInResultsTheNested">
select s.id sid, s.name sname, t.id tid, t.name tname
from mybatis.student s,
mybatis.teacher t
where s.tid = t.id
</select>
<!-- 結果集查詢方式結果集 -->
<resultMap id="StudentInResultsTheNested" type="Student">
<id property="id" column="sid"/>
<result property="name" column="sname"/>
<association property="teacher" javaType="Teacher">
<id property="id" column="tid"/>
<result property="name" column="tname"/>
</association>
</resultMap>
</mapper>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ghost.mybatis.mapper.TeacherMapper">
</mapper>
- 測試類
import com.ghost.mybatis.mapper.StudentMapper;
import com.ghost.mybatis.mapper.TeacherMapper;
import com.ghost.mybatis.pojo.Student;
import com.ghost.mybatis.pojo.Teacher;
import com.ghost.mybatis.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class MyTest {
/*
聯合查詢
*/
@Test
public void selectStudentInNestedQuery() {
SqlSession sqlSession = MybatisUtil.getSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> students = mapper.selectStudentInNestedQuery();
for (Student student : students) {
System.out.println(student);
}
sqlSession.close();
}
/*
結果集查詢方式
*/
@Test
public void selectStudentInResultsTheNested() {
SqlSession sqlSession = MybatisUtil.getSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> students = mapper.selectStudentInResultsTheNested();
for (Student student : students) {
System.out.println(student);
}
sqlSession.close();
}
}
5.5.3 一對多處理
- 集合:collection - 集合【一對多】
- 按照查詢巢狀處理
- 按結果巢狀處理
5.5.4 練習
- mybatis工具類和配置檔案與“多對一查詢”一樣
- 實體類
import lombok.Data;
@Data
public class Student {
private int id;
private String name;
private int tid;
}
import lombok.Data;
import java.util.List;
@Data
public class Teacher {
private int id;
private String name;
List<Student> studentList;
}
- Mapper介面
public interface StudentMapper {
}
import com.ghost.mybatis.pojo.Teacher;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface TeacherMapper {
@Select("select * from teacher where id = #{tid}")
Teacher selectTeacherById(@Param("tid") int id);
List<Teacher> selectTeacherInNestedQuery();
List<Teacher> selectTeacherInResultsTheNested();
}
- Mapper.xml檔案
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ghost.mybatis.mapper.StudentMapper">
</mapper>
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ghost.mybatis.mapper.TeacherMapper">
<select id="selectTeacherInNestedQuery" resultMap="TeacherInNestedQueryMap">
select *
from mybatis.teacher
</select>
<resultMap id="TeacherInNestedQueryMap" type="Teacher">
<collection property="studentList" ofType="Student" select="selectStudent" column="id">
<id property="id" column="id"/>
<result property="name" column="name"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
<select id="selectStudent" parameterType="int" resultType="Student">
select *
from mybatis.student
where tid = #{id}
</select>
<select id="selectTeacherInResultsTheNested" resultMap="TeacherInResultsTheNested">
select t.id, t.name tname, s.id sid, s.name sname, s.tid
from mybatis.teacher t,
mybatis.student s
where t.id = s.tid
</select>
<resultMap id="TeacherInResultsTheNested" type="Teacher">
<id property="id" column="id"/>
<result property="name" column="tname"/>
<collection property="studentList" ofType="Student">
<id property="id" column="sid"/>
<result property="name" column="sname"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
</mapper>
- 測試類
import com.ghost.mybatis.mapper.TeacherMapper;
import com.ghost.mybatis.pojo.Teacher;
import com.ghost.mybatis.utils.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class MyTest {
@Test
public void selectTeacherById() {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher = mapper.selectTeacherById(1);
System.out.println(teacher);
sqlSession.close();
}
@Test
public void selectTeacherInNestedQuery() {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
List<Teacher> teachers = mapper.selectTeacherInNestedQuery();
for (Teacher teacher : teachers) {
System.out.println(teacher);
}
sqlSession.close();
}
@Test
public void selectTeacherInResultsTheNested() {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
List<Teacher> teachers = mapper.selectTeacherInResultsTheNested();
for (Teacher teacher : teachers) {
System.out.println(teacher);
}
sqlSession.close();
}
}
5.5.5 注意點
- 保證SQL的可讀性,儘量保證通俗易懂
- 注意一對多和多對一中,屬性名和欄位的問題
- 如果問題不好排查,可以使用日誌,建議使用Log4j
6、使用註解開發
6.1 面向介面程式設計
- 什麼是面向介面程式設計
- 根本原因:解耦
- 關於介面的理解
- 介面從更深層次的理解,應是定義(規範,約束)與實現(名實分離的原則)的分離。
- 介面的本身反映了系統設計人員對系統的抽象理解。
- 介面應有兩類:第一類是對一個個體的抽象,它可對應為一個抽象體(abstract class);第二類是對一個個體某一方面的抽象,即形成一個抽象面(interface)
- 一個體有可能有多個抽象面;抽象體與抽象面是有區別的
- 介面是一種規範和約束,更高層的抽象更像是一類行為,面向介面程式設計只是程式碼層體現的一種格式體現而已,真正的面向介面設計更貼近面向行為程式設計
- 三個面向區別
- 面向物件是指,我們考慮問題時,以物件為單位,考慮它的屬性及方法
- 面向過程是指,我們考慮問題時,以一個具體的流程(事務過程)為單位,考慮它的實現 .
- 介面設計與非介面設計是針對複用技術而言的,與面向物件(過程)不是一個問題.更多的體現就是對系統整體的架構
- 面向介面程式設計的優點
- 解耦
- 可擴充套件性高
- 提高複用
- 分層開發中,上層不用管具體的實現,大家都遵守共同的標準,使得開發變得容易,規範性更好
- 符合“開放-關閉原則”(對擴充套件開放,對修改關閉)
6.2 使用註解開發
- 本質:反射機制實現
- 底層:動態代理
- 註解在介面上實現
- 需要在核心配置檔案中繫結介面
6.3 MyBatis詳細執行過程
6.4 關於@Param()註解
- 基本型別的引數或者String型別,都需要加上
- 引用型別不需要加
- 如果只有一個基本型別的話,可以省略,但是建議大家加上
- 我們在SQL中引用的就是我們這裡的@Param中設定的屬性名
7、Lombok
7.1 使用步驟
- 在IDEA安裝lombok外掛
- 在專案中匯入lombok的jar包(最好使用高版本,低版本的有時會出現“com.sun.tools.javac.code.TypeTags”錯誤)
- 在實體類加註解即可
7.2 部分註解
- @Data
- 無參構造器
- getter
- setter
- toString
- hashcode
- equals
- @AllArgsConstructor - 全參構造器
- @NoArgsConstructor - 無參構造器
- pom檔案匯入
<dependencies>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.16</version>
</dependency>
</dependencies>
8、動態SQL
8.1 什麼是動態SQL
動態SQL就是指根據不同的條件生成不同的SQL語句;
所謂的動態SQL,本質還是SQL語句,只是我們可以在SQL層面,去執行一個邏輯程式碼;
動態SQL就是在拼接SQL語句,我們只要保證SQL的正確性,按照SQL的格式,去排列組合就行了
首先要寫原生的 sql 語句出來,然後在通過 mybatis 動態sql 對照著改,防止出錯
8.2 IF
8.3 choose、when、otherwise
- 不想使用所有的條件,而只是想從多個條件中選擇一個使用
- 針對這種情況,MyBatis 提供了 choose 元素,它有點像 Java 中的 switch 語句
8.4 trim、where、set
- where
- 若子句的開頭為 “AND” 或 “OR”,where 元素也會將它們去除
- where 元素只會在子元素返回任何內容的情況下才插入 “WHERE” 子句
- set
- 用於動態更新語句的類似解決方案叫做 set
- set 元素可以用於動態包含需要更新的列,忽略其它不更新的列
- set 元素會動態地在行首插入 SET 關鍵字,並會刪掉額外的逗號
- trim
- prefixOverrides 屬性會忽略通過管道符分隔的文字序列
- 插入 prefix 屬性中指定的內容(字首值)
- suffixOverrides(字尾值)
8.5 SQL片段
- 有的時候我們會將一些功能抽取出來方便複用
- sql標籤 - 公共SQL語句
- include標籤 - 引入公共SQL片段
- 注意事項
- 最好基於單表來定義SQL片段
- 不要存在where標籤
8.6 foreach
- foreach 元素的功能非常強大,它允許你指定一個集合,宣告可以在元素體內使用的集合項(item)和索引(index)變數
- 它也允許你指定開頭與結尾的字串以及集合項迭代之間的分隔符
8.7 練習
- 建立MyBatis工具類
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
import java.io.InputStream;
public class MyBatisUtil {
private static SqlSessionFactory sqlSessionFactory;
static {
String resource = "mybatis-config.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
public static SqlSession getSqlSession() {
return sqlSessionFactory.openSession(true);
}
}
- 建立UUID工具類
import java.util.UUID;
public class UUIDUtil {
public static String getUUID() {
return UUID.randomUUID().toString().replace("-", "");
}
}
- 建立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>
<!--引入外部properties檔案-->
<properties resource="properties/db.properties"/>
<settings>
<!--開啟駝峰命名自動對映,即從經典資料庫列名 A_COLUMN 對映到經典 Java 屬性名 aColumn-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
<!--開啟日誌-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<typeAliases>
<!--別名-->
<typeAlias type="com.ghost.mybatis.pojo.Book" alias="Book"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/ghost/mybatis/mapper/BookMapper.xml"/>
</mappers>
</configuration>
- 建立資料庫配置檔案
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://192.168.50.129:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
username=mybatis
password=123456
- 新建Book表
create table book
(
id varchar(32) not null
primary key,
book_name varchar(100) null,
author varchar(50) null,
publication_time datetime default CURRENT_TIMESTAMP not null
);
- 建立實體類
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Book {
public Book(String id, String bookName, String author) {
this.id = id;
this.bookName = bookName;
this.author = author;
}
private String id;
private String bookName;
private String author;
private Date publicationTime;
}
- 建立Mapper介面
import com.ghost.mybatis.pojo.Book;
import org.apache.ibatis.annotations.Insert;
import java.util.List;
public interface BookMapper {
@Insert("insert into book(id, book_name, author) values (#{id}, #{bookName}, #{author})")
int addBook(Book book);
List<Book> selectBook(Book book);
List<Book> selectBookByChoose(Book book);
int updateBook(Book book);
}
- 建立Mapper.xml檔案
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ghost.mybatis.mapper.BookMapper">
<sql id="selectSQL" >
<if test="id != null">
id = #{id}
</if>
<if test="author != null">
and author = #{author}
</if>
<if test="bookName != null">
and book_name = #{bookName}
</if>
</sql>
<select id="selectBook" parameterType="Book" resultType="Book">
select * from mybatis.book
<trim prefix="where" prefixOverrides="and | or">
<include refid="selectSQL"></include>
</trim>
</select>
<select id="selectBookByChoose" parameterType="Book" resultType="Book">
select * from mybatis.book
<where>
<choose>
<when test="id != null">
id = #{id}
</when>
<when test="author != null">
and author = #{author}
</when>
<otherwise>
and book_name = "測試書籍"
</otherwise>
</choose>
</where>
</select>
<update id="updateBook" parameterType="Book">
update mybatis.book
<set>
<if test="author != null">author = #{author},</if>
<if test="bookName != null">book_name = #{bookName},</if>
</set>
where id = #{id}
</update>
</mapper>
- 建立測試類
import com.ghost.mybatis.mapper.BookMapper;
import com.ghost.mybatis.pojo.Book;
import com.ghost.mybatis.utils.MyBatisUtil;
import com.ghost.mybatis.utils.UUIDUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.List;
public class MyTest {
@Test
public void getUUID() {
System.out.println(UUIDUtil.getUUID());
}
@Test
public void addBook() {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
BookMapper mapper = sqlSession.getMapper(BookMapper.class);
mapper.addBook(new Book(UUIDUtil.getUUID(), "測試書籍1", "ghost"));
mapper.addBook(new Book(UUIDUtil.getUUID(), "測試書籍2", "ghost"));
mapper.addBook(new Book(UUIDUtil.getUUID(), "測試書籍3", "ghost"));
mapper.addBook(new Book(UUIDUtil.getUUID(), "測試書籍4", "ghost"));
mapper.addBook(new Book(UUIDUtil.getUUID(), "測試書籍5", "ghost"));
sqlSession.close();
}
@Test
public void selectBook() {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
BookMapper mapper = sqlSession.getMapper(BookMapper.class);
try {
Book book = new Book();
List<Book> books = mapper.selectBook(book);
for (Book book1 : books) {
System.out.println(book1);
}
} finally {
sqlSession.close();
}
}
@Test
public void selectBookByChoose() {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
BookMapper mapper = sqlSession.getMapper(BookMapper.class);
Book book = new Book();
book.setId("0cfd6fb6aa414df7b1483f66d0862b40");
List<Book> books = mapper.selectBookByChoose(book);
for (Book book1 : books) {
System.out.println(book1);
}
sqlSession.close();
}
@Test
public void updateBook() {
SqlSession sqlSession = MyBatisUtil.getSqlSession();
BookMapper mapper = sqlSession.getMapper(BookMapper.class);
mapper.updateBook(new Book("9e07c2afff7b471186116a99355ce965", "測試修改書籍", "ghost"));
sqlSession.close();
}
}
9、快取
資料庫優化:讀寫分離、主從複製
9.1 什麼是快取【Cache】
- 存在記憶體中的臨時資料
- 將使用者經常查詢的資料放在快取(記憶體)中,使用者去查詢資料就不用從磁碟上(關係型資料庫資料檔案)查詢,從快取中查詢,從而提高查詢效率,解決了高併發系統的效能問題
9.2 為什麼使用快取
- 減少和資料庫的互動次數
- 減少系統開銷
- 提高系統效率
9.3 什麼樣的資料能夠使用快取
- 經常查詢並且不經常改變的資料
9.4 MyBatis快取
- MyBatis包含一個非常強大的查詢快取特性,它可以非常方便地定製和配置快取。快取可以極大的提升查詢效率
- MyBatis系統中預設定義了兩級快取:一級快取和二級快取
- 預設情況下,只有一級快取開啟。(SqlSession級別的快取,也稱為本地快取)
- 二級快取需要手動開啟和配置,他是基於namespace級別的快取
- 為了提高擴充套件性,MyBatis定義了快取介面Cache。我們可以通過實現Cache介面來自定義二級快取
9.5 一級快取
- 一級快取也叫本地快取
- 與資料庫同一次會話期間查詢到的資料會放在本地快取中
- 以後如果需要獲取相同的資料,直接從快取中拿,沒必須再去查詢資料庫
- 一級快取是SqlSession級別的快取,是一直開啟的,我們關閉不了它
- 一級快取失效情況:沒有使用到當前的一級快取,效果就是,還需要再向資料庫中發起一次查詢請求
- 一級快取失效的四種情況
- sqlSession不同(每個sqlSession中的快取相互獨立)
- sqlSession相同,查詢條件不同(當前快取中,不存在這個資料)
- sqlSession相同,兩次查詢之間執行了增刪改操作(因為增刪改操作可能會對當前資料產生影響)
- sqlSession相同,手動清除一級快取(一級快取就是一個map)
9.6 二級快取
-
二級快取也叫全域性快取,一級快取作用域太低了,所以誕生了二級快取
-
基於namespace級別的快取,一個名稱空間,對應一個二級快取
-
工作機制
- 一個會話查詢一條資料,這個資料就會被放在當前會話的一級快取中
- 如果當前會話關閉了,這個會話對應的一級快取就沒了;但是我們想要的是,會話關閉了,一級快取中的資料被儲存到二級快取中
- 新的會話查詢資訊,就可以從二級快取中獲取內容
- 不同的mapper查出的資料會放在自己對應的快取(map)中
-
使用步驟
- 開啟全域性快取 【mybatis-config.xml】
<setting name="cacheEnabled" value="true"/>
- 去每個mapper.xml中配置使用二級快取,這個配置非常簡單;【xxxMapper.xml】
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
-
需要將實體類序列化
9.7 MyBatis一級快取和二級快取總結
- 只要開啟了二級快取,我們在同一個Mapper中的查詢,可以在二級快取中拿到資料
- 查出的資料都會被預設先放在一級快取中
- 查出的資料都會被預設先放在一級快取中
9.8 其他快取
- 自定義快取 - EhCache
- Redis