1. 程式人生 > 其它 >蛇形矩陣起點在中間

蛇形矩陣起點在中間

MyBatis

一、需要的環境,工具

  • JDK1.8

  • Mysql 5.7

  • maven 3.6.1

  • IDEA

SSM框架:有配置檔案,最好看官網文件;

二、學習

1、簡介

1.1什麼是Mybatis

  • MyBatis 是一款優秀的持久層框架,

  • 它支援自定義 SQL、儲存過程以及高階對映。

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

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

如何獲得Mybatis?

  • maven倉庫

  • Github

  • 中文文件

1.2持久化

資料持久化

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

  • 記憶體:斷電即失

  • 資料庫(JDBC),io檔案持久化。

  • 生活:冷藏,罐頭。

為什麼需要持久化?

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

  • 記憶體太貴了

1.3、持久層

Dao層,Service層,Controller層....

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

  • 層界限十分明顯

1.4、為什麼需要Mybatis

  • 幫助程式設計師將資料存入到資料庫中。

  • 方便

  • 傳統的JDBC程式碼太複雜了,簡化,框架,自動化。

  • 不用Mybatis也可以,只不過使用Mybatis更容易上手,技術沒有高低之分。

  • 優點:

    • 簡單易學:本身就很小且簡單。沒有任何第三方依賴,最簡單安裝只要兩個jar檔案+配置幾個sql對映檔案易於學習,易於使用,通過文件和原始碼,可以比較完全的掌握它的設計思路和實現。

    • 靈活:mybatis不會對應用程式或者資料庫的現有設計強加任何影響。 sql寫在xml裡,便於統一管理和優化。通過sql語句可以滿足操作資料庫的所有需求。

    • 解除sql與程式程式碼的耦合:通過提供DAO層,將業務邏輯和資料訪問邏輯分離,使系統的設計更清晰,更易維護,更易單元測試。sql和程式碼的分離,提高了可維護性。

    • 提供對映標籤,支援物件與資料庫的orm欄位關係對映

    • 提供物件關係對映標籤,支援物件關係組建維護

    • 提供xml標籤,支援編寫動態sql。

最重要的一點:使用的人多!

2、第一個Mybatis程式

思路:搭建環境 --> 匯入Mybatis --> 編寫程式碼 -->測試

2.1、搭建環境

搭建資料庫

 1 CREATE DATABASE `mybatis`;
 2  3 USE `mybatis`;
 4  5 CREATE TABLE `user`(
 6    `id` INT(20) NOT NULL PRIMARY KEY,
 7    `name` VARCHAR(30) DEFAULT NULL,
 8    `pwd` VARCHAR(30) DEFAULT NULL
 9 )ENGINE=INNODB DEFAULT CHARSET=utf8;
10 11 INSERT INTO `user`(`id`,`name`,`pwd`) VALUES
12 (1,'張三','123456'),
13 (2,'李四','1223'),
14 (3,'王五','1112')

新建專案

1、新建一個普通的maven專案

2、刪除src目錄,標記這是一個父工程,在父工程下面建造新的module。

3、匯入maven依賴

 1 <!--mysql驅動-->
 2 <dependecies>
 3     <dependency>
 4         <groupId>mysql</groupId>
 5         <artifactId>mysql-connector-java</artifactId>
 6         <version>5.1.46</version>
 7     </dependency>
 8  9     <!--mybatis-->
10     <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
11     <dependency>
12         <groupId>org.mybatis</groupId>
13         <artifactId>mybatis</artifactId>
14         <version>3.5.2</version>
15     </dependency>
16 17     <!--junit測試依賴-->
18     <dependency>
19         <groupId>junit</groupId>
20         <artifactId>junit</artifactId>
21         <version>4.12</version>
22     </dependency>
23 24 </dependecies>

2.2、建立一個模組

  • 編寫mybatis得核心配置檔案

 1 <?xml version="1.0" encoding="UTF-8" ?>
 2 <!DOCTYPE configuration
 3         PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 4         "http://mybatis.org/dtd/mybatis-3-config.dtd">
 5 <!--configuration核心配置檔案-->
 6 <configuration>
 7     <environments default="development">
 8         <environment id="development">
 9             <transactionManager type="JDBC"/>
10             <dataSource type="POOLED">
11                 <property name="driver" value="com.mysql.jdbc.Driver"/>
12                 <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
13                 <property name="username" value="root"/>
14                 <property name="password" value="root"/>
15             </dataSource>
16         </environment>
17     </environments>
18 </configuration>
  • 編寫mybatis工具類

 1 //sqlSessionFactory --> sqlSession
 2 public class mybatisUtils {
 3  4     private static SqlSessionFactory sqlSessionFactory;
 5  6     //靜態程式碼塊,意味著一開始就進行載入
 7     static{
 8         try {
 9             //使用mybatis第一步:獲取sqlSessionFactory物件
10             String resource="mybatis-config.xml";
11             InputStream inputStream = Resources.getResourceAsStream(resource);
12             sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
13         } catch (IOException e) {
14             e.printStackTrace();
15         }
16 17     }
18 19     //既然有了sqlSessionFactory,顧名思義,我們就可以從中獲得sqlSession例項物件了
20     //sqlSession完全包含了面向資料庫執行sql命令所需得所有方法
21     public static SqlSession getSqlSession(){
22         return sqlSessionFactory.openSession();
23     }
24     
25 }

2.3、編寫程式碼

  • 實體類

 1 //實體類
 2 public class User {
 3     private int id;
 4     private String name;
 5     private String pwd;
 6  7     public User() {
 8     }
 9 10     public User(int id, String name, String pwd) {
11         this.id = id;
12         this.name = name;
13         this.pwd = pwd;
14     }
15 16     public int getId() {
17         return id;
18     }
19 20     public void setId(int id) {
21         this.id = id;
22     }
23 24     public String getName() {
25         return name;
26     }
27 28     public void setName(String name) {
29         this.name = name;
30     }
31 32     public String getPwd() {
33         return pwd;
34     }
35 36     public void setPwd(String pwd) {
37         this.pwd = pwd;
38     }
39 40     @Override
41     public String toString() {
42         return "User{" +
43             "id=" + id +
44             ", name='" + name + '\'' +
45             ", pwd='" + pwd + '\'' +
46             '}';
47     }
48 }
  • Dao介面類

1 //Dao/Mapper介面類:dao與mybatis中得mapper介面類是一個東西,一個原理
2 public interface UserDao {
3     public List<User> getUserList();
4 }
  • 介面實現類由原來的UserDaoImpl轉變為一個Mapper配置檔案

 1 <?xml version="1.0" encoding="UTF-8" ?>
 2 <!DOCTYPE mapper
 3         PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 4         "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 5 <!--namespace繫結一個Dao/mapper介面類 -->
 6 <!--id:對應實現介面中方法的名字;resultType:對應獲得結果的實體類-->
 7 <!--resultType:代表返回一個結果類,resultMap返回一個集合-->
 8 <mapper namespace="com.zmz.Dao.UserDao">
 9     <!--select查詢語句-->
10     <select id="getUserList" resultType="com.zmz.pojo.User">
11         select * from mybatis.user
12     </select>
13 </mapper>

2.4、測試

  • junit測試

 1 public class UserMapperTest {
 2  3     @Test
 4     public void test(){
 5  6         //第一步:獲得SqlSession物件
 7         SqlSession sqlSession = mybatisUtils.getSqlSession();
 8  9         //第二步:getMapper
10         UserMapper mapper = sqlSession.getMapper(UserMapper.class);
11         List<User> userList = mapper.getUserList();
12         for (User user: userList){
13             System.out.println(user);
14         }
15 16         //第三步:關閉sqlSession
17         sqlSession.close();
18     }
19 20 }

