ssm第一講--Mybatis詳細學習

Mybatis學習

本文圖文學習教程
學自b站狂神:狂神說–Mybatis篇

1.簡介

1.1什麼是Mybatis

  • MyBatis 是一款優秀的持久層框架
  • 它支持自定義 SQL、存儲過程以及高級映射。
  • MyBatis 免除了幾乎所有的 JDBC 代碼以及設置參數和獲取結果集的工作。
  • MyBatis 可以通過簡單的 XML 或註解來配置和映射原始類型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 對象)爲數據庫中的記錄。
  • MyBatis 本是apache的一個開源項目iBatis,
  • 2010年這個項目由apache software foundation 遷移到了google code,並且改名爲MyBatis 。
  • 2013年11月遷移到Github。

如何獲取Mybais:

  • maven倉庫:
  <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis -->
  <dependency>
      <groupId>org.mybatis</groupId>
      <artifactId>mybatis</artifactId>
      <version>3.5.2</version>
  </dependency>
  • 中文文檔:https://mybatis.org/mybatis-3/zh/index.html

  • Github源碼:https://github.com/mybatis/mybatis-3/releases

1.2持久化

數據持久化:

  • 持久化就是將程序在持久狀態和瞬時狀態轉化的過程
  • 內存:斷電即失
  • 即將數據從原來不“穩定”的內存中轉移到“穩定”的數據庫的過程
  • 舉例:冷藏食物

爲什麼需要持久化:

  • 防止丟失重要信息
  • 內存太貴了,
  • 存放在數據庫,即用即取更好

1.3持久層

Dao層、Service層、Controller層…

  • 完成持久化工作的代碼塊
  • 層是界限十分明顯

1.4爲什麼需要Mybatis?

  • 幫助程序猿更加方便的將存入到數據庫中

  • 傳統的JDBC代碼太複雜了。

  • 框架更加簡化,更加自動化,易上手。

  • 使用人羣多。

  • 優點:

    • 簡單易學:本身就很小且簡單。沒有任何第三方依賴,最簡單安裝只要兩個jar文件+配置幾個sql映射文件易於學習,易於使用,通過文檔和源代碼,可以比較完全的掌握它的設計思路和實現。
    • 靈活:mybatis不會對應用程序或者數據庫的現有設計強加任何影響。 sql寫在xml裏,便於統一管理和優化。通過sql語句可以滿足操作數據庫的所有需求。
    • 解除sql與程序代碼的耦合:通過提供DAO層,將業務邏輯和數據訪問邏輯分離,使系統的設計更清晰,更易維護,更易單元測試。sql和代碼的分離,提高了可維護性。
    • 提供映射標籤,支持對象與數據庫的orm字段關係映射
    • 提供對象關係映射標籤,支持對象關係組建維護
    • 提供xml標籤,支持編寫動態sql。

2.第一個Mybatis程序

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

2.1搭建環境

搭建數據庫:

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 VALUES(1,"劉一手","123");

INSERT into user VALUES(2,"劉二手","123"),(3,"劉三手","123"),(4,"劉四手","123");

INSERT into user(id,name) VALUES(5,"劉五手");

新建項目

  1. 新建一個普通的maven項目,注:固定Maven路徑

  2. 刪除src目錄,將項目作爲父工程

  3. 導入maven依賴

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <!--父工程-->
    <groupId>com.lxf</groupId>
    <artifactId>20200604-Mybatis-study</artifactId>
    <version>1.0-SNAPSHOT</version>

    <!--導入依賴-->
    <dependencies>
        <!--mysql驅動-->
        <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.2</version>
        </dependency>
        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>
</project>
2.2創建一個子模塊
  • 編寫mybatis的核心配置文件

連接數據庫錯誤解決:https://blog.csdn.net/ITMan2017/article/details/100601438

<?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 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?userSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
                <property name="username" value="root"/>
                <property name="password" value="xxxx"/>
            </dataSource>
        </environment>
    </environments>
     <!--每一個Mapper.xml都需要在Mybatis核心配置文件中註冊!-->
     <!--namespace中的包名要和Dao/mapper接口的包名一致!-->
    <mappers>
        <mapper resource="com/lxf/dao/UserMapper.xml"></mapper>
    </mappers>
</configuration>
  • 編寫mybatis工具類
package com.lxf.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;

public class MybatisUtils {
    private static SqlSessionFactory sqlSessionFactory;

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

//    public static void main(String[] args) {
//        System.out.println(sqlSessionFactory);
//    }

    //既然有了SqlSessionFactory,就可以從中獲取SqlSession的實例了。
    //SqlSession  完全包含了面向數據庫執行SQL命令所需的所有方法。
    public static SqlSession getSqlSession() {
        return sqlSessionFactory.openSession();
    }

}
2.3編寫代碼
  • 實體類
package com.lxf.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;
    }

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

  • Dao接口
package com.lxf.dao;

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

import java.util.List;

public interface UserDao {
    //查詢所有的User
    List<User> getUserList();
    
