Mybatis


Mybatis

環境

  • JDK1.8

  • Mysql5.7

  • maven 3.6.3

  • IDEA

回顧

  • JDBC

  • Mysql

  • Java基礎

  • Maven

  • Junit

SSM框架:配置文件的最好的方式:看官網文檔

2 Mybatis

1、Mybatis簡介(2020-10-21)

1.1 什么是Mybatis

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

MyBatis 避免了幾乎所有的 JDBC 代碼和手動設置參數以及獲取結果集的過程

MyBatis 可以使用簡單的 XML 或注解來配置和映射原生信息,將接口和 Java 的 實體類 【Plain Old Java Objects,普通的 Java對象】映射成數據庫中的記錄。

MyBatis 本是apache的一個開源項目ibatis, 2010年這個項目由apache 遷移到了google code,並且改名為MyBatis 。

2013年11月遷移到Github .

Mybatis官方文檔 : http://www.mybatis.org/mybatis-3/zh/index.html

GitHub : https://github.com/mybatis/mybatis-3

如何獲得Mybatis

1.2 持久化

數據持久化

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

  • 內存:斷電即失

  • 數據庫(JDBC),io文件持久化

  • 生活方面的例子:冷藏,罐頭

為什么需要持久化

  • 不想丟掉一些對象

  • 內存太貴

1.3 持久層

Dao層,Service層,Controller層

  • 完成持久化工作的代碼塊

  • 層界限十分明顯

1.4 為什么需要Mybatis

  • 幫助程序員將數據存入到數據庫中

  • 方便

  • 傳統的JDBC代碼太復雜,簡化->框架->自動化

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

  • 優點:

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

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

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

    • 提供xml標簽,支持編寫動態sql。

    • ......

    • 最重要的一點,使用的人多!公司需要!

2、第一個Mybatis程序

思路流程:搭建環境-->導入Mybatis--->編寫代碼--->測試

2.1 搭建環境

搭建數據庫

CREATE DATABASE `mybatis`;

USE `mybatis`;

DROP TABLE IF EXISTS `user`;

CREATE TABLE `user` (
`id` int(20) NOT NULL,
`name` varchar(30) DEFAULT NULL,
`pwd` varchar(30) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

insert into `user`(`id`,`name`,`pwd`) values (1,'狂神','123456'),(2,'張三','abcdef'),(3,'李四','987654');

新建項目

  • 新建一個普通maven項目

  • 刪除src目錄

  • 導入maven依賴

<!--import dependencies-->
<dependencies>
<!--mysql driver-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--mybatis-->
<!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>

2.2 創建一個模塊

編寫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>
<environments default="development">
<environment id="development">
<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&amp;useUnicode=true&amp;characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/kuang/dao/userMapper.xml"/>
</mappers>
</configuration>

編寫mybatis工具類

package com.kuang.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.InputStream;

//SqlSessionFactory -->SqlSession
public class MybatisUtils {

private static SqlSessionFactory sqlSessionFactory;
static {

try {
//使用Mybaties第一步:獲取sqlSessionFactory對象
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
} catch (Exception e) {
e.printStackTrace();
}
}

//既然有了 SqlSessionFactory,顧名思義,我們可以從中獲得 SqlSession 的實例。
// SqlSession 提供了在數據庫執行 SQL 命令所需的所有方法。你可以通過 SqlSession 實例來直接執行已映射的 SQL 語句。
public static SqlSession getSqlSession(){
// SqlSession sqlSession = sqlSessionFactory.openSession();
// return sqlSession;

return sqlSessionFactory.openSession();
}

}

2.3 編寫代碼

  • 實體類

//實體類
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;
}

@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", pwd='" + pwd + '\'' +
'}';
}
}
  • Dao接口

public interface UserDao {
List<User> getUserList();
}
  • 接口實現類由原來的UserDaoImpl轉變成一個Mapper配置文件。

<?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=綁定一個對應的Dao/Mapper窗口-->
<mapper namespace="com.kuang.dao.UserMapper">
<!--select查詢語句-->
<select id="selectUser" resultType="com.kuang.pojo.User">
select * from user
</select>
</mapper>

2.4 注意點:

  • org.apache.ibatis.binding.BindingException: Type interface com.kuang.dao.UserDao is not known to the MapperRegistry.

MapperRegistry是什么?

核心配置文件中注冊mappers

<!--    每一個Mapper.xml文件都需要在Mybatis核心配置文件中注冊-->
<mappers>
<mapper resource="com/kuang/dao/userMapper.xml"/>
</mappers>
  • junit測試

public class UserDaoTest {

@Test
public void test() {

// 第一步:獲得SqlSession對象
SqlSession sqlSession = MybatisUtils.getSqlSession();

//方式一:getMapper
UserDao userDao = sqlSession.getMapper(UserDao.class);
List<User> userList = userDao.getUserList();

for (User user : userList) {
System.out.println(user);
}

//關閉SqlSession
sqlSession.close();
}
}

可能遇到的問題:

  1. 配置文件沒有注冊;

  2. 綁定接口錯誤;

  3. 方法名不對;

  4. 返回類型不對;

  5. Maven導出資源問題。

問題說明

可能出現問題說明:Maven靜態資源過濾問題

<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>

3、CRUD以及配置解析

3.1 namespace

namespace中的包名要和Dao/Mapper接口的包名一致!

3.2 select

選擇,查詢語句;

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

  • resultType:Sql語句執行的返回值!

  • parameterType:參數類型!

1、編寫接口

//根據id查詢用戶
User getUserById(int id);

2、編寫對應mapper中的sql語句

	<select id="getUserById" parameterType="int" resultType="com.kuang.pojo.User">
select * from mybatis.user where id = #{id}
</select>

3、測試

@Test
public void getUserById(){
SqlSession sqlSession = MybatisUtils.getSqlSession();

UserMapper mapper = sqlSession.getMapper(UserMapper.class);
User user = mapper.getUserById(1);
System.out.println(user);

//關閉SqlSession
sqlSession.close();
}

3.3 insert

我們一般使用insert標簽進行插入操作,它的配置和select標簽差不多!

需求:給數據庫增加一個用戶

1、在UserMapper接口中添加對應的方法

//添加一個用戶
int addUser(User user);

2、在UserMapper.xml中添加insert語句