會遇到一個問題:

Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: java.io.IOException: Could not find resource com/zmz/Dao/UserMapper.xml

這個問題是由於maven專案所都會遇到的一個問題,就是不能識別resource路徑。

maven由於他的約定大於配置,我們之後可能遇到我們寫的配置檔案,無法被匯出或者生效的問題,解決方案:

 1 <!--再buil中配置resources,來防止我們資源匯出失敗的問題-->
 2 <build>
 3     <resources>
 4         <resource>
 5             <directory>src/main/resources</directory>
 6             <includes>
 7                 <include>**/*.properties</include>
 8                 <include>**/*.xml</include>
 9             </includes>
10             <filtering>true</filtering>
11         </resource>
12         <resource>
13             <directory>src/main/java</directory>
14             <includes>
15                 <include>**/*.properties</include>
16                 <include>**/*.xml</include>
17             </includes>
18             <filtering>true</filtering>
19         </resource>
20     </resources>
21 </build>

遇到的問題:

  1. 配置檔案沒有註冊

  2. 繫結介面錯誤

  3. 方法名不對

  4. 返回型別不對

  5. Maven匯出資源問題

3、CRUD

3.1、namespace

namespace中的包名要和Dao/mapper介面的報名一致

3.2、select

選擇,查詢語句;

  • id:就是對應得namespace中得方法名;

  • resultType:Sql語句執行得返回值

  • parameterType:引數型別

  1. 編寫介面

1 //查詢全部使用者
2     public List<User> getUserList();
3 //根據id查詢使用者
4     public User getUserById(int id);

2.編寫對應得mapper中得sql語句
<!--select查詢語句-->
<select id="getUserList" resultType="com.zmz.pojo.User">
    select * from mybatis.user
</select>
<!--根據id進行查詢-->
<select id="getUserById" parameterType="int" resultType="com.zmz.pojo.User">
    select * from mybatis.user where id = #{id}
</select>

3.測試

1 @Test
2 public void test1(){
3     SqlSession sqlSession = mybatisUtils.getSqlSession();
4 
5     UserMapper mapper = sqlSession.getMapper(UserMapper.class);
6     User userById = mapper.getUserById(1);
7     System.out.println(userById);
8     sqlSession.close();
9 }