    //查詢單獨一個User
    User getUser(int id);
}
  • 接口實現類(由原來的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.lxf.dao.UserDao">
    <!--select查詢語句-->
    <select id="getUserList" resultType="com.lxf.pojo.User">
        select * from mybatis.user;
  </select>
</mapper>
2.4測試

出錯點:

1.org.apache.ibatis.binding.BindingException:Type interface com.lxf.dao.UserDao is not konwn to the MapperRegistry

:每一個Mapper.xml沒有在Mybatis核心配置文件中註冊!

2.路徑正確但是Mapper.xml文件資源無法找到:因爲maven項目約定大於配置,資源只會在resource文件夾下搜索,pom.xml文件加上以下配置就可以解決錯誤:

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

測試類(Junit):

package com.lxf.dao;

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

public class UserDaoTest {
    @Test
    public void test(){


        SqlSession sqlSession =null;
        try{
            //1.獲取session
            sqlSession=MybatisUtils.getSqlSession();
            //2.執行SQL
            //方式一(推薦):
            UserDao userDao = sqlSession.getMapper(UserDao.class);
            System.out.println("userDao.getUserList() = " + userDao.getUserList());

            //方式二:
            User user = sqlSession.selectOne("getUser", 1);
            System.out.println("user = " + user);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //關閉sqlSession
            sqlSession.close();
        }
    }
}
2.5.增刪改查

1.修改UserMapper接口:

package com.lxf.dao;

import com.lxf.pojo.User;

import java.util.List;

public interface UserMapper {
    //方式一:
    //查詢所有的User,配置在mapper.xml文件中
    List<User> getUserList();

    //查詢單獨一個User,註解不需要配置xml文件
    User getUserById(int id);

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

    //增加多個用戶
    int addUsers(List<User> users);

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

    //刪除一個用戶
    int deleteUser(int id);

}

2.修改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">
<!--namespace=綁定一個對應的Dao/Mapper接口-->
<mapper namespace="com.lxf.dao.UserMapper">


    <!--select查詢語句,查詢所有的User-->
    <select id="getUserList" resultType="com.lxf.pojo.User">
        select * from mybatis.user;
  </select>



    <!--select查詢語句,查詢id對應的User-->
    <select id="getUserById" resultType="com.lxf.pojo.User" parameterType="int">
        select * from mybatis.user where id=#{id};
  </select>


    <!--insert插入語句,插入一個數據-->
    <insert id="addUser" parameterType="com.lxf.pojo.User">
        insert into mybatis.user values(#{id},#{name},#{pwd});
  </insert>


    <!--修改語句,修改一個數據-->
    <update id="updateUser" parameterType="com.lxf.pojo.User" >
        update User as u set u.pwd=#{pwd},u.name=#{name} where u.id=#{id};
    </update>


    <!--insert插入語句,插入多個數據-->
    <insert id="addUsers" parameterType="java.util.List" useGeneratedKeys="true">
        INSERT INTO User (id,name,pwd)
        VALUES
        <foreach collection="list"  separator="," item="item">
            (#{item.id,jdbcType=INTEGER},
            #{item.name,jdbcType=VARCHAR},
            #{item.pwd,jdbcType=VARCHAR})
        </foreach>
  </insert>

	<!--delete刪除語句,刪除指定用戶-->
    <delete id="deleteUser" parameterType="int">
        delete from User where id=#{id};
    </delete>
</mapper>

3.編寫測試類:

package com.lxf.dao;

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

import java.util.ArrayList;
import java.util.List;

public class UserMapperTest {
    @Test
    public void test(){
        SqlSession sqlSession =null;
        try{
            //1.獲取session
            sqlSession=MybatisUtils.getSqlSession();
            //2.執行SQL
            //方式一(推薦):
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            System.out.println("userDao.getUserList() = " + userMapper.getUserList());

            //方式二:
            //User user = sqlSession.selectOne("getUser", 1);
            //System.out.println("user = " + user);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            //關閉sqlSession
            sqlSession.close();
        }
    }
    @Test
    public void getUserById(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();

        //根據id查詢一個用戶
//        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//        User user = mapper.getUserById(1);
//        System.out.println("user = " + user);

        //增加一個用戶
//        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//       int i=mapper.addUser(new User(9,"劉德華","234"));
//          if(i>0){
//            System.out.println("插入多條數據成功");
//         }

        //增加多個用戶
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList=new ArrayList<User>();
        userList.add(new User(10,"張學友","zxy"));
        userList.add(new User(11,"周潤發","zrf"));
        userList.add(new User(12,"周星馳","zxc"));
        int i = mapper.addUsers(userList);
        if (i>0){
            System.out.println("插入多條數據成功");
        }

        //修改一個用戶
        //UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        //mapper.updateUser(new User(8,"劉八手","888"));

        //刪除指定的一個用戶
//        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
//        mapper.deleteUser(8);

        //提交事務
        sqlSession.commit();
        sqlSession.close();
    }
}

注意點:

  1. 增刪改需要提交事務

  2. 在增加多個數據時,url要加上:&allowMultiQueries=true

  3. mapper文件中如果錯用標籤會發生截然不同的事情,例如:在select標籤下sql用增加語句,會導致查詢後返回爲null,且不需要提交事務。原因:select標籤下也可以用其他的查詢語句,但select標籤默認返回值爲null,且不需要提交事務。

2.6萬能Map

如果我們的實體類含有大量字段,但是在插入、修改的時候不需要全部填寫,就可以用這種方法省時省力

UserMapper接口:

//增加一個用戶,萬能Map方式
    int addUser2(Map<String,Object> map);

UserMapper.xml配置文件

  <!--insert插入語句,萬能Map法-->
    <insert id="addUser2">
        insert into mybatis.user(id,name) values(#{userId},#{userName});
    </insert>

測試類:

 @Test
    public void addUser2(){
        //獲取session對象
        SqlSession sqlSession = MybatisUtils.getSqlSession();

        //獲取UserMapper對象
        UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

        Map<String,Object> map=new HashMap<String,Object>();
        map.put("userId",14);
        map.put("userName","王小法");

        //執行方法
        int  i=userMapper.addUser2(map);
        if(i>0){
            System.out.println("插入成功!");
        }
        sqlSession.commit();
        sqlSession.close();
    }
2.7.模糊查詢:

1.傳統挺配符:% %

List<User> usersLike = userMapper.getUsersLike("%周%");

2.在sql拼接中使用通配符

 select * from user where name like "%"#{value}"%";

3.注意

標籤中註釋的sql也會編譯,所以不用的sql就要刪了,註釋沒用,會報錯,一般就是參數數量不匹配。

3.配置解析

1.核心配置文件
properties(屬性)
settings(設置)
typeAliases(類型別名)
typeHandlers(類型處理器)(需要再瞭解)
objectFactory(對象工廠)(需要再瞭解)
plugins(插件)(需要再瞭解)
environments(環境配置)
environment(環境變量)
transactionManager(事務管理器)
dataSource(數據源)
databaseIdProvider(數據庫廠商標識)(需要再瞭解)
mappers(映射器)(需要再瞭解)
2.環境配置(environments)
<!--default:默認的環境,寫哪個id就對應哪個環境-->
<environments default="test">
    <environment id="development">
        <!--transactionManager:事務管理,
			type:[JDBC|MANAGED]
			JDBC:– 這個配置直接使用了 JDBC 的提交和回滾設施,它依賴從數據源獲得的連接來管理事務作用域(默認)。
			MANAGED:這個配置幾乎沒做什麼。它從不提交或回滾一個連接,而是讓容器來管理事務的整個生命週期(比如 JEE 應用服務器的上下文)。 默認情況下它會關閉連接。然而一些容器(例如EJB)並不希望連接被關閉,因此需要將 closeConnection 屬性設置爲 false 來阻止默認的關閉行爲。
		-->
        <transactionManager type="JDBC"/>
        <!--
 			type=[UNPOOLED|POOLED|JNDI]
			UNPOOLED:這個數據源的實現會每次請求時打開和關閉連接。雖然有點慢,但對那些數據庫連接可用性要求不高的簡單應用程序來說,是一個很好的選擇。 性能表現則依賴於使用的數據庫,對某些數據庫來說,使用連接池並不重要,這個配置就很適合這種情形。
			POOLED: 這種數據源的實現利用“池”的概念將 JDBC 連接對象組織起來,避免了創建新的連接實例時所必需的初始化和認證時間。 這種處理方式很流行,能使併發 Web 應用快速響應請求。
			JNDI – 這個數據源實現是爲了能在如 EJB 或應用服務器這類容器中使用,容器可以集中或在外部配置數據源,然後放置一個 JNDI 上下文的數據源引用。
		-->
        <dataSource type="POOLED">
            <property name="driver" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/mybatis?userSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;allowMultiQueries=true"/>
            <property name="username" value="xxx"/>
            <property name="password" value="xxx"/>
        </dataSource>
    </environment>
    
    
    <environment id="test">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/mybatis?userSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;allowMultiQueries=true"/>
            <property name="username" value="xxx"/>
            <property name="password" value="xxx"/>
        </dataSource>
    </environment>
</environments>
3.屬性(properties)

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

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

編寫配置文件:

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatis?userSSL=true&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
username=xxx
password=xxx

mybatis-config.xml文件配置:

 	<!--引入外部配置文件-->
    <properties resource="db.properties"/>
	<!-直接配置:可以增加,或者全部配置-->
	<!-若外部配置文件與這裏重複,優先外部-->
    <!--<properties resource="db.properties">
        <property name="driver" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?userSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8&amp;allowMultiQueries=true"/>
        <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="${driver}"/>
            <property name="url" value="${url}"/>
            <property name="username" value="${username}"/>
            <property name="password" value="${password}"/>
        </dataSource>
    </environment>
</environments>

常見錯誤:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-ltE7MlRl-1591540957775)(E:\typoraPic\image-20200605205440739.png)]

4.類型別名(typeAliases)
  • 類型別名可爲 Java 類型設置一個縮寫名字。
  • 它僅用於 XML 配置,意在降低冗餘的全限定類名書寫。

1.直接指定一個類,然後給他起別名(可以DIY別名)

Mybatis.xml文件:

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

UserMapper.xml:

 <!--select查詢語句,查詢id對應的User-->
    <select id="getUserById" resultType="User" parameterType="int">
        select * from mybatis.user where id=#{id};
  </select>

2.也可以指定一個包名,MyBatis 會在包名下面搜索需要的 Java Bean。

掃描實體類的包,它的默認別名就爲這個類的類名,首字母小寫。就例如掃描com.lxf.pojo,pojo下有兩個類:User、Student

那麼他們兩個的包路徑別名分別爲user、student(包名多的時候非常有用,默認是類名首字母小寫).

如要DIY類名:在這個包下的類上加上註解:@Alias(“xxx”)

Mybatis.xml文件:

 <!--可以給實體類起別名-->
    <typeAliases>
        <package name="com.lxf.pojo"/>
    </typeAliases>

UserMapper.xml:

 <!--select查詢語句,查詢id對應的User-->
    <select id="getUserById" resultType="User" parameterType="int">
        select * from mybatis.user where id=#{id};
  </select>
5.設置

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-p8MepAdB-1591540957790)(E:\typoraPic\image-20200605212847043.png)]

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-78KrQ6ws-1591540957795)(E:\typoraPic\image-20200605212907807.png)]