<insert id="addUser" parameterType="com.kuang.pojo.User">
insert into user (id,name,pwd) values (#{id},#{name},#{pwd})
</insert>

3、測試

@Test
public void testAddUser() {
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = new User(5,"王五","zxcvbn");
int i = mapper.addUser(user);
System.out.println(i);
session.commit(); //提交事務,重點!不寫的話不會提交到數據庫
session.close();
}

注意點:增、刪、改操作需要提交事務!

3.4 upadte

我們一般使用update標簽進行更新操作,它的配置和select標簽差不多!

需求:修改用戶的信息

1、同理,編寫接口方法

//修改一個用戶
int updateUser(User user);

2、編寫對應的配置文件SQL

<update id="updateUser" parameterType="com.kuang.pojo.User">
update user set name=#{name},pwd=#{pwd} where id = #{id}
</update>

3、測試

@Test
public void testUpdateUser() {
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
User user = mapper.selectUserById(1);
user.setPwd("asdfgh");
int i = mapper.updateUser(user);
System.out.println(i);
session.commit(); //提交事務,重點!不寫的話不會提交到數據庫
session.close();
}

3.5 delete

我們一般使用delete標簽進行刪除操作,它的配置和select標簽差不多!

需求:根據id刪除一個用戶

1、同理,編寫接口方法

//根據id刪除用戶
int deleteUser(int id);

2、編寫對應的配置文件SQL

<delete id="deleteUser" parameterType="int">
delete from user where id = #{id}
</delete>

3、測試

@Test
public void testDeleteUser() {
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
int i = mapper.deleteUser(5);
System.out.println(i);
session.commit(); //提交事務,重點!不寫的話不會提交到數據庫
session.close();
}

小結:

  • 所有的增刪改操作都需要提交事務!

  • 接口所有的普通參數,盡量都寫上@Param參數,尤其是多個參數時,必須寫上!

  • 有時候根據業務的需求,可以考慮使用map傳遞參數!

  • 為了規范操作,在SQL的配置文件中,我們盡量將Parameter參數和resultType都寫上!

3.6 分析錯誤

  1. xml文件中注釋不能出現中文報錯,查看自己的是UTF-8還是GBK編碼,改成為相應的就行。

<?xml version="1.0" encoding="UTF-8" ?>
<?xml version="1.0" encoding="GBK" ?>

即可成功測試。

  1. 標簽不要匹配錯!

  2. resource綁定mapper,需要使用路徑!

  3. 程序配置文件必須符合規范!

  4. NullPointerException,沒有注冊到資源!

  5. maven資源沒有導出問題!

3.7 萬能Map

假設,我們的實體類,或者數據庫中的表,字段或者參數過多,我們應當考慮使用Map!

//萬能的Map
int addUser2(Map<String,Object> map);
<!--對象中的屬性,可以直接取出來 傳遞map的key-->
<insert id="addUser2" parameterType="map">
insert into mybatis.user (id,pwd) values (#{userid},#{password})
</insert>
@Test
public void addUser2(){
SqlSession sqlSession = MybatisUtils.getSqlSession();

UserMapper mapper = sqlSession.getMapper(UserMapper.class);
Map<String,Object> map = new HashMap<String, Object>();
map.put("userid",4);
map.put("password","123321");

mapper.addUser2(map);


sqlSession.commit();
sqlSession.close();
}

Map傳遞參數,直接在sql中取出key即可!【parameterType=“map”】 對象傳遞參數,直接在sql中取對象的屬性即可!【parameterType=“Object”】 只有一個基本類型參數的情況下,可以直接在sql中取到! 多個參數用Map,或者注解!

3.8 思考題

模糊查詢like語句該怎么寫?

第1種:在Java代碼中添加sql通配符。

string wildcardname = “%smi%”;
list<name> names = mapper.selectlike(wildcardname);

<select id=”selectlike”>
select * from foo where bar like #{value}
</select>

第2種:在sql語句中拼接通配符,會引起sql注入

string wildcardname = “smi”;
list<name> names = mapper.selectlike(wildcardname);

<select id=”selectlike”>
select * from foo where bar like "%"#{value}"%"
</select>

4 配置解析

1. 核心配置文件

  • mybatis-config.xml

  • Mybatis的配置文件包含了會深深影響MyBatis行為的設置和屬性信息。

configuration(配置)
properties(屬性)
settings(設置)
typeAliases(類型別名)
typeHandlers(類型處理器)
objectFactory(對象工廠)
plugins(插件)
environments(環境配置)
environment(環境變量)
transactionManager(事務管理器)
dataSource(數據源)
databaseIdProvider(數據庫廠商標識)
mappers(映射器)

2. 環境配置(environments)

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

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

Mybatis 默認的事務管理器是JDBC,連接池:POOLED

3. 屬性

我們可以通過properties屬性來引用配置文件

這些屬性可以在外部進行配置,並可以進行動態替換。你既可以在典型的 Java 屬性文件中配置這些屬性,也可以在 properties 元素的子元素中設置。 (db.properties)

編寫一個配置文件

driver = com.mysql.jdbc.Driver
url = "jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=utf8""
username = root
password = root

在核心配置文件中引入

mybatis-config.xml (同時有的話,優先走外面properties)

<?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>
<!--引入外部配置文件-->
<!--<properties resource="db.properties">-->

<properties resource="db.properties">
<property name="username" value="root"/>
<property name="password" value="root"/>
</properties>

<environments default="development">
<environment id="development">
<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&amp;useUnicode=true&amp;characterEncoding=utf8"/>
<property name="username" value="root"/>
<property name="password" value="root"/>
</dataSource>
</environment>
</environments>

<!--每一個Mapper.xml文件都需要在Mybatis核心配置文件中注冊-->
<mappers>
<mapper resource="com/kuang/dao/UserMapper.xml"/>
</mappers>

</configuration>
  1. 可以直接引入外部文件

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

  3. 如果兩個文件有同一個字段,優先使用外部配置文件的

4. 類型別名 typeAliases

  • 類型別名可為 Java 類型設置一個縮寫名字。 它僅用於 XML 配置.

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

<!--可以給實體類起別名-->
<typeAliases>
<typeAlias type="com.kuang.pojo.User" alias="User"/>
</typeAliases>

也可以指定一個包,每一個在包 domain.blog 中的 Java Bean,在沒有注解的情況下,會使用 Bean 的首字母小寫的非限定類名來作為它的別名。 比如 domain.blog.Author 的別名為 author,;若有注解,則別名為其注解值。見下面的例子:

<typeAliases>
<package name="com.kuang.pojo"/>
</typeAliases>

在實體類比較少的時候,使用第一種方式。

如果實體類十分多,建議用第二種掃描包的方式。

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

@Alias("author")
public class Author {
...
}

5.設置

設置名 描述 有效值 默認
cacheEnabled 全局性地開啟或關閉所有映射器配置文件中已配置的任何緩存。 true | false true
lazyLoadingEnabled 延遲加載的全局開關。當開啟時,所有關聯對象都會延遲加載。 特定關聯關系中可通過設置 fetchType 屬性來覆蓋該項的開關狀態。 true | false false
logImpl 指定 MyBatis 所用日志的具體實現,未指定時將自動查找。 SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING 未設置

6. 其他

7. 映射器 mappers

MapperRegistry:注冊綁定我們的Mapper文件;

方式一: [推薦使用]

<mappers>
<mapper resource="com/hou/dao/UserMapper.xml"/>
</mappers>

方式二:

<mappers>
<mapper class="com.hou.dao.UserMapper" />
</mappers>
  • 接口和它的Mapper必須同名

  • 接口和他的Mapper必須在同一包下

方式三:

<mappers>
<!--<mapper resource="com/hou/dao/UserMapper.xml"/>-->
<!--<mapper class="com.hou.dao.UserMapper" />-->
<package name="com.hou.dao" />
</mappers>
  • 接口和它的Mapper必須同名

  • 接口和他的Mapper必須在同一包下

8. 作用域和生命周期

在這里插入圖片描述

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

SqlSessionFactoryBuilder:

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

  • 局部變量

SqlSessionFactory:

  • 就是數據庫連接池。

  • 一旦被創建就應該在應用的運行期間一直存在 ,沒有任何理由丟棄它或重新創建另一個實例 。 多次重建 SqlSessionFactory 被視為一種代碼“壞習慣”。

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

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

SqlSession

  • 每個線程都應該有它自己的 SqlSession 實例。

  • 連接到連接池的請求!

  • SqlSession 的實例不是線程安全的,因此是不能被共享的 ,所以它的最佳的作用域是請求或方法作用域。

  • 用完之后趕緊關閉,否則資源被占用。

在這里插入圖片描述

這里面的每一個Mapper就代表每一個具體業務!

5 解決屬性名和字段名不一致的問題

1 問題

數據庫中的字段

新建一個項目,拷貝之前,測試實體字段不一致的情況

User

package com.hou.pogo;

public class User {

private int id;
private String name;
private String password;
}

問題:

User{id=2, name='wang', password='null'}

解決方法:

核心配置文件

  • 起別名

<select id="getUserById" resultType="User"
parameterType="int">
select id,name,pwd as password from mybatis.user where id = #{id}
</select>
  • resultMap 結果集映射

<?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綁定一個對應的mapper接口-->
<mapper namespace="com.hou.dao.UserMapper">

<select id="getUserById" resultMap="UserMap" parameterType="int">
select * from mybatis.user where id = #{id}
</select>

<!--結果集映射-->
<resultMap id="UserMap" type="User">
<!--colunm 數據庫中的字段,property實體中的屬性-->
<result column="id" property="id"></result>
<result column="name" property="name"></result>
<result column="pwd" property="password"></result>
</resultMap>

</mapper>
  • resultMap 元素是 MyBatis 中最重要最強大的元素。

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

<resultMap id="UserMap" type="User">
<!--colunm 數據庫中的字段,property實體中的屬性-->
<!--<result column="id" property="id"></result>-->
<!--<result column="name" property="name"></result>-->
<result column="pwd" property="password"></result>
</resultMap>
  • resultMap 元素是 MyBatis 中最重要最強大的元素。

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

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

  • 如果這個世界總是這么簡單就好了。

6. 日志

1. 日志工廠

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

曾經:sout,debug

現在:日志工廠

img

  • SLF4J

  • LOG4J [掌握]

  • LOG4J2

  • JDK_LOGGING

  • COMMONS_LOGGING

  • STDOUT_LOGGING [掌握]

  • NO_LOGGING

具體使用哪一個,在設置中設定

STDOUT_LOGGING 標志日志輸出

mybatis-confi中

<settings>
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

在這里插入圖片描述

2. Log4j

  1. 先導包

    pom.xml下

    <dependencies>
    <!-- https://mvnrepository.com/artifact/log4j/log4j -->
    <dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
    </dependency>
    </dependencies>
  2. 新建log4j.properties文件

### set log levels ###
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/hou.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=DEBUG
  1. 配置實現

<settings>
<setting name="logImpl" value="LOG4J"/>
</settings>
  1. Log4j使用

package com.hou.dao;

import com.hou.pojo.User;
import com.hou.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.apache.log4j.Logger;
import org.junit.Test;

public class UserDaoTest {

static Logger logger = Logger.getLogger(UserDaoTest.class);

@Test
public void test(){
// 獲得sqlsession對象
SqlSession sqlSession = MybatisUtils.getSqlSession();
try{
// 1.執行 getmapper
UserMapper userDao = sqlSession.getMapper(UserMapper.class);
logger.info("測試");
User user = userDao.getUserById(2);
System.out.println(user);
}catch(Exception e){
e.printStackTrace();
}finally{
//關閉
sqlSession.close();
}
}

@Test
public void testLog4j(){
logger.info("info:進入了testlog4j");
logger.debug("debug:進入了testlog4j");
logger.error("error:進入了testlog4j");
}

}

Log4j簡單使用

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

  2. 日志對象,參數為當前類的class對象

 Logger logger = Logger.getLogger(UserDaoTest.class);
  1. 日志級別

logger.info("info: 測試log4j");
logger.debug("debug: 測試log4j");
logger.error("error:測試log4j");
  1. info

  2. debug

  3. error

7 分頁

思考:為什么分頁?

  • 減少數據的處理量

7.1 使用Limit分頁

SELECT * FROM user LIMIT startIndex,pageSize 

使用MyBatis實現分頁,核心SQL

  1. 接口

    //分頁
    List<User> getUserByLimit(Map<String,Integer> map);
  2. Mapper.xml

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

        @Test
    public void getUserByLimit(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

    Map<String, Integer> map = new HashMap<String, Integer>();
    map.put("startIndex", 1);
    map.put("pageSize", 2);
    List<User> userList = mapper.getUserByLimit(map);

    for(User user:userList){
    System.out.println(user);
    }

    sqlSession.close();
    }

7.2 使用RowBounds分頁

不再使用SQL實現分頁

  1. 接口

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

    <!--分頁查詢2-->
    <select id="getUserByRowBounds">
    select * from user limit #{startIndex},#{pageSize}
    </select>
  3. 測試

    @Test
    public void getUserByRow(){
    SqlSession sqlSession = MybatisUtils.getSqlSession();
    //RowBounds實現
    RowBounds rowBounds = new RowBounds(1, 2);

    //通過java代碼層面
    List<User> userList = sqlSession.selectList
    ("com.hou.dao.UserMapper.getUserByRowBounds",
    null,rowBounds);

    for (User user : userList) {
    System.out.println(user);
    }

    sqlSession.close();
    }

7.3 分頁插件

在這里插入圖片描述

8 使用注解開發

8.1 面向接口開發

三個面向區別

  • 面向對象是指,我們考慮問題時,以對象為單位,考慮它的屬性和方法

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

  • 接口設計與非接口設計是針對復用技術而言的,與面向對象(過程)不是一個問題,更多的體現就是對系統整體的架構;

8.2 使用注解開發

  1. 刪除 UserMapper.xml

  2. UserMapper

    package com.hou.dao;

    import com.hou.pojo.User;
    import org.apache.ibatis.annotations.Select;

    import java.util.List;

    public interface UserMapper {

    @Select("select * from user")
    List<User> getUsers();
    }
  3. 核心配置 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>

    <!--引入外部配置文件-->
    <properties resource="db.properties"/>

    <!--可以給實體類起別名-->
    <typeAliases>
    <typeAlias type="com.hou.pojo.User" alias="User"></typeAlias>
    </typeAliases>

    <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>

    <!--綁定接口-->
    <mappers>
    <mapper class="com.hou.dao.UserMapper"></mapper>
    </mappers>
    </configuration>

    本質:反射機制

    底層:動態代理

Mybatis詳細執行流程:

  1. Resource獲取全局配置文件

  2. 實例化SqlsessionFactoryBuilder

  3. 解析配置文件流XMLCondigBuilder

  4. Configration所有的配置信息

  5. SqlSessionFactory實例化

  6. trasactional事務管理

  7. 創建executor執行器

  8. 創建SqlSession

  9. 實現CRUD

  10. 查看是否執行成功

  11. 提交事務

  12. 關閉

在這里插入圖片描述

在這里插入圖片描述

8.3 注解CRUD

編寫接口

package com.kuang.dao;

import com.hou.pojo.User;
import org.apache.ibatis.annotations.*;

import java.util.List;

public interface UserMapper {

@Select("select * from user")
List<User> getUsers();

//方法存在多個參數,所有的參數必須加@Param
@Select("select * from user where id = #{id}")
User getUserById(@Param("id") int id);

@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=#{id}")
int deleteUser(@Param("id") int id);

}

MybatisUtile

package com.kuang.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 --> sqlSession
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();
}
}

