Mybatis極速入門

yoleen發表於2020-05-16

搭建mybatis的環境

匯入相關jar包

  • mybatis-3.5.3.jar
  • commons-logging-1.1.1.jar
  • log4j-1.2.16.jar
  • cglib-2.2.2.jar
  • asm-3.3.1.jar
  • druid-1.1.9.jar
  • mysql-connector-java-8.0.16.jar

建立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>
    <!--載入類路徑下的屬性檔案-->
 	<!--這裡我選擇匯入連線池的配置檔案-->
	<properties resource="druid.properties" />
	<!--設定一個預設連線環境資訊-->
	<environments default="mysql_mybatis">
	<!--連線環境資訊,取一個任意唯一的名字-->
	<environment id="mysql_mybatis">
	<!--使用mybatis的事物方式,這裡使用jdbc方式-->
    <transactionManager type="jdbc"/>
    <!--使用連線池方式連線-->
    <dataSource type="pooled">
    <!--配置與資料庫互動的四個必要屬性,注意要跟配置檔案裡面定義的相同
	在mysql8.0版本以上配置檔案要改成這樣,否則會出現classNotfound異常
        <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatistest?useSSL=true&serverTimezone=GMT"/>
        <property name="username" value="root"/>
        <property name="password" value="293911"/>
	-->
        <property name="driver" value="${DriverClassName}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
    </dataSource>
    </environment>
    </environments>
</configuration>

資料庫連線池的配置

其中mybatistest是我資料庫的名字,大家可以自行更改。

DriverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/mybatistest?useSSL=true&serverTimezone=GMT
username=root
password=293911
initialSize=5
maxActice=10
maxWait=3000

建立mybatis工具類

像之前的DButils一樣,用於獲取連線,這裡獲取的是mybatis的SqlSession連線,一般寫好後不用怎麼改了,通用的。

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.Reader;
import java.sql.Connection;


public class MybatisUtil {
private static ThreadLocal<SqlSession> threadlocal = new ThreadLocal<SqlSession> ();
private static SqlSessionFactory sqlSessionFactory;

static {
    try {
/**
*每 一 個 MyBatis 的 應 用 程 序 都 以 一 個 SqlSessionFactory 對 象 的 實 例 為 核 心 。
SqlSessionFactory 對 象 的 實 例 可 以 通 過 SqlSessionFactoryBuilder 對 象 來 獲 得 。
SqlSessionFactoryBuilder 物件可以從 XML 配置檔案,
或從 Configuration 類的習慣準備的例項中構建 SqlSessionFactory 物件。	
*/
        //讀取xml檔案
        Reader reader = Resources.getResourceAsReader("mybatis.xml");
        //構建工廠類
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
    }
  catch (Exception e){
        e.printStackTrace();
        throw new RuntimeException(e);
    }
}
	
/**
 * 禁止new出來
 */
private MybatisUtil(){}

/**
 * 獲取SQLSession
 * @return
 */
public static SqlSession getSqlSession(){
    //從當前執行緒獲取SQLSession物件,可以從當前執行緒獲取也可以從工廠類獲取
    SqlSession sqlSession = threadlocal.get();
    if (sqlSession==null){
        sqlSession = sqlSessionFactory.openSession();
        //繫結當前執行緒
        threadlocal.set(sqlSession);
    }
    return sqlSession;
}
	
/**
 * 關閉連線物件
 */
public static void closeSqlSession(){
    SqlSession sqlSession = threadlocal.get();
    if (sqlSession!=null){
        sqlSession.close();
        //解綁執行緒目的是讓gc快回收。
        threadlocal.remove();
    }
}
	
/**
 * 測試能否獲取連線,首先確保資料庫已存在!
 * @param args
 */
public static void main(String[] args) {
    Connection connection = MybatisUtil.getSqlSession().getConnection();
    System.out.println(connection!=null?"success":"false");
}
}

測試環境是否搭建成功

1. 新建資料庫mybatistest
2. 建立students表(id,name,sal)
3. 執行mybatis工具類的主函式

資料庫sql語句:

CREATE DATABASE mybatisTest; 
CREATE TABLE students( id INT(5) PRIMARY KEY AUTO_INCREMENT, NAME VARCHAR(10), sal DOUBLE(8,2) ); 

執行結果:

建立實體類與表的對映配置檔案

用來寫sql語句並對結果進行封裝。