6.映射器

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

方式一【推薦使用】:

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

方式二:使用class文件綁定註冊

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

注意點:

  • 接口和他的Mapper配置文件必須同名!

  • 接口和他的Mapper配置文件必須在同一個包下!

方式三:使用掃描包進行注入

  <!--每一個Mapper.xml都需要在Mybatis核心配置文件中註冊!-->
    <mappers>
       <package name="com.lxf.dao"/>
    </mappers>

注意點:

  • 接口和他的Mapper配置文件必須同名!

  • 接口和他的Mapper配置文件必須在同一個包下!

7.生命週期與作用域

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-fnkqZ6si-1591540957804)(E:\typoraPic\image-20200605221153519.png)]

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

SqlSessionFactory

  • 一旦創建了SqlSessionFactory,就不再需要它了
  • 局部變量

SqlSessionFactory:

  • SqlSessionFactory 一旦被創建就應該在應用的運行期間一直存在,沒有任何理由丟棄它或重新創建另一個實例
  • 使用 SqlSessionFactory 的最佳實踐是在應用運行期間不要重複創建多次,多次重建 SqlSessionFactory 被視爲一種代碼“壞習慣”。
  • 因此 SqlSessionFactory 的最佳作用域是應用作用域。 有很多方法可以做到,最簡單的就是使用單例模式或者靜態單例模式
  • 類似於全局變量

SqlSession

  • 每個線程都應該有它自己的 SqlSession 實例(連接到連接池的一個請求!)。
  • SqlSession 的實例不是線程安全的,因此是不能被共享的,所以它的最佳的作用域是請求或方法作用域。
  • 絕對不能將 SqlSession 實例的引用放在一個類的靜態域,甚至一個類的實例變量也不行。 也絕不能將 SqlSession 實例的引用放在任何類型的託管作用域中,比如 Servlet 框架中的 HttpSession。
  • 用完即關閉,類似於局部變量

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-pShWq3iR-1591540957814)(E:\typoraPic\image-20200605221556514.png)]

4.解決屬性名與字段名不一致的問題

數據庫的字段:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-QN7zXPg6-1591540957821)(E:\typoraPic\image-20200605221915336.png)]

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

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-QIKx0q8U-1591540957826)(E:\typoraPic\image-20200605224905546.png)]

測試問題:查出來的字段password爲null(數據庫不爲null)

 select * from mybatis.user where id=#{id};
 類型處理器
 select id,name,pwd from mybatis.user where id=#{id};

解決方法:

  1. 起別名

    select id,name,pwd as password from mybatis.user where id=#{id};
    
  2. resultMap

結果集映射

resultMap 元素是 MyBatis 中最重要最強大的元素。它可以讓你從 90% 的 JDBC ResultSets 數據提取代碼中解放出來,並在一些情形下允許你進行一些 JDBC 不支持的操作。實際上,在爲一些比如連接的複雜語句編寫映射代碼的時候,一份 resultMap 能夠代替實現同等功能的數千行代碼。ResultMap 的設計思想是,對簡單的語句做到零配置,對於複雜一點的語句,只需要描述語句之間的關係就行了。

id name pwd
id name password

UserMapper.xml文件配置:

 <!--select查詢語句,查詢id對應的User-->
    <select id="getUserById" resultMap="UserMap" parameterType="int">
       select *  from mybatis.user where id=#{id};
  </select>
    <!--結果集映射-->
    <resultMap id="UserMap" type="user">
        <!--column:數據庫中的字段,property:實體類中的屬性-->
        <result column="id" property="id"/>
        <result column="name" property="name"/>
        <result column="pwd" property="password"/>
    </resultMap>

5.日誌

5.1日誌工廠

  • 如果一個數據庫操作,出現了異常,我們需要排錯。

  • 日誌可以打印清晰的日誌信息,方便我們查詢錯誤信息、sql信息、步驟信息等

  • 曾經:sout、debug。現在:日誌

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-zTbtGUv0-1591540957832)(E:\typoraPic\image-20200605212907807.png)]

  • SLF4J
  • LOG4J 【掌握】
  • LOG4J2
  • JDK_LOGGING
  • COMMONS_LOGGING
  • STDOUT_LOGGING 【掌握】
  • NO_LOGGING

在Mybatis具體使用哪一日誌,在設置中設定!

1.STDOUT_LOGGING 標準日誌輸出Mapper.xml配置:
<settings>
        <setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>

注意:logImpl大小寫要注意,名字和值都不能多出空格

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-eIWdlq8g-1591540957836)(E:\typoraPic\image-20200605234745991.png)]

2.log4j
  • Log4j是Apache的一個開源項目,通過使用Log4j,我們可以控制日誌信息輸送的目的地是控制檯、文件、GUI組件,
  • 我們也可以控制每一條日誌的輸出格式
  • 通過定義每一條日誌信息的級別,我們能夠更加細緻地控制日誌的生成過程。
  • 最令人感興趣的就是,這些可以通過一個配置文件來靈活地進行配置,而不需要修改應用的代碼。

(1)、先導入log4j的包

<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