3.3、Insert

 1 //insert一個使用者
 2     public int insertUser(User user);
 3 <insert id="insertUser" parameterType="com.zmz.pojo.User">
 4     insert into mybatis.user (id, name, pwd) values (#{id}, #{name}, #{pwd});
 5 </insert>
 6 //增刪改需要提交事務
 7 @Test
 8 public void test2(){
 9     SqlSession sqlSession = mybatisUtils.getSqlSession();
10 
11     UserMapper mapper = sqlSession.getMapper(UserMapper.class);
12 
13     int i = mapper.insertUser(new User(4, "zmz", "11"));
14     if (i > 0){
15         System.out.println("插入成功!");
16     }
17     //提交事務
18     sqlSession.commit();
19     sqlSession.close();
20 }

3.4、Update

 1 //update一個使用者
 2     public int updateUser(User user);
 3 <update id="updateUser" parameterType="com.zmz.pojo.User">
 4     update mybatis.user set name=#{name},pwd=#{pwd} where id=#{id};
 5 </update>
 6 @Test
 7 public void test3(){
 8     SqlSession sqlSession = mybatisUtils.getSqlSession();
 9 
10     UserMapper mapper = sqlSession.getMapper(UserMapper.class);
11     int i = mapper.updateUser(new User(4, "z", "113"));
12     if (i>0){
13         System.out.println("修改成功");
14     }
15     sqlSession.commit();
16     sqlSession.close();
17 }

3.5、Delete

 1 //delete一個使用者
 2     public int deleteUser(int id);
 3 <delete id="deleteUser" parameterType="int">
 4     delete from mybatis.user where id = #{id};
 5 </delete>
 6 @Test
 7 public void test4(){
 8     SqlSession sqlSession = mybatisUtils.getSqlSession();
 9 
10     UserMapper mapper = sqlSession.getMapper(UserMapper.class);
11     int i = mapper.deleteUser(4);
12     if (i > 0 ){
13         System.out.println("刪除成功!");
14     }
15     sqlSession.commit();
16     sqlSession.close();
17 }

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

3.6、萬能Map

使用map之後可以將你想修改或者插入的值進行map.put(key,value)

key值對應你要修改的值。

假設,我們的實體類,或者資料庫中的表,欄位或者引數過多,我們應當考慮使用Map!

 1 //萬能得Map
 2     public int insertUser2(Map<String, Object> map);
 3 <insert id="insertUser2" parameterType="map">
 4     insert into mybatis.user (id, name, pwd) values (#{userId}, #{userName}, #{UserPwd});
 5 </insert>
 6 @Test
 7 public void insertUser2(){
 8     SqlSession sqlSession = mybatisUtils.getSqlSession();
 9 
10     UserMapper mapper = sqlSession.getMapper(UserMapper.class);
11 
12     Map<String,Object> map = new HashMap<String,Object>();
13     map.put("userId",5);
14     map.put("userName","zzz");
15     map.put("UserPwd","1123");
16     mapper.insertUser2(map);
17 
18     sqlSession.commit();
19     sqlSession.close();
20 }

Map傳遞引數,直接在sql中取出key即可!【parameterType="map"】

物件傳遞引數,直接在sql中取出物件的屬性即可!【parameterType="Object"】

只有一個基本型別引數的情況下,可以直接在sql中取到!

多個引數用Map,或者註解!

3.7模糊查詢(某些業務才是用到)

模糊查詢怎麼寫?

  • java程式碼執行的時候,傳遞萬用字元%%

 1 <select id="getUserListLike" resultType="com.zmz.pojo.User">
 2     select * from user where name like #{value};
 3 </select>
 4 @Test
 5 public void getUserListLike(){
 6     SqlSession sqlSession = mybatisUtils.getSqlSession();
 7     UserMapper mapper = sqlSession.getMapper(UserMapper.class);
 8     List<User> userListLike = mapper.getUserListLike("%李%");
 9     for (User user: userListLike){
10         System.out.println(user);
11     }
12     sqlSession.close();
13 }
  • 在sql拼接中使用萬用字元!

1 <select id="getUserListLike" resultType="com.zmz.pojo.User">
2     select * from user where name like "%"#{value}"%";
3 </select>
 1 @Test
 2 public void getUserListLike(){
 3     SqlSession sqlSession = mybatisUtils.getSqlSession();
 4     UserMapper mapper = sqlSession.getMapper(UserMapper.class);
 5     List<User> userListLike = mapper.getUserListLike("李");
 6     for (User user: userListLike){
 7         System.out.println(user);
 8     }
 9     sqlSession.close();
10 }
11 
12 }

4、配置解析

4.1、核心配置檔案

  • mybatis-config.xml

  • MyBatis的配置檔案包含了會深深影響MyBatis行為的設定和屬性資訊

configuration(配置):
	properties(屬性)
	settings(設定)
	typeAliases(類型別名)
	typeHandlers(型別處理器)
	objectFactory(物件工廠)
	plugins(外掛)
	environments(環境配置):
		environment(環境變數)
		transactionManager(事務管理器)
		dataSource(資料來源)
	databaseIdProvider(資料庫廠商標識)
	mappers(對映器)

4.2、環境配置(environments)

MyBatis可以配置成適應多種環境

不過要記住,儘管可以配置多個環境,但每個SqlSessionFactory例項只能選擇一種環境。

學會使用配置多套執行環境!

Mybatis預設的事務管理器:JDBC 連線池:POOLED

4.3、屬性(properties)

我們可以通過properties屬性來實現引用配置檔案

這些屬性都是可外部配置且動態替換的,既可以在典型的java屬性檔案中配置,亦可通過properties元素的子元素來傳遞。[db.properties]

編寫一個配置檔案

db.properties

1 driver=com.mysql.jdbc.Driver
2 url=jdbc:mysql://localhost:/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8
3 username=root
4 password=root

在核心配置檔案中引入

 1 <!--    必須按照順序進行放置屬性標籤,否則會報錯,引入外部配置檔案-->
 2 <properties resource="db.properties">
 3     <property name="username" value="root"/>
 4     <property name="password" value="root"/>
 5 </properties>
 6 
 7 <environments default="development">
 8     <environment id="development">
 9         <transactionManager type="JDBC"/>
10         <dataSource type="POOLED">
11             <property name="driver" value="${driver}"/>
12             <property name="url" value="${url}"/>
13             <property name="username" value="${username}"/>
14             <property name="password" value="${password}"/>
15         </dataSource>
  • 可以直接引入外部檔案

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

  • 如果兩個檔案有同一個欄位,優先使用外部配置檔案的(就是db.properties檔案)!

4.4、類型別名(typeAliases)

  • 類型別名可為 Java 型別設定一個縮寫名字。

  • 意在降低冗餘的全限定類名書寫。

1 <typeAliases>
2      <typeAlias type="com.zmz.pojo.User" alias="User"/>
3 </typeAliases>

也可以指定一個包名,MyBatis 會在包名下面搜尋需要的 Java Bean,

掃描實體類的包,它的預設別名就為這個類的類名,首字母大寫(預設是這樣,但不大寫也能掃描出來)

1 <!--可以給實體類起別名-->
2 <typeAliases>
3     <package name="com.zmz.pojo"/>
4 </typeAliases>
  • 在實體類比較少的時候,使用第一種方式;

  • 如果實體類十分多,建議使用第二種;

  • 第一種可以DIY別名,第二種”不行”,如果非要改,需要在實體上增加註解。

@Alias("user")
public class User { ... }

4.5、設定

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

1 <settings>
2     <setting name="cacheEnabled" value="true"/>
3     <setting name="lazyLoadingEnabled" value="true"/>
4     <setting name="useGeneratedKeys" value="false"/>
5     <setting name="mapUnderscoreToCamelCase" value="false"/>
6 </settings>
設定名描述有效值預設值
cacheEnabled 全域性性地開啟或關閉所有對映器配置檔案中已配置的任何快取。 true | false true
lazyLoadingEnabled 延遲載入的全域性開關。當開啟時,所有關聯物件都會延遲載入。 特定關聯關係中可通過設定 fetchType 屬性來覆蓋該項的開關狀態。 true | false false
useGeneratedKeys 允許 JDBC 支援自動生成主鍵,需要資料庫驅動支援。如果設定為 true,將強制使用自動生成主鍵。儘管一些資料庫驅動不支援此特性,但仍可正常工作(如 Derby)。 true | false False
logImpl 指定 MyBatis 所用日誌的具體實現,未指定時將自動查詢。 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING 未設定
mapUnderscoreToCamelCase 是否開啟駝峰命名自動對映,即從經典資料庫列名 A_COLUMN 對映到經典 Java 屬性名 aColumn。 true | false False

比較重要的幾個,放在這;具體的請看:https://mybatis.org/mybatis-3/zh/configuration.html

4.6、其他配置

初學不涉及這方面;

4.7、對映器(mappers)

MapperRegistry:註冊繫結我們的Mapper檔案;

方式一:【推薦使用】

1 <!--每一個Mapper.xml都需要在Mybatis核心配置檔案中註冊-->
2 <mappers>
3     <mapper resource="com/zmz/Dao/UserMapper.xml"/>
4 </mappers>

方式二:使用class檔案繫結註冊

<!--每一個Mapper.xml都需要在Mybatis核心配置檔案中註冊-->
<mappers>
    <mapper class="com.zmz.Dao.UserMapper"/>
</mappers>

注意:

  • 介面和他的Mapper配置檔案必須同名!

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

方式三:使用掃描包進行注入繫結

<!--每一個Mapper.xml都需要在Mybatis核心配置檔案中註冊-->
<mappers>
    <package name="com.zmz.Dao"/>
</mappers>

注意:

  • 介面和他的Mapper配置檔案必須同名!

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

4.8、生命週期和作用域

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

SqlSessionFactoryBuilder:

  • 一旦建立了SqlSessionFactory,就不再需要它了

  • 區域性變數

SqlSessionFactory:

  • 說白了就是可以想象為:資料庫連線池

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

  • 因此SqlSessionFactory的最佳作用域是應用作用域。

  • 最簡單的就是使用單例模式或者靜態單例模式。

SqlSession:

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

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

  • 用完之後需要趕緊關閉,否則資源被佔用!

這裡面的每一個Mapper,就代表一個具體的業務!(可以這麼理解)

5、解決屬性名和欄位名不一致的問題

資料庫中表的屬性名和類的欄位不一樣,

資料庫user表

id  name  pwd

對應類的欄位:

1 public class User {
2     private int id;
3     private String name;
4     private String password;
5 }

解決方法:

1.起別名

1 <select id="getUserById" parameterType="int" resultType="User">
2     select id,name,pwd as password from mybatis.user where id = #{id}
3 </select>

2.ResultMap對映

 1 <!--結果集對映-->
 2 <resultMap id="UserMap" type="User">
 3     <!--column:代表資料庫表中屬性,property:代表類中欄位-->
 4     <result column="id" property="id"/>
 5     <result column="name" property="name"/>
 6     <result column="pwd" property="password"/>
 7 </resultMap>
 8 
 9 <select id="getUserById" parameterType="int" resultMap="UserMap">
10     select * from user where id = #{id}
11 </select>
  • resultMap 元素是 MyBatis 中最重要最強大的元素。

  • ResultMap 的設計思想是,對簡單的語句做到零配置,對於複雜一點的語句,只需要描述語句之間的關係就行了。

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

當你的屬性為一個另一個類時,這就牽扯到了一對多,和多對多的問題了。

<association ...> 代表關聯關係,多個學生關聯一個老師

<collection ...> 代表集合關係, 一個老師包含多個學生

6、日誌

6.1、日誌工廠

如果一個數據庫操作,出現了異常,我們需要排錯。日誌就是最好的助手!

曾經:sout,debug

現在:日誌工廠!

logImpl :指定 MyBatis 所用日誌的具體實現,未指定時將自動查詢。

  • SLF4J

  • LOG4J (*)

  • LOG4J2

  • JDK_LOGGING

  • COMMONS_LOGGING

  • STDOUT_LOGGING (*)

  • NO_LOGGING

在Mybatis中具體使用哪一個日誌實現,在設定中設定!

STDOUT_LOGGING標準日誌輸出

在mybatis核心配置檔案中,配置我們的日誌!

1 <!--標準,每一個包括大小寫和空格,不能有錯誤,標準的日誌工程實現-->
2 <settings>
3     <setting name="logImpl" value="STDOUT_LOGGING"/>
4 </settings>

6.2、LOG4J

簡介:

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

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

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

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

  1. 先匯入LOG4J的包,

    1 <!-- https://mvnrepository.com/artifact/log4j/log4j -->
    2 <dependency>
    3     <groupId>log4j</groupId>
    4     <artifactId>log4j</artifactId>
    5     <version>1.2.17</version>
    6 </dependency>
  2. log4j兩種配置檔案:

    • log4j.properties

      # priority  :debug<info<warn<error
      #you cannot specify every priority with different file for log4j 
      log4j.rootLogger=debug,stdout,info,debug,warn,error 
      
      #console
      log4j.appender.stdout=org.apache.log4j.ConsoleAppender 
      log4j.appender.stdout.layout=org.apache.log4j.PatternLayout 
      log4j.appender.stdout.layout.ConversionPattern= [%d{yyyy-MM-dd HH:mm:ss a}]:%p %l%m%n
      #info log
      log4j.logger.info=info
      log4j.appender.info=org.apache.log4j.DailyRollingFileAppender 
      log4j.appender.info.DatePattern='_'yyyy-MM-dd'.log'
      log4j.appender.info.File=./src/com/hp/log/info.log
      log4j.appender.info.Append=true
      log4j.appender.info.Threshold=INFO
      log4j.appender.info.layout=org.apache.log4j.PatternLayout 
      log4j.appender.info.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
      #debug log
      log4j.logger.debug=debug
      log4j.appender.debug=org.apache.log4j.DailyRollingFileAppender 
      log4j.appender.debug.DatePattern='_'yyyy-MM-dd'.log'
      log4j.appender.debug.File=./src/com/hp/log/debug.log
      log4j.appender.debug.Append=true
      log4j.appender.debug.Threshold=DEBUG
      log4j.appender.debug.layout=org.apache.log4j.PatternLayout 
      log4j.appender.debug.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
      #warn log
      log4j.logger.warn=warn
      log4j.appender.warn=org.apache.log4j.DailyRollingFileAppender 
      log4j.appender.warn.DatePattern='_'yyyy-MM-dd'.log'
      log4j.appender.warn.File=./src/com/hp/log/warn.log
      log4j.appender.warn.Append=true
      log4j.appender.warn.Threshold=WARN
      log4j.appender.warn.layout=org.apache.log4j.PatternLayout 
      log4j.appender.warn.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
      #error
      log4j.logger.error=error
      log4j.appender.error = org.apache.log4j.DailyRollingFileAppender
      log4j.appender.error.DatePattern='_'yyyy-MM-dd'.log'
      log4j.appender.error.File = ./src/com/hp/log/error.log 
      log4j.appender.error.Append = true
      log4j.appender.error.Threshold = ERROR 
      log4j.appender.error.layout = org.apache.log4j.PatternLayout
      log4j.appender.error.layout.ConversionPattern = %d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c >> Method: %l ]%n%p:%m%n
      log4j.xml
    • <?xml version="1.0" encoding="UTF-8"?>  
      <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd">  
          
      <log4j:configuration  debug="true" xmlns:log4j='http://jakarta.apache.org/log4j/' >  
       
          <!-- ========================== 自定義輸出格式說明================================ -->
            <!-- %p 輸出優先順序,即DEBUG,INFO,WARN,ERROR,FATAL -->
            <!-- %r 輸出自應用啟動到輸出該log資訊耗費的毫秒數  -->
            <!-- %c 輸出所屬的類目,通常就是所在類的全名 -->
            <!-- %t 輸出產生該日誌事件的執行緒名 -->
            <!-- %n 輸出一個回車換行符,Windows平臺為“/r/n”,Unix平臺為“/n” -->
            <!-- %d 輸出日誌時間點的日期或時間,預設格式為ISO8601,也可以在其後指定格式,比如:%d{yyy MMM dd HH:mm:ss,SSS},輸出類似:2002年10月18日 22:10:28,921  -->
            <!-- %l 輸出日誌事件的發生位置,包括類目名、發生的執行緒,以及在程式碼中的行數。舉例:Testlo4.main(TestLog4.java:10)  -->
            <!-- ========================================================================== -->
       
            <!-- ========================== 輸出方式說明================================ -->
            <!-- Log4j提供的appender有以下幾種:  -->
            <!-- org.apache.log4j.ConsoleAppender(控制檯),  -->
            <!-- org.apache.log4j.FileAppender(檔案),  -->
            <!-- org.apache.log4j.DailyRollingFileAppender(每天產生一個日誌檔案), -->
            <!-- org.apache.log4j.RollingFileAppender(檔案大小到達指定尺寸的時候產生一個新的檔案),  -->
            <!-- org.apache.log4j.WriterAppender(將日誌資訊以流格式傳送到任意指定的地方)   -->
        <!-- ========================================================================== -->
         
          <appender name="CONSOLE" class="org.apache.log4j.ConsoleAppender">
               <!-- <param name="Target" value="System.out"/> -->
               <layout class="org.apache.log4j.PatternLayout">
                       <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c  Method: %l ]%n%p:%m%n"/>
               </layout>
              <!--  <filter class="org.apache.log4j.varia.LevelRangeFilter">
                  <param name="LevelMin" value="DEBUG"/>
                  <param name="LevelMax" value="DEBUG"/>
              </filter> -->
          </appender>
          <!-- output the debug   -->
         <!--  <appender name="log4jDebug" class="org.apache.log4j.DailyRollingFileAppender">
              <param name="File" value="log_"/>    
              <param name="MaxFileSize" value="KB"/> 
              <param name="MaxBackupIndex" value="2"/> -->
         <appender name="log4jDebug"  class="org.apache.log4j.rolling.RollingFileAppender">  
              <param name="Append" value="true"/>
              <rollingPolicy  class="org.apache.log4j.rolling.TimeBasedRollingPolicy">  
                     <param name="FileNamePattern" value="./log/log_%d{yyyy-MM-dd}.log" />  
              </rollingPolicy>  
              <layout class="org.apache.log4j.PatternLayout">
                  <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c Method: %l ]%n%p:%m%n"/>
              </layout>
              <filter class="org.apache.log4j.varia.LevelRangeFilter">
                  <param name="LevelMin" value="DEBUG"/>
                  <param name="LevelMax" value="DEBUG"/>
              </filter>
          </appender>
         <!--  <appender name="log4jInfo" class="org.apache.log4j.DailyRollingFileAppender">
              <param name="File" value="log_"/>       
              <param name="DatePattern" value="'.log'yyyy-MM-dd"/>
              <param name="Append" value="true"/>
             <param name="MaxFileSize" value="5KB"/>
              <param name="MaxBackupIndex" value="2"/> -->
          <appender name="log4jInfo"  class="org.apache.log4j.rolling.RollingFileAppender">  
              <param name="Append" value="true"/>
              <rollingPolicy  class="org.apache.log4j.rolling.TimeBasedRollingPolicy">  
                     <param name="FileNamePattern" value="./log/log_%d{yyyy-MM-dd}.log" />  
              </rollingPolicy> 
              <layout class="org.apache.log4j.PatternLayout">
                   <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c  Method: %l ]%n%p:%m%n"/>
              </layout>
              <filter class="org.apache.log4j.varia.LevelRangeFilter">
                  <param name="LevelMin" value="INFO"/>
                  <param name="LevelMax" value="INFO"/>
              </filter>
          </appender>
         <!--  <appender name="log4jWarn" class="org.apache.log4j.DailyRollingFileAppender">
              <param name="File" value="/log_"/>      
              <param name="DatePattern" value="'.log'yyyy-MM-dd"/>
              <param name="Append" value="true"/>
              <param name="MaxFileSize" value="5KB"/>
              <param name="MaxBackupIndex" value="2"/> -->
          <appender name="log4jWarn" class="org.apache.log4j.rolling.RollingFileAppender">  
              <param name="Append" value="true"/>
              <rollingPolicy  class="org.apache.log4j.rolling.TimeBasedRollingPolicy">  
                     <param name="FileNamePattern" value="./log/log_%d{yyyy-MM-dd}.log" />  
              </rollingPolicy> 
              <layout class="org.apache.log4j.PatternLayout">
                   <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c Method: %l ]%n%p:%m%n"/>
              </layout>
              <filter class="org.apache.log4j.varia.LevelRangeFilter">
                  <param name="LevelMin" value="WARN"/>
                  <param name="LevelMax" value="WARN"/>
              </filter>
          </appender>
        <!--  <appender name="log4jError" class="org.apache.log4j.DailyRollingFileAppender"> -->
         <appender name="log4jError"  class="org.apache.log4j.rolling.RollingFileAppender">  
             <!--  <param name="File" value="/error_"/>    
              <param name="DatePattern" value="'.log'yyyy-MM-dd"/> -->
              <param name="Append" value="true"/>
              <rollingPolicy  class="org.apache.log4j.rolling.TimeBasedRollingPolicy">  
                     <param name="FileNamePattern" value="./log/error_%d{yyyy-MM-dd}.log" />  
              </rollingPolicy> 
              
            <!--   <param name="MaxFileSize" value="5KB"/> -->
            <!--   <param name="MaxBackupIndex" value="2"/> -->
              <layout class="org.apache.log4j.PatternLayout">
                   <param name="ConversionPattern" value="%d{yyyy-MM-dd HH:mm:ss a} [Thread: %t][ Class:%c Method: %l ]%n%p:%m%n"/>
              </layout>
              <filter class="org.apache.log4j.varia.LevelRangeFilter">
                  <param name="LevelMin" value="ERROR"/>
                  <param name="LevelMax" value="ERROR"/>
              </filter>
          </appender>
       <!--通過<category></category>的定義可以將各個包中的類日誌輸出到不同的日誌檔案中-->
          <!--     <category name="com.gzy">
                  <priority value="debug" />
                  <appender-ref ref="log4jTestLogInfo" />
                  <appender-ref ref="log4jTestDebug" />
              </category> -->
        <appender name="MAIL"     
            class="org.apache.log4j.net.SMTPAppender">     
            <param name="threshold" value="debug" />     
            <!-- 日誌的錯誤級別     
             <param name="threshold" value="error"/>     
            -->     
            <!-- 快取檔案大小,日誌達到512K時傳送Email -->     
            <param name="BufferSize" value="512" /><!-- 單位K -->     
            <param name="From" value="[email protected]" />     
            <param name="SMTPHost" value="smtp.163.com" />     
            <param name="Subject" value="juyee-log4jMessage" />     
            <param name="To" value="[email protected]" />     
            <param name="SMTPUsername" value="test" />     
            <param name="SMTPPassword" value="test" />     
            <layout class="org.apache.log4j.PatternLayout">     
             <param name="ConversionPattern"     
              value="%-d{yyyy-MM-dd HH:mm:ss.SSS a} [%p]-[%c] %m%n" />     
            </layout>     
         </appender> 
          
          
           <root>
              <priority value="debug"/>
              <appender-ref ref="CONSOLE" /> 
              <appender-ref ref="log4jDebug" /> 
              <appender-ref ref="log4jInfo" />
              <appender-ref ref="log4jWarn" />
              <appender-ref ref="log4jError" />
              <!-- <appender-ref ref="MAIL" /> -->
          </root>
      </log4j:configuration>
  3. 配置log4j為日誌的實現

    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>
  4. Log4j的使用!直接測試執行剛才的查詢