<?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="np_Student">
<!--
resultMap標籤:對映實體與表
type屬性:表示實體全路徑名
id屬性:為實體與表的對映取一個任意的唯一的名字
-->
<resultMap id="StudentMap" type="model.Student">
    <!--
    id標籤:對映主鍵屬性
    result標籤:對映非主鍵屬性
    property屬性:實體的屬性名
    column屬性:表的欄位名
    一般來說表名跟bean類的屬性名一樣
    id代表資料庫表的主鍵
    -->
    <id property="id" column="id"/>
    <result property="name" column="name"/>
    <result property="sal" column="sal"/>
</resultMap>
<!--sql語句寫在這裡
    在JDBC中我們通常使用?號作為佔位符,而在Mybatis中,我們是使用#{}作為佔位符
    parameterType我們指定了傳入引數的型別
    #{}實際上就是呼叫了Student屬性的get方法
    注意type或者parameterType裡面放的都是
    實體類的全路徑名,即包名.類名
-->
<insert id="add" parameterType="model.Student">
insert into students values(#{id},#{name},#{sal});
</insert>
</mapper>

然後將寫好的mapper配置檔案新增到一開始寫的mybatis配置檔案中去,即在mybatis.xml的/configuration標籤上面去。

<!--載入實體類與表的對映關係配置-->
<mappers>
    <mapper resource="StudentMapper.xml" />
</mappers>

測試向資料庫中新增一條資料

1. 建立model包,在model包中新建student類
2. 建立dao包,在dao包中新建add(Student student)方法

Student類程式碼:

    package model;
	public class Student {
	    private Integer id;
	    private String name;
	    private Double sal;
	
	    @Override
	    public String toString() {
	        return "Student{" +
	                "id=" + id +
	                ", name='" + name + '\'' +
	                ", sal=" + sal +
	                '}';
	    }
	
	    public Student() {
	    }
	
	    public Student(Integer id, String name, Double sal) {
	        this.id = id;
	        this.name = name;
	        this.sal = sal;
	    }
	
	    public Integer getId() {
	        return id;
	    }
	
	    public void setId(Integer id) {
	        this.id = id;
	    }
	
	    public String getName() {
	        return name;
	    }
	
	    public void setName(String name) {
	        this.name = name;
	    }
	
	    public Double getSal() {
	        return sal;
	    }
	
	    public void setSal(Double sal) {
	        this.sal = sal;
	    }
	}

dao介面類程式碼:

package dao;
import model.Student;
public interface StudentDao {
public void add(Student student);
}

dao實現類程式碼:

package dao;
import model.Student;
import org.apache.ibatis.session.SqlSession;
import utils.MybatisUtil;

public class StudentDaoImpl implements StudentDao {
    @Override
    public void add(Student student) {
        SqlSession sqlSession = null;
        try {
            //獲取sqlSession物件
            sqlSession = MybatisUtil.getSqlSession();
            /**
             * 呼叫插入方法。
             * insert方法的引數有兩個:
             * String s = 名稱空間.方法名(id名)
             * Object o = 引數列表。
             * commit方法引數有true/false,預設為true
             * 需要我們手動提交事務。
             */
            sqlSession.insert("np_Student.add",student);
            sqlSession.commit();
        }catch (Exception e){
            e.printStackTrace();
            //事務回滾
            sqlSession.rollback();
            throw e;
        }finally {
            MybatisUtil.closeSqlSession();
        }
    }
}

專案結構:

Mybatis工作流程

  1. 通過Reader獲取mybatis對映檔案。
  2. 通過SqlSessionFactoryBuilder()構建SqlSessionFactory()工廠物件。
  3. 獲取當前執行緒SQLSession物件。
  4. 通過SQLSession讀取對映檔案中的操作編號,從而讀取SQL語句。
  5. 關閉資源。

完成上面步驟後我們就基本上可以用mybatis連線到資料庫並會進行一定的操作了,下面我們將使用mybatis進行資料庫的增刪改查。

使用mybatis對資料庫進行增刪改查

SQLSession物件:

所有的執行語句的方法,提交或回滾事務都由這個例項來進行操作。

SQLSession物件常用方法:

  • T selectOne(String statement, Object parameter) 只查一個物件,如果為空會拋異常。
  • List selectList(String statement, Object parameter) 查詢結果以List集合返回
  • <K,V> Map<K,V> selectMap(String statement, Object parameter, String mapKey)
    查詢結果以Map集合返回
  • int insert(String statement, Object parameter) 插入
  • int update(String statement, Object parameter) 更新
  • int delete(String statement, Object parameter) 刪除
  • void commit(boolean force) 提交事務
  • void rollback(boolean force) 回滾事務

增加資料改進:

由於我們student表id是自增的,所以我們可以不設定id屬性,使用mybatis的useGeneratedKeys方法來進行改進新增資料。

<!--對於id主鍵自增進行自動生成-->
<insert id="add" parameterType="model.Student" useGeneratedKeys="true" keyProperty="id">
    insert into students(name,sal) values(#{name},#{sal});
</insert>

查詢一條記錄:

在StudentMapper.xml中新增select標籤,然後在dao類中新增findStuById(int id)介面,
最後在dao實現類中實現findStuById方法;

新增select標籤:

<!--resultMap就是上面定義的,可以將查詢結果以鍵值對形式返回-->
 <select id="findStuById" parameterType="int" resultMap="StudentMap">
    select * from students where id = #{id};
 </select>

說明一下:MyBatis 中parameter是非常強大的元素,上面的這個示例說明了一個非常簡單的命名引數對映。
引數型別被設定為“int” ,因此這個引數可以被設定成任何內容。原生的型別或簡單資料型別,
比如整型和沒有相關屬性的字串,因此它會完全用引數來替代。對於複雜的引數型別如student類,
它會自動查詢相關屬性,並將它們的值傳遞到預處理語句的引數中去。

dao介面類新增:

public Student findStuById(int id);

dao實現類新增:

@Override
public Student findStuById(int id) {
SqlSession sqlSession = MybatisUtil.getSqlSession();
try {
    return sqlSession.selectOne("np_Student.findStuById",id);
}catch (Exception e){
    e.printStackTrace();
    throw e;
}finally {
    MybatisUtil.closeSqlSession();
}
}

查詢所有資料:

在StudentMapper.xml中新增select標籤,然後在dao類中新增findAll()介面,最後在dao實現類中通過SQLSession.selectList()方法,實現findAll()方法;

新增select標籤:

<select id="findAll" resultMap="StudentMap">
    select * from students;
</select>

dao介面類新增:

public List<Student> findAll();

dao實現類新增:

@Override
public List<Student> findAll() {
SqlSession sqlSession = MybatisUtil.getSqlSession();
try{
    List<Student> students = sqlSession.selectList("np_Student.findAll");
    return students;
}catch (Exception e){
    e.printStackTrace();
}finally {
    MybatisUtil.closeSqlSession();
}
return null;
}

刪除資料:

在StudentMapper.xml中新增標籤,然後在dao類中新增delStudent(int id)介面,最後在dao實現類中實現delStudent方法;

新增delete標籤:

<delete id="delStudent" parameterType="int">
    delete from students where id = #{id};
 </delete>

dao介面類新增:

public Student DelStuById(int id);

dao實現類新增:

@Override
public Student DelStuById(int id) {
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    Student stuById = null;
    try {
		//查詢所刪除的資料。
        stuById = sqlSession.selectOne("np_Student.findStuById",id);
		//刪除對應id的資料。	
        sqlSession.delete("np_Student.delStudent",id);
        sqlSession.commit();
    }catch (Exception e){
        e.printStackTrace();
        sqlSession.rollback();
    }finally {
        MybatisUtil.closeSqlSession();
    }
    return stuById;
 }

更新資料:

在StudentMapper.xml中新增標籤,然後在dao類中新增updateStudent(Student student)介面,最後在dao實現類中實現updateStudent(Student student)方法;

新增update標籤:

<update id="updateStudent" parameterType="model.Student">
	update students set name=#{name},sal=#{sal} where id=#{id};
</update>

dao介面類新增:

public void updateStudent(Student student);

dao實現類新增:

@Override
public void updateStudent(Student student) {
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    try {
        sqlSession.update("np_Student.updateStudent",student);
        //對於增刪改操作都需要我們手動提交事務,不然操作是不會成功的。
        sqlSession.commit();
    }catch (Exception e){
        e.printStackTrace();
        sqlSession.rollback();
    }finally {
        MybatisUtil.closeSqlSession();
    }
}

mybatis分頁:

分頁是指查詢資料庫一定區間範圍內的所有資料,sql語句:select * from students limit 0,5;表示從第0行開始,查詢5條資料。

新增select標籤:

<select id="pagination" parameterType="Map" resultMap="StudentMap">
    <!--根據key自動找到對應Map集合的value-->
    select * from students limit #{start},#{count};
</select>

dao介面類新增:

public List<Student> pagination(int start, int count);

dao實現類新增:

 @Override
    public List<Student> pagination(int start, int count) {
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    try {
        Map<String,Integer> map = new HashMap<>();
        map.put("start",start);
        map.put("count",count);
        //由於引數只能傳遞一個,所以我們使用map集合,到時候通過key取值。
        List<Student> students = sqlSession.selectList("np_Student.pagination", map);
        return students;
    }catch (Exception e){
        e.printStackTrace();
    }finally {
        MybatisUtil.closeSqlSession();
    }
    return null;
    }

動態sql

MyBatis 的一個強大的特性之一通常是它的動態 SQL 能力。如果你有使用 JDBC 或其他相似框架的經驗,
你就明白條件地串聯 SQL 字串在一起是多麼的痛苦,確保不能忘了空格或在列表的最後省略逗號。
動態 SQL 可以徹底處理這種痛苦。
通常使用動態 SQL 不可能是獨立的一部分,MyBatis 當然使用一種強大的動態 SQL 語言來改進這種情形,
這種語言可以被用在任意對映的 SQL 語句中。 動態 SQL 元素和使用 JSTL 或其他相似的基於 XML 的文字處理器相似。

常用標籤:

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

1.if

在動態 SQL 中所做的最通用的事情是包含部分 where 字句的條件。比如: 多條件查詢。

用法:

<select id="findByCondition" resultMap="StudentMap" parameterType="Map">
    select * from students
    <where>
    <if test="name!=null">
        and name=#{name};
    </if>
    </where>
 </select>

看到這裡不知道大家是不是有點疑問呢?我們在JSTL中一般都是用EL表示式如

<c:if test="${name}!=null"}>這樣用的,這裡怎麼直接就寫name呢,不是還沒有賦值嗎?

於是我就想是不是跟map的鍵有關?為了驗證這個想法,我就寫了這麼一個測試類。

測試標籤裡test獲取的值是map集合的鍵:

為了更清楚的看出效果,首先將標籤裡面的"name"改為"mapname"。

<select id="findByCondition" resultMap="StudentMap" parameterType="Map">
    select * from students
    <where>
    <if test="mapname!=null">
        and name=#{mapname};
    </if>
    </where>
 </select>

測試方法:

@Test
  public void test() {
    HashMap<String, Object> map = new HashMap<>();
    map.put("name","lisi");
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    List<Student> students = sqlSession.selectList("np_Student.findByCondition", map);
    System.out.println(students);
}

測試結果:

[Student{id=1, name='lisi', sal=11.0}, Student{id=2, name='lee', sal=22.0}, Student{id=3, name='zhangsan', sal=1200.0}]

我資料庫裡面的所有資料都被取了出來,說明了執行findBYCondition方法並沒有拼接name="lisi";

將其更改為map.put("mapname",lisi),注意這裡的mapname和我們的findBYCondition方法裡面定義的
if標籤名字對應,如果這次能查詢出"lisi"這條記錄,說明if標籤裡面定義的正是引數map的鍵的名字。

@Test
  public void test() {
    HashMap<String, Object> map = new HashMap<>();
    map.put("mapname","lisi");
    SqlSession sqlSession = MybatisUtil.getSqlSession();
    List<Student> students = sqlSession.selectList("np_Student.findByCondition", map);
    System.out.println(students);
}

更改後的測試結果:

[Student{id=1, name='lisi', sal=11.0}]

可以證實,標籤裡面定義的就是map集合的鍵名。

2. choose(when,otherwise)

跟switch用法差不多。

<select id="choose" parameterType="map" resultMap="StudentMap">
   select * from students
   <where>
       <choose>
           <when test="name!=null">
               and name = #{name}
           </when>
           <otherwise>
               and id = #{id}
           </otherwise>
       </choose>
   </where>
</select>

3. trim(where,set)

修剪元素,很強大,我們之所以可以如此方便的使用動態sql,它功不可沒。
比如:where 元素知道如果由被包含的標記返回任意內容,就僅僅插入"where" 。
而且,如果以"and "或"or"開頭的內容,那麼就會跳過 where 不插入。
如果 where 元素沒有做出你想要的,你可以使用 trim 元素來自定義。


比如,和 where 元素相等的 trim 元素是:

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ... 
</trim>

自己定義一個trim標籤。

<select id="findByCondition" resultMap="StudentMap" parameterType="Map">
    select * from students
    <!--這裡的trim標籤就相當於where標籤,
    如果where後面是以1=1開頭的就將其修剪掉。-->
    <trim prefix="where" prefixOverrides="1=1 ">
    <if test="name!=null">
        1=1 name=#{name};
    </if>
    </trim>
 </select>

於where相同的還有set,同樣可以用trim標籤進行操作。



4. foreach

foreach操作是迭代一個集合, 通常是構建在 in 條件中的。

例子:批量查詢

<!--查詢一個id集合裡面的每個id對應的資料-->
<select id="findStuByIDs" resultMap="StudentMap" parameterType="List">
    select * from students where id in
    <!--collection代表穿進來的集合名稱,無論陣列還是list集合都是從 "(" 開始 ")" 結束的,分隔符就是","-->
    <foreach collection="list" open="(" close=")" separator="," item="item">
        #{item}
    </foreach>
 </select>

進階

1. 多條件模擬查詢:

模糊查詢的sql語句:select* from students where name like %name%;

<!--多條件模擬查詢-->
    <select id="findByLike" parameterType="map" resultMap="StudentMap">
        select * from students
        <where>
            <if test="name!=null">
                <bind name="newname" value="'%'+name+'%'"/>
                and name like #{newname}
            </if>
            <if test="sal!=null">
                <bind name="newsal" value="'%'+sal+'%'"/>
                and sal like #{newsal}
            </if>
        </where>
    </select>

2. 動態更新:

set 元素可以被用於動態包含更新的列,而不包含不需更新的

<!--動態更新-->
 <!--注意不要漏掉逗號,-->
 <update id="updateIfNecessary" parameterType="model.Student">
    update students
    <set>
        <if test="name!=null">
            name=#{name},
        </if>
        <if test="sal!=null">
            sal=#{sal},
        </if>
    </set>
    where id = #{id};
 </update>

3. 批量刪除:

<!--批量刪除-->
<delete id="delStuInList" parameterType="List">
    delete from students where id in
        <foreach collection="list" open="(" close=")" separator="," item="item">
            #{item}
        </foreach>
</delete>

4. 動態插入:

說實話,這裡我有點懵,在網上看別人動態插入的程式碼很長很長的一段,又拼接key,又拼接value,是怕擔心順序亂了?

其實我們只要保證順序一致,就可以不用拼接和判斷是否為空了。

<!--動態插入-->
<insert id="dynamicInsert" parameterType="model.Student" useGeneratedKeys="true" keyProperty="id">
    insert into students values
    <!--代表從"("開始,")"結束,如果是已","結尾就把它去掉...
        其實這段不用這麼寫,我是想熟悉一下這個標籤...-->
    <trim prefix="(" suffix=")" suffixOverrides=",">
	<!--這裡保證與資料庫裡的表的順序一致,不用判斷是否為空-->
        #{id},#{name},#{sal}
    </trim>
 </insert>

5. 設定別名:

當我們在設定Mapper檔案的時候,需要指定parameterType屬性或者resultType屬性,
該屬性值如果不是一般的型別或String型別,比如是物件型別的話,就需要指定全類名,
如果有多個SQL對映語句的話,那麼每次都指定全類名的話,可能會比較麻煩,是否有更好的方法,
可以簡化一下。你可以通過設定別名的方式來簡化。

<!--設定別名-->
<!--放在mybatis配置檔案中,放置位置在<configuration>標籤下<environments>標籤前-->
<typeAliases>
    <!--type是全路徑名,alias是設定的別名-->
    <typeAlias type="model.Student" alias="Student"/>
</typeAliases>

之後我們再對parameterType屬性賦值時可以直接用別名就會方便很多。

<!--動態插入-->
<insert id="dynamicInsert" parameterType="Student" useGeneratedKeys="true"  keyProperty="id">
    insert into students values
    <trim prefix="(" suffix=")" suffixOverrides=",">
        #{id},#{name},#{sal}
    </trim>
 </insert>

總結

1. sql寫在xml裡,便於統一和管理,解除了sql與程式程式碼的耦合。
2. Mybatis的事務是預設開啟的,我們需要手動提交事務。
3. 寫Mapper.xml對映檔案時,更像在寫對應的CURD方法,id(方法名),parameterType(傳入引數),resultMap(返回值)。

如果文章有錯的地方歡迎指正,大家互相交流。
最後,碼字不易,喜歡點個贊呀!

相關文章