public static SqlSession getSqlSession(){
return sqlSessionFactory.openSession(true);
}

}

Test

【注意:我們必須要將接口注冊綁定到我們的核心配置文件中】

package com.hou.dao;

import com.hou.pojo.User;
import com.hou.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class UserDaoTest {

@Test
public void test(){
// 獲得sqlsession對象
SqlSession sqlSession = MybatisUtils.getSqlSession();
try{
// 1.執行 getmapper
UserMapper userDao = sqlSession.getMapper(UserMapper.class);
List<User> userList = userDao.getUsers();
for (User user : userList) {
System.out.println(user);
}

}catch(Exception e){
e.printStackTrace();
}finally{
//關閉
sqlSession.close();
}
}

@Test
public void getuserById(){
// 獲得sqlsession對象
SqlSession sqlSession = MybatisUtils.getSqlSession();
try{
// 1.執行 getmapper
UserMapper userDao = sqlSession.getMapper(UserMapper.class);
User user = userDao.getUserById(1);

System.out.println(user);


}catch(Exception e){
e.printStackTrace();
}finally{
//關閉
sqlSession.close();
}
}

@Test
public void addUser(){
// 獲得sqlsession對象
SqlSession sqlSession = MybatisUtils.getSqlSession();
try{
// 1.執行 getmapper
UserMapper userDao = sqlSession.getMapper(UserMapper.class);
userDao.addUser(new User(6, "kun","123"));

}catch(Exception e){
e.printStackTrace();
}finally{
//關閉
sqlSession.close();
}
}

@Test
public void updateUser(){
// 獲得sqlsession對象
SqlSession sqlSession = MybatisUtils.getSqlSession();
try{
// 1.執行 getmapper
UserMapper userDao = sqlSession.getMapper(UserMapper.class);
userDao.updateUser(new User(6, "fang","123"));

}catch(Exception e){
e.printStackTrace();
}finally{
//關閉
sqlSession.close();
}
}

@Test
public void deleteUser(){
// 獲得sqlsession對象
SqlSession sqlSession = MybatisUtils.getSqlSession();
try{
// 1.執行 getmapper
UserMapper userDao = sqlSession.getMapper(UserMapper.class);
userDao.deleteUser(6);

}catch(Exception e){
e.printStackTrace();
}finally{
//關閉
sqlSession.close();
}
}
}