(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/lxf.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

3.配置log4j爲日誌的實現

 <!--設置-->
    <settings>
        <setting name="logImpl" value="LOG4J"/>
    </settings>

4.log4j的使用

"C:\Program Files\Java\jdk1.8.0_161\bin\java.exe" -ea -Didea.test.cyclic.buffer.size=1048576 "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.4\lib\idea_rt.jar=65112:C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.4\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.4\lib\idea_rt.jar;C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.4\plugins\junit\lib\junit5-rt.jar;C:\Program Files\JetBrains\IntelliJ IDEA 2019.3.4\plugins\junit\lib\junit-rt.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_161\jre\lib\rt.jar;C:\Users\dell\IdeaProjects\20200604-Mybatis-study\mybatis-03\target\test-classes;C:\Users\dell\IdeaProjects\20200604-Mybatis-study\mybatis-03\target\classes;E:\maven\repository\log4j\log4j\1.2.17\log4j-1.2.17.jar;E:\maven\repository\mysql\mysql-connector-java\5.1.47\mysql-connector-java-5.1.47.jar;E:\maven\repository\org\mybatis\mybatis\3.5.2\mybatis-3.5.2.jar;E:\maven\repository\junit\junit\4.12\junit-4.12.jar;E:\maven\repository\org\hamcrest\hamcrest-core\1.3\hamcrest-core-1.3.jar" com.intellij.rt.junit.JUnitStarter -ideVersion5 -junit4 com.lxf.dao.UserMapperTest,test
[org.apache.ibatis.logging.LogFactory]-Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
[org.apache.ibatis.logging.LogFactory]-Logging initialized using 'class org.apache.ibatis.logging.log4j.Log4jImpl' adapter.
[org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
[org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
[org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
[org.apache.ibatis.datasource.pooled.PooledDataSource]-PooledDataSource forcefully closed/removed all connections.
[org.apache.ibatis.io.VFS]-Class not found: org.jboss.vfs.VFS
[org.apache.ibatis.io.JBoss6VFS]-JBoss 6 VFS API is not available in this environment.
[org.apache.ibatis.io.VFS]-Class not found: org.jboss.vfs.VirtualFile
[org.apache.ibatis.io.VFS]-VFS implementation org.apache.ibatis.io.JBoss6VFS is not valid in this environment.
[org.apache.ibatis.io.VFS]-Using VFS adapter org.apache.ibatis.io.DefaultVFS
[org.apache.ibatis.io.DefaultVFS]-Find JAR URL: file:/C:/Users/dell/IdeaProjects/20200604-Mybatis-study/mybatis-03/target/test-classes/com/lxf/dao
[org.apache.ibatis.io.DefaultVFS]-Not a JAR: file:/C:/Users/dell/IdeaProjects/20200604-Mybatis-study/mybatis-03/target/test-classes/com/lxf/dao
[org.apache.ibatis.io.DefaultVFS]-Reader entry: UserMapperTest.class
[org.apache.ibatis.io.DefaultVFS]-Listing file:/C:/Users/dell/IdeaProjects/20200604-Mybatis-study/mybatis-03/target/test-classes/com/lxf/dao
[org.apache.ibatis.io.DefaultVFS]-Find JAR URL: file:/C:/Users/dell/IdeaProjects/20200604-Mybatis-study/mybatis-03/target/test-classes/com/lxf/dao/UserMapperTest.class
[org.apache.ibatis.io.DefaultVFS]-Not a JAR: file:/C:/Users/dell/IdeaProjects/20200604-Mybatis-study/mybatis-03/target/test-classes/com/lxf/dao/UserMapperTest.class
[org.apache.ibatis.io.DefaultVFS]-Reader entry: ����   4 W
[org.apache.ibatis.io.DefaultVFS]-Find JAR URL: file:/C:/Users/dell/IdeaProjects/20200604-Mybatis-study/mybatis-03/target/classes/com/lxf/dao
[org.apache.ibatis.io.DefaultVFS]-Not a JAR: file:/C:/Users/dell/IdeaProjects/20200604-Mybatis-study/mybatis-03/target/classes/com/lxf/dao
[org.apache.ibatis.io.DefaultVFS]-Reader entry: UserMapper.class
[org.apache.ibatis.io.DefaultVFS]-Reader entry: UserMapper.xml
[org.apache.ibatis.io.DefaultVFS]-Listing file:/C:/Users/dell/IdeaProjects/20200604-Mybatis-study/mybatis-03/target/classes/com/lxf/dao
[org.apache.ibatis.io.DefaultVFS]-Find JAR URL: file:/C:/Users/dell/IdeaProjects/20200604-Mybatis-study/mybatis-03/target/classes/com/lxf/dao/UserMapper.class
[org.apache.ibatis.io.DefaultVFS]-Not a JAR: file:/C:/Users/dell/IdeaProjects/20200604-Mybatis-study/mybatis-03/target/classes/com/lxf/dao/UserMapper.class
[org.apache.ibatis.io.DefaultVFS]-Reader entry: ����   4 	  getUserById (I)Lcom/lxf/pojo/User; 
[org.apache.ibatis.io.DefaultVFS]-Find JAR URL: file:/C:/Users/dell/IdeaProjects/20200604-Mybatis-study/mybatis-03/target/classes/com/lxf/dao/UserMapper.xml
[org.apache.ibatis.io.DefaultVFS]-Not a JAR: file:/C:/Users/dell/IdeaProjects/20200604-Mybatis-study/mybatis-03/target/classes/com/lxf/dao/UserMapper.xml
[org.apache.ibatis.io.DefaultVFS]-Reader entry: <?xml version="1.0" encoding="UTF-8" ?>
[org.apache.ibatis.io.ResolverUtil]-Checking to see if class com.lxf.dao.UserMapperTest matches criteria [is assignable to Object]
[org.apache.ibatis.io.ResolverUtil]-Checking to see if class com.lxf.dao.UserMapper matches criteria [is assignable to Object]
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Opening JDBC Connection
[org.apache.ibatis.datasource.pooled.PooledDataSource]-Created connection 1095293768.
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4148db48]
[com.lxf.dao.UserMapper.getUserById]-==>  Preparing: select * from mybatis.user where id=?; 
[com.lxf.dao.UserMapper.getUserById]-==> Parameters: 1(Integer)
[com.lxf.dao.UserMapper.getUserById]-<==      Total: 1
user = User{id=1, name='劉一手', password='123'}
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Resetting autocommit to true on JDBC Connection [com.mysql.jdbc.JDBC4Connection@4148db48]
[org.apache.ibatis.transaction.jdbc.JdbcTransaction]-Closing JDBC Connection [com.mysql.jdbc.JDBC4Connection@4148db48]
[org.apache.ibatis.datasource.pooled.PooledDataSource]-Returned connection 1095293768 to pool.

Process finished with exit code 0

4.簡單使用

  • 在要使用的類中,導入import org.apache.log4j.Logger;
  • new一個logger對象,開始測試
 private static Logger logger=Logger.getLogger(UserMapperTest.class);
    @Test
    public void logTest(){
        logger.info("成功進入");
        logger.debug("debug信息");
        logger.error("錯誤信息");
    }
  • 結果
[com.lxf.dao.UserMapperTest]-成功進入
[com.lxf.dao.UserMapperTest]-debug信息
[com.lxf.dao.UserMapperTest]-錯誤信息

6.分頁

爲什麼分頁:減少數據的處理量

6.1使用Limit分頁

select * from user limit startIndex,pageSize;
select * from user limit 0,2;

使用Mybatis實現分頁,核心SQL

  1. 接口
//分頁查詢
    List<User> getUserBylimit(Map<String,Integer> map);
  1. Mapper.xml
<!-- 分頁查詢-->
<select id="getUserBylimit" resultMap="userMap" parameterType="map">
    select * from user limit #{startIndex},#{pageSize};
</select>
  1. 測試
 @Test
    public void getUserByLimit(){
        //獲取SqlSession對象
        SqlSession session = MybatisUtil.getSession();

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

        Map<String,Integer> map=new HashMap<String,Integer>();
        //給map添加鍵值對
        map.put("startIndex",1);
        map.put("pageSize",2);
        //執行方法
        List<User> userBylimit = mapper.getUserBylimit(map);
        for (User user : userBylimit) {
            System.out.println("user = " + user);
        }
        //關閉session
        session.close();

    }

6.2RowBounds分頁

UserMapper.java接口:

  //分頁查詢2
    List<User> getUserByRoundBounds();

UserMapper.xml配置文件:

 <!-- 分頁查詢2-->
    <select id="getUserByRoundBounds" resultMap="userMap">
        select * from user;
    </select>

測試類:

  @Test
    public void getUserByRowBounds(){
        //獲取SqlSession對象
        SqlSession session = MybatisUtil.getSession();

        //RowBounds實現
        RowBounds rowBounds = new RowBounds(1, 3);

        List<User> users = session.selectList("com.lxf.dao.UserMapper.getUserByRoundBounds", null, rowBounds);

        for (User user : users) {
            System.out.println("user = " + user);
        }
        //關閉session
        session.close();
    }

6.3分頁插件(瞭解即可)

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-jPiD8C7r-1591540957842)(E:\typoraPic\image-20200606111010360.png)]

7.使用註解開發

7.1運行過程
  1. 註解在接口上實現
    @Select(value = "select * from user")
    List<User> getUsers();
  1. 需要在覈心配置文件中綁定
 <!--綁定接口-->
    <mappers>
        <mapper class="com.lxf.dao.UserMapper"/>
    </mappers>

3.測試(和之前的Mapper方法一樣)

運行過程拓展:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-OWLr6iZN-1591540957846)(E:\typoraPic\1.jpg)]

