狂神mybatis學習筆記
阿新 • • 發佈:2022-03-04
1、mybatis簡介
MyBatis 是一款優秀的持久層框架
它支援定製化 SQL、儲存過程以及高階對映。
MyBatis 避免了幾乎所有的 JDBC程式碼、手動設定引數以及獲取結果集。
MyBatis 可以使用簡單的XML,將Java 的 POJO對映成資料庫中的記錄。
2、mybatis使用過程
//1.maven工程匯入依賴,非maven工程匯入jar包 <!-- mysql --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.28</version> </dependency> <!-- mybatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.9</version> </dependency> <!-- log4j --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> //2.建立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> <!--1.引入外部檔案--> <properties resource="db.properties"/> <!--2.設定引數--> <settings> <setting name="logImpl" value="LOG4J"/> </settings> <!--3.給pojo起別名--> <typeAliases> <package name="pojo"/> </typeAliases> <!--4.環境引數 --> <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> <!--5.註冊mappers--> <mappers> <mapper resource="mapper/userMapper.xml"/> </mappers> </configuration> //3.建立MybatisUtils工具類 package mapper; 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 MybatisUtils { private static String resource; private static InputStream inputStream; private static SqlSessionFactory sqlSessionFactory; static { resource = "mybatis-config.xml"; try { inputStream = Resources.getResourceAsStream(resource); } catch (IOException e) { e.printStackTrace(); } sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } public static SqlSession getSqlSession(){ return sqlSessionFactory.openSession(); } } //4.建立實體類 package pojo; public class User { private Integer id; private String name; private Integer age; public User() { } public User(Integer id, String name, Integer age) { this.id = id; this.name = name; this.age = age; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } } //5.建立持久層介面UserMapper package mapper; import pojo.User; import java.util.List; public interface UserMapper { List<User> selectUser(); } //6.建立與介面對應的userMapper.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="mapper.UserMapper"> <select id="selectUser" resultType="pojo.User"> select * from User </select> </mapper> //7.簡單使用 @Test public void test01(){ SqlSession session = null; try { session = MybatisUtils.getSqlSession(); UserMapper mapper = session.getMapper(UserMapper.class); List<User> list = mapper.selectUser(); list.forEach(System.out::println); }finally { if(session!=null){ session.close(); } } } @Test public void test03(){ SqlSession session = null; try { session = MybatisUtils.getSqlSession(); UserMapper mapper = session.getMapper(UserMapper.class); User user = new User(null, "李四", 28); mapper.addUser(user); session.commit(); }finally { if(session!=null){ session.close(); } } }
3、mybatis過程解析
//1.mybatis-config.xml 該配置檔案時mybatis的核心配置檔案,在這裡面手動設定了資料庫連線引數,以及註冊mappers(介面對映的xml檔案) 因此,java只需要讀取這一個配置檔案就能把相關的非java檔案都讀取到了 //2.UserMapper 該類存在的必要性其實就是單純使用userMapper.xml無法在java層面呼叫方法,需要userMapper做一個媒介 並且可以在介面中定義持久層實現的規範 //3.userMapper.xml 該對映xml檔案的作用就是書寫SQL語句,其內建了mapper標籤可與介面繫結,增刪改查標籤可與介面中的方法繫結 語句執行後返回增刪改查標籤中約定的type或者map型別 //4.一切就緒,如何使用 想在java層面去呼叫非java檔案的內容,必須要先讀取資源 讀取之後, 由會話工廠構造器去構建會話工廠//會話工廠就對應mybatis的最高一級 由會話工廠開啟一個會話 //上述步驟封裝成工具類了 由會話獲取對映 由對映執行具體方法 //5.mybatis預設開啟事務 在增刪改的方法呼叫時,別忘了commit提交事務 如果業務不需要事務處理,也可以在開啟session會話的時候直接使用 openSession(true)過載的方式設定預設提交事務
4、小問題
//1.如果資料庫列名跟pojo中屬性名不一致 解決方案一: 查詢時給列名起別名 解決方案二: 使用resultMap標籤設定屬性跟列的匹配 1>新增resultMap標籤 <resultMap id="userResultMap" type="User"> <result property="name" column="username"/> <result property="pwd" column="password"/> </resultMap> 2>設定增刪改查標籤的返回值為resultMap <select id="selectUser" resultMap="userResultMap"> select * from user </select> //2.userMapper.xml中 如果有傳參的SQL語句,使用#{xx}來設定引數 推薦跟繫結的介面放在同一包下
5、mybatis核心配置檔案
//1.properties
作用,引入外部檔案,通常是db.properties,log4j.properties
<properties resource="db.properties"/>
//2.settings
作用,改變 MyBatis 的執行時行為,通常使用預設即可,如有需求自主配置
<settings>
<setting name="logImpl" value="LOG4J"/>
<setting name="lazyLoadingEnabled" value="true"/>
</settings>
//3.typeAliases
作用,給pojo起別名
<typeAliases>
//第一種,單個的,可以DIY
<typeAlias type="pojo.User" alias="User"/>
//第二種,直接掃描實體類的包,名字預設就是實體類的名字首字母小寫,推薦使用
<package name="pojo"/>
</typeAliases>
//4.enviroments
作用,環境配置,設定事務管理和資料來源,預設事務管理是jdbc,預設資料來源是pooled
<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>
//5.mappers
作用,註冊mapper,把與介面繫結的xml檔案註冊進mybatis核心檔案中
<mappers>
//第一種,注意,resource如果有多級目錄,需要用/分割而不能使用.去分割,推薦使用
<mapper resource="userMapper.xml"/>
//第二種,類的方式,xml必須放在mapper介面包下,而且名字必須一致
<mapper class="UserMapper"/>
//第三種,包掃描的方式,xml必須放在mapper介面包下,而且名字必須一致
<package name="mapper"/>
</mappers>
6、mybatis四大核心物件
//1.Sql Session Factory Builder
//2.Sql Session Factory
//3.Sql Session
//4.Mapper
builder載入核心配置檔案=》factory開啟會話=》session載入介面=》mapper執行方法
7、日誌
//1.日誌有什麼用
作用,列印執行過程中的一些內容,便於排錯
//2.mybatis中日誌怎麼用
//在mybatis核心配置檔案中設定日誌實現
<settings>
//STDOUT_LOGGING是mybatis內建的標準日誌,可以直接使用,
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
//3.除了STDOUT_LOGGING我們還需要掌握的日誌是LOG4J
1>匯入log4j依賴
<!-- log4j -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2>建立log4j.properties
# 定義輸出級別為debug,輸出名稱為stdout
log4j.rootLogger=debug,stdout
# 定義stdout的輸出採用哪個類來執行
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
# 定義stdout的輸出型別的樣式佈局
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
# 定義stdout樣式佈局的訊息格式
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
//上述都是最基本的,有需求直接去搜一個完整的,不需要記憶
3>在mybatis核心配置檔案中設定日誌實現
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
4>log4j的簡單使用
import org.apache.log4j.Logger;
static Logger logger = Logger.getLogger(M1.class);
logger.info()、logger.debug()、logger.error()
8、limit分頁
//1.dao層
@Test
public void testLimit(){
SqlSession session = null;
try {
session = MybatisUtils.getSqlSession();
UserMapper mapper = session.getMapper(UserMapper.class);
HashMap<String, Integer> map = new HashMap<>();
//通過map集合傳給資料庫一個起始值,一個頁容量
map.put("startIndex",2);
map.put("pageSize",5);
List<User> list = mapper.queryUserByLimit(map);
list.forEach(System.out::println);
}finally {
if(session!=null){
session.close();
}
}
}
//2.mapper.xml
<select id="queryUserByLimit" parameterType="map" resultMap="userResultMap">
select * from user limit #{startIndex},#{pageSize}
</select>
9、使用註解開發
//1.註解如何使用
當我們使用註解時,就不需要在把這個語句寫入mapper.xml了
1>在核心配置檔案中繫結介面
<mappers>
<mapper class="mapper.UserMapper"/>
</mappers>
2>在介面中的方法上新增需要的註解
@Select("select * from user")
List<User> selectUser();
3>測試
//2.常用註解
@Select
@Insert
@Update
@Delete
@Result
@Results
@ResultMap
@Param
//查詢程式碼示例,增刪改同理
@Select("select * from user")
@Results(id = "userResultMap", value={
@Result(property = "name",column = "username",javaType = String.class),
@Result(property = "pwd",column = "password",javaType = Integer.class)
})
List<User> selectUser();
@Select("select * from user where id=#{id}")
@ResultMap("userResultMap")
User queryUserById(@Param("id") Integer id);
//這四個關係到多對應結果集對映,動態SQL,快取,複雜程度高,建議直接用xml得了
@One
@Many
@SelectProvider
@CacheNamespace
//3.註解總結
雖然省略了xml檔案的書寫,但是如果sql語句非常複雜時,註解的方式反而弄巧成拙
當然,作為一名合格的程式設計師,要能做到在xml和註解的方式中靈活切換(主要自己說了不算=.=)
10、lombok
//1.lombok是什麼
外掛,構建工具
//2.lombok能幹什麼
簡化pojo建立
//3.lombok怎麼用
1>idea安裝lombok外掛
2>匯入maven依賴
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>provided</scope>
</dependency>
3>在pojo上加註解
@NoArgsConstructor
@AllArgsConstructor
@Data
public class User {
private Integer id;
private String name;
private Integer pwd;
}
//4.總結
不能實現構造器過載,需要手動新增
屬於黃階心法,連玄階都夠不上,可用可不用
11、多對一
//1.第一種方式
<!--子查詢-->
<select id="selectStudent" resultMap="selectStudent">
select * from student
</select>
<resultMap id="selectStudent" type="Student">
<association property="teacher" column="tid" select="selectTeacherById"/>
</resultMap>
<select id="selectTeacherById" resultType="teacher">
select * from teacher where id=#{tid}
</select>
//resultMap下除了關聯欄位都可以省略
//association中的column屬性就是傳值
//在子查詢中,傳值不需要parameterType屬性,而且#{xx}的xx可以隨便寫,當然最好跟傳值一致
//2.第二種方式
<!--聯表查詢-->
<select id="selectStudent2" resultMap="selectStudent2">
select s.id,s.name,t.id _id,t.name _name from student s join teacher t on s.tid = t.id
</select>
<resultMap id="selectStudent2" type="Student">
<id property="id" column="id"/>
<result property="name" column="name"/>
<association property="teacher">
<id property="id" column="_id"/>
<result property="name" column="_name"/>
</association>
</resultMap>
//resultMap下任何欄位都不可以省略
//所有的column屬性值都是根據查詢結果集來的,因此要不能重複,起別名解決就行
12、一對多
//1.第一種方式
<!--子查詢-->
<select id="selectTeacher" resultMap="selectTeacher">
select * from teacher where id=#{id}
</select>
<resultMap id="selectTeacher" type="Teacher">
<id property="id" column="id"/>
<collection property="list" column="id" ofType="Student" select="studentAdd"/>
</resultMap>
<select id="studentAdd" resultType="Student">
select * from student where tid = #{id}
</select>
//resultMap下非關聯的非主鍵欄位可以省略
//collection中的column屬性就是傳值,ofType是泛型型別
//在子查詢中,傳值不需要parameterType屬性,而且#{xx}的xx可以隨便寫,當然最好跟傳值一致
//2.第二種方式
<!--聯表查詢-->
<select id="selectTeacher2" resultMap="selectTeacher2">
select t.id,t.name,s.id _id,s.name _name, tid from teacher t join student s on t.id=s.tid
</select>
<resultMap id="selectTeacher2" type="Teacher">
<id property="id" column="id"/>
<result property="name" column="name"/>
<collection property="list" ofType="Student">
<id property="id" column="_id"/>
<result property="name" column="_name"/>
<result property="tid" column="tid"/>
</collection>
</resultMap>
//resultMap下任何欄位都不可以省略
//collection中只需要寫泛型型別ofType
//所有的column屬性值都是根據查詢結果集來的,因此要不能重複,起別名解決就行
13、動態SQL
//1.if
<select id="selectBlog" parameterType="map" resultType="Blog">
select * from blog where 1=1
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</select>
注意點:
1.傳參用hashmap,這樣可以隨意控制條件
2.初始語句後面加一個where 1=1保證語法正確
3.每個if的拼接語句都要字首一個and
//2.where
上述語句中where 1=1是不符合規範的無奈之舉,我們可以使用where標籤來替換
<where>
<if test="title != null">
and title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
當if至少有一個滿足的時候,才新增where,並且第一個if拼接的語句中的開頭and會被去掉
//3.choose
choose等於java中的switch,where等於case,otherwise等於default
<select id="selectBlog" parameterType="map" resultType="Blog">
select * from blog
<where>
<choose>
<when test="title!=null">
and title = #{title}
</when>
<otherwise>
and 1=1
</otherwise>
</choose>
</where>
</select>
//4.set
set 元素會動態前置 SET 關鍵字,同時也會刪掉最後一個逗號
<update id="updateBlog" parameterType="map">
update blog
<set>
<if test="title!=null">
title=#{title},
</if>
<if test="author!=null">
author=#{author},
</if>
</set>
where id=#{id}
</update>
//5.sql片段
作用,抽取公共程式碼,在需要的地方引用,跟java中的方法意思差不多
1>抽取程式碼
<sql id="xx">
<if test="title!=null">
title=#{title},
</if>
<if test="author!=null">
author=#{author},
</if>
</sql>
2>引用
<include refid="xx"/>
注意,where和set這種能幫助我們優化的東西不要抽取,僅僅抽取條件判斷或者普通的SQL語句就行
//6.foreach
作用,類似集合遍歷一樣
<foreach item="item" collection="list" open="(" separator="," close=")">
#{item}
</foreach>
collection是集合 item是集合項 open是字首 separator是分隔符 close是字尾
14、快取
//1.快取幹嘛的
當有大量客戶端頻繁訪問資料時,資料庫的壓力是非常大的,因此把常檢視且不常修改的資料放入快取,不走資料庫,減少資料庫的壓力
//2.一級快取
mybatis預設開啟一級快取,作用在sqlSession範圍內
==========================================
Opening JDBC Connection
Created connection 2114684409.
Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7e0b85f9]
==> Preparing: select * from user where id = ?
==> Parameters: 1(Integer)
<== Columns: id, username, password
<== Row: 1, 老趙, 11
<== Total: 1
User(id=1, username=老趙, password=11)
User(id=1, username=老趙, password=11)
true
Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7e0b85f9]
Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7e0b85f9]
==========================================
//查詢同一條資料,在一個sqlSession範圍內,只會執行一次SQL語句,第二次的查詢直接從快取中拿資料
//一級快取是會失效的
1.增刪改會清除快取
2.手動清理session.clearCache()
//3.二級快取
1>顯示設定二級快取,其實預設就是開啟的
<setting name="cacheEnabled" value="true"/>
2>在mapper.xml檔案
<cache/>
//這樣就開啟了二級快取
//當一個session關閉時,一級快取的東西就會被序列化存入二級快取,然後別的session如果查詢相同內容就會從二級快取中取
//因為快取類要被序列化因此要實現serializable介面
//4.這些東西看看熟悉下就行,後續會用Redis來做快取