關於@Param( )注解

  • 基本類型的參數或者String類型,需要加上

  • 引用類型不需要加

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

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

#{} 和 ${}

9 Lombok

Lombok項目是一個Java庫,它會自動插入編輯器和構建工具中,Lombok提供了一組有用的注釋,用來消除Java類中的大量樣板代碼。僅五個字符(@Data)就可以替換數百行代碼從而產生干凈,簡潔且易於維護的Java類。

使用步驟:

  1. 在IDEA中安裝Lombok插件

  2. 在項目中導入Lombok的jar包

    <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.10</version>
    <scope>provided</scope>
    </dependency>
  3. 在程序上加注解

    @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

    @Data:無參構造,get,set,toString,hashCode

    在實體類上加注解

    package com.kuang.pojo;

    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    import lombok.ToString;

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @ToString
    public class User2 {

    private int id;
    private String name;
    private String password;

    }

在這里插入圖片描述

10 多對一處理

多對一:

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

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

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

SQL語句:

CREATE TABLE `teacher` (
`id` INT(10) NOT NULL,
`name` VARCHAR(30) DEFAULT NULL,
PRIMARY KEY (`id`)
)ENGINE = INNODB DEFAULT CHARSET=utf8

INSERT 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=utf8

INSERT 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');

搭建測試環境

  1. IDEA安裝Lombok插件

  2. 引入Maven依賴

    <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
    <dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.16.10</version>
    </dependency>
  3. 新建實體類Teacher,Student

    @Data //GET,SET,ToString,有參,無參構造
    public class Teacher {
    private int id;
    private String name;
    }



    @Data
    public class Student {
    private int id;
    private String name;
    //多個學生可以是同一個老師,即多對一
    private Teacher teacher;
    }
  4. 編寫實體類對應的Mapper接口 【兩個】

    無論有沒有需要,都應該寫上,以備后來只需!

    public interface StudentMapper {
    }
    public interface TeacherMapper {
    }
  5. 編寫Mapper接口對應的 mapper.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="com.kuang.dao.StudentMapper">

    </mapper>
    <?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="com.kuang.dao.TeacherMapper">

    </mapper>
  6. 在核心配置文件中綁定注冊我們的Mapper接口或者文件!【方法很多,隨心選】

    <!--綁定接口-->
    <mappers>
    <mapper class="com.kuang.dao.TeacherMapper"/>
    </mappers>
  7. 測試查詢是否能夠成功!

    <!--TeaacherMapper-->
    @Select("select * from teacher where id = #{tid}")
    Teacher getTeacher(@Param("tid") int id);