7.2增刪改查

1.UserMapper.java接口類

package com.lxf.dao;


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

import java.util.List;

public interface UserMapper {
    @Select(value = "select * from user")
    List<User> getUsers();

    //方法存在多個參數,必須每個參數前面必須加上@Param註解
    @Select(value = "select * from user where id =#{id}")
    User getUserById(int id2);

    @Insert("insert into user(id,name,pwd) values(#{id},#{name},#{password})")
    int addUser(User user);

    @Update("update user set id=#{id},name=#{name},pwd=#{password} where id=#{id}")
    int updateUser(User user);

    @Delete("delete from user where id=#{id2}")
    int deleteUser(@Param("id2") int id);
}

2.測試類

import com.lxf.dao.UserMapper;
import com.lxf.pojo.User;
import com.lxf.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;

import java.util.List;

public class UserMapperTest {

    //查詢一個數據
    @Test
    public void test(){
        SqlSession session = MybatisUtil.getSession();

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

//        List<User> users = mapper.getUsers();
//
//        for (User user : users) {
//            System.out.println("user = " + user);
//        }
        User user = mapper.getUserById(1);
        System.out.println("user = " + user);
        session.close();
    }
    //增加一個數據
    @Test
    public void test2(){
        SqlSession session = MybatisUtil.getSession();

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

        int i = mapper.addUser(new User(15,"成龍","cl"));
        if(i>0){
            System.out.println("插入成功!");
        }
        session.close();
    }
    //更新一個數據
    @Test
    public void test3(){
        SqlSession session = MybatisUtil.getSession();

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

        int i = mapper.updateUser(new User(14,"劉小法","lxf"));
        if(i>0){
            System.out.println("更新成功!");
        }
        session.close();
    }


    //刪除一個用戶
    @Test
    public void test4(){
        SqlSession session = MybatisUtil.getSession();

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

        int i = mapper.deleteUser(5);
        if(i>0){
            System.out.println("刪除成功!");
        }
        session.close();
    }

}

3.注意:

  • 我已經在工具類設置自動提交,所以增刪改不需要提交事務
  //返回selsession,並設置自動提交爲true
  return sqlSessionFactory.openSession(true);
  • 在使用註解開發時,方法存在多個參數,必須每個參數前面必須加上@Param註解,而且如果Param指定名字,sql語句中的也要對應
@Delete("delete from user where id=#{id2}")
    int deleteUser(@Param("id2") int id);
  • 接口類也要註冊到mybatis-config.xml文件中
 <!--綁定接口-->
    <mappers>
        <mapper class="com.lxf.dao.UserMapper"/>
    </mappers>

8.多對一處理

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-PH23suqQ-1591540957852)(E:\typoraPic\image-20200606170128390.png)]

  • 對學生而言,關聯,多個學生關聯一個老師【多對一】
  • 對老師而言,集合,一個老師教多個學生【一對多】

SQl數據庫建立對應的表:

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

INSERT INTO teacher(id,name) VALUES(1,'劉老師');

create table `student`(
	`id` INT(10) not null PRIMARY KEY,
	`name` VARCHAR(30) DEFAULT null,
	`tid` INT(10) DEFAULT null,
	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. 導入lombok
  2. 新建實體類Teacher、Student
  3. 建立Mapper接口
  4. 建立Mapper.xml文件
  5. 在覈心配置文件中綁定註冊我們的Mapper接口或者文件!
  6. 測試查詢是否成功!
8.1按照查詢嵌套處理

Student類:

package com.lxf.pojo;

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

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private int id;
    private String name;

    //學生需要關聯一個老師!
    private Teacher teacher;
}

Teacher類

package com.lxf.pojo;

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

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
    private int id;
    private String name;
}

StudentMapper.java接口

package com.lxf.dao;

import com.lxf.pojo.Student;

import java.util.List;

public interface StudentMapper {
    //查詢所有的學生信息,以及對應的老師的信息!
    List<Student> getStudents();
}

StudentMapper.xml文件:

<?xml version="1.0" encoding="UTF-8" ?>
        <!DOCTYPE mapper
                PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
                "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
        <!--configuration核心配置文件-->
<mapper namespace="com.lxf.dao.StudentMapper">
        <select id="getStudents" resultMap="studentMap" >
                select * from student
        </select>

        <resultMap id="studentMap" type="Student">
                <association property="teacher" column="tid" javaType="Teacher" select="getTeacher"/>
        </resultMap>

        <select id="getTeacher" resultType="com.lxf.pojo.Teacher">
                select  * from teacher where id=#{id};
        </select>
</mapper>

測試類:

   @Test
    public  void getStudents(){
        SqlSession sqlSession = MybatisUtil.getSession();

        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);

        List<Student> students = studentMapper.getStudents();
        for (Student student: students) {
            System.out.println("student = " + student);
        }

        sqlSession.close();
    }
8.2按照結果嵌套處理

StudentMapper.java接口類:

//查詢所有的學生信息,以及對應的老師的信息!
    List<Student> getStudents2();

StudentMapper.xml配置文件:

 <!--方式二:按結果嵌套處理-->
        <select id="getStudents2" resultMap="studentMap2">
                select s.id sid,s.name sname,t.name tname from student s,teacher t where s.tid=t.id;
        </select>

        <resultMap id="studentMap2" type="Student">
                <result property="id" column="sid"/>
                <result property="name" column="sname"/>
                <association property="teacher" javaType="Teacher" >
                    	<result property="id" column="sid"/>
                        <result property="name" column="tname"/>
                </association>
        </resultMap>