簡單使用

  1. 在要使用Log4j的類中,匯入包import org.apache.Logger;

  2. 日誌物件,引數為當前類的class

    static Logger logger = Logger.getLogger(UserMapperTest.class);
  3. 日誌級別

    logger.info("info:進入了log4j");
    logger.debug("debug:進入了log4j");
    logger.error("error:進入了log4j");

總結:日誌就是一個對於你執行程式過程中的一個輸出,包括各種info,debug,error等的輸出類別分類輸出,他取代了最早之前的System.out.println()的輸出方式,使得成為一個體系,同時,logger.debug("進入測試")可以使得日誌工廠變得靈活,多變,同時增加配置檔案更加方便你進行輸出格式的變換。

7、分頁

思考:為什麼要分頁

  • 減少資料的處理量

7.1使用Limit分頁

語法:SELECT * from user limit startIndex, pageSize;
(從第幾個數開始,然後輸出多少個數,這個就叫分頁)
SELECT * from user limit 3;  #[0, n](只有一個引數時,預設從0開始算起)

使用Mybatis實現分頁,核心SQL

  1. 介面

    1 //根據分頁查詢使用者
    2 public List<User> getUserByLimit(Map<String, Integer> map);
  2. Mapper.xml

    1 <!-- 分頁 -->
    2 <select id="getUserByLimit" parameterType="map" resultMap="UserMap">
    3     select * from user limit #{startIndex}, #{pageSize}
    4 </select>
  3. 測試

     1 @Test
     2 public void testLimit(){
     3     SqlSession sqlSession = mybatisUtils.getSqlSession();
     4     UserMapper mapper = sqlSession.getMapper(UserMapper.class);
     5     HashMap<String, Integer> map = new HashMap<String, Integer>();
     6     map.put("startIndex", 0);
     7     map.put("pageSize",2);
     8 
     9     List<User> userList = mapper.getUserByLimit(map);
    10     for (User user: userList){
    11         System.out.println(user);
    12     }
    13 
    14     sqlSession.close();
    15 }

    7.2RowBounds分頁

    不再使用SQL實現分頁

    1. 介面

      1 //分頁2
      2 public List<User> getUserByRowBounds();
    2. mapper.xml

      1 <!-- 分頁2 -->
      2 <select id="getUserByRowBounds"  resultMap="UserMap">
      3     select * from user
      4 </select>
    3. 測試

       1 @Test
       2 public void getUserByRowBounds(){
       3     SqlSession sqlSession = mybatisUtils.getSqlSession();
       4     //        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
       5 
       6     RowBounds rowBounds = new RowBounds(1,2);
       7 
       8     //通過java程式碼層面進行實現分頁
       9     List<User> userlist = sqlSession.selectList("com.zmz.Dao.UserMapper.getUserByRowBounds",null,rowBounds);
      10 
      11     for (User user : userlist) {
      12         System.out.println(user);
      13     }
      14 
      15     sqlSession.close();
      16 }