<!--MyTest-->
package com.kuang.dao;

import com.kuang.pojo.Teacher;
import com.kuang.utils.MybatisUtils;
import org.apache.ibatis.session.SqlSession;

public class MyTest {

public static void main(String[] args) {

SqlSession sqlSession = MybatisUtils.getSqlSession();
TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
Teacher teacher = mapper.getTeacher(1);
System.out.println(teacher);
sqlSession.close();

}

}

按照查詢嵌套處理

  1. 給StudentMapper接口增加方法

    //獲取所有學生及對應老師的信息
    public List<Student> getStudents();
  2. 編寫對應打的Mapper文件

    <?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="com.kuang.dao.StudentMapper">

    <!--
    需求:獲取所有學生及對應老師的信息
    思路:
    1. 獲取所有學生的信息
    2. 根據獲取的學生信息的老師ID->獲取該老師的信息
    3. 思考問題,這樣學生的結果集中應該包含老師,該如何處理呢,數據庫中我們一般使用關聯查詢?
    1. 做一個結果集映射:StudentTeacher
    2. StudentTeacher結果集的類型為 Student
    3. 學生中老師的屬性為teacher,對應數據庫中為tid。
    多個 [1,...)學生關聯一個老師=> 一對一,一對多
    4. 查看官網找到:association – 一個復雜類型的關聯;使用它來處理關聯查詢
    -->
    <select id="getStudents" resultMap="StudentTeacher">
    select * from student
    </select>
    <resultMap id="StudentTeacher" type="Student">
    <!--association關聯屬性 property屬性名 javaType屬性類型 column在多的一方的表中的列名-->
    <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
    </resultMap>
    <!--
    這里傳遞過來的id,只有一個屬性的時候,下面可以寫任何值
    association中column多參數配置:
    column="{key=value,key=value}"
    其實就是鍵值對的形式,key是傳給下個sql的取值名稱,value是片段一中sql查詢的字段名。
    -->
    <select id="getTeacher" resultType="teacher">
    select * from teacher where id = #{id}
    </select>

    </mapper>
  3. 編寫完畢去Mybatis配置文件中,注冊Mapper!

  4. 注意點說明:

    <resultMap id="StudentTeacher" type="Student">
    <!--association關聯屬性 property屬性名 javaType屬性類型 column在多的一方的表中的列名-->
    <association property="teacher" column="{id=tid,name=tid}" javaType="Teacher" select="getTeacher"/>
    </resultMap>
    <!--
    這里傳遞過來的id,只有一個屬性的時候,下面可以寫任何值
    association中column多參數配置:
    column="{key=value,key=value}"
    其實就是鍵值對的形式,key是傳給下個sql的取值名稱,value是片段一中sql查詢的字段名。
    -->
    <select id="getTeacher" resultType="teacher">
    select * from teacher where id = #{id} and name = #{name}
    </select>
  5. 測試

    @Test
    public void testGetStudents(){
    SqlSession session = MybatisUtils.getSession();
    StudentMapper mapper = session.getMapper(StudentMapper.class);

    List<Student> students = mapper.getStudents();

    for (Student student : students){
    System.out.println(
    "學生名:"+ student.getName()
    +"\t老師:"+student.getTeacher().getName());
    }
    }

按照結果嵌套處理

除了上面這種方式,還有其他思路嗎?

我們還可以按照結果進行嵌套處理;

  1. 接口方法編寫

    public List<Student> getStudents2();
  2. 編寫對應的mapper文件

    <!--
    按查詢結果嵌套處理
    思路:
    1. 直接查詢出結果,進行結果集的映射
    -->
    <select id="getStudents2" resultMap="StudentTeacher2" >
    select s.id sid, s.name sname , t.name tname
    from student s,teacher t
    where s.tid = t.id
    </select>

    <resultMap id="StudentTeacher2" type="Student">
    <id property="id" column="sid"/>
    <result property="name" column="sname"/>
    <!--關聯對象property 關聯對象在Student實體類中的屬性-->
    <association property="teacher" javaType="Teacher">
    <result property="name" column="tname"/>
    </association>
    </resultMap>
  3. 去mybatis-config文件中注入【此處應該處理過了】

    <mapper class="com.kuang.dao.StudentMapper"/>
  4. 測試

    @Test
    public void testGetStudents2(){
    SqlSession session = MybatisUtils.getSession();
    StudentMapper mapper = session.getMapper(StudentMapper.class);

    List<Student> students = mapper.getStudents2();

    for (Student student : students){
    System.out.println(
    "學生名:"+ student.getName()
    +"\t老師:"+student.getTeacher().getName());
    }
    }

小結

  • 按照查詢進行嵌套處理就像SQL中的子查詢

  • 按照結果進行嵌套處理就像SQL中的聯表查詢

回顧Mysql多對一查詢方式:

  • 子查詢

  • 聯表查詢

11 一對多處理

比如:一個老師擁有多個學生! 對於老師而言,就是一對多的關系!

環境搭建

  1. 環境搭建,和剛才一樣 實體類:

    @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;
    }

    ..... 和之前一樣,搭建測試的環境!

