mybatis架構 持久層
Spring
Spring MVC 表示層
shiro 安全架構
git 版本控制工具
項目階段
Junit
使用Junit做單元測試的目的:
- 友善運作代碼,人為檢視結果符不符合預期
- 使用斷言測試讓機器去判斷結果符不符合預期,a開頭的很多junit斷言測試
User one = UserDao.getOne(1); asserEquals(null,one);
@Test注釋的方法成為測試用例
測試注解使用的要求:
- 要注解在某個方法上
- 該方法要求無參,傳回值為空,且為public
在test裡面添加參數
@Test(timeout=1000)
限時測試:timeout屬性指定毫秒值,要求該單元測試用例在規定時間内完成
@Before
作用于某個方法上
作用:所有的測試單元執行之前執行該方法,每執行一個測試單元就會執行一次Before
要求:
- 要注解在某個方法上
- 該方法要求無參,傳回值為空,且為public
@After
作用于某個方法上
作用:在所有單元測試用例之後執行該方法(多用于資源回收)
要求:
- 要注解在某個方法上
- 該方法要求無參,傳回值為空,且為public
@BeforeClass
在整個測試類的所有測試用例運作之前運作一次
- 要注解在某個方法上
- 該方法要求無參,傳回值為空,還要被public static修飾!!
@AfterClass
在整個測試類的所有測試用例運作完之後運作一次
- 要注解在某個方法上
- 該方法要求無參,傳回值為空,還要被public static修飾!!
@Ignore
忽略這個測試用例,但是會在總計結果中顯示統計資訊
@Runwith()
套件測試,同時測試多個測試類。所謂Runner提供單元你測試運作的環境,預設為Junit4.class
@RunWith(Suite.class)
@Suite.SuiteClasses(UserDaoImplTest.class,AppTest.class)
@Suite.SuiteClasses指定該套件中包含了哪些測試類
1.簡介
- MyBatis 是一款優秀的持久層架構
- 它支援自定義 SQL、存儲過程以及進階映射。
- MyBatis 免除了幾乎所有的 JDBC 代碼以及設定參數和擷取結果集的工作。
- MyBatis 可以通過簡單的 XML 或注解來配置和映射原始類型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 對象)為資料庫中的記錄。
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsICM38FdsYkRGZkRG9lcvx2bjxiNx8VZ6l2cs0TRENGaSh0YoJ1MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLwQDOyMTNwcTM3EDOwEjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
1.1.擷取mybaties
- maven倉庫:https://mvnrepository.com/artifact/org.mybatis/mybatis
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
- github
- 官方中文文檔:https://mybatis.org/mybatis-3/zh/index.html
1.2.持久層
Dao層(資料庫操作層),Service層(業務層),Control層(專門接收使用者請求層)。
- 持久層:完成持久化工作的代碼塊
- 層是界限十分明顯的
持久化是一個動作,持久層是一個概念。
1.3.為什麼需要Mybatis
- 友善
- 傳統的JDBC太複雜,簡化,架構,自動化
- 幫助程式員将資料存入到資料庫中
- 不用mybatis也可以,更容易上手
- 用的人特别多
spring,spring mvc,springboot
2.第一個mybatis程式
步驟:搭建環境-》導入mybatis-》編寫代碼-》測試
搭建資料庫環境
2.1導入xml依賴
<dependencies>
<!-- mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!-- mysql驅動-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>
<!-- 測試工具junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13</version>
<scope>test</scope>
</dependency>
</dependencies>
2.2.建立一個maven子項目
- 配置mybatis的核心配置檔案
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <!--configuration核心配置檔案--> <configuration> <!-- environments配置多個環境--> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver"/> <!-- 在xml中&符号需要進行轉義&--> <property name="url" value="jdbc:mysql:///mybatis"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> </configuration>
2.3編寫mybatis工具類
//第一步從 XML 中建構 SqlSessionFactory
public class MybatisUtil {
private static SqlSessionFactory sqlSessionFactory;
static {
try {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
}
//第二步既然有了 SqlSessionFactory,顧名思義,我們可以從中獲得 SqlSession 的執行個體。
public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession();
}
}
2.4編寫代碼
- 實體類
public class User {
private int id;
private String name;
private String password;
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 getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public User() {
}
public User(int id, String name, String password) {
this.id = id;
this.name = name;
this.password = password;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", password='" + password + '\'' +
'}';
}
}
- Dao接口
public interface UserDao { List<User> getUserList(); }
- 接口實作類
<?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">
<!--namespace命名空間綁定一個Maper接口-->
<mapper namespace="com.liu.Dao.UserDao">
<!-- id對應UserDao的方法,resultType對應實體類-->
<select id="getUserList" resultType="com.liu.Pojo.User">
select * from mybatis.user
</select>
</mapper>
2.5測試
注意:
org.apache.ibatis.binding.BindingException: Type interface com.liu.Dao.UserMaper is not known to the MapperRegistry.
MapperRegistry是什麼?
核心配置檔案中注冊mapers
- junit測試
public class UserDaoTest {
@Test
public void test() {
//第一步擷取sqlSession對象
SqlSession sqlSession = MybatisUtil.getSqlSession();
//執行SQL
//方式一getMapper
UserMaper mapper = sqlSession.getMapper(UserMaper.class);
List<User> userList = mapper.getUserList();
for (User user : userList) {
System.out.println(user);
}
//關閉sqlSession
sqlSession.close();
}
}
可能遇到的問題:
1.配置檔案沒有注冊
2.綁定接口錯誤
3.方法名不對
4.傳回類型不對
5.Maven導出資源問題
3.CRUD
1.namespace
namespace中的包名要和Mapper接口的報名一緻
2.select
選擇,查詢語句;
- id:就是對應namespace中的方法名
- resulType:sql語句執行的傳回值!
- paramterType:參數類型!
1.編寫接口
2.編寫對應的mapper中的sql
3.測試
3.Insert
UserMapper:
//插入一條記錄
public void insertOne(User user);
UserMapper.xml
<insert id="insertOne" parameterType="com.liu.Pojo.User">
insert into user values (#{id},#{name},#{password})
</insert>
Test:
//插入使用者
@Test
public void insertOne(){
SqlSession sqlSession = MybatisUtil.getsqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
mapper.insertOne(new User(4,"liulang","123456"));
//送出事務
sqlSession.commit();
sqlSession.close();
}
4.Update
UserMapper:
//修改記錄
public int alterUser(User user);
UserMapper.xml
<update id="alterUser" parameterType="com.liu.Pojo.User">
update user set name=#{name},password=#{password} where id=#{id};
</update>
Test:
//修改資訊
@Test
public void alterUser(){
SqlSession sqlSession = MybatisUtil.getsqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int i = mapper.alterUser(new User(4, "liulang", "123"));
if (i>0)
System.out.println("修改成功");
else
System.out.println("修改失敗");
//送出事務
sqlSession.commit();
sqlSession.close();
}
5.Delete
UserMapper:
//删除使用者
public int deleteUser(int id);
UserMapper.xml
<delete id="deleteUser" parameterType="int">
delete from user where id=#{id}
</delete>
Test:
//删除使用者
@Test
public void deleteUser(){
SqlSession sqlSession = MybatisUtil.getsqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
int i = mapper.deleteUser(4);
if (i>0)
System.out.println("删除成功");
else
System.out.println("删除失敗");
//送出事務
sqlSession.commit();
sqlSession.close();
}
注意:增删改都需要送出事務才會生效!!! sqlSession.commit();
6.錯誤
- namespace="com.liu.Mapper.UserMapper";必須寫詳細路徑
- sql語句标簽錯誤
- 注冊mapper;resource的路徑要用/而不是點
<mappers> <mapper resource="com/liu/Mapper/UserMapper.xml"/> </mappers>
4.maven資源導出問題
<build>
<resources>
<!-- 設定正常情況的resources目錄下的properties檔案-->
<resource>
<!-- 配置路徑-->
<directory>src/main/resources</directory>
<includes>
<!-- 包含什麼檔案-->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
<!-- 設定java路徑的properties檔案-->
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>
7.非常實用的Map
如果我們的實體類或資料中的表,字段過多,我們考慮使用Map。
修改或添加部分資訊:
UserMapper:
//使用map修改部分資料
public int alterUser2(Map<String,Object> map);
UserMapper.xml
<update id="alterUser2" parameterType="map">
update user set name=#{username} where id=#{userid};
</update>
Test:
//使用map更新部分資訊
@Test
public void alterUser2(){
SqlSession sqlSession = MybatisUtil.getsqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put("username","李小盛");
map.put("userid",3);
int i = mapper.alterUser2(map);
if (i>0)
System.out.println("修改成功");
else
System.out.println("修改失敗");
//送出事務
sqlSession.commit();
sqlSession.close();
}
根據多個條件查詢資訊:
UserMapper:
//指定多條件查詢
public User getUserById2(Map<String,Object> map);
UserMapper.xml
<select id="getUserById2" parameterType="map" resultType="com.liu.Pojo.User">
select * from user where id=#{id} and name=#{name}
</select>
Test:
@Test
public void getUserById2(){
SqlSession sqlSession = MybatisUtil.getsqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String, Object> map = new HashMap<String, Object>();
map.put("id",3);
map.put("name","李小盛");
User user = mapper.getUserById2(map);
System.out.println(user);
sqlSession.close();
}
Map傳遞參數,直接在sql中取出key即可。
使用Map的時候一定要注意map裡面的鍵值對必須和UserMapper.xml中使用的變量對應!!!
對象傳遞參數,直接在sql中去對象的屬性。
隻有一個基本資料類型參數的情況下,可以直接在sql中使用
多個參數用map或注解!
8.模糊查詢
1.java執行的時候傳遞通配符%
List<User> users = mapper.likeUser("%李%");
2.在sql拼接中使用通配符
<select id="likeUser" resultType="com.liu.Pojo.User">
select * from user where name like "%"#{name}"%"
</select>
第二種寫法:
<select id="queryAll" resultMap="USER" parameterType="map">
select * from user
<where>
<if test="name!=null">
name like '%${name}%'
</if>
</where>
</select>
4.XML配置
1.核心配置檔案
mybaits-config,xml
- properties(屬性)
- settings(設定)
- typeAliases(類型别名)
- typeHandlers(類型處理器)
- objectFactory(對象工廠)
- plugins(插件)
- environments(環境配置)
- environment(環境變量)
- transactionManager(事務管理器)
- dataSource(資料源)
- environment(環境變量)
- databaseIdProvider(資料庫廠商辨別)
- mappers(映射器)
2.環境配置
Mybatis可以配置成适應多環境
注:盡管可以配置多個環境,但是每個sqlSessionFactory執行個體隻能選擇一種環境
學會使用配置多套運作環境!
Mybatis預設的事務管理器就是JDBC,連接配接池:POOLED
3.屬性(properties)
通過propertis來引入配置檔案
1.編寫一個配置檔案:
driver=com.mysql.jdbc.Driver
url=jdbc:mysql:///mybatis
username=root
password=123456
2.在核心配置檔案中引入:在核心配置檔案中所有的标簽都有規定的先後順序
資源在核心配置同一個路徑下可以使用相對路徑定位
沒有添加額外參數
<properties resource="db.properties"/>
添加額外參數
<properties resource="db.properties">
<property name="username" value="root1"/>
<property name="password" value="123456"/>
</properties>
- 可以直接引入配置檔案
- 可以在其中增加一些配置屬性
- 如果兩種方式有相同的屬性,優先使用外部配置檔案的屬性!!!
4.類型别名(typeAliases)
類型别名可為 Java 類型設定一個縮寫名字。 它僅用于 XML 配置,意在降低備援的全限定類名書寫
第一種方式:
<typeAliases>
<typeAlias type="com.liu.Pojo.User" alias="User"/>
</typeAliases>
第二種方式
可以指定一個包名,Mybatis會在這個包名下面搜尋需要的JavaBean,比如:
掃描的實體類的名稱,它的預設别名就是這個類的首字母小寫。
<typeAliases>
<package name="com.liu.Pojo"/>
</typeAliases>x
在實體類比較少的情況使用第一種,
如果實體類十分多的時候,建議第二種
第一種可以DIY别名,第二種不行。如果非要在實體類上增加注解,注解方式大于預設搜尋
@Alias("hello")
public class User(){};
5.映射器(mappers)
MapperRegistry:注冊綁定我們需要的Mapper檔案
方式一:【推薦使用】
<mappers>
<mapper resource="com/liu/Mapper/UserMapper.xml"/>
</mappers>
方式二:使用class方式綁定注冊
<mappers>
<mapper class="com.liu.Mapper.UserMapper"/>
</mappers>
方式三:使用包掃描注冊綁定
<mappers>
<package name="com.liu.Mapper"/>
</mappers>
方式二和方式三注意!!:
- 接口和它的Mapper配置檔案必須同名
- 接口和它的Mapper配置檔案必須在同一個包下面
6.生命周期
生命周期類和作用域是至關重要的,因為錯誤的使用會導緻非常嚴重的并發問題。
SqlSessionFactoryBuilder:
- 一旦建立了SqlSessionFactory就不在需要他了
- 局部變量
SqlSessionFactory:
- 可以想象為資料庫連接配接池
- SqlSessionFactory 一旦被建立就應該在應用的運作期間一直存在,沒有任何理由丢棄它或重新建立另一個執行個體。
- 是以 SqlSessionFactory 的最佳作用域是應用作用域。(程式開始他就開始,程式結束他就結束)
- 簡單的就是使用單例模式或者靜态單例模式。
SqlSession:
- 連接配接到連接配接池的一個請求!
- SqlSession 的執行個體不是線程安全的,是以是不能被共享的,是以它的最佳的作用域是請求或方法作用域
- 用完之後趕緊關閉,否則資源被占用
每一個Mapper代表一個具體的業務。
5.解決屬性名和字段名不一緻的問題
解決方法:
- 起别名
<select id="getUserById" parameterType="int" resultType="com.liu.Pojo.User">
select id,name,password as pwd from user where id = #{id}
</select>
2.resultMap
結果集映射
id name password
id name pwd
resultMap
元素是 MyBatis 中最重要最強大的元素。它可以讓你從 90% 的 JDBC
ResultSets
資料提取代碼中解放出來,并在一些情形下允許你進行一些 JDBC 不支援的操作。
ResultMap 的設計思想是,對簡單的語句做到零配置,對于複雜一點的語句,隻需要描述語句之間的關系就行了。
<!-- resultMap就是使用這裡的id,類型映射為實體類 -->
<resultMap id="UserMap" type="User">
<!-- property表示實體類屬性,column表示資料庫字段映射。将字段映射為實體類的對應屬性 -->
<result property="id" column="id"/>
<result property="name" column="name"/>
<result property="pwd" column="password"/>
</resultMap>
<select id="getUserList" resultMap="UserMap">
select * from user
</select>
6.日志
6.1日志工廠
如果一個資料庫操作出現了異常,我們需要排錯。日志就是我們最好的助手。
曾經:sout、debug
現在:日志工廠!
- SLF4J
- LOG4J【掌握】
- LOG4J2
- JDK_LOGGING
- COMMONS_LOGGING
- STDOUT_LOGGING【掌握】
- NO_LOGGING
在Mybatis中具體使用哪一個日志實作,在設定中設定!
STDOUT_LOGGING:标準日志輸出
配置設定的時候一定要注意格式、大小寫、空格等問題
<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
6.2Log4j
Apache的開源項目log4j是一個功能強大的日志元件,提供友善的日志記錄。
- 日志可以輸出到控制到或GUI元件
- 我們可以控制每一條日志輸出的格式
- 通過設定每一條日志的級别,我們能夠更細緻的控制日志生成的過程。
- 通過配置檔案來進行配置,不需要我們修改應用的代碼
1.導入LOG4J的包
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
2.log4j.properties
### 配置根 ###
#配置日志類型
log4j.rootLogger = debug,console ,file
### 設定輸出sql的級别,其中logger後面的内容全部為jar包中所包含的包名 ###
log4j.logger.org.apache=debug
log4j.logger.java.sql.Connection=debug
log4j.logger.java.sql.Statement=debug
log4j.logger.java.sql.PreparedStatement=debug
log4j.logger.java.sql.ResultSet=debug
### 配置輸出到控制台 ###
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 = %d{ABSOLUTE} %5p %c{ 1 }:%L - %m%n
### 配置輸出到檔案 ###
log4j.appender.file = org.apache.log4j.RollingFileAppender
log4j.appender.file.File = ./logs/mybatis.log
log4j.appender.file.Append = true
log4j.appender.file.Threshold = DEBUG
log4j.appender.file.layout = org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
### 配置輸出到檔案,并且每天都建立一個檔案 ###
log4j.appender.dailyRollingFile = org.apache.log4j.DailyRollingFileAppender
log4j.appender.dailyRollingFile.File = logs/log.log
log4j.appender.dailyRollingFile.Append = true
log4j.appender.dailyRollingFile.Threshold = DEBUG
log4j.appender.dailyRollingFile.layout = org.apache.log4j.PatternLayout
log4j.appender.dailyRollingFile.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %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.PraparedStatement=DEBUG
3.配置log4j為日志的實作
<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
4.Log4j的使用,直接測試運作查詢就行了
簡單使用
1.在需要使用log4j的類中,導入包,包不要導錯了,需要導入apeach的log4j
2.生成日志對象,加載參數為目前類的class
static Logger logger = Logger.getLogger(mybatistest.class);
3.日志級别
logger.info("info:進入testlog4j");
logger.debug("debug:進入testlog4j");
logger.error("error:進入testlog4j");
7.分頁
為什麼要分頁?
- 減少資料的處理量
#{參數名}:按照preparedstatement解析sql語句時所使用的的?占位符
${參數名}:傳什麼參數,就按字元串拼接方式進行填充
7.1使用limit分頁,文法:
select * from user limit statIndex,pagesize;
select * from user limit 2,5
select * from user limit 3
使用mybatis實作分頁,核心SQl
1.接口
//分頁查詢
public List<User> limitUser(Map<String,Integer> map);
2.Mapper.xml
<!-- 實作分頁查詢-->
<select id="limitUser" parameterType="map" resultType="user">
select * from user limit #{startIndex},#{pageSize}
</select>
3.測試
//分頁查詢
@Test
public void limitUser(){
SqlSession sqlSession = MybatisUtil.getsqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("startIndex",0);
map.put("pageSize",3);
List<User> users = mapper.limitUser(map);
for (User user : users) {
System.out.println(user);
}
}
7.2useGeneratedKeys
如果想要拿到插入資料後自動遞增的主鍵值時,使用useGeneratedKeys=true
KeyColumn:指定自動遞增的列明
KeyProperty:指定傳入的參數對象中用于存放自動遞增的值對應的屬性
8.使用注解開發
8.1面向接口程式設計
- 接口定義與實作的分離
- 接口的本身反應了系統設計人員對系統的抽象了解
- 接口也應有兩個類:
- 一個是對一個個體的抽象abstract
- 一個是對個體某個方面的抽象interface
8.2使用注解開發
1.注解直接在接口上實作
@Select("select * from user")
List<User> getUsers();
2.需要在核心配置檔案中綁定接口!
<mappers>
<mapper class="com.liu.Mapper.UserMapper2"/>
</mappers>
3.測試
//注解方式
@Test
public void zjUsers(){
SqlSession sqlSession = MybatisUtil.getsqlSession();
UserMapper2 mapper = sqlSession.getMapper(UserMapper2.class);
List<User> users = mapper.getUsers();
for (User user : users) {
System.out.println(user);
}
sqlSession.close();
}
本質:反射機制實作
底層:動态代理
8.3CRUD
我們可以在工具類建立的時候實作自動送出事務!
return sqlSessionFactory.openSession(true);
編寫接口,增加注解
//方法存在多個參數時,所有的參數前面必須加上@Param("id")注解
@Select("select * from user where id=#{id} and name=#{name}")
public User getUserById(@Param("id") int id,@Param("name") String username);
@Insert("insert into user values (#{id},#{name},#{password})")
public int addUser(User user);
@Update("update user set name=#{name},password=#{password} where id=#{id}")
int UpdateUser(User user);
@Delete("delete from user where id=#{id}")
public int delUser(@Param("id") int pid);
【我們必須将接口綁定到注冊到我們的核心配置檔案中】
<mappers>
<package name="com.liu.Mapper"/>
</mappers>
關于@Param(")注解
- 基本類型的參數或則String類型的參數需要加上
- 引用資料類型不需要加
- 如果隻有一個資料類型的話可以忽略,建議也加上
- 我們在SQL中引用的就是我們這裡的@Param設定的屬性名
#{}和${}差別:
- #{}和${}這兩個文法是為了動态傳遞參數而存在的,是Mybatis實作動态SQL的基礎
- #{} 是 占位符 :動态解析 -> 預編譯 -> 執行
- ${} 是 拼接符 **:動态解析 -> 編譯 -> 執行
類似statement和preparestatement
9.Lombok
- java library
- plugs
- build tools
使用步驟:
1.在IDEA安裝plug,Lombok插件
2.在maven中導入Lombok依賴
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
@Data:,toString,GetandSet,hashcode,equals
@AllArgsConstructor
@NoArgsConstructor
全參構造和無參構造
3.在實體類上加注解即可
10.多對一
- 對學生來說,關聯,多個學生關聯一個老師【多對一】
- 對老師而言,集合,一個老師輔導多個學生【一對多】
環境搭建:
1.導入Lombok
2.建立實體類Teacher,Student
3.建立Mapper接口
4.建立Mapper.xml檔案
5.在核心配合檔案中綁定Mapper接口或檔案
6.測試
需求查詢所有的學生資訊以及對應的老師資訊。
1.按照查詢嵌套處理
<!-- 使用結果集映射需要使用resultMap-->
<select id="getStudent" resultMap="StudentTeacher">
select * from student
</select>
<resultMap id="StudentTeacher" type="student">
<!-- 複雜的屬性,我們需要單獨處理-->
<!-- 對象使用association-->
<result property="id" column="id"/>
<result property="name" column="name"/>
<association property="teacher" column="id" javaType="Teacher" select="getTeacher"/>
<!-- 集合使用collection-->
<!-- <collection property=""-->
</resultMap>
<select id="getTeacher" resultType="teacher">
select * from teacher where id=#{id}
</select>
第二條select語句中的where裡面的條件#{},花括号裡面随便寫都不會影響查詢結果
2.按照結果嵌套處理
<!-- 按照結果嵌套處理-->
<select id="getStudent2" resultMap="StudentTeacher2">
select s.id sid,s.name sname,t.name tname
from student as s,teacher as t where s.id=t.id
</select>
<resultMap id="StudentTeacher2" type="student">
<result column="sid" property="id"/>
<result column="sname" property="name"/>
<association property="teacher" javaType="Teacher">
<result property="tid" column="id"/>
<result property="name" column="tname"/>
</association>
</resultMap>
查詢結果:
Student(id=1, name=liulang, cno=1, teacher=Teacher(id=1, name=chentao, cno=1)) Student(id=2, name=lixiaosheng, cno=1, teacher=Teacher(id=1, name=chentao, cno=1)) Student(id=3, name=zhenghaiqing, cno=1, teacher=Teacher(id=1, name=chentao, cno=1))
11.一對多
一個老師擁有多個學生
1.結果集映射
<select id="getTeacher2" resultMap="TeacherStudent">
select s.id sid,s.name sname,t.name tname,t.id tid,t.cno tcno,s.cno scno
from student s,teacher t where s.cno=t.cno
</select>
<resultMap id="TeacherStudent" type="teacher">
<result property="id" column="tid"/>
<result property="cno" column="tcno"/>
<result property="name" column="tname"/>
<collection property="students" ofType="student">
<result property="id" column="sid"/>
<result property="cno" column="scno"/>
<result property="name" column="sname"/>
</collection>
</resultMap>
2.子查詢方式
<!-- 子查詢-->
<select id="getTeacher" resultMap="TeacherStudent1">
select * from teacher
</select>
<resultMap id="TeacherStudent1" type="Teacher">
<result property="id" column="id"/>
<result property="name" column="name"/>
<result property="cno" column="cno"/>
<!-- column使用者傳遞給子查詢作為where條件 -->
<collection property="students" column="cno" javaType="ArrayList" ofType="Student" select="getStudent"/>
</resultMap>
<select id="getStudent" resultType="Student">
select * from student where cno=#{cno}
</select>
查詢結果:
Teacher(id=1, name=chentao, cno=1, students=[Student(id=1, name=liulang, cno=1), Student(id=2, name=lixiaosheng, cno=1), Student(id=3, name=zhenghaiqing, cno=1)])
總結:
1.關聯-association【多對一】
2.集合-collection【一對多】
3.javaType & ofType
1.javaType用來指定實體類中屬性的類型
2.ofTyp用來指定映射到list或則集合中的pojo類型,泛型中的集合類型
注意:
- 保證SQL的可讀性,盡量保證通俗易懂
- 注意一對多和多對一中,屬性名和字段的問題
- 如果問題不好排查,可以使用日志,建立使用log4j
12.動态SQL
什麼是動态SQL:根據不同的條件生成不同的SQL語句
所謂動态SQL:本質還是SQL語句,隻是我們可以再SQL層面,去執行一個邏輯代碼
1.搭建環境
開啟駝峰命名:隻應用于資料庫映射java使用的駝峰命名
<settings>
<!-- 開啟駝峰命名映射-->
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
2.where
where 元素隻會在子元素傳回任何内容的情況下才插入 “WHERE” 子句。而且,若子句的開頭為 “AND” 或 “OR”,where 元素也會将它們去除。
使用where标簽如果你滿足的第一個條件在where 後面添加了一個and,會被這個标簽自動删除and然後正常執行,如果你沒有過濾條件,那麼where标簽會将where字句删除
<where>
<if test="state != null">
state = #{state}
</if>
<if test="title != null">
AND title like #{title}
</if>
<if test="author != null and author.name != null">
AND author_name like #{author.name}
</if>
</where>
3.IF
BlogMapper.xml
<insert id="addBlog" parameterType="blog">
insert into blog values(#{id},#{title},#{author},#{createTime},#{view});
</insert>
<select id="queryBlogIf" 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>
test:
public void queryBlogIf(){
SqlSession sqlSession = MybatisUtil.getsqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
map.put("title","Mybatis");
// map.put("author","ll");
List<Blog> blogs = mapper.queryBlogIf(map);
for (Blog item : blogs) {
System.out.println(item);
}
sqlSession.close();
}
4.choose(swtich)
<select id="queryBlogChoose" resultType="blog" parameterType="map">
select * from blog
<where>
<choose>
<when test="title != null">
title=#{title}
</when>
<when test="author != null">
author=#{author}
</when>
<otherwise>
view > 0
</otherwise>
</choose>
</where>
</select>
作用:和java的swtich功能類似
- 隻會滿足其中之一,如果存在滿足多個條件的話也隻會滿足第一個條件
5.Set
作用:set 元素會動态地在行首插入 SET 關鍵字,并會删掉額外的逗号```xml
<update id="updateBlogSet" parameterType="map" >
update blog
<set>
<if test="title != null">
title=#{title},
</if>
<if test="author != null">
author=#{author},
</if>
<if test="view != null">
view=#{view},
</if>
</set>
where id = '35a166859fb84884b1e18732a05516ff'
</update>
6.foreach(幾乎不用)
我們想查出資料庫前兩條資料
<select id="queryfreeBlog" resultType="Blog">
select * from Blog
<where>
<foreach collection="list" item="id" open="and (" close=")" separator="or">
id = #{id}
</foreach>
</where>
</select>
open:循環開始之前拼接的内容;
close結:循環結束拼接的内容
separator:指定循環中的分隔符
collection:循環周遊的内容
item:周遊的單個變量,在标簽體重來使用
@Test
public void queryfreeBlog(){
SqlSession sqlSession = MybatisUtil.getsqlSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
HashMap map = new HashMap();
ArrayList<String> str = new ArrayList<String>();
str.add("35a166859fb84884b1e18732a05516ff");
str.add("6d5cb2ff745c4f658eefd4b21add44f9");
map.put("list",str);
List<Blog> blogs = mapper.queryfreeBlog(map);
for (Blog blog : blogs) {
System.out.println(blog);
}
sqlSession.close();
}
結果:
Blog{id='35a166859fb84884b1e18732a05516ff', title='這不是結局', author='小新', createTime=2021-07-29, view=998} Blog{id='6d5cb2ff745c4f658eefd4b21add44f9', title='java', author='劉星', createTime=2021-07-29, view=1005}
注意:
如果這裡我們沒有傳遞任何參數,就會自動删除where條件,将會查出所有資訊
7.SQL片段
有的時候,我們可能會将一些功能的部分抽取出來,友善複用
<sql id="if-author-title">
<if test="title != null">
and title=#{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>
<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from blog
<where>
<include refid="if-author-title"/>
</where>
</select>
實作代碼的複用
使用sql标簽提取公共部分
在使用的地方用include标簽引入SQL片段通過id引入
注意:
- 最好基于單表來定義SQL片段
- 不要存在where标簽
- 最好隻要一些IF判斷就好了
13.緩存(了解)
1.什麼是緩存?
- 存在記憶體中的臨時資料
- 将使用者資料查詢到的資料放在緩存中,使用者去查詢資料就不用從磁盤上查詢,從緩存中讀取,進而提高效率,解決了高并發系統的性能問題
2.為什麼要使用緩存?
- 減少和資料庫的互動次數,減少系統開銷,提高系統效率
3.什麼樣的資料能使用緩存?
查詢:連接配接資料庫,耗記憶體
解決:一次查詢的結果,給他暫存在一個可以直接讀取的地方--》記憶體:緩存
我們再次查詢相同資料的時候我們走緩存,就不用走資料庫了。
14.Mybatis緩存
- Mybatis包含一個非常強大的查詢緩存特性,它可以非常友善的定制和配置緩存,緩存可以極大地提升查詢效率
- Mybatis系統中預設定義了兩級緩存:一級緩存和二級緩存
- 預設情況下,隻有一級緩存開啟。(本地緩存)
- 二級緩存需要手動開啟和配置,他是基于namespace級别的緩存
- 為了提高擴充性,mybatis定了緩存接口Cache,我們可以通過實作Cache接口來定義二級緩存
一級緩存
- 一級緩存也叫本地緩存
- 與資料庫同義詞會話期間查詢到的資料會被放在本地緩存中
- 以後如果需要擷取相同的資料的話,直接從緩存中拿,沒有必要在去查資料庫
實驗步驟
1.開啟日志
2.測試在一個Session中查詢兩次相同的記錄
3.檢視日志輸出
@Test
public void queryAllUser(){
SqlSession sqlSession = MybatisUtil.getsqlSession();
UserMapper mapper = sqlSession.getMapper(UserMapper.class);
List<User> users = mapper.queryAllUser(6);
System.out.println(users);
System.out.println("=============================================");
List<User> users1 = mapper.queryAllUser(6);
System.out.println(users1);
System.out.println(users==users1);
sqlSession.close();
}
增删改會重新整理原來的東西
緩存失效的情況:
- 增删改都有可能改變原來的資料,必定會重新整理緩存
- 查詢不同的Mapper.xml
- 手動清理緩存
- 查詢不同的東西
小結:一級緩存預設是開啟的,隻在一次SqlSessionFactory中有效,也就是拿到連結到關閉連接配接區間有效。一級緩存也是無法關閉的。
一級緩存作用域就是sqlsession對象,存儲的内容,查詢後的結果集,在sqlSession對象中有Map結果,Map的值就是結果集的對象。
二級緩存
- 二級緩存也叫全局緩存,一級緩存作用域太低了,是以誕生了二級緩存
- 基于namespace級别的緩存,一個名稱空間,對應一個二級緩存
- 工作機制:
- 一個會話查詢一條資料,這個資料就會被放在目前會話的一級緩存中
- 如果目前會話關閉了,這個會話對應的一級緩存就沒有了;但是我們想要的是,會話關閉了,一級緩存中的資料會被儲存到二級緩存中
- 新的會話查詢資訊,就可以從二級緩存中擷取内容;
預設情況下,隻啟用了本地的會話緩存,它僅僅對一個會話中的資料進行緩存。 要啟用全局的二級緩存,隻需要在你的 SQL 映射檔案中添加一行:<cache/>
步驟:
1.開啟緩存設定
<setting name="cacheEnabled" value="true"/>
2.要是用二級緩存需要在Mapper中開啟
<!--在目前的xml中開啟二級緩存-->
<cache eviction="FIFO" flushInterval="60000" size="512" readOnly="true"/>
也可以自定義一些參數
3.測試
@Test
public void queryAllUser(){
SqlSession sqlSession1 = MybatisUtil.getsqlSession();
SqlSession sqlSession2 = MybatisUtil.getsqlSession();
UserMapper mapper = sqlSession1.getMapper(UserMapper.class);
List<User> users = mapper.queryAllUser(6);
System.out.println(users);
sqlSession1.close();
UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
List<User> users2 = mapper2.queryAllUser(6);
System.out.println(users2);
sqlSession2.close();
}
隻進行了一次查詢
小結:二級緩存隻能在一個namespace内有效,也就是隻在一個xml檔案内。所有的資料都會先放在一級緩存中,隻有當會話送出或關閉的時候轉存到二級緩存
問題!:我們需要将實體類序列化
implements Serializable
查找順序
自定義緩存--ehcache
ehcache是一種廣發使用的開源java分布式緩存