測試類:

 @Test
    public  void getStudents2(){
        SqlSession sqlSession = MybatisUtil.getSession();

        StudentMapper studentMapper = sqlSession.getMapper(StudentMapper.class);

        List<Student> students = studentMapper.getStudents2();
        for (Student student: students) {
            System.out.println("student = " + student);
        }

        sqlSession.close();
    }

9.一對多處理

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

1.環境搭建,和第8點一樣

Student實體類:

package com.lxf.pojo;

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

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Student {
    private int id;
    private String name;

    //學生需要關聯一個老師!
    private int tid;
}

Teacher實體類:

package com.lxf.pojo;

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

import java.util.List;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
    private int id;
    private String name;

    //一個學生擁有多個學生
    private List<Student> students;
}
9.1按照查詢嵌套查詢

TeacherMapper.java接口類:

//獲取這個老師及其所有的學生
    Teacher getTeachers2(@Param("tid") int id);

TeacherMapper.java配置文件:

 <!--方式一:按查詢嵌套處理-->
        <select id="getTeachers2" resultMap="teacherMap2" >
                select * from mybatis.teacher where id=#{tid};
        </select>
        <resultMap id="teacherMap2" type="com.lxf.pojo.Teacher">
                <result column="id" property="id"/>
                <result column="name" property="name"/>
                <collection property="students" column="id" javaType="ArrayList" ofType="com.lxf.pojo.Student" select="getStudentByTeacherId"/>
        </resultMap>

        <select id="getStudentByTeacherId" resultType="com.lxf.pojo.Student">
                select * from mybatis.student where tid=#{tid};
        </select>

測試類:

 @Test
    public void getTeachers2(){
        SqlSession sqlSession = MybatisUtil.getSession();
        TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
        Teacher teacher = mapper.getTeachers2(1);
        System.out.println("teacher = " + teacher);
        sqlSession.close();
    }
9.2按照結果嵌套查詢

TeacherMapper.java接口類:

  //獲取這個老師及其所有的學生
    Teacher getTeachers3(@Param("tid") int id);

TeacherMapper.java配置文件:

 <!--方式二:按結果嵌套處理-->
        <select id="getTeachers3" resultMap="teacherMap3">
                select t.id tid,t.name tname,s.id sid,s.name sname,s.tid stid from teacher t,student s where t.id=#{tid} and s.tid=#{tid};
        </select>
        <resultMap id="teacherMap3" type="com.lxf.pojo.Teacher">
                <result property="id" column="tid"/>
                <result property="name" column="tname"/>
                <collection property="students" ofType="com.lxf.pojo.Student">
                        <result property="id" column="sid"/>
                        <result property="name" column="sname"/>
                        <result property="tid" column="stid"/>
                </collection>
        </resultMap>

測試類:

   @Test
    public void getTeachers3(){
        SqlSession sqlSession = MybatisUtil.getSession();
        TeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);
        Teacher teacher = mapper.getTeachers3(1);
        System.out.println("teacher = " + teacher);
        sqlSession.close();
    }
小結:
  1. 關聯-association【多對一】

  2. 集合-collection【一對多】

  3. javaType & ofType

    1. javaType 用來指定實體類中屬性的類型
    2. ofType用來指定映射到List或者集合中的pojo類型,泛型中的約束類型

注意點:

  • 保證sql的可讀性
  • 注意一對多和多對一中,屬性名和字段的問題!
  • 如果問題不好排查錯誤,可以使用日誌,建議使用Log4j

10.動態SQL

什麼是動態sql:動態SQL就是根據不同的條件生成不同的SQL語句

如果你使用過 JDBC 或其它類似的框架,你應該能理解根據不同條件拼接 SQL 語句有多痛苦,例如拼接時要確保不能忘記添加必要的空格,還要注意去掉列表最後一個列名的逗號。利用動態 SQL,可以徹底擺脫這種痛苦。

如果你之前用過 JSTL 或任何基於類 XML 語言的文本處理器,你對動態 SQL 元素可能會感覺似曾相識。在 MyBatis 之前的版本中,需要花時間瞭解大量的元素。藉助功能強大的基於 OGNL 的表達式,MyBatis 3 替換了之前的大部分元素,大大精簡了元素種類,現在要學習的元素種類比原來的一半還要少。

  • if
  • choose (when, otherwise)
  • trim (where, set)
  • foreach
10.1搭建環境

sql語句:

create table `blog`(
		`id` VARCHAR(50) NOT NULL COMMENT '博客id',
		`title` VARCHAR(100) NOT NULL COMMENT '博客標題',
		`author` VARCHAR(30) NOT NULL COMMENT '博客作者',
		`create_time` VARCHAR(30) NOT NULL COMMENT '創建時間',
		`views` INT(30) not null COMMENT '瀏覽量'
)ENGINE=INNODB DEFAULT CHARSET=utf8

創建一個基礎工程

  1. 導包
  2. 編寫配置文件
  3. 編寫實體類
  4. 編寫實體類對應的Mapper接口和Mapper.XML文件
10.2增加數據

BlogMapper.java接口文件:

  //插入數據
    int addBlog(Blog blog);

BlogMapper.xml配置文件:

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

測試類

  @Test
    public void addBlog(){
        SqlSession sqlSession = MybatisUtil.getSession();

        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
        int i = mapper.addBlog(new Blog(IDUtils.getID(), "Mybatis學習方法", "劉一手", new SimpleDateFormat("yyyy-MM-dd HH-mm-ss").format(new Date()), 1060));
        int j = mapper.addBlog(new Blog(IDUtils.getID(), "SpringMVC學習方法", "劉二手", new SimpleDateFormat("yyyy-MM-dd HH-mm-ss").format(new Date()), 530));
        int k = mapper.addBlog(new Blog(IDUtils.getID(), "Spring5學習方法", "劉三手", new SimpleDateFormat("yyyy-MM-dd HH-mm-ss").format(new Date()), 777));
        int l = mapper.addBlog(new Blog(IDUtils.getID(), "SSM學習方法", "劉四手", new SimpleDateFormat("yyyy-MM-dd HH-mm-ss").format(new Date()), 666));
        if(i>0&&j>0&&k>0&&l>0){
            System.out.println("成功插入四條數據!");
        }
        sqlSession.close();
    }
10.3 IF查詢

BlogMapper.java接口類

 //查詢博客
    List<Blog> queryBlogIF(Map map);

BlogMapper.xml配置文件:

<select id="queryBlogIF" parameterType="map" resultType="com.lxf.pojo.Blog">
            select * from  mybatis.blog where 1=1
            <if test="title !=null">
                and title=#{title}
            </if>
            <if test="author !=null">
                or author=#{author}
            </if>
        </select>

測試類:

	@Test
    public void queryBlogIF(){
        SqlSession sqlSession = MybatisUtil.getSession();

        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

        HashMap map=new HashMap();
        map.put("title","Mybatis學習方法");
        map.put("author","劉四手");

        List<Blog> blogs = mapper.queryBlogIF(map);
        for (Blog blog : blogs) {
            System.out.println("blog = " + blog);
        }
        sqlSession.close();
    }