按結果嵌套處理

  1. TeacherMapper接口編寫方法

    //獲取指定老師,及老師下的所有學生
    public Teacher getTeacher(int id);
  2. 編寫接口對應的Mapper配置文件

    <mapper namespace="com.kuang.mapper.TeacherMapper">

    <!--
    思路:
    1. 從學生表和老師表中查出學生id,學生姓名,老師姓名
    2. 對查詢出來的操作做結果集映射
    1. 集合的話,使用collection!
    JavaType和ofType都是用來指定對象類型的
    JavaType是用來指定pojo中屬性的類型
    ofType指定的是映射到list集合屬性中pojo的類型。
    -->
    <select id="getTeacher" resultMap="TeacherStudent">
    select s.id sid, s.name sname , t.name tname, t.id tid
    from student s,teacher t
    where s.tid = t.id and t.id=#{id}
    </select>

    <resultMap id="TeacherStudent" type="Teacher">
    <result property="name" column="tname"/>
    <collection property="students" ofType="Student">
    <result property="id" column="sid" />
    <result property="name" column="sname" />
    <result property="tid" column="tid" />
    </collection>
    </resultMap>
    </mapper>
  3. 將Mapper文件注冊到MyBatis-config文件中

    <mappers>
    <mapper resource="mapper/TeacherMapper.xml"/>
    </mappers>
  4. 測試

    @Test
    public void testGetTeacher(){
    SqlSession session = MybatisUtils.getSession();
    TeacherMapper mapper = session.getMapper(TeacherMapper.class);
    Teacher teacher = mapper.getTeacher(1);
    System.out.println(teacher.getName());
    System.out.println(teacher.getStudents());
    }

按查詢嵌套處理

  1. TeacherMapper接口編寫方法

    public Teacher getTeacher2(int id);
  2. 編寫接口對應的Mapper配置文件

    <select id="getTeacher2" resultMap="TeacherStudent2">
    select * from teacher where id = #{id}
    </select>
    <resultMap id="TeacherStudent2" type="Teacher">
    <!--column是一對多的外鍵 , 寫的是一的主鍵的列名-->
    <collection property="students" javaType="ArrayList" ofType="Student" column="id" select="getStudentByTeacherId"/>
    </resultMap>
    <select id="getStudentByTeacherId" resultType="Student">
    select * from student where tid = #{id}
    </select>
  3. 將Mapper文件注冊到MyBatis-config文件中

  4. 測試

    @Test
    public void testGetTeacher2(){
    SqlSession session = MybatisUtils.getSession();
    TeacherMapper mapper = session.getMapper(TeacherMapper.class);
    Teacher teacher = mapper.getTeacher2(1);
    System.out.println(teacher.getName());
    System.out.println(teacher.getStudents());
    }

小結

  1. 關聯-association

  2. 集合-collection

  3. 所以association是用於一對一和多對一,而collection是用於一對多的關系

  4. javaType和ofType都是用來指定對象類型的

    • JavaType是用來指定pojo中屬性的類型

    • ofType指定的是映射到list集合屬性中pojo的類型。

注意說明:

  1. 保證SQL的可讀性,盡量通俗易懂

  2. 根據實際要求,盡量編寫性能更高的SQL語句

  3. 注意屬性名和字段不一致的問題

  4. 注意一對多和多對一 中:字段和屬性對應的問題

  5. 盡量使用Log4j,通過日志來查看自己的錯誤

12 動態SQL

介紹

什么是動態SQL:動態SQL指的是根據不同的查詢條件 , 生成不同的Sql語句.

官網描述:
MyBatis 的強大特性之一便是它的動態 SQL。如果你有使用 JDBC 或其它類似框架的經驗,你就能體會到根據不同條件拼接 SQL 語句的痛苦。例如拼接時要確保不能忘記添加必要的空格,還要注意去掉列表最后一個列名的逗號。利用動態 SQL 這一特性可以徹底擺脫這種痛苦。
雖然在以前使用動態 SQL 並非一件易事,但正是 MyBatis 提供了可以被用在任意 SQL 映射語句中的強大的動態 SQL 語言得以改進這種情形。
動態 SQL 元素和 JSTL 或基於類似 XML 的文本處理器相似。在 MyBatis 之前的版本中,有很多元素需要花時間了解。MyBatis 3 大大精簡了元素種類,現在只需學習原來一半的元素便可。MyBatis 采用功能強大的基於 OGNL 的表達式來淘汰其它大部分元素。

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

我們之前寫的 SQL 語句都比較簡單,如果有比較復雜的業務,我們需要寫復雜的 SQL 語句,往往需要拼接,而拼接 SQL ,稍微不注意,由於引號,空格等缺失可能都會導致錯誤。

那么怎么去解決這個問題呢?這就要使用 mybatis 動態SQL,通過 if, choose, when, otherwise, trim, where, set, foreach等標簽,可組合成非常靈活的SQL語句,從而在提高 SQL 語句的准確性的同時,也大大提高了開發人員的效率。

搭建環境

新建一個數據庫表:blog

字段:id,title,author,create_time,views

CREATE TABLE `blog` (
`id` varchar(50) NOT NULL COMMENT '博客id',
`title` varchar(100) NOT NULL COMMENT '博客標題',
`author` varchar(30) NOT NULL COMMENT '博客作者',
`create_time` datetime NOT NULL COMMENT '創建時間',
`views` int(30) NOT NULL COMMENT '瀏覽量'
) ENGINE=InnoDB DEFAULT CHARSET=utf8

創建一個基礎工程

  1. 創建Mybatis基礎工程

  2. IDutil工具類