7.3、分頁外掛

mybatis-PageHelper分頁外掛使用

瞭解即可,知道怎麼使用即可,網上有教程使用。

8、使用註解開發

8.1、面向介面程式設計

  • 面向物件是指,我們考慮問題時,以物件為單位,考慮它的屬性及方法。

  • 面向過程是指,我們考慮問題時,以一個具體的流程(事務過程)為單位,考慮它的實現。

  • 介面設計與非介面設計是針對複用技術而言的,與面向物件(過程)不是一個問題,更多的體現就是對系統整體的架構。

abstract class 針對某一個個體的抽象

interface針對某一個個體的某一方面的抽象編寫。

8.2、使用註解開發

  1. 註解在介面上實現

    1 @Select("select * from user")
    2 public List<User> getUsers();
  2. 需要在核心配置檔案中繫結介面!

    1 <mappers>
    2     <mapper class="com.zmz.Dao.UserMapper"/>
    3 </mappers>
  3. 測試

     1 @Test
     2 public void test01(){
     3     SqlSession sqlSession = mybatisUtils.getSqlSession();
     4     UserMapper mapper = sqlSession.getMapper(UserMapper.class);
     5 
     6     List<User> users = mapper.getUsers();
     7     for (User user : users) {
     8         System.out.println(user);
     9     }
    10 
    11     sqlSession.close();
    12 
    13 }

本質:反射機制實現

底層:動態代理!

總結:註解方式只能試用那些簡單的sql語句,如果太過複雜,比如一些別名比較多,比較複雜的sql語句,還是需要xml方式進行名稱對映。

Mybatis詳細的執行流程!

8.3、CRUD

我們可以在工具類建立的時候實現自動提交事務!

1 public static SqlSession getSqlSession(){
2     return sqlSessionFactory.openSession(true);
3     //是一個重構的函式,加上引數ture,意味著事務自動提交,不需要在手動提交事務了
4 }