10.4choose(when、otherwise)查詢

有時候,我們不想使用所有的條件,而只是想從多個條件中選擇一個使用。針對這種情況,MyBatis 提供了 choose 元素,它有點像 Java 中的 switch 語句。

還是上面的例子,但是策略變爲:傳入了 “title” 就按 “title” 查找,傳入了 “author” 就按 “author” 查找的情形。若兩者都沒有傳入,就返回views大於多少的 BLOG(這可能是管理員認爲,與其返回大量的無意義隨機 Blog,還不如返回一些由管理員挑選的 Blog)。

BlogMapper.java接口類:

 //查詢博客
    List<Blog> queryBlogChoose(Map map);

BlogMapper.xml配置文件:

<select id="queryBlogChoose" parameterType="map" resultType="com.lxf.pojo.Blog">
    select * from mybatis.blog
     <!--where在10.5中有示例-->
    <where>
        <choose>
            <when test="title !=null">
                title=#{title}
            </when>
            <when test="author !=null">
                author=#{author}
            </when>
            <otherwise>
                views > #{views}
            </otherwise>
        </choose>
    </where>
</select>

測試類:

 @Test
    public void queryBlogChoose(){
        SqlSession sqlSession = MybatisUtil.getSession();

        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

        HashMap map=new HashMap();
        map.put("title","Mybatis學習方法");
        map.put("author","劉四手");
        map.put("views",666);

        List<Blog> blogs = mapper.queryBlogChoose(map);
        for (Blog blog : blogs) {
            System.out.println("blog = " + blog);
        }
        sqlSession.close();
    }
10.5trim、where、set標籤
1.where標籤

思考:如果沒有where 1=1這個條件該怎麼查?以下有解決方法:

<select id="queryBlogIF" parameterType="map" resultType="com.lxf.pojo.Blog">
            select * from  mybatis.blog where
            <if test="title !=null">
                 title=#{title}
            </if>
            <if test="author !=null">
                or author=#{author}
            </if>
        </select>

如果沒有匹配的條件會怎麼樣?最終這條 SQL 會變成這樣:

SELECT * FROM BLOG
WHERE

這會導致查詢失敗。如果匹配的只是第二個條件又會怎樣?這條 SQL 會是這樣:

SELECT * FROM BLOG
WHERE
or author=#{author}

這個查詢也會失敗。這個問題不能簡單地用條件元素來解決。這個問題是如此的難以解決,以至於解決過的人不會再想碰到這種問題。

MyBatis 有一個簡單且適合大多數場景的解決辦法。而在其他場景中,可以對其進行自定義以符合需求。而這,只需要一處簡單的改動:

 <select id="queryBlogIF" parameterType="map" resultType="com.lxf.pojo.Blog">
            select * from  mybatis.blog
            <where>
                <if test="title !=null">
                    title=#{title}
                </if>
                <if test="author !=null">
                    or author=#{author}
                </if>
            </where>
</select>

where 元素只會在子元素返回任何內容的情況下才插入 “WHERE” 子句。而且,若子句的開頭爲 “AND” 或 “OR”,where 元素也會將它們去除。

2.set標籤

用於動態更新語句的類似解決方案叫做 setset 元素可以用於動態包含需要更新的列,忽略其它不更新的列,它會檢測,是否多餘,多餘則會自動去掉,比如下面最後views參數沒傳遞就會造成,多餘。

BlogMapper.java接口類:

//更新數據
    int updateBlogSet(Map map);

BlogMapper.xml配置文件:

 <!--update set標籤-->
    <update id="updateBlogSet" parameterType="map" >
        update blog
        <set>
            <if test="title !=null">
                title=#{title},
            </if>
            <if test="author !=null">
                author=#{author},
            </if>
            <if test="views !=null">
                views=#{views}
            </if>
        </set>
        where id=#{id}
    </update>

測試類:

 @Test
    public void updateBlogSet(){
        SqlSession sqlSession = MybatisUtil.getSession();

        BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

        HashMap map=new HashMap();
        map.put("title","Mybatis學習簡便方法");
        map.put("author","劉五手");
        //map.put("views",690);
        map.put("id","72bae714f52f4cf196e194114cb35b1f");
        int i = mapper.updateBlogSet(map);
        if(i>0){
            System.out.println("更新數據成功");
        }
        sqlSession.close();
    }
3.trim

where 元素只會在子元素返回任何內容的情況下才插入 “WHERE” 子句。而且,若子句的開頭爲 “AND” 或 “OR”,where 元素也會將它們去除。

如果 where 元素與你期望的不太一樣,你也可以通過自定義 trim 元素來定製 where 元素的功能。比如,和 where 元素等價的自定義 trim 元素爲:

<select id="queryBlogIF" parameterType="map" resultType="com.lxf.pojo.Blog">
            select * from  mybatis.blog
            <trim prefix="WHERE" prefixOverrides="AND |OR ">
                <if test="title !=null">
                    title=#{title}
                </if>
                <if test="author !=null">
                    or author=#{author}
                </if>
            </trim>
</select>

prefixOverrides 屬性會忽略通過管道符分隔的文本序列(注意此例中的空格是必要的)。上述例子會移除所有 prefixOverrides 屬性中指定的內容,並且插入 prefix 屬性中指定的內容。

set 元素會動態地在行首插入 SET 關鍵字,並會刪掉額外的逗號(這些逗號是在使用條件語句給列賦值時引入的)。

來看看與 set 元素等價的自定義 trim 元素吧:

<!--update set標籤-->
    <update id="updateBlogSet" parameterType="map" >
        update blog
        <trim prefix="SET" suffixOverrides=",">
            <if test="title !=null">
                title=#{title},
            </if>
            <if test="author !=null">
                author=#{author},
            </if>
            <if test="views !=null">
                views=#{views}
            </if>
        </trim>
        where id=#{id}
    </update>

注意,我們覆蓋了後綴值設置,並且自定義了前綴值。一般上面兩種用where和set就可以了,如果mybatis沒有的建議再用trim

10.6foreach

舉例(查詢一些指定id的Blog):

BlogMapper.java接口類:

//查詢第多個記錄的博客
    List<Blog> queryBlogForeach(Map map);

BlogMapper.xml配置文件:

<!--foreach標籤-->
    <select id="queryBlogForeach" parameterType="map" resultType="com.lxf.pojo.Blog">
        select * from blog
        <where>
            /*
                collection:要循環的list名字
                open:開始語句
                separator:每個循環後添加的分隔符(除最後一次)
                close:結束語句
                item:list中每個數據
                index:list的下標
             */
            <foreach collection="ids" separator="," open="id in (" close=")"  item="item">
                #{item}
            </foreach>
        </where>
    </select>

測試類:

@Test
public void queryBlogForeach(){
    SqlSession sqlSession = MybatisUtil.getSession();

    BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);

    HashMap map=new HashMap();
    List list=new ArrayList();
    list.add("4");
    list.add("3");
    list.add("1");
    map.put("ids",list);
    List<Blog> blogs = mapper.queryBlogForeach(map);
    for (Blog blog : blogs) {
        System.out.println("blog = " + blog);
    }
    sqlSession.close();
}
10.7 SQL片段
  • 目的:實現SQL代碼的複用:
		<!--SQl片段-->    
        <sql id="if-title-authorOR">
            <if test="title !=null">
                title=#{title}
            </if>
            <if test="author !=null">
                or author=#{author}
            </if>
        </sql>
        <!--where if查詢-->
        <select id="queryBlogIF" parameterType="map" resultType="com.lxf.pojo.Blog">
            select * from  mybatis.blog
            <where>
               <include refid="if-title-authorOR"></include>
            </where>
        </select>