public class IDUtil {

public static String genId(){
return UUID.randomUUID().toString().replaceAll("-","");
}

}
  1. 實體類編寫 【注意set方法作用】

    import java.util.Date;

    public class Blog {

    private String id;
    private String title;
    private String author;
    private Date createTime;
    private int views;
    //set,get....
    }
  2. 編寫Mapper接口及xml文件

    public interface BlogMapper {  }



    <?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="com.kuang.mapper.BlogMapper">

    </mapper>
  3. mybatis核心配置文件,下划線駝峰自動轉換

    <settings>
    <setting name="mapUnderscoreToCamelCase" value="true"/>
    <setting name="logImpl" value="STDOUT_LOGGING"/>
    </settings>
    <!--注冊Mapper.xml-->
    <mappers>
    <mapper resource="mapper/BlogMapper.xml"/>
    </mappers>
  4. 插入初始數據

    編寫接口

    //新增一個博客
    int addBlog(Blog blog);
    sql配置文件

    <insert id="addBlog" parameterType="blog">
    insert into blog (id, title, author, create_time, views)
    values (#{id},#{title},#{author},#{createTime},#{views});
    </insert>

    初始化博客方法

    @Test
    public void addInitBlog(){
    SqlSession session = MybatisUtils.getSession();
    BlogMapper mapper = session.getMapper(BlogMapper.class);

    Blog blog = new Blog();
    blog.setId(IDUtil.genId());
    blog.setTitle("Mybatis如此簡單");
    blog.setAuthor("狂神說");
    blog.setCreateTime(new Date());
    blog.setViews(9999);

    mapper.addBlog(blog);

    blog.setId(IDUtil.genId());
    blog.setTitle("Java如此簡單");
    mapper.addBlog(blog);

    blog.setId(IDUtil.genId());
    blog.setTitle("Spring如此簡單");
    mapper.addBlog(blog);

    blog.setId(IDUtil.genId());
    blog.setTitle("微服務如此簡單");
    mapper.addBlog(blog);

    session.close();
    }

    初始化數據完畢!

if

需求:根據作者名字和博客名字來查詢博客!如果作者名字為空,那么只根據博客名字查詢,反之,則根據作者名來查詢

  1. 編寫接口類

    //需求1
    List<Blog> queryBlogIf(Map map);
  2. 編寫SQL語句

    <!--需求1:
    根據作者名字和博客名字來查詢博客!
    如果作者名字為空,那么只根據博客名字查詢,反之,則根據作者名來查詢
    select * from blog where title = #{title} and author = #{author}
    -->
    <select id="queryBlogIf" parameterType="map" resultType="blog">
    select * from blog where
    <if test="title != null">
    title = #{title}
    </if>
    <if test="author != null">
    and author = #{author}
    </if>
    </select>
  3. 測試

    @Test
    public void testQueryBlogIf(){
    SqlSession session = MybatisUtils.getSession();
    BlogMapper mapper = session.getMapper(BlogMapper.class);

    HashMap<String, String> map = new HashMap<String, String>();
    map.put("title","Mybatis如此簡單");
    map.put("author","狂神說");
    List<Blog> blogs = mapper.queryBlogIf(map);

    System.out.println(blogs);

    session.close();
    }

這樣寫我們可以看到,如果 author 等於 null,那么查詢語句為 select * from user where title=#{title},但是如果title為空呢?那么查詢語句為 select * from user where and author=#{author},這是錯誤的 SQL 語句,如何解決呢?請看下面的 where 語句!

Where

修改上面的SQL語句;

<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from blog
<where>
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</where>
</select>

這個“where”標簽會知道如果它包含的標簽中有返回值的話,它就插入一個‘where’。此外,如果標簽返回的內容是以AND 或OR 開頭的,則它會剔除掉。

choose語句

有時候,我們不想用到所有的查詢條件,只想選擇其中的一個,查詢條件有一個滿足即可,使用 choose 標簽可以解決此類問題,類似於 Java 的 switch 語句

  1. 編寫接口方法

    List<Blog> queryBlogChoose(Map map);
  2. sql配置文件

    <select id="queryBlogChoose" parameterType="map" resultType="blog">
    select * from 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>
  3. 測試類

    @Test
    public void testQueryBlogChoose(){
    SqlSession session = MybatisUtils.getSession();
    BlogMapper mapper = session.getMapper(BlogMapper.class);

    HashMap<String, Object> map = new HashMap<String, Object>();
    map.put("title","Java如此簡單");
    map.put("author","狂神說");
    map.put("views",9999);
    List<Blog> blogs = mapper.queryBlogChoose(map);

    System.out.println(blogs);

    session.close();
    }

補充

MyBatis 提供了 choose 元素。if標簽是與(and)的關系,而 choose 是或(or)的關系。

choose標簽是按順序判斷其內部when標簽中的test條件出否成立,如果有一個成立,則 choose 結束。當 choose 中所有 when 的條件都不滿則時,則執行 otherwise 中的sql。類似於Java 的 switch 語句,choose 為 switch,when 為 case,otherwise 則為 default。

Set

同理,上面的對於查詢 SQL 語句包含 where 關鍵字,如果在進行更新操作的時候,含有 set 關鍵詞,我們怎么處理呢?

  1. 編寫接口方法

    int updateBlog(Map map);
  2. sql配置文件

    <!--注意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>
  3. 測試

    @Test
    public void testUpdateBlog(){
    SqlSession session = MybatisUtils.getSession();
    BlogMapper mapper = session.getMapper(BlogMapper.class);

    HashMap<String, String> map = new HashMap<String, String>();
    map.put("title","動態SQL");
    map.put("author","秦疆");
    map.put("id","9d6a763f5e1347cebda43e2a32687a77");

    mapper.updateBlog(map);


    session.close();
    }

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

SQL片段

有時候可能某個 sql 語句我們用的特別多,為了增加代碼的重用性,簡化代碼,我們需要將這些代碼抽取出來,然后使用時直接調用。

提取SQL片段:

<sql id="if-title-author">
<if test="title != null">
title = #{title}
</if>
<if test="author != null">
and author = #{author}
</if>
</sql>

引用SQL片段:

<select id="queryBlogIf" parameterType="map" resultType="blog">
select * from blog
<where>
<!-- 引用 sql 片段,如果refid 指定的不在本文件中,那么需要在前面加上 namespace -->
<include refid="if-title-author"></include>
<!-- 在這里還可以引用其他的 sql 片段 -->
</where>
</select>

注意:

①、最好基於 單表來定義 sql 片段,提高片段的可重用性 ②、在 sql 片段中不要包括 where

Foreach

  • 動態 SQL 的另一個常見使用場景是對集合進行遍歷(尤其是在構建 IN 條件語句的時候)。

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

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

將數據庫中前三個數據的id修改為1,2,3;

需求:我們需要查詢 blog 表中 id 分別為1,2,3的博客信息

  1. 編寫接口

    List<Blog> queryBlogForeach(Map map);
  2. 編寫SQL語句

    <select id="queryBlogForeach" parameterType="map" resultType="blog">
    select * from blog
    <where>
    <!--
    collection:指定輸入對象中的集合屬性
    item:每次遍歷生成的對象
    open:開始遍歷時的拼接字符串
    close:結束時拼接的字符串
    separator:遍歷對象之間需要拼接的字符串
    select * from blog where 1=1 and (id=1 or id=2 or id=3)
    -->
    <foreach collection="ids" item="id" open="and (" close=")" separator="or">
    id=#{id}
    </foreach>
    </where>
    </select>
  3. 測試

    @Test
    public void testQueryBlogForeach(){
    SqlSession session = MybatisUtils.getSession();
    BlogMapper mapper = session.getMapper(BlogMapper.class);

    HashMap map = new HashMap();
    List<Integer> ids = new ArrayList<Integer>();
    ids.add(1);
    ids.add(2);
    ids.add(3);
    map.put("ids",ids);

    List<Blog> blogs = mapper.queryBlogForeach(map);

    System.out.println(blogs);

    session.close();
    }

小結

其實動態 sql 語句的編寫往往就是一個拼接的問題,為了保證拼接准確,我們最好首先要寫原生的 sql 語句出來,然后在通過 mybatis 動態sql 對照着改,防止出錯。多在實踐中使用才是熟練掌握它的技巧。

13 緩存

什么是緩存

  • 存在內存中的臨時數據。

  • 將用戶經常查詢的數據放在緩存(內存)中,用戶去查詢數據就不用從磁盤上(關系型數據庫查詢文件)查詢,從緩存中查詢,從而提高查詢效率,解決了高並發系統的性能問題。

為什么使用緩存

  • 減少和數據庫的交互次數,減少系統開銷,提高系統效率。

什么樣的數據能使用緩存

  • 經常查詢並且不經常改變的數據。【可以使用緩存】

Mybatis緩存

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

MyBatis系統中默認定義了兩級緩存:一級緩存和二級緩存

默認情況下,只有一級緩存開啟。(SqlSession級別的緩存,也稱為本地緩存)

二級緩存需要手動開啟和配置,他是基於namespace級別的緩存。

為了提高擴展性,MyBatis定義了緩存接口Cache。我們可以通過實現Cache接口來自定義二級緩存

一級緩存

一級緩存也叫本地緩存:

與數據庫同一次會話期間查詢到的數據會放在本地緩存中。

以后如果需要獲取相同的數據,直接從緩存中拿,沒必須再去查詢數據庫;

測試

  1. 在mybatis中加入日志,方便測試結果

  2. 編寫接口方法

//根據id查詢用戶
User queryUserById(@Param("id") int id);
  1. 接口對應的Mapper文件

<select id="queryUserById" resultType="user">
select * from user where id = #{id}
</select>
  1. 測試

@Test
public void testQueryUserById(){
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);

User user = mapper.queryUserById(1);
System.out.println(user);
User user2 = mapper.queryUserById(1);
System.out.println(user2);
System.out.println(user==user2);

session.close();
}
  1. 結果分析