編寫介面,增加註解

 1 //使用註解方式進行使用mybatis
 2 //實體類:pojo --》 entity --》 domain
 3 
 4 public interface UserMapper {
 5 
 6     @Select("select * from user")
 7     public List<User> getUsers();
 8 
 9     //方法存在多個引數,所有的引數前面必須加上@Param("id")註解
10     //裡面引數的值隨便取名稱,但是註解中的名稱必須要與#{id}相對應
11     @Select("select * from user where id=#{id}")
12     public User getUserById(@Param("id") int id2);
13 
14     @Insert("insert into user(id, name, pwd) values (#{id}, #{name}, #{password})")
15     public int addUser(User user);
16 
17     @Update("update user set name=#{name}, pwd=#{password} where id=#{id}")
18     public int updateUser(User user);
19 
20     @Delete("delete from user where id=#{uid}")
21     public int deleteUser(@Param("uid") int id);
22 
23 }

測試類

 1 @Test
 2 public void test01(){
 3     SqlSession sqlSession = mybatisUtils.getSqlSession();
 4     UserMapper mapper = sqlSession.getMapper(UserMapper.class);
 5 
 6     //        List<User> users = mapper.getUsers();
 7     //        for (User user : users) {
 8     //            System.out.println(user);
 9     //        }
10     //        User userById = mapper.getUserById(1);
11     //        System.out.println(userById);
12 
13     //        mapper.addUser(new User(7, "hello", "123122"));
14 
15     //        mapper.updateUser(new User(7,"to","122112"));
16 
17     mapper.deleteUser(7);
18 
19     sqlSession.close();
20 
21 }

【注意】:我們必須要將介面註冊繫結到我們的核心配置檔案中!

關於@Param()註解

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

  • 引用型別不需要加

  • 如果只有一個基本型別的話,可以忽略,但是建議大家都加上

  • 我們在SQL中引用的就是我們這裡的@Param()中設定的屬性名!

#{} , ${}區別

網上的答案是:#{}是預編譯處理,$ {}是字串替換。mybatis在處理#{}時,會將sql中的#{}替換為?號,呼叫PreparedStatement的set方法來賦值;mybatis在處理 $ { } 時,就是把 ${ } 替換成變數的值。使用 #{} 可以有效的防止SQL注入,提高系統安全性。

對於這個題目我感覺要抓住兩點: (1)$ 符號一般用來當作佔位符,常使用Linux指令碼的人應該對此有更深的體會吧。既然是佔位符,當然就是被用來替換的。知道了這點就能很容易區分$和#,從而不容易記錯了。 (2)預編譯的機制。預編譯是提前對SQL語句進行預編譯,而其後注入的引數將不會再進行SQL編譯。我們知道,SQL注入是發生在編譯的過程中,因為惡意注入了某些特殊字元,最後被編譯成了惡意的執行操作。而預編譯機制則可以很好的防止SQL注入。

總結:多用#{}(安全),少用或不用${}(不安全)

9、Lombok

簡介

  • Lombok 是一種 Java™ 實用工具,可用來幫助開發人員消除 Java 的冗長,尤其是對於簡單的 Java 物件(POJO)。它通過註解實現這一目的。

  • 要使用這個物件,必須還要寫一些getter和setter方法,可能還要寫一個構造器、equals方法、或者hash方法.這些方法很冗長而且沒有技術含量,我們叫它樣板式程式碼. lombok的主要作用是通過一些註解,消除樣板式程式碼,像這樣:

    1 @Data
    2 public class Mountain{
    3     private String name;
    4     private double longitude;
    5     private String country;
    6 }
  • 如果覺得@Data這個註解有點簡單粗暴的話,Lombok提供一些更精細的註解,比如@Getter,@Setter,(這兩個是field註解),@ToString,@AllArgsConstructor(這兩個是類註解). 這些可能是最常見的用法,更詳細的用法可以看Lombok feature overview這裡.

使用步驟:

  1. 在IDEA中安裝Lombok外掛!

  2. 在專案中匯入lombok的jar包!

    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.18.12</version>
    </dependency>
  3. 在實體類上加註解即可!

    @Data: 無參構造,get,set, toString,hashcode,equals
    @AllArgsConstructor
    @NoArgsConstructor
    @Getter and @Setter:可以放在類上,也可以放在欄位上單個建立這個方法
    @ToString
  4. 說明方法及使用

    @Getter and @Setter
    @FieldNameConstants
    @ToString
    @EqualsAndHashCode
    @AllArgsConstructor, @RequiredArgsConstructor and @NoArgsConstructor
    @Log, @Log4j, @Log4j2, @Slf4j, @XSlf4j, @CommonsLog, @JBossLog, @Flogger, @CustomLog
    @Data
    @Builder
    @SuperBuilder
    @Singular
    @Delegate
    @Value
    @Accessors
    @Wither
    @With
    @SneakyThrows
    @val
    @var
    experimental @var
    @UtilityClass
    Lombok config system

    說明:

    @Data: 無參構造,get,set, toString,hashcode,equals
    @AllArgsConstructor
    @NoArgsConstructor
    @Getter and @Setter:可以放在類上,也可以放在欄位上單個建立這個方法
    @ToString
    基本常用的幾個,有參,無參建構函式

    優點是使用註解即可幫忙自動生成程式碼,大大減少了程式碼量,使程式碼非常簡潔。

    但是並不意味著Lombok的使用沒有任何問題,在使用Lombok的過程中,還可能存在對隊友不友好、對程式碼不友好、對除錯不友好、對升級不友好等問題。

    最重要的是,使用Lombok還會導致破壞封裝性的問題。

    雖然使用Lombok存在著很多方便,但是也帶來了一些問題。

總結:儘量少用,他減少了程式碼可讀性和舒適性,有一些外掛減免了我們一些工作,但也要知道,適合最重要,而不是一味追求高大上技術。

10、多對一處理

  • 多對一:多個學生,對應一個老師

  • 對於學生這邊而言,關聯...多個學生,關聯一個老師【多對一】

  • 對於老師而言,集合..一個老師有很多學生,【一對多】