10.8bind

bind 元素允許你在 OGNL 表達式以外創建一個變量,並將其綁定到當前的上下文。比如:

<select id="selectBlogsLike" resultType="Blog">
  <bind name="pattern" value="'%' + _parameter.getTitle() + '%'" />
  SELECT * FROM BLOG
  WHERE title LIKE #{pattern}
</select>
10.9多數據庫支持

如果配置了 databaseIdProvider,你就可以在動態代碼中使用名爲 “_databaseId” 的變量來爲不同的數據庫構建特定的語句。比如下面的例子:

<insert id="insert">
  <selectKey keyProperty="id" resultType="int" order="BEFORE">
    <if test="_databaseId == 'oracle'">
      select seq_users.nextval from dual
    </if>
    <if test="_databaseId == 'db2'">
      select nextval for seq_users from sysibm.sysdummy1"
    </if>
  </selectKey>
  insert into users values (#{id}, #{name})
</insert>

11.緩存

11.1、簡介

查詢:連接數據庫,耗資源!

​ 一次查詢的結果,給他暫存到一個可以直接取得地方!–>內存;緩存

我們再次查詢相同得數據時,直接走緩存,就不用走數據庫了

  1. 什麼是緩存[Cache]?
  • 存在內存中的臨時數據。

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

  1. 爲什麼使用緩存
  • 減少和數據庫的交互次數,減少系統開銷,提高系統效率。
  1. 什麼樣的數據能使用緩存?
  • 經常查詢並且不經常改變的數據
11.2、Mybatis緩存

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-naXRYTUA-1591540957860)(E:\typoraPic\2.jpg)]

  • MyBatis包含一個非常強大的查詢緩存特性,它可以非常方便地定製和配置緩存。緩存可以極大的提升查詢效率。
  • MyBatis系統中默認定義了兩級緩存:一級緩存二級緩存
    • 默認情況下,只有一級緩存開啓。(SqlSession級別的緩存,也稱爲本地緩存)
    • 二級緩存需要手動開啓和配置,它是基於namespace級別的緩存。
    • 爲了提高擴展性,MyBatis定義了緩存接口Cache。我們可以通過實現Cache接口來自定義二級緩存
13.3、一級緩存
  • 一級緩存也叫本地緩存

    • 與數據庫同一次會話期間查詢到的數據會放在本地緩存中。
    • 以後如果需要獲取相同的數據,直接從緩存中拿,沒必要再去查詢數據庫;
  • 緩存失效的情況:

    • 查詢不同的東西
    • 查詢之間有增刪改操作,可能會改變原來的數據,所以必定會刷新緩存!
    • 手動清除緩存也會導致緩存失效
14.4、二級緩存
  • 二級緩存也叫全局緩存,一級緩存作用域太低了,所以誕生了二級緩存
  • 基於namespace級別的緩存,一個命名空間對應一個二級緩存
  • 工作機制
    • 一個會話查詢一條數據,這個數據就會被放在當前會話的一級緩存中;
    • 如果當前會話關閉了,這個會話對應的一級緩存就沒了;但是我們想要的是,會話關閉了,一級緩存中的數據被保存到二級緩存中;
    • 新的會話查詢信息,就可以從二級緩存中獲取內容;
    • 不同的mapper查出的數據會放在自己對應的緩存(map)中

步驟:

  1. 開啓全局緩存
		 <!--開啓全局緩存-->
        <setting name="cacheEnabled" value="true"/>
  1. UserMapper文件配置
 <cache eviction="FIFO" flushInterval="600000" size="500" />

UserMapper.java接口類:

//查詢所有的User
List<User> getUsers();

UserMapper.xml配置文件

<select id="getUsers" resultType="com.lxf.pojo.User">
            select * from user;
        </select>
        <cache eviction="FIFO" flushInterval="600000" size="500" />

測試類:

 @Test
        public void getUsers(){
            SqlSession sqlSession1 = MybatisUtil.getSession();
            SqlSession sqlSession2 = MybatisUtil.getSession();

            UserMapper mapper1 = sqlSession1.getMapper(UserMapper.class);
            List<User> users1 = mapper1.getUsers();
            for (User user : users1) {
                System.out.println("user = " + user);
            }
            sqlSession1.close();

            System.out.println("==========================================");

            UserMapper mapper2 = sqlSession2.getMapper(UserMapper.class);
            List<User> users2 = mapper2.getUsers();
            for (User user : users2) {
                System.out.println("user = " + user);
            }

            sqlSession2.close();
        }

問題:未將實體類序列化而報錯。解決方法:實體類繼承serializable接口就可以了

14.5、小結:
  • 只有開啓了二級緩存,在一個Mapper下有效
  • 所有的數據都會先放在一級緩存中;
  • 只有當會話提交,或者關閉的時候,纔會提交到二級緩存中
14.6、自定義緩存Ehcache

Ehache是一種廣泛使用的開源Java分佈緩存,主要面向通用緩存

  1. 導依賴

    <!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache -->
    <dependency>
        <groupId>org.mybatis.caches</groupId>
        <artifactId>mybatis-ehcache</artifactId>
        <version>1.2.1</version>
    </depenency>
    
  2. UserMapper.xml配置:

 <!--eh緩存-->
        <cache type="org.mybatis.caches.ehcache.EhcacheCache" />
  1. resource目錄下建一個ehcache.xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"
         updateCheck="false">
    <!--
      name:緩存名稱
      maxElementsInMemory:緩存中最大允許創建的對象數
      maxInMemory:設定內存中創建對象的最大值。
      eternal:設置元素(譯註:內存中對象)是否永久駐留。如果是,將忽略超時限制且元素永不消亡。
      overflowToDisk:設置當內存中緩存達到 maxInMemory 限制時元素是否可寫到磁盤上。
      timeToIdleSeconds:設置某個元素消亡前的停頓時間。
      timeToLiveSeconds:爲元素設置消亡前的生存時間.
      memoryStoreEvictionPolicy:當達到maxElementsInMemory限制時,Ehcache將會根據指定的策略去清理內存。默認策略是LRU(最近最少使用)。你可以設置爲FIFO(先進先出)或是LFU(較少使用)。
      diskPersistent:重啓時內存不持久化到硬盤。
     -->
    <diskStore path="./tmpdir/Tmp_EhCache"/>
    <defaultCache
                  eternal="false"
                  maxElementsInMemory="10000"
                  overflowToDisk="false"
                  diskPersistent="false"
                  timeToIdleSeconds="1800"
                  timeToLiveSeconds="259200"
                  memoryStoreEvictionPolicy="LRU"
                  />

    <cache name="cloud_user"
           eternal="false"
           maxElementsInMemory="5000"
           overflowToDisk="false"
           diskPersistent="false"
           timeToIdleSeconds="1800"
           timeToLiveSeconds="1800"
           memoryStoreEvictionPolicy="LRU"/>
</ehcache>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章