蛇形矩陣起點在中間
一、需要的環境,工具
-
-
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&useUnicode=true&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>
遇到的問題:
-
配置檔案沒有註冊
-
繫結介面錯誤
-
方法名不對
-
返回型別不對
-
Maven匯出資源問題
3、CRUD
3.1、namespace
namespace中的包名要和Dao/mapper介面的報名一致
3.2、select
選擇,查詢語句;
-
id:就是對應得namespace中得方法名;
-
resultType:Sql語句執行得返回值
-
parameterType:引數型別
- 編寫介面
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、其他配置
-
[plugins(外掛)]
-
mybatis-generator-core
-
mybatis-plus 學習網站:https://baomidou.com/
-
通用mapper
-
初學不涉及這方面;
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元件;
-
我們也可以控制每一條日誌的輸出格式;
-
通過定義每一條日誌資訊的級別,我們能夠更加細緻地控制日誌的生成過程;
-
以通過一個配置檔案來靈活地進行配置,而不需要修改應用的程式碼。
-
先匯入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>
-
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>
-
-
配置log4j為日誌的實現
<settings> <setting name="logImpl" value="LOG4J"/> </settings>
-
Log4j的使用!直接測試執行剛才的查詢
簡單使用
-
在要使用Log4j的類中,匯入包import org.apache.Logger;
-
日誌物件,引數為當前類的class
static Logger logger = Logger.getLogger(UserMapperTest.class);
-
日誌級別
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 //根據分頁查詢使用者 2 public List<User> getUserByLimit(Map<String, Integer> map);
-
Mapper.xml
1 <!-- 分頁 --> 2 <select id="getUserByLimit" parameterType="map" resultMap="UserMap"> 3 select * from user limit #{startIndex}, #{pageSize} 4 </select>
-
測試
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 //分頁2 2 public List<User> getUserByRowBounds();
-
mapper.xml
1 <!-- 分頁2 --> 2 <select id="getUserByRowBounds" resultMap="UserMap"> 3 select * from user 4 </select>
-
測試
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 @Select("select * from user") 2 public List<User> getUsers();
-
需要在核心配置檔案中繫結介面!
1 <mappers> 2 <mapper class="com.zmz.Dao.UserMapper"/> 3 </mappers>
-
測試
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這裡.
使用步驟:
-
在IDEA中安裝Lombok外掛!
-
在專案中匯入lombok的jar包!
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.12</version> </dependency>
-
在實體類上加註解即可!
@Data: 無參構造,get,set, toString,hashcode,equals @AllArgsConstructor @NoArgsConstructor @Getter and @Setter:可以放在類上,也可以放在欄位上單個建立這個方法 @ToString
-
說明方法及使用
@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');
測試環境搭建
-
匯入lombok
-
新建實體類Teacher, Student
-
建立Mapper介面
-
建立Mapper.xml檔案
-
在核心配置檔案中繫結註冊我們的Mapper介面或者檔案!
-
測試查詢是否能夠成功!
按照查詢巢狀處理
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>
小結:
-
關聯 - association [多對一]
-
集合 - collection [一對多]
-
javaType & ofType
-
javaType 用來指定實體類中屬性的型別
-
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 @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 }
-
編寫實體類對應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片段
有的時候,我們可能會講一些功能的部分抽取出來,方便複用!
-
使用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>
-
在需要使用的地方使用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、簡介
-
什麼是快取[Cache]?
-
存在記憶體中的臨時資料。
-
將使用者經常查詢的資料放在快取(記憶體)中,使用者去查詢資料就不用從磁碟上(關係型資料庫檔案)查詢,從快取中查詢,從而提高查詢效率,解決了高併發系統的效能問題。
-
-
為什麼使用快取?
-
減少和資料的互動次數,減少系統開銷,提高系統效率。
-
-
什麼樣的資料能使用快取?
-
經常查詢並且不經常改變的資料。
-
13.2、Mybatis快取
-
MyBatis包含一個非常強大的查詢快取特性,它可以非常方便地定製和配置快取。快取可以極大的提升查詢效率。
-
MyBatis系統中預設定義了兩級快取:一級快取和二級快取
-
預設情況下,只有一級快取開啟。(SqlSession級別的快取,也成為本地快取)
-
二級快取需要手動開啟和配置,它是基於namespace級別的快取。
-
為了提高擴充套件性,MyBatis定義了快取介面Cache。我們可以通過實現Cache介面來自定義二級快取。
-
13.3、一級快取
測試步驟:
-
開啟日誌!
-
測試在一個Session中查詢兩次相同記錄
-
查詢過程中進行比較,發現查詢相同id的使用者,在一次sqlSession連線中,是相同的,所以第二次的查詢的地方是在快取中進行查詢。
-
-
檢視日誌輸出
-
輸出為true,對於兩次結果比較得出,查詢的地址都相同。
-
分為兩次sqlSession進行查詢的結果進行比較為false。
-
快取失效的情況:
-
查詢不同的東西
-
增刪改操作,可能會改變原來的資料,所以必定會重新整理快取!
-
查詢不同的Mapper.xml
-
手動清理快取!
-
快取會使用最近最少使用演算法(LRU, Least Recently Used)演算法來清除不需要的快取。
-
快取不會定時進行重新整理(也就是說,沒有重新整理間隔)。
小結:一級快取預設是開啟的,只在一次SqlSession中有效,也就是拿到連線到關閉連線這個區間段!
13.4、二級快取
-
二級快取也叫全域性快取,一級快取作用域太低了,所以誕生了二級快取
-
基於namespace級別的快取,一個名稱空間,對應一個二級快取;
-
工作機制
-
一個會話查詢一條資料,這個資料就會被放在當前會話的一級快取;
-
如果當前會話關閉了,這個會話對應的一級快取就沒了;但是我們想要的是,會話關閉了,一級快取中的資料被儲存到二級快取中;
-
新的會話查詢資訊,就可以從二級快取中獲取內容;
-
不同的mapper查出的資料會放在自己對應的快取(map)中;
-
步驟:
-
開啟全域性快取
1 <!--顯示的開啟全域性快取--> 2 <setting name="cacheEnabled" value="true"/>
-
在要使用的二級快取的Mapper中開啟
1 <!--在當前Mapper.xml中使用二級快取--> 2 <cache/>
也可以自定義引數
1 <cache 2 eviction="FIFO" 3 flushInterval="60000" 4 size="512" 5 readOnly="true"/>
-
測試
-
問題:我們需要將實體類序列化,否則就會報錯
Cause: java.io.NotSerializableException: com.zmz.pojo.User
-
小結:
-
只要開啟了二級快取,在同一個Mapper下就有效
-
所有的資料都會先放在一級快取中;
-
只有當會話提交,或者關閉的時候,才會提交到二級快取中!
13.5、快取原理
快取原理圖:
快取順序:
-
先看二級快取中有沒有
-
再看一級快取中有沒有
-
查詢資料庫
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簡單書寫,複雜還不行,慢慢練習增加熟練度,應用在專案中。這只是第一步。
-------寶劍鋒從磨礪出,梅花香自苦寒來。