建立兩個多對一的實體類表(SQL語句):

 1 CREATE TABLE `teacher`(
 2     `id` INT(10) NOT NULL,
 3     `name` VARCHAR(30) DEFAULT NULL,
 4     PRIMARY KEY (`id`)
 5 )ENGINE=INNODB DEFAULT CHARSET=utf8
 6 
 7 INSERT INTO teacher(`id`,`name`) VALUES (1,'慶老師');
 8 
 9 CREATE TABLE `student`(
10     `id` INT(10) NOT NULL,
11     `name` VARCHAR(30) DEFAULT NULL,
12     `tid` INT(10) DEFAULT NULL,
13     PRIMARY KEY(`id`),
14     KEY `fktid` (`tid`),
15     CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`)
16 )ENGINE=INNODB DEFAULT CHARSET=utf8
17 
18 INSERT into `student`(`id`,`name`,`tid`) VALUES ('1','小明','1');
19 INSERT into `student`(`id`,`name`,`tid`) VALUES ('2','小紅','1');
20 INSERT into `student`(`id`,`name`,`tid`) VALUES ('3','小張','1');
21 INSERT into `student`(`id`,`name`,`tid`) VALUES ('4','小王','1');
22 INSERT into `student`(`id`,`name`,`tid`) VALUES ('5','小李','1');

測試環境搭建

  1. 匯入lombok

  2. 新建實體類Teacher, Student

  3. 建立Mapper介面

  4. 建立Mapper.xml檔案

  5. 在核心配置檔案中繫結註冊我們的Mapper介面或者檔案!

  6. 測試查詢是否能夠成功!

按照查詢巢狀處理

 1 <!--
 2    思路:
 3      1、查詢所有的學生資訊
 4      2、根據查詢出來的學生的tid,尋找對應的老師! 子查詢
 5 -->
 6 
 7 <resultMap id="StudentTeacher" type="Student">
 8     <result property="id" column="id"/>
 9     <result property="name" column="name"/>
10     <!--複雜的屬性,我們需要單獨處理-->
11     <!--
12            物件:association
13            集合:collection
14         -->
15     <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
16 </resultMap>
17 
18 <select id="getStudent" resultMap="StudentTeacher">
19     select * from student
20 </select>
21 <select id="getTeacher" resultType="Teacher">
22     select * from teacher where id=#{id}
23 </select>

按照結果巢狀處理

 1 <!--按照結果巢狀處理-->
 2 <select id="getStudent2" resultMap="StudentTeacher2">
 3     select s.id sid, s.name sname, t.name tname
 4     from student s, teacher t
 5     where s.tid = t.id
 6 </select>
 7 
 8 <resultMap id="StudentTeacher2" type="Student">
 9     <result property="id" column="sid"/>
10     <result property="name" column="sname"/>
11     <association property="teacher" javaType="Teacher">
12         <result property="name" column="tname"/>
13     </association>
14 </resultMap>

回顧Mysql多對一查詢方式

  • 子查詢

  • 聯表查詢

11、一對多處理

例:一個老師擁有多個學生

對於老師來說,就是一對多關係

搭建環境

與上面一樣

按照結果巢狀處理(推薦這個)

 1 <!--按結果巢狀查詢-->
 2 <resultMap id="TeacherStudent" type="Teacher">
 3     <result property="id" column="tid"/>
 4     <result property="name" column="tname"/>
 5     <!--複雜的屬性,我們需要單獨處理-->
 6     <!--
 7            物件:association
 8            集合:collection
 9         -->
10     <collection property="students" ofType="Student">
11         <result property="id" column="sid"/>
12         <result property="name" column="sname"/>
13         <result property="tid" column="tid"/>
14     </collection>
15 </resultMap>
16 
17 <select id="getTeacher" resultMap="TeacherStudent">
18     select s.id sid, s.name sname, t.id tid, t.name tname
19     from student s, teacher t
20     where t.id=#{tid} and t.id = s.tid
21 </select>

按照查詢巢狀處理

 1 <resultMap id="TeacherStudent2" type="Teacher">
 2     <result property="id" column="id"/>
 3     <result property="name" column="name"/>
 4     <collection property="students" javaType="ArrayList" ofType="Student" column="id" select="getStudent"/>
 5 </resultMap>
 6 <select id="getTeacher2" resultMap="TeacherStudent2">
 7     select * from teacher where id =#{tid}
 8 </select>
 9 <select id="getStudent" resultType="Student">
10     select * from student where tid = #{tid}
11 </select>

小結:

  1. 關聯 - association [多對一]

  2. 集合 - collection [一對多]

  3. javaType & ofType

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

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

注意

  • 保證SQL的可讀性,儘量保證通俗易懂

  • 注意一對多和多對一中;屬性名和欄位的問題!

  • 如果問題不好排查錯誤,可以使用日誌,建議使用Log4j

sql面試高頻

  • MySQL引擎

  • INNODB底層原理

  • 索引

  • 索引優化!

12、動態SQL

簡介

動態 SQL 是 MyBatis 的強大特性之一。如果你使用過 JDBC 或其它類似的框架,你應該能理解根據不同條件拼接 SQL 語句有多痛苦,例如拼接時要確保不能忘記新增必要的空格,還要注意去掉列表最後一個列名的逗號。利用動態 SQL,可以徹底擺脫這種痛苦。

使用動態 SQL 並非一件易事,但藉助可用於任何 SQL 對映語句中的強大的動態 SQL 語言,MyBatis 顯著地提升了這一特性的易用性。

動態SQL就是根據不同的條件生成不同的SQL語句
如果你之前用過 JSTL 或任何基於類 XML 語言的文字處理器,你對動態 SQL 元素可能會感覺似曾相識。在 MyBatis 之前的版本中,需要花時間瞭解大量的元素。藉助功能強大的基於 OGNL 的表示式,MyBatis 3 替換了之前的大部分元素,大大精簡了元素種類,現在要學習的元素種類比原來的一半還要少。

if
choose (when, otherwise)
trim (where, set)
foreach

搭建環境

1 CREATE TABLE `blog`(
2     `id` VARCHAR(50) NOT NULL COMMENT '部落格id',
3     `title` VARCHAR(100) NOT NULL COMMENT '部落格標題',
4     `author` VARCHAR(30) NOT NULL COMMENT '部落格作者',
5     `create_time` datetime NOT NULL COMMENT '建立時間',
6     `views` INT(30) NOT NULL COMMENT '瀏覽量'
7 )ENGINE=INNODB DEFAULT CHARSET=utf8

建立一個基礎工程

  1. 導包

  2. 編寫配置檔案

  3. 編寫實體類

    1 @Data
    2 public class Blog {
    3     private String id;
    4     private String title;
    5     private String author;
    6     private Date createTime;  //屬性名和資料庫欄位名不一致
    7     private int views;
    8 }
  4. 編寫實體類對應Mapper介面及Mapper.xml檔案

IF

1 <select id="queryBlogIF" parameterType="map" resultType="Blog">
2     select * from blog where 1=1
3     <if test="author !=null">
4         and author =#{author}
5     </if>
6     <if test="title !=null">
7         and title=#{title}
8     </if>
9 </select>

choose (when, otherwise)

 1 <select id="queryBlogChoose" parameterType="map" resultType="Blog">
 2     select * from blog
 3     <where>
 4         <choose>
 5             <when test="author !=null">
 6                 author =#{author}
 7             </when>
 8             <when test="title != null">
 9                 title = #{title}
10             </when>
11             <otherwise>
12                 views = #{views}
13             </otherwise>
14         </choose>
15     </where>
16 </select>

choose標籤類似switch..case語句;

trim(where,set)

 1 <select id="queryBlogIF" parameterType="map" resultType="Blog">
 2     select * from blog
 3     <where>
 4         <if test="author !=null">
 5             author =#{author}
 6         </if>
 7         <if test="title !=null">
 8             and title=#{title}
 9         </if>
10     </where>
11 </select>

where 元素只會在子元素返回任何內容的情況下才插入 “WHERE” 子句。而且,若子句的開頭為 “AND” 或 “OR”,where 元素也會將它們去除。

 1 <update id="updateBlog" parameterType="map">
 2     update blog
 3     <set>
 4         <if test="author != null">
 5             author = #{author},
 6         </if>
 7         <if test="title != null">
 8             title = #{title}
 9         </if>
10     </set>
11     where id =#{id}
12 </update>

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

所謂的動態SQL,本質還是SQL語句,只是我們可以再SQL層面,去執行一個邏輯程式碼.

Foreach

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

提示 你可以將任何可迭代物件(如 List、Set 等)、Map 物件或者陣列物件作為集合引數傳遞給 foreach。當使用可迭代物件或者陣列時,index 是當前迭代的序號,item 的值是本次迭代獲取到的元素。當使用 Map 物件(或者 Map.Entry 物件的集合)時,index 是鍵,item 是值。

1     <select id="queryBlogForeach" parameterType="map" resultType="Blog">
2         select * from blog
3         <where>
4             <foreach collection="ids" item="id" open="and (" separator="or" close=")">
5                 id=#{id}
6             </foreach>
7         </where>
8     </select>

總結:將你想遍歷的list進行一個一個遍歷,之間還可以進行拼接,將其拼接成不同樣式。

測試類:

 1 @Test
 2 public void test03(){
 3     SqlSession sqlSession = mybatisUtils.getSqlSession();
 4     BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
 5 
 6 
 7 
 8     ArrayList<Integer> ids = new ArrayList<Integer>();
 9     ids.add(1);
10     ids.add(2);
11     ids.add(3);
12 
13     HashMap map = new HashMap();
14     map.put("ids",ids);
15 
16     List<Blog> blogs = mapper.queryBlogForeach(map);
17     for (Blog blog : blogs) {
18         System.out.println(blog);
19     }
20     sqlSession.close();
21 }

SQL片段

有的時候,我們可能會講一些功能的部分抽取出來,方便複用!

  1. 使用SQL標籤抽取公共的部分

    1 <!--將你經常或多次要複用的程式碼放入sql標籤中,然後在其他標籤中使用include標籤進行呼叫即可-->
    2 <sql id="queryBlogIF">
    3     <if test="author !=null">
    4         and author =#{author}
    5     </if>
    6     <if test="title !=null">
    7         and title=#{title}
    8     </if>
    9 </sql>
  2. 在需要使用的地方使用include標籤引用即可

    1 <select id="queryBlogIF" parameterType="map" resultType="Blog">
    2     select * from blog
    3     <where>
    4         <include refid="queryBlogIF"/>
    5     </where>
    6 </select>

注意事項:

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

  • 不要存在where標籤

總結:動態SQL就是在拼接SQL語句,我們只要保證SQL的正確性,按照SQL的格式,去排列組合就可以了。所以,建議在mysql中寫出完整的SQL,再對應的去修改成為我們的動態sql實現通用即可!

13、快取

資料讀取架構

13.1、簡介

  1. 什麼是快取[Cache]?

    • 存在記憶體中的臨時資料。

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

  2. 為什麼使用快取?

    • 減少和資料的互動次數,減少系統開銷,提高系統效率。

  3. 什麼樣的資料能使用快取?

    • 經常查詢並且不經常改變的資料。

13.2、Mybatis快取

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

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

    • 預設情況下,只有一級快取開啟。(SqlSession級別的快取,也成為本地快取)

    • 二級快取需要手動開啟和配置,它是基於namespace級別的快取。

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

13.3、一級快取

測試步驟:

  1. 開啟日誌!

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

    • 查詢過程中進行比較,發現查詢相同id的使用者,在一次sqlSession連線中,是相同的,所以第二次的查詢的地方是在快取中進行查詢。

  3. 檢視日誌輸出

    • 輸出為true,對於兩次結果比較得出,查詢的地址都相同。

    • 分為兩次sqlSession進行查詢的結果進行比較為false。

快取失效的情況:

  1. 查詢不同的東西

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

  3. 查詢不同的Mapper.xml

  4. 手動清理快取!

  5. 快取會使用最近最少使用演算法(LRU, Least Recently Used)演算法來清除不需要的快取。

  6. 快取不會定時進行重新整理(也就是說,沒有重新整理間隔)。

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

13.4、二級快取

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

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

  • 工作機制

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

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

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

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

步驟:

  1. 開啟全域性快取

    1 <!--顯示的開啟全域性快取-->
    2 <setting name="cacheEnabled" value="true"/>
  2. 在要使用的二級快取的Mapper中開啟

    1 <!--在當前Mapper.xml中使用二級快取-->
    2 <cache/>

    也可以自定義引數

    1 <cache
    2        eviction="FIFO"
    3        flushInterval="60000"
    4        size="512"
    5        readOnly="true"/>
  3. 測試

    1. 問題:我們需要將實體類序列化,否則就會報錯

      Cause: java.io.NotSerializableException: com.zmz.pojo.User

小結:

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

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

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

13.5、快取原理

快取原理圖:

快取順序:

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

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

  3. 查詢資料庫

13.6、自定義快取-ehcache

EhCache 是一個純Java的程序內快取框架,具有快速、精幹等特點;

要在程式中使用ehcache,先要導包!

1 <!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
2 <dependency>
3     <groupId>org.mybatis.caches</groupId>
4     <artifactId>mybatis-ehcache</artifactId>
5     <version>1.2.1</version>
6 </dependency>

在mapper中指定使用我們的ehcache快取實現!

1 <cache type="org.mybatis.caches.ehcache.EhcacheCache"/>

ehcache.xml

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 3          xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
 4          updateCheck="false">
 5     <!--
 6        diskStore:為快取路徑,ehcache分為記憶體和磁碟兩級,此屬性定義磁碟的快取位置。引數解釋如下:
 7        user.home – 使用者主目錄
 8        user.dir  – 使用者當前工作目錄
 9        java.io.tmpdir – 預設臨時檔案路徑
10      -->
11     <diskStore path="java.io.tmpdir/Tmp_EhCache"/>
12     <!--
13        defaultCache:預設快取策略,當ehcache找不到定義的快取時,則使用這個快取策略。只能定義一個。
14      -->
15     <!--
16       name:快取名稱。
17       maxElementsInMemory:快取最大數目
18       maxElementsOnDisk:硬碟最大快取個數。
19       eternal:物件是否永久有效,一但設定了,timeout將不起作用。
20       overflowToDisk:是否儲存到磁碟,當系統宕機時
21       timeToIdleSeconds:設定物件在失效前的允許閒置時間(單位:秒)。僅當eternal=false物件不是永久有效時使用,可選屬性,預設值是0,也就是可閒置時間無窮大。
22       timeToLiveSeconds:設定物件在失效前允許存活時間(單位:秒)。最大時間介於建立時間和失效時間之間。僅當eternal=false物件不是永久有效時使用,預設是0.,也就是物件存活時間無窮大。
23       diskPersistent:是否快取虛擬機器重啟期資料 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.
24       diskSpoolBufferSizeMB:這個引數設定DiskStore(磁碟快取)的快取區大小。預設是30MB。每個Cache都應該有自己的一個緩衝區。
25       diskExpiryThreadIntervalSeconds:磁碟失效執行緒執行時間間隔,預設是120秒。
26       memoryStoreEvictionPolicy:當達到maxElementsInMemory限制時,Ehcache將會根據指定的策略去清理記憶體。預設策略是LRU(最近最少使用)。你可以設定為FIFO(先進先出)或是LFU(較少使用)。
27       clearOnFlush:記憶體數量最大時是否清除。
28       memoryStoreEvictionPolicy:可選策略有:LRU(最近最少使用,預設策略)、FIFO(先進先出)、LFU(最少訪問次數)。
29       FIFO,first in first out,這個是大家最熟的,先進先出。
30       LFU, Less Frequently Used,就是上面例子中使用的策略,直白一點就是講一直以來最少被使用的。如上面所講,快取的元素有一個hit屬性,hit值最小的將會被清出快取。
31       LRU,Least Recently Used,最近最少使用的,快取的元素有一個時間戳,當快取容量滿了,而又需要騰出地方來快取新的元素的時候,那麼現有快取元素中時間戳離當前時間最遠的元素將被清出快取。
32    -->
33     <defaultCache
34                   eternal="false"
35                   maxElementsInMemory="10000"
36                   overflowToDisk="false"
37                   diskPersistent="false"
38                   timeToIdleSeconds="1800"
39                   timeToLiveSeconds="259200"
40                   memoryStoreEvictionPolicy="LRU"/>
41 
42     <cache
43            name="cloud_user"
44            eternal="false"
45            maxElementsInMemory="5000"
46            overflowToDisk="false"
47            diskPersistent="false"
48            timeToIdleSeconds="1800"
49            timeToLiveSeconds="1800"
50            memoryStoreEvictionPolicy="LRU"/>
51 
52 </ehcache>

spring下載地址

https://repo.spring.io/release/org/springframework/spring/

總結:mybatis學習完畢,跟著學習的導師質量很高,對於目前我所學習的方面有很多收穫。mybatis簡單書寫,複雜還不行,慢慢練習增加熟練度,應用在專案中。這只是第一步。

-------寶劍鋒從磨礪出,梅花香自苦寒來。