Mybatis(笔记)
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-sVjVvSsH-1609421261302)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201230155751713.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-T7eTtpZo-1609421261311)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201230155810111.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-OR4rnozx-1609421261314)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201230155900100.png)]
基礎知識
JDBC Mysql Java基礎 Maven Junit框架:是有配置文件的。最好的方式:看官網文檔
簡介
什么是 MyBatis?
MyBatis 是一款優秀的持久層框架 它支持定制化 SQL、存儲過程以及高級映射。 MyBatis 避免了幾乎所有的 JDBC 代碼和手動設置參數以及獲取結果集。 MyBatis 可以使用簡單的 XML 或注解來配置和映射原生類型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 對象)為數據庫中的記錄。如何獲得MyBatis?
Maven倉庫<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis --> <dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.3</version> </dependency>github:https://github.com/mybatis/mybatis-3/releases中文文檔:https://mybatis.org/mybatis-3/zh/index.html1.2、持久化
數據持久化
持久化就是將程序的數據在持久狀態和瞬時狀態轉化的過程 內存:斷電即失 數據庫(JDBC),io文件持久化。 生活:冷藏、罐頭。為什么需要持久化?
有一些對象,不能讓他丟掉。1.3、持久層
Dao層、Service層、Controller層…
完成持久化工作的代碼塊 層界限十分明顯為什么需要Mybatis?
幫助程序員將數據存入到數據庫中。方便傳統的JDBC代碼太復雜了。簡化、框架、自動化。不用Mybatis也可以。更容易上手。技術沒有高低之分優點:簡單易學靈活sql和代碼的分離,提高了可維護性。提供映射標簽,支持對象與數據庫的orm字段關系映射提供對象關系映射標簽,支持對象關系組建維護提供xml標簽,支持編寫動態sql。最重要的一點:使用的人多!
Spring SpringMVC SpringBoot
第一個Mybatis程序
思路:搭建環境–>導入Mybatis–>編寫代碼–>測試!
搭建環境
搭建數據庫
CREATE DATABASE mybatis;
use mybatis;
CREATE TABLE user(
id INT(20) not null PRIMARY KEY,
name VARCHAR(30) DEFAULT NULL,
pwd VARCHAR(30) DEFAULT NULL
)ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO user (id,name,pwd) VALUES
(1,‘狂神’,‘123456’),
(2,‘張三’,‘123456’),
(3,‘李四’,‘123890’)
新建項目
新建一個普通的maven項目 刪除src目錄 導入maven依賴 <?xml version="1.0" encoding="UTF-8"?>4.0.0
<!--父工程--> <groupId>com.rui</groupId> <artifactId>MyBatis-Study</artifactId> <version>1.0-SNAPSHOT</version><!--導入依賴--> <dependencies><!--mysql驅動--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.17</version></dependency><!--mybatis--><dependency><groupId>org.mybatis</groupId><artifactId>mybatis</artifactId><version>3.5.2</version></dependency><!--junit--><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version></dependency> </dependencies><build><resources><resource><directory>src/main/resources</directory><includes><include>**/*.properties</include><include>**/*.xml</include></includes></resource><resource><directory>src/main/java</directory><includes><include>**/*.properties</include><include>**/*.xml</include></includes><filtering>true</filtering></resource></resources> </build>創建一個模塊
& xml
編寫mybatis的核心需要轉義配置文件<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configurationPUBLIC "-//mybatis.org//DTD Config 3.0//EN""http://mybatis.org/dtd/mybatis-3-config.dtd"> <!--configuration核心配置文件--> <configuration><!--environments配置環境組--><!--default默認環境--><environments default="development"><!--environment單個環境--><environment id="development"><!--transactionManager配置事務管理器--><transactionManager type="JDBC"/><!--配置連接池--><dataSource type="POOLED"><property name="driver" value="com.mysql.jdbc.Driver"/><property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UFT-8"/><property name="username" value="root"/><property name="password" value="Cc105481"/></dataSource></environment></environments></configuration>編寫mybatis工具類
package com.rui.utils;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;//sqlSessionFactory—>SessionFactory public class MyBatisUtils { private static SqlSessionFactory sqlSessionFactory; static { try{ //使用mybatis第一步、獲取sqlSessionFactory對象 String resource = “mybatis-config.xml”; InputStream inputStream = Resources.getResourceAsStream(resource); sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); }catch(IOException e) { e.printStackTrace(); } } //既然有了 SqlSessionFactory,顧名思義,我們就可以從中獲得 SqlSession 的實例了。 // SqlSession 完全包含了面向數據庫執行 SQL 命令所需的所有方法。 // 你可以通過 SqlSession 實例來直接執行已映射的 SQL 語句。 public static SqlSession getSqlSession(){return sqlSessionFactory.openSession(); } }編寫代碼
實體類package com.rui.pojo;public class user {private int id;private String name;private String pwd;public user() {}public user(int id, String name, String pwd) {this.id = id;this.name = name;this.pwd = pwd;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPwd() {return pwd;}public void setPwd(String pwd) {this.pwd = pwd;}@Overridepublic String toString() {return "user{" +"id=" + id +", name='" + name + '\'' +", pwd='" + pwd + '\'' +'}';} }Dao接口package com.rui.dao;import com.rui.pojo.User;import java.util.List;public interface UserDao {List<User> getUserList(); }接口實現類由原來的UserImpl轉變為一個Mapper配置文件 <?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.kuang.dao.UserDAo"> <select id="getUserList" resultType="com.kuang.pojo.User"> select * from mybatis.user </select> </mapper>2.4、測試
注意點:
org.apache.ibatis.binding.BindingException: Type interface com.rui.dao.UserDao is not known to the MapperRegistry.
MapperRegistry是什么?
核心配置文件中注冊mappers
junit測試package com.rui;import com.rui.dao.UserDao; import com.rui.pojo.User; import com.rui.utils.MyBatisUtils; import org.apache.ibatis.session.SqlSession; import org.junit.Test;import java.util.List;public class UserDaoTest {@Testpublic void test(){//第一步:獲得SqlSession對象SqlSession sqlSession = MyBatisUtils.getSqlSession();//執行SQLUserDao mapper = sqlSession.getMapper(UserDao.class);List<User> userList = mapper.getUserList();for (User user : userList) {System.out.println(user);}//關閉SqlSessionsqlSession.close();} }可能會遇到的問題:配置文件沒有注冊綁定接口錯誤方法名不對返回類型不對Maven導出資源問題CRUD
namespace
namespace中的包名要和Dao/mapper接口的包名保持一致
select
選擇查詢語句;
id:就是對應的namespace中的方法名; resultType:Sql語句執行的返回值! parameterType:參數類型!編寫接口package com.rui.dao;import com.rui.pojo.User;import java.util.List;public interface UserMapper {//根據id查詢用戶User getUserById(int id); }編寫對應的mapper中的sql語句<select id="getUserById" resultType="com.rui.pojo.User" parameterType="int">/*定義sql*/select * from mybatis.user where id = #{id};</select>測試@Testpublic void getUserById(){SqlSession sqlSession = MyBatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);User user = mapper.getUserById(1);System.out.println(user);sqlSession.close();}Insert
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-sgtpnsff-1609421261322)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201230191728648.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-HtENm566-1609421261324)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201230191959575.png)]
Update
Delete
注意點:增刪改需要提交事務
分析錯誤
標簽不要匹配錯誤 resource綁定mapper,需要使用路徑! 程序配置文件必須符合規范 NullPointerException,沒有注冊到資源 輸出的xml文件中存在中文亂碼問題 maven資源沒有導出問題[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-4sqxOFdI-1609421261325)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201230192720390.png)]
萬能Map
假設,我們的實體類,或者數據庫中的表,字段或者參數過多,我們應當考慮使用Map!
//萬能Map
int addUser2(Map<String,Object> map);
//萬能map
@Test
public void addUser2(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Object> map = new HashMap<>();
map.put(“userId”,4);
map.put(“userName”,“王五”);
map.put(“password”,“23333”);
mapper.addUser2(map);
//提交事務
sqlSession.commit();
sqlSession.close();
}
Map傳遞參數,直接在sql中取出key即可!【parameterType=“map”】
對象傳遞參數,直接在sql中取對象的屬性即可!【parameterType=“Object”】
只有一個基本類型參數的情況下,可以直接在sql中取到!
多個參數用Map,或者注解!
思考
模糊查詢怎么寫?
Java代碼執行的時候傳遞通配符%%List<User> userList=mapper.getUserLike("%李%");在sql拼接中使用通配符!select * from mybatis.user where name like "%"#{value}"%"mybatis解決 1 字節的 UTF-8 序列的字節 1 無效
xmls上的utf-8變為utf8
配置解析xml
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-SXCE1SQr-1609421261327)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201230203824739.png)]
核心配置文件
mybatis-config.xml MyBatis的配置文件包含了會深深影響MyBatis行為的設置和屬性信息configuration(配置)
properties(屬性)
settings(設置)
typeAliases(類型別名)
typeHandlers(類型處理器)
objectFactory(對象工廠)
plugins(插件)
environments(環境配置)
environment(環境變量)
transactionManager(事務管理器)
dataSource(數據源)
databaseIdProvider(數據庫廠商標識)
mappers(映射器)
環境配置(environments)
MyBatis 可以配置成適應多種環境
不過要記住:盡管可以配置多個環境,但每個 SqlSessionFactory 實例只能選擇一種環境。
學會使用配置多套運行環境!
MyBatis默認的事務管理器就是JDBC,連接池:POOLED
屬性(properties)
我們可以通過properties屬性來實現引用配置文件
這些屬性都是可外部配置且可動態替換的,既可以在典型的 Java 屬性文件中配置,亦可通過 properties 元素的子元素來傳遞。【db.properties】
編寫一個配置文件
db.properties
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?
useSSL=true&useUnicode=true&characterEncoding=utf8
username=root
password=Cc105481
在核心配置文件中引入
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-XOIjXGPl-1609421261328)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201230211034703.png)]
<properties resource="db.properties"><property name="username" value="root"/><property name="password" value="Cc105481"/> </properties>可以直接引入外部文件 可以在其中增加一些屬性配置 如果兩個文件有同一字段,優先使用外部配置文件的!類型別名(typeAliases)
類型別名是為 Java 類型設置一個短的名字。存在的意義僅在于用來減少類完全限定名的冗余。1. <!--可以給實體類起別名--><typeAliases><typeAlias type="com.rui.pojo.User" alias="User"/></typeAliases>也可以指定一個包名,MyBatis 會在包名下面搜索需要的 Java Bean,比如:2.掃描實體類的包,他的默認別名就為這個類的類名,首字母小寫!<!--可以給實體類起別名--><typeAliases><package name="com.rui.pojo"/></typeAliases>在實體類比較少的時候,使用第一種方式。
如果實體類十分多,建議使用第二種方式。
第一種可以DIY別名,第二種則不行,如果非要改,需要在實體類(pojo)上增加@Alias注解
@Alias(“author”)
public class Author {
…
}
映射器(mappers)
MapperRegistry:注冊綁定我們的Mapper文件;
方式一: 【推薦使用】
<!--每一個Mapper.XML都需要在Mybatis核心配置文件中注冊!--> <mappers><mapper resource="com/kuang/dao/UserMapper.xml"/> </mappers>方式二:使用class文件綁定注冊
<!--每一個Mapper.XML都需要在Mybatis核心配置文件中注冊!--> <mappers><mapper class="com.kuang.dao.UserMapper"/> </mappers>注意點:
- 接口和他的Mapper配置文件必須同名!
- 接口和他的Mapper配置文件必須在同一個包下!
方式三:使用掃描包進行注入綁定
<!--每一個Mapper.XML都需要在Mybatis核心配置文件中注冊!--> <mappers><package name="com.kuang.dao"/> </mappers>注意點:
- 接口和他的Mapper配置文件必須同名!
- 接口和他的Mapper配置文件必須在同一個包下!
練習時間:
- 將數據庫配置文件外部引入
- 實體類別名
- 保證UserMapper 接口 和 UserMapper .xml 改為一致!并且放在同一個包下!
生命周期和作用域
生命周期,和作用域是至關重要的,因為錯誤的使用會導致非常嚴重的并發問題。
SqlSessionFactoryBuilder
mybatis運行流程SqlSessionFactory:
可以想象為:數據庫連接池 SqlSessionFactory 一旦被創建就應該在應用的運行期間一直存在,沒有任何理由丟棄它或重新創建另一個實例。 因此SqlSessionFactory的最佳作用域是應用作用域。SqlSession
連接到連接池的一個請求!SqlSession 的實例不是線程安全的,因此是不能被共享的,所以它的最佳的作用域是請求或方法作用域。用完之后需要趕緊關閉,否則會占用資源SqlSessionFactory這里的每一個Mapper,就代表一個具體的業務!
xml vs db.properties
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=UTF-8"/> url=jdbc:mysql://localhost:3306/mybatis? useSSL=true&useUnicode=true&characterEncoding=utf8解決屬性名和字段名不一致的問題
1、問題
數據庫中的字段
數據庫中的字段
新建一個項目,拷貝之前的,測試實體類字段不一致的情況。
public class User {
private int id;
private String name;
private String pwd;
}
測試錯誤
//select * from mybatis.user where id = #{id}
//類型處理器
//select id,name,pwd from mybatis.user where id = #{id}
解決方法:
起別名select id,name,pwd as password from mybatis.user where id = #{id}
2、resultMap
結果集映射
id name pwd
id name password
日志
日志工廠
如果一個數據庫操作,出現了異常,我們需要排錯。日志就是最好的助手!
曾經:sout、debug
現在:日志工廠
日志
SLF4J
LOG4J【掌握】
LOG4J2
JDK_LOGGING
COMMONS_LOGGING
STDOUT_LOGGING【掌握】
NO_LOGGING
在Mybatis中具體使用那個日志實現,在設置中設定!
STDOUT_LOGGING標準日志輸出(控制臺)
在mybatis核心配置文件中,配置我們的日志!
日志配置
<settings><setting name="logImpl" value="STDOUT_LOGGING"/> </settings>Log4j
什么叫log4j
Log4j是Apache的一個開源項目,通過使用Log4j,我們可以控制日志信息輸送的目的地是控制臺、文件、GUI組件 我們也可以控制每一條日志的輸出格式 通過定義每一條日志信息的級別,我們能夠更加細致地控制日志的生成過程。 通過一個配置文件來靈活地進行配置,而不需要修改應用的代碼。1.先導入log4j的包
<dependencies><!-- https://mvnrepository.com/artifact/log4j/log4j --><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency> </dependencies>2.log4j.properties
#將等級為DEBUG的日志信息輸出到console和file這兩個目的地,console和file的定義在下面的代碼 log4j.rootLogger=DEBUG,console,file#控制臺輸出的相關設置 log4j.appender.console = org.apache.log4j.ConsoleAppender log4j.appender.console.Target = System.out log4j.appender.console.Threshold=DEBUG log4j.appender.console.layout = org.apache.log4j.PatternLayout log4j.appender.console.layout.ConversionPattern=[%c]-%m%n#文件輸出的相關設置 log4j.appender.file = org.apache.log4j.RollingFileAppender log4j.appender.file.File=./log/kuang.log log4j.appender.file.MaxFileSize=10mb log4j.appender.file.Threshold=DEBUG log4j.appender.file.layout=org.apache.log4j.PatternLayout log4j.appender.file.layout.ConversionPattern=[%p][%d{yy-MM-dd}][%c]%m%n#日志輸出級別 log4j.logger.org.mybatis=DEBUG log4j.logger.java.sql=DEBUG log4j.logger.java.sql.Statement=DEBUG log4j.logger.java.sql.ResultSet=DEBUG log4j.logger.java.sql.PreparedStatement=DEBUG3.配置log4j為日志實現
<settings><setting name="logImpl" value="LOG4J"/> </settings>4.log4j的使用!直接測試運行剛才的查詢
DEBUG [main] (LogFactory.java:105) - Logging initialized using ‘class org.apache.ibatis.logging.log4j.Log4jImpl’ adapter.
DEBUG [main] (LogFactory.java:105) - Logging initialized using ‘class org.apache.ibatis.logging.log4j.Log4jImpl’ adapter.
DEBUG [main] (PooledDataSource.java:353) - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] (PooledDataSource.java:353) - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] (PooledDataSource.java:353) - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] (PooledDataSource.java:353) - PooledDataSource forcefully closed/removed all connections.
DEBUG [main] (JdbcTransaction.java:136) - Opening JDBC Connection
Loading class com.mysql.jdbc.Driver'. This is deprecated. The new driver class iscom.mysql.cj.jdbc.Driver’. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.
DEBUG [main] (PooledDataSource.java:424) - Created connection 2049051802.
DEBUG [main] (JdbcTransaction.java:100) - Setting autocommit to false on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7a220c9a]
DEBUG [main] (BaseJdbcLogger.java:143) - ==> Preparing: /定義sql/ select * from mybatis.user where id = ?;
DEBUG [main] (BaseJdbcLogger.java:143) - > Parameters: 1(Integer)
DEBUG [main] (BaseJdbcLogger.java:143) - < Total: 1
User{id=1, name=‘狂神’, password=‘123456’}
DEBUG [main] (JdbcTransaction.java:122) - Resetting autocommit to true on JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7a220c9a]
DEBUG [main] (JdbcTransaction.java:90) - Closing JDBC Connection [com.mysql.cj.jdbc.ConnectionImpl@7a220c9a]
DEBUG [main] (PooledDataSource.java:381) - Returned connection 2049051802 to pool.
Disconnected from the target VM, address: ‘127.0.0.1:58296’, transport: ‘socket’
Process finished with exit code 0
簡單使用
在要使用Log4j 的類中,導入org.apache.log4j.Logger;日志對象,加載參數為當前類的classstatic Logger logger = Logger.getLogger(UserDaoTest.class);日志級別logger.info("info:進入了testLog4j方法"); logger.debug("debug:進入了testLog4j"); logger.error("error:進入了testLog4j");分頁
思考:為什么要分頁?
減少數據的處理量使用Limit分頁
select * from user limit startIndex,pageSize
使用Mybatis實現分頁,核心SQL
接口//分頁 List<User> getUserByLimit(Map<String,Integer> map);Mapper.xml<!--分頁--> <select id="getUserByLimit" parameterType="map" resultMap="UserMap">select * from mybatis.user limit #{startIndex},#{pageSize} </select>測試@Testpublic void getUserByLimit(){SqlSession sqlSession = MyBatisUtils.getSqlSession();UserMapper mapper = sqlSession.getMapper(UserMapper.class);HashMap<String, Integer> map = new HashMap<>();map.put("startIndex",0);map.put("pageSize",2);List<User> userList = mapper.getUserByLimit(map);for (User user : userList) {System.out.println(user);}sqlSession.close(); }RowBounds分頁
不再使用SQL實現分頁
接口
List<User> getUserByRowBounds();mapper.xml<!--分頁2--><select id="getUserByRowBounds" resultMap="UserMap">select * from mybatis.user</select>測試@Testpublic void getUserByRowBounds(){SqlSession sqlSession = MyBatisUtils.getSqlSession();//RowBounds實現RowBounds rowBounds = new RowBounds(1, 2);//通過java代碼層面實現分頁List<User> userList = sqlSession.selectList("com.rui.dao.UserMapper.getUserByRowBounds",null,rowBounds);for (User user : userList) {System.out.println(user);}sqlSession.close();}分頁插件
了解即可,萬一以后公司的架構師,說要使用,只需要知道它是什么東西!
安裝插件
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-QS8r53Ae-1609421261332)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201231111258418.png)]
使用注解開發
面向接口編程的根本原因:解耦,可拓展,提高復用,分層開發中、上層不用管具體的實現,大家都遵守共同的標準,使得開發變得容易,規范性好
8.2、使用注解開發
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-3mw7qTNR-1609421261335)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201231112107709.png)]
注解在接口上實現@Select(value = "select * from user") List<User> getUsers();需要在核心配置文件中綁定接口!<!--綁定接口--> <mappers><mapper class="rui.dao.UserMapper"/> </mappers>測試public class UserMapperTest {
@Test
public void test(){
SqlSession sqlSession = MyBatisUtils.getSqlSession();
//底層主要應用反射
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List users = mapper.getUsers();
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
}
}
本質:反射機制實現
底層:動態代理!
CRUD
我們可以在工具類創建的時候實現自動提交事務!
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession(true);
}
編寫接口,增加注解
public interface UserMapper { @Select(value = “select * from user”) List getUsers();//方法存在多個參數,所有的參數前面必須加上@Param注解 @Select("select * from user where id = #{id} or name = #{name}") User getUserByID(@Param("id")int id,@Param("name")String name);@Insert("insert into user(id,name,pwd) values (#{id},#{name},#{password})") int addUser(User user);@Update("update user set name = #{name},pwd = #{password} where id = #{id}") int updateUser(User user);@Delete("delete from user where id = #{uid}") int deleteUser(@Param("uid") int id);}測試類
【注意:我們必須要將接口注冊綁定到我們的核心配置文件中!】
關于@Param()注解
基本類型的參數或者String類型,需要加上 引用類型不需要加 如果只有一個基本類型的話,可以忽略,但是建議大家都加上 我們在SQL中引用的就是我們這里的@Param()中設定的屬性名#{} ${}區別
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-j1MisDUH-1609421261336)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201231004542662.png)]
Lombok
Project Lombok is a java library that automatically plugs into your editor and build tools, spicing up your java.
Never write another getter or equals method again, with one annotation your class has a fully featured builder, Automate your logging variables, and much more.
使用步驟:
在IDEA中安裝Lombok插件在項目中導入lombok的jar包<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.10</version></dependency>在實體類上加注解即可@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、toSring、hashcode、equals
@AllArgsConstructor
@NoArgsConstructor
@ToString
@EqualsAndHashCode
多對一處理
多對一:
多個學生,對應一個老師 對于學生這邊而言,關聯...多個學生,關聯一個老師【多對一】 對于老師而言,集合,一個老師又很多學生【一對多】SQL:
CREATE TABLE `teacher`( `id` INT(10) NOT NULL, `name` VARCHAR(30) DEFAULT NULL, PRIMARY KEY (`id`) )ENGINE=INNODB DEFAULT CHARSET=utf8INSERT INTO teacher(`id`,`name`) VALUES (1,'秦老師');CREATE TABLE `student`( `id` INT(10) NOT NULL, `name` VARCHAR(30) DEFAULT NULL, `tid` INT(10) DEFAULT NULL, PRIMARY KEY (`id`), KEY `fktid`(`tid`), CONSTRAINT `fktid` FOREIGN KEY (`tid`) REFERENCES `teacher` (`id`) )ENGINE=INNODB DEFAULT CHARSET=utf8INSERT INTO student(`id`,`name`,`tid`) VALUES (1,'小明',1); INSERT INTO student(`id`,`name`,`tid`) VALUES (2,'小紅',1); INSERT INTO student(`id`,`name`,`tid`) VALUES (3,'小張',1); INSERT INTO student(`id`,`name`,`tid`) VALUES (4,'小李',1); INSERT INTO student(`id`,`name`,`tid`) VALUES (5,'小王',1);[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-cUcsgD1L-1609421261338)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201231121234379.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-zeeucWYK-1609421261344)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201231122125396.png)]
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-ZndIVVbr-1609421261345)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201231122747070.png)]
測試環境
導入lombok 新建實體類Teacher,Student 新建Mapper接口 建立Mapper.XML文件 在核心配置文件中綁定注冊我們的MApper接口或者文件!【方式很多,隨意選】 測試查詢是否成功!按照查詢嵌套處理
select * from student select * from teacher where id = #{id}
按照結果嵌套處理
select s.id sid,s.name sname,t.name tname,t.id tid from student s,teacher t where s.tid=t.id;
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-YnejrJb5-1609421261346)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201231124701611.png)]
按照查詢嵌套處理
<!--思路:1. 查詢所有的學生信息2. 根據查詢出來的學生的tid,尋找對應的老師! 子查詢--><select id="getStudent" resultMap="StudentTeacher">select * from student </select><resultMap id="StudentTeacher" type="Student"><result property="id" column="id"/><result property="name" column="name"/><!--復雜的屬性,我們需要單獨處理 對象: association 集合: collection --><association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/> </resultMap><select id="getTeacher" resultType="Teacher">select * from teacher where id = #{id} </select>按照結果嵌套處理
<!--按照結果嵌套處理--> <select id="getStudent2" resultMap="StudentTeacher2">select s.id sid,s.name sname,t.name tnamefrom student s,teacher twhere s.tid = t.id; </select><resultMap id="StudentTeacher2" type="Student"><result property="id" column="sid"/><result property="name" column="sname"/><association property="teacher" javaType="Teacher"><result property="name" column="tname"/></association> </resultMap>回顧Mysql 多對一查詢方式:
- 子查詢
- 聯表查詢
項目名字不一致
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-RyYNHSbm-1609421261351)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201231130923162.png)]
后面會多一個括號
一對多處理
比如:一個老師擁有多個學生!
對于老師而言,就是一對多的關系!
環境搭建
實體類
@Data public class Student {private int id;private String name;private int tid;} @Data public class Teacher {private int id;private String name;//一個老師擁有多個學生private List<Student> students; }按照結果嵌套處理
Teacher getTeacher(@Param("tid")int id); <!--按結果嵌套查詢--><select id="getTeacher" resultMap="TeacherStudent">select s.id sid, s.name sname, t.name tname,t.id tidfrom student s,teacher twhere s.tid = t.id and t.id = #{tid}</select><resultMap id="TeacherStudent" type="Teacher"><result property="id" column="tid"/><result property="name" column="tname"/><!--復雜的屬性,我們需要單獨處理 對象: association 集合: collectionjavaType="" 指定屬性的類型!集合中的泛型信息,我們使用ofType獲取--><collection property="students" ofType="Student"><result property="id" column="sid"/><result property="name" column="sname"/><result property="tid" column="tid"/></collection></resultMap>按照查詢嵌套處理
<select id="getTeacher2" resultMap="TeacherStudent2">select * from mybatis.teacher where id = #{tid} </select><resultMap id="TeacherStudent2" type="Teacher"><collection property="students" javaType="ArrayList" ofType="Student" select="getStudentByTeacherId" column="id"/> </resultMap><select id="getStudentByTeacherId" resultType="Student">select * from mybatis.student where tid = #{tid} </select>小結
注意點:
- 保證SQL的可讀性,盡量保證通俗易懂
- 注意一對多和多對一中,屬性名和字段的問題!
- 如果問題不好排查錯誤,可以使用日志 , 建議使用 Log4j
慢SQL 1s 1000s
面試高頻
- Mysql引擎
- InnoDB底層原理
- 索引
- 索引優化!
動態SQL
什么是動態SQL:動態SQL就是指根據不同的條件生成不同的SQL語句
利用動態SQL這一特性可以徹底擺脫這種痛苦
動態 SQL 元素和 JSTL 或基于類似 XML 的文本處理器相似。在 MyBatis 之前的版本中,有很多元素需要花時間了解。MyBatis 3 大大精簡了元素種類,現在只需學習原來一半的元素便可。MyBatis 采用功能強大的基于 OGNL 的表達式來淘汰其它大部分元素。
if choose (when, otherwise) trim (where, set) foreach搭建環境
CREATE TABLE `bolg`( `id` VARCHAR(50) NOT NULL COMMENT '博客id', `title` VARCHAR(100) NOT NULL COMMENT '博客標題', `author` VARCHAR(30) NOT NULL COMMENT '博客作者', `creat_time` DATETIME NOT NULL COMMENT '創建時間', `views` INT(30) NOT NULL COMMENT '瀏覽量' )ENGINE=INNODB DEFAULT CHARSET=utf8創建一個基礎工程
導包編寫配置文件編寫實體類@Data public class Blog {private int id;private String title;private String author;private Date creatTime;//用utilprivate int views; }編寫實體類對應的Mapper接口和Mapper.xml[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-llp41rd9-1609421261356)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201231164121408.png)]
<setting name="mapUnderscoreToCamelCase" value="true"/>IF
<select id="queryBlogIF" parameterType="map" resultType="blog">select * from mybatis.blog where 1=1<if test="title != null">and title = #{title}</if><if test="author != null">and author = #{author}</if> </select>choose (when, otherwise)
能自動去掉and
<select id="queryBlogChoose" parameterType="map" resultType="blog">select * from mybatis.blog<where><choose><when test="title != null">title = #{title}</when><when test="author != null">and author = #{author}</when><otherwise>and views = #{views}</otherwise></choose></where></select>trim (where,set)
select * from mybatis.blog <where><if test="title != null">title = #{title}</if><if test="author != null">and author = #{author}</if> </where> <update id="updateBlog" parameterType="map">update mybatis.blog<set><if test="title != null">title = #{title},</if><if test="author != null">author = #{author}</if></set>where id = #{id} </update>所謂的動態SQL,本質還是SQL語句 , 只是我們可以在SQL層面,去執行一個邏輯代碼
if
where , set , choose ,when
SQL片段
有的時候,我們可能會將一些功能的部分抽取出來,方便復用!
使用SQL標簽抽取公共的部分
<sql id="if-title-author"><if test="title != null">title = #{title}</if><if test="author != null">and author = #{author}</if> </sql>在需要使用的地方使用Include標簽引用即可
<select id="queryBlogIF" parameterType="map" resultType="blog">select * from mybatis.blog<where><include refid="if-title-author"></include></where> </select>注意事項:
- 最好基于單表來定義SQL片段!
- 不要存在where標簽
Foreach
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-XfGDnG3j-1609421261358)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201231192208358.png)]
select * from user where 1=1 and <foreach item="id" collection="ids"open="(" separator="or" close=")">#{id}</foreach>(id=1 or id=2 or id=3) <!--select * from mybatis.blog where 1=1 and (id=1 or id = 2 or id=3)我們現在傳遞一個萬能的map , 這map中可以存在一個集合! --> <select id="queryBlogForeach" parameterType="map" resultType="blog">select * from mybatis.blog<where><foreach collection="ids" item="id" open="and (" close=")" separator="or">id = #{id}</foreach></where></select>動態SQL就是在拼接SQL語句,我們只要保證SQL的正確性,按照SQL的格式,去排列組合就可以了
建議:
- 現在Mysql中寫出完整的SQL,再對應的去修改成為我們的動態SQL實現通用即可!
緩存簡介
查詢 : 連接數據庫,耗資源!
一次查詢的結果,給他暫存在一個可以直接取到的地方!—>內存 : 緩存
我們再次查詢相同數據的時候,直接走緩存,就不用走數據庫了
什么事緩存[Cache]?存在內存中的臨時數據。將用戶經常查詢的數據放在緩存(內存)中,用戶去查詢數據就不用從磁盤上(關系型數據庫數據文件)查詢,從緩存中查詢,從而提高查詢效率,解決了高并發系統的性能問題。為什么使用緩存?減少和數據庫的交互次數,減少系統開銷,提高系統效率。什么樣的數據能使用緩存?經常查詢并且不經常改變的數據。Mybatis緩存
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-62Pk4JE2-1609421261368)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201231193827608.png)]
MyBatis包含一個非常強大的查詢緩存特性,它可以非常方便地定制和配置緩存。緩存可以極大的提升查詢效率。 MyBatis系統中默認定義了兩級緩存:一級緩存和二級緩存默認情況下,只有一級緩存開啟。(SqlSession級別的緩存,也稱為本地緩存)二級緩存需要手動開啟和配置,他是基于namespace級別的緩存。為了提擴展性,MyBatis定義了緩存接口Cache。我們可以通過實現Cache接口來自定義二級緩存 一級緩存也叫本地緩存:SqlSession與數據庫同義詞會話期間查詢到的數據會放在本地緩存中。以后如果需要獲取相同的數據,直接從緩存中拿,沒有必要再去查詢數據;測試步驟:
開啟日志! 測試在一個Session中查詢兩次相同的記錄 查看日志輸出緩存失效的情況:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-h2Qk7npu-1609421261370)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201231195447615.png)]
查詢不同的東西 增刪改操作,可能會改變原來的數據,所以必定會刷新緩存! 查詢不同的Mapper.xml 手動清理緩存! sqlsession.clearCache(); //手動清理緩存小節:一級緩存默認是開啟的,只在一次SqlSession中有效,也就是拿到連接到關閉連接這個區間段!
一級緩存就是一個Map。
二級緩存
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-O3scunpT-1609421261372)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201231195901433.png)]
二級緩存也叫全局緩存,一級緩存作用域太低了,所以誕生了二級緩存 基于namespace級別的緩存,一個名稱空間,對應一個二級緩存; 工作機制一個會話查詢一條數據,這個數據就會被放在當前會話的一級緩存中;如果當前會話關閉了,這個會話對應的一級緩存就沒了;但是我們想要的是,會話關閉了,一級緩存中的數據會被保存到二級緩存中;新的會話查詢信息,就可以從二級緩存中獲取內容;不同的mapper查出的數據會放在自己對應的緩存(map)中;步驟:
開啟全局緩存<!--顯式的開啟全局緩存--> <setting name="cacheEnabled" value="true"/>在要使用二級緩存的Mapper中開啟<!--在當前Mapper.xml中使用二級緩存--> <cache/> 也可以自定義參數 <cache eviction="FIFO"flushInterval="60000"size="512"readOnly="true"/>測試問題:我們需要將實體類序列化!否則就會報錯java.io.NotSerializableException: com.rui.pojo.User序列化
測試
問題:我們需要將實體類序列化!否則就會報錯java.io.NotSerializableException: com.rui.pojo.User[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-2V1GnZBg-1609421261374)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201231200920270.png)]
小結:
只要開啟了二級緩存,在同一個Mapper下就有效 所有的數據都會先放在一級緩存中; 只有當會話提交,或者關閉的時候,才會提交到二級緩存中!自定義緩存-encache
EhCache 是一個純Java的進程內緩存框架,具有快速、精干等特點,是Hibernate中默認的CacheProvider。
要在程序中使用ehcache,先要導包!
https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache
org.mybatis.caches
mybatis-ehcache
1.1.0
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-XyuT37Qe-1609421261376)(C:\Users\王東梁\AppData\Roaming\Typora\typora-user-images\image-20201231203614123.png)]
然后在mapper中指定使用ehcache緩存實現
<在當前Mapper.xml中使用二級緩存>
導入配置文件 ehcache.xml
<?xml version="1.0" encoding="UTF-8"?> diskStore:為緩存路徑,ehcache分為內存和磁盤兩級,此屬性定義磁盤的緩存位置。參數解釋如下:user.home – 用戶主目錄user.dir – 用戶當前工作目錄java.io.tmpdir – 默認臨時文件路徑<diskStore path="java.io.tmpdir/Tmp_EhCache"/>defaultCache:默認緩存策略,當ehcache找不到定義的緩存時,則使用這個緩存策略。只能定義一個。name:緩存名稱。maxElementsInMemory:緩存最大數目maxElementsOnDisk:硬盤最大緩存個數。eternal:對象是否永久有效,一但設置了,timeout將不起作用。overflowToDisk:是否保存到磁盤,當系統當機時timeToIdleSeconds:設置對象在失效前的允許閑置時間(單位:秒)。僅當eternal=false對象不是永久有效時使用,可選屬性,默認值是0,也就是可閑置時間無窮大。timeToLiveSeconds:設置對象在失效前允許存活時間(單位:秒)。最大時間介于創建時間和失效時間之間。僅當eternal=false對象不是永久有效時使用,默認是0.,也就是對象存活時間無窮大。diskPersistent:是否緩存虛擬機重啟期數據 Whether the disk store persists between restarts of the Virtual Machine. The default value is false.diskSpoolBufferSizeMB:這個參數設置DiskStore(磁盤緩存)的緩存區大小。默認是30MB。每個Cache都應該有自己的一個緩沖區。diskExpiryThreadIntervalSeconds:磁盤失效線程運行時間間隔,默認是120秒。memoryStoreEvictionPolicy:當達到maxElementsInMemory限制時,Ehcache將會根據指定的策略去清理內存。默認策略是LRU(最近最少使用)。你可以設置為FIFO(先進先出)或是LFU(較少使用)。clearOnFlush:內存數量最大時是否清除。memoryStoreEvictionPolicy:可選策略有:LRU(最近最少使用,默認策略)、FIFO(先進先出)、LFU(最少訪問次數)。FIFO,first in first out,這個是大家最熟的,先進先出。LFU, Less Frequently Used,就是上面例子中使用的策略,直白一點就是講一直以來最少被使用的。如上面所講,緩存的元素有一個hit屬性,hit值最小的將會被清出緩存。LRU,Least Recently Used,最近最少使用的,緩存的元素有一個時間戳,當緩存容量滿了,而又需要騰出地方來緩存新的元素的時候,那么現有緩存元素中時間戳離當前時間最遠的元素將被清出緩存。 <defaultCacheeternal="false"maxElementsInMemory="10000"overflowToDisk="false"diskPersistent="false"timeToIdleSeconds="1800"timeToLiveSeconds="259200"memoryStoreEvictionPolicy="LRU"/ <cachename="cloud_user"eternal="false"maxElementsInMemory="5000"overflowToDisk="false"diskPersistent="false"timeToIdleSeconds="1800"timeToLiveSeconds="1800"memoryStoreEvictionPolicy="LRU"/>
總結
以上是生活随笔為你收集整理的Mybatis(笔记)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 5500元电脑最强组装配置单?
- 下一篇: lombok进行有参无参构造出现的问题