img

一級緩存失效的四種情況

一級緩存是SqlSession級別的緩存,是一直開啟的,我們關閉不了它;

一級緩存失效情況:沒有使用到當前的一級緩存,效果就是,還需要再向數據庫中發起一次查詢請求!

  1. sqlSession不同

@Test
public void testQueryUserById(){
SqlSession session = MybatisUtils.getSession();
SqlSession session2 = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
UserMapper mapper2 = session2.getMapper(UserMapper.class);

User user = mapper.queryUserById(1);
System.out.println(user);
User user2 = mapper2.queryUserById(1);
System.out.println(user2);
System.out.println(user==user2);

session.close();
session2.close();
}

觀察結果:發現發送了兩條SQL語句!

結論:每個sqlSession中的緩存相互獨立

  1. sqlSession相同,查詢條件不同

@Test
public void testQueryUserById(){
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);
UserMapper mapper2 = session.getMapper(UserMapper.class);

User user = mapper.queryUserById(1);
System.out.println(user);
User user2 = mapper2.queryUserById(2);
System.out.println(user2);
System.out.println(user==user2);

session.close();
}

觀察結果:發現發送了兩條SQL語句!很正常的理解

結論:當前緩存中,不存在這個數據

  1. sqlSession相同,兩次查詢之間執行了增刪改操作!

增加方法

//修改用戶
int updateUser(Map map);

編寫SQL

<update id="updateUser" parameterType="map">
update user set name = #{name} where id = #{id}
</update>

測試

@Test
public void testQueryUserById(){
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);

User user = mapper.queryUserById(1);
System.out.println(user);

HashMap map = new HashMap();
map.put("name","kuangshen");
map.put("id",4);
mapper.updateUser(map);

User user2 = mapper.queryUserById(1);
System.out.println(user2);

System.out.println(user==user2);

session.close();
}

觀察結果:查詢在中間執行了增刪改操作后,重新執行了

結論:因為增刪改操作可能會對當前數據產生影響

4、sqlSession相同,手動清除一級緩存

@Test
public void testQueryUserById(){
SqlSession session = MybatisUtils.getSession();
UserMapper mapper = session.getMapper(UserMapper.class);

User user = mapper.queryUserById(1);
System.out.println(user);

session.clearCache();//手動清除緩存

User user2 = mapper.queryUserById(1);
System.out.println(user2);

System.out.println(user==user2);

session.close();
}

一級緩存就是一個map

二級緩存

二級緩存也叫全局緩存,一級緩存作用域太低了,所以誕生了二級緩存

基於namespace級別的緩存,一個名稱空間,對應一個二級緩存

工作機制

一個會話查詢一條數據,這個數據就會被放在當前會話的一級緩存中;

如果當前會話關閉了,這個會話對應的一級緩存就沒了;但是我們想要的是,會話關閉了,一級緩存中的數據被保存到二級緩存中;

新的會話查詢信息,就可以從二級緩存中獲取內容;

不同的mapper查出的數據會放在自己對應的緩存(map)中;

使用步驟

  1. 開啟全局緩存 【mybatis-config.xml】

<setting name="cacheEnabled" value="true"/>
  1. 去每個mapper.xml中配置使用二級緩存,這個配置非常簡單;【xxxMapper.xml】

<cache/>

官方示例=====>查看官方文檔
<cache
eviction="FIFO"
flushInterval="60000"
size="512"
readOnly="true"/>
這個更高級的配置創建了一個 FIFO 緩存,每隔 60 秒刷新,最多可以存儲結果對象或列表的 512 個引用,而且返回的對象被認為是只讀的,因此對它們進行修改可能會在不同線程中的調用者產生沖突。
  1. 代碼測試

所有的實體類先實現序列化接口

測試代碼

@Test
public void testQueryUserById(){
SqlSession session = MybatisUtils.getSession();
SqlSession session2 = MybatisUtils.getSession();

UserMapper mapper = session.getMapper(UserMapper.class);
UserMapper mapper2 = session2.getMapper(UserMapper.class);

User user = mapper.queryUserById(1);
System.out.println(user);
session.close();

User user2 = mapper2.queryUserById(1);
System.out.println(user2);
System.out.println(user==user2);

session2.close();
}

結論

只要開啟了二級緩存,我們在同一個Mapper中的查詢,可以在二級緩存中拿到數據

查出的數據都會被默認先放在一級緩存中

只有會話提交或者關閉以后,一級緩存中的數據才會轉到二級緩存中

緩存原理圖

img

自定義緩存-ehcache(可以了解)

Ehcache是一種廣泛使用的開源Java分布式緩存,主要面向通用緩存。

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

在mapper中指定使用我們的ehcache緩存實現!

目前:Redis數據庫來做緩存!K-V


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM