mybatis學(xué)習(xí)記錄

mybatis學(xué)習(xí)記錄

1.mybatis是什么

? mybatis是一款優(yōu)秀的持久層框架,它支持定制化sql,存儲過程以及高級映射的功能,mybatis幾乎避免所有jdbc原生代碼復(fù)雜的操作和手動獲取參數(shù)以及對結(jié)果集的封裝,。mybatis中可以使用簡單xml或注解來配置與描述對象映射關(guān)系,將java實(shí)體類對象映射成數(shù)據(jù)庫中的優(yōu)勢。

2.mybatis有哪些優(yōu)勢

  • ? 簡單易學(xué):mybatis本身就很輕巧且簡單,沒有任何第三方的依賴,最簡單的安裝只需要兩個jar文件+配置幾個sql文件。易于學(xué)習(xí),易于使用,通過文檔和源代碼,可以比較完全的掌握它的設(shè)計思路和實(shí)現(xiàn)。
  • ? 靈活:mybatis不會對程序或者數(shù)據(jù)庫的現(xiàn)有設(shè)計強(qiáng)加任何影響。sql寫入xml文件里,便于統(tǒng)一管理與優(yōu)化,通過sql基本上可以實(shí)現(xiàn)任何對數(shù)據(jù)庫操作的需求。
  • ? 接觸了sql于程序代碼的耦合:通過提供DAO層,將業(yè)務(wù)邏輯與數(shù)據(jù)訪問邏輯分離,使系統(tǒng)的設(shè)計更加清晰,更易維護(hù),更易單元測試,sql與代碼的分離,提高了可維護(hù)性。
  • ? 提供映射標(biāo)簽:支持對象與數(shù)據(jù)庫的字段關(guān)系映射。
  • ? 提供對象關(guān)系映射標(biāo)簽:支持對象關(guān)系組建維護(hù)。
  • ? 提供xml標(biāo)簽:提供xml標(biāo)簽來編寫動態(tài)sql。

3.mybatis有哪些缺點(diǎn)

  • 編寫sql時工作量很大,尤其是字段多,關(guān)聯(lián)多表時,更是如此。
  • sql語句依賴與數(shù)據(jù)庫,導(dǎo)致sql語句的可移植性變差,更換數(shù)據(jù)庫時,需要做更多的工作。
  • 二級緩存機(jī)制不佳。

4.mybatis框架搭建(下載jar包等前置工作不贅述)

1.創(chuàng)建配置文件

? 在src目錄下創(chuàng)建mybatis-config.xml文件,該文件時mybatis的核心配置文件,用戶配置數(shù)據(jù)庫連接信息以及mybatis的一些屬性設(shè)置。

<configuration>
    <environments default="default">
        <environment id="default">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/bank?serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
</configuration>

? 標(biāo)簽作用:

? configuration:根標(biāo)簽

? environments:配置數(shù)據(jù)庫連接環(huán)境,default屬性標(biāo)識該默認(rèn)使用的環(huán)境,在mybatis中可以配置多套環(huán)境用于切換

? environment:數(shù)據(jù)庫連接環(huán)境配置,id表示該環(huán)境的名稱,在environments的default屬性中可以使用id指定當(dāng)前環(huán)境

? transactionManager:事務(wù)管理模式標(biāo)簽,這里使用的是JDBC事務(wù)

? dataSource:數(shù)據(jù)源,type屬性中POOLED表示使用mybatis自身的連接池數(shù)據(jù)源

? property:配置連接源的各項(xiàng)屬性信息

2.創(chuàng)建session工廠
public class SessionFactory {
    private static SqlSessionFactory sqlSessionFactory;

    static {
        String resource = "config.xml";
        try {
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactoryBuilder factoryBuilder = new SqlSessionFactoryBuilder();
            sqlSessionFactory = factoryBuilder.build(inputStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

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

? 創(chuàng)建連接工廠便于我們獲取連接進(jìn)行操作,下面我們測試一下連接是否獲取正常

SqlSession sqlSession = SessionFactory.getSqlSession();
System.out.println(sqlSession.getConnection());

[圖片上傳失敗...(image-60aced-1590670310779)]

? 可以看到,我們已經(jīng)在控制臺輸出了連接信息,執(zhí)行到這一步,已經(jīng)成功獲取到連接了

3.通過mapper代理實(shí)現(xiàn)自定義接口

? mybatis在設(shè)計時采用動態(tài)代理模式,我們在使用mybatis進(jìn)行增刪改查時,只需要提供數(shù)據(jù)層接口以及對應(yīng)該接口的xml映射文件,映射文件中要針對數(shù)據(jù)層接口的每一個方法提供對應(yīng)的sql數(shù)據(jù),參數(shù)類型,以及返回值類型,最終mybatis會創(chuàng)建動態(tài)代理對象實(shí)現(xiàn)數(shù)據(jù)層接口,而動態(tài)代理中的方法會執(zhí)行映射文件中對應(yīng)的sql語句。

? 下面我們寫一個查詢來了解一下mapper代理的是怎樣實(shí)現(xiàn)的

mapper(數(shù)據(jù)層接口)

public interface UserMapper {  
    public User selectById(int id);
}

mapper.xml(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.woniuxy.mapper.UserMapper">
     <select id="selectById" parameterType="int" resultType="com.woniuxy.entity.User">
        select <include refid="columns"></include> from users where id=#{id}
    </select>
</mapper>

在進(jìn)行xml文件書寫時需要遵守以下幾點(diǎn)規(guī)則

  1. 映射文件的根標(biāo)簽mapper的namespace屬性必須等于數(shù)據(jù)層接口的全限定名
  2. 映射文件中的id必須等于接口方法的名稱

在數(shù)據(jù)層接口映射文件中同樣要引入DTD文件作為驗(yàn)證標(biāo)準(zhǔn),現(xiàn)簡單介紹一下幾個常用的標(biāo)簽

  • mapper:根標(biāo)簽,那么namespace屬性必須于該映射文件對應(yīng)的接口的全限定名一致
  • delete-select-insert-update:這四種標(biāo)簽對應(yīng)了數(shù)據(jù)操作中的增刪改查,其id必須與要對應(yīng)的數(shù)據(jù)層接口的方法名一致,resultType表示mybatis在遍歷結(jié)果集時要封裝的實(shí)體類類型,parameterType,表示該方法的參數(shù)類型,標(biāo)簽之間的內(nèi)容用于sql語句的書寫以及一些其他的標(biāo)簽(如上述代碼中的引用標(biāo)簽或動態(tài)sql諸如此類等等)。
  • select:查詢標(biāo)簽
  • insert:新增標(biāo)簽
  • update:更新標(biāo)簽
  • delete:刪除標(biāo)簽
  • 并不是說新增數(shù)據(jù)的sql就一定要寫在insert標(biāo)簽中,但是每一種不同的標(biāo)簽,執(zhí)行sql的過程會有所差異,中間執(zhí)行的功能也不一致。例如:select標(biāo)簽開啟了事務(wù)的自動提交,除此之外的增刪改都沒有開啟自動提交。insert、update、delete標(biāo)簽執(zhí)行完畢會自動返回受影響行數(shù)、insert還具備返回主鍵的功能。所以以后在開發(fā)過程中盡量使用對應(yīng)的標(biāo)簽完成對應(yīng)的操作。

注冊mapper.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>
    <settings>
        <setting name="logImpl" value="STDOUT_LOGGING" />
    </settings>
    <typeAliases>
        <package name="com.woniuxy.entity"/>
    </typeAliases>
    <environments default="default">
        <environment id="default">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/bank?serverTimezone=UTC"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <package name="com.woniuxy"/>
       <!-- <mapper resource="com/woniuxy/mapper/UserMapper.xml"></mapper>
        <mapper class="com.woniuxy.mapper.UserMapper"></mapper>-->
    </mappers>
</configuration>

注冊mapper.xml映射文件使用mappers標(biāo)簽,有三種方式,在上述中xml片段中已經(jīng)列出(注釋掉的是另外兩種方式)


測試

public class test {
    public static void main(String[] args) {
        SqlSession sqlSession = SessionFactory.getSqlSession();
        System.out.println(sqlSession.getConnection());
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        User user = mapper.selectById(27); //byid查詢
        System.out.println(user);
    }
}

[圖片上傳失敗...(image-356b24-1590670310779)]

這里我們需要先得到sqlsession對象,通過sqlsession對象再得到我們需要的數(shù)據(jù)層接口的代理類,這里我們用接口的class對象作為參數(shù),直接調(diào)用getMapper方法就可以得到代理對象,由代理對象直接調(diào)用方法,就可以實(shí)現(xiàn)數(shù)據(jù)操作了。

5.使用mybaits實(shí)現(xiàn)增刪改查操作

  • Mapper接口開發(fā)需要遵循以下規(guī)范:
  • Mapper.xml文件中的namespace與mapper接口的類路徑相同。
  • Mapper接口方法名和Mapper.xml中定義的每個增刪改查標(biāo)簽的id相同
  • Mapper接口方法的參數(shù)類型和mapper.xml中定義的每個sql 的parameterType的類型相同
  • Mapper接口方法的返回值類型和mapper.xml中定義的每個sql的resultType的類型相同
  • 在執(zhí)行某些數(shù)據(jù)庫操作時,在sql語句中需要動態(tài)的賦予參數(shù),參數(shù)的來源是數(shù)據(jù)層接口中方法的參數(shù),在映射文件中使用這些參數(shù)有兩種寫法#{}和${}:
  • #{}表示一個占位符號,通過#{}可以實(shí)現(xiàn)preparedStatement向占位符中設(shè)置值,自動進(jìn)行java類型和jdbc類型轉(zhuǎn)換,#{}可以有效防止sql注入。 #{}可以接收簡單類型值或pojo屬性值。如果parameterType傳輸單個簡單類型值,#{}括號中可以是value或其它名稱。
  • {}表示拼接sql串,通過{}可以將parameterType 傳入的內(nèi)容拼接在sql中且不進(jìn)行jdbc類型轉(zhuǎn)換,{}可以接收簡單類型值或pojo屬性值,如果parameterType傳輸單個簡單類型值,{}括號中只能是value。
  • 基本上能使用#{}就不要使用{}避免sql注入,當(dāng)我們的關(guān)鍵字是{}來拼接的時候。例如:語句是
  • select * from {table},傳入的參數(shù)是Student,就只能用因?yàn)橐坏┦褂?,語句會變成select * from ‘student’。
  • 如果傳入的參數(shù)是實(shí)體類的對象,那么可以使用${實(shí)體類的屬性名稱}或者#{實(shí)體類的屬性名稱}都可以使用參數(shù)完成sql的入?yún)ⅰ?/li>
  • 如果傳入的參數(shù)是一個基本數(shù)據(jù)類型,你可以使用#{}括號內(nèi)任意的名稱來獲取該參數(shù)并完成入?yún)?,也可以使?{}括號內(nèi)填充_parameter來入?yún)?/li>
  • 如果需要傳入多個參數(shù),并且這多個參數(shù)無法封裝為一個對象的時候。我們可以將所有的數(shù)據(jù)封裝為一個鍵值對。同時將方法的參數(shù)也定義為鍵值對類型。你可以使用#{}或${},括號內(nèi)使用鍵值對的鍵獲取數(shù)據(jù)入?yún)ⅰ?/li>

6.映射文件的配置

1.新增數(shù)據(jù)時獲取主鍵值

? 在某些業(yè)務(wù)場景中,我們需要用到新增的這一行的主鍵數(shù)據(jù),例如我新增了訂單,我需要獲取訂單的主鍵,對訂單詳情中的訂單外鍵進(jìn)行賦值,這樣我們就可以使用配置的方式來獲取到主鍵的值。

<insert id="insert" parameterType="user" useGeneratedKeys="true" keyProperty="id" >
    insert into users values(null,#{password},#{account})
</insert>
  • useGeneratedKeys設(shè)置為true表示當(dāng)前數(shù)據(jù)庫使用自增長的方式生成主鍵值,在操作完畢以后會使用該自增長的值
  • keyProperty則表示在新增完畢以后生成的主鍵值保存在參數(shù)的那個屬性中,比如說id屬性
  • 最后執(zhí)行新增以后就可以從新增的用戶對象中取出id的值
2.使用mybatis的日志打印輸出sql語句的執(zhí)行過程

? 在開發(fā)中,可以使用這個打印功能,我們可以更加清晰的看到sql語句最終是什么樣的,參數(shù)如何賦入的,使用這個功能,需要在核心配置文件中添加這一句標(biāo)簽

<settings>
    <setting name="logImpl" value="STDOUT_LOGGING" />
</settings>
3.在mybatis核心配置文件中指定類型的別名

? 在映射文件中的標(biāo)簽中經(jīng)常需要用到類型的名稱,例如parameterType 和resultType,雖然直接寫類型的全限定名稱完全可以,但是類的限定名稱往往特別復(fù)雜,寫起來非常麻煩,mybatis同時還支持以別名的方式來定義參數(shù)類型或者返回值類型。對于java中的常見類型mybatis已經(jīng)為其提供了對應(yīng)的別名,見下圖:

img

? 如果想為項(xiàng)目中的自定義實(shí)體類型定義別名就需要在mybatis的核心配置文件的根標(biāo)簽中使用typeAliases標(biāo)簽。

<typeAliases>
         <!--定義單個類型的別名-->
        <typeAlias type="com.woniuxy.entity.User" alias="User"></typeAlias>
         <!--批量定義別名:將自動以該包中的所有類的類名作為別名,不區(qū)分大小寫-->
        <package name="com.woniuxy.entity"/>
 </typeAliases>

在核心配置文件中注意標(biāo)簽的書寫位置:

properties,settings,typeAliases,typeHandlers,objectFactory,objectWrapperFactory,reflectorFactory,plugins,environments,databaseIdProvider,mappers

4.使用sql標(biāo)簽定義可重用的sql片段

? 使用sql標(biāo)簽來定義可重用的sql語句片段,可以在需要使用的時候使用include標(biāo)簽來引入定義好的sql語句,下面來做個示范

<sql id="columns">id,account,password</sql>

定義sql標(biāo)簽

<select id="selectById" parameterType="int" resultType="user">
    select <include refid="columns"></include> from users where id=#{id}
</select>

使用include標(biāo)簽引入sql語句

5.使用foreach標(biāo)簽多行新增數(shù)據(jù)

? 某些數(shù)據(jù)庫是支持多行新增的,例如mysql,mysql中多行新增的語法是:

? insert into 表 values(第一行的數(shù)據(jù)),(第二行的數(shù)據(jù)),....以此類推

? 根據(jù)語法結(jié)構(gòu)我們不難發(fā)現(xiàn),要實(shí)現(xiàn)多行新增需要在values后重復(fù)書寫每一行的數(shù)據(jù)內(nèi)容,mybatis提供了foreach標(biāo)簽可以在執(zhí)行新增時根據(jù)參數(shù),循環(huán)拼接sql語句,以此我們可以實(shí)現(xiàn)多行新增。

? 多行新增的參數(shù)應(yīng)該定義為集合類型

public int  batchInsert(List<User> users);

? 映射文件中的標(biāo)簽寫法:

<insert id="batchInsert" parameterType="list">
    insert into users values
    <foreach collection="list" item="user" separator=",">
        (null,#{user.password},#{user.account})
    </foreach>
</insert>

? foreach標(biāo)簽屬性說明:

  • collection:需要遍歷的集合類型,必須寫作list或者collection,如果參數(shù)是set集合,則必須寫作collection
  • item:foeach進(jìn)行循環(huán)時的局部變量,可以使用該變量獲取每一個對象的屬性,通過對象.屬性獲取
  • separator:分隔符,每循環(huán)一次在末尾自動添加(除最后一次循環(huán))
6.動態(tài)sql語句編寫

? 動態(tài)sql主要用于動態(tài)的添加查詢條件,或者動態(tài)的修改某個數(shù)據(jù),動態(tài)添加查詢條件可以使用if標(biāo)簽和where標(biāo)簽

<select id="select" parameType="user">
    select <include refid="columns"></include> from users
    <where>
        <if test="account!=null and account!=''">
            and account=#{account}
        </if>
    </where>
</select>

? where 標(biāo)簽只會在至少有一個子元素的條件返回 SQL 子句的情況下才去插入“WHERE”子句。而且,若語句的開頭為“AND”或“OR”,where 標(biāo)簽也會將它們?nèi)コ?/p>

? if標(biāo)簽不止用在此處,在編寫sql時可以單獨(dú)靈活的使用

? 動態(tài)的修改數(shù)據(jù)可以使用set+if標(biāo)簽:

<update id="update" parameterType="user" >
    update users
    <set>
        <if test="account!=null and account!=''">
            account=#{account},
        </if>
        <if test="password!=null and password!=''">
            password=#{password},
        </if>
    </set>
    where id=#{id}
</update>

? set和where的原理類似,用于動態(tài)生成更新語句中的字段列表,set中嵌套if標(biāo)簽,if標(biāo)簽中書寫更新字段在最后加上一個","號,mybatis會自動去掉最后一個更新字段的逗號

7.手動的結(jié)果集映射
1.單表的結(jié)果集映射

? mybatis在執(zhí)行查詢時,首先會使用我們提供的sql語句和參數(shù)查詢出結(jié)果集,然后再將結(jié)果集的數(shù)據(jù)封裝成我們想要的實(shí)體類對象。那么將結(jié)果集的數(shù)據(jù)封裝成實(shí)體類對象的過程中,mybatis必須知道字段名和屬性名之間的對應(yīng)關(guān)系,mybatis會默認(rèn)的將數(shù)據(jù)保存在和實(shí)體類屬性名相同的屬性中,在使用mybaits時,推薦將實(shí)體類的屬性名與數(shù)據(jù)庫的字段名保持一致,這樣可以省去我們映射關(guān)系的這一步操作,但是在屬性名和字段名不一致時,mybatis也提供了解決方案,我們這時就需要使用resultMap標(biāo)簽來實(shí)現(xiàn)手動的結(jié)果集映射。

<resultMap id="userMap" type="User">
    <id property="id" column="id"></id>
    <result column="account" property="account"></result>
    <result column="password" property="password"></result>
</resultMap>

? 標(biāo)簽以及屬性說明:

  • <resultMap id="userMap" type="User">:id屬性表示映射關(guān)系的id,在查詢時使用該id完成結(jié)果集映射,type表示要對應(yīng)映射的實(shí)體類類型,這里使用的是前面定義的別名。
  • <id property="id" column="id"></id>:mybatis對于特殊的主鍵提供了id標(biāo)簽,主鍵的映射需要使用該標(biāo)簽
  • <result column="password" property="password"></result>:對于其他字段的映射標(biāo)簽:column屬性表示其在數(shù)據(jù)庫中的列明,property表示其在實(shí)體類中的屬性名
2.一對一結(jié)果集映射

? 一對一關(guān)聯(lián)查詢的結(jié)果封裝有兩種形式,一種直接以map<string,boject>鍵值對的方式直接響應(yīng),這種方式只需將resultType的值定義為map即可。

? 第二種方式是通過實(shí)體類封裝來完成,例如我有一個存款信息,我查詢存款信息時希望將所有的賬戶也查詢出來。

 <resultMap id="recordMap" type="Record">
     <id column="id" property="id"></id>
     <result column="amount" property="amount"></result>
     <result property="datetime" column="time"></result>
     <result property="type" column="type"></result>
     <association property="user" javaType="User">
         <id column="uid" property="id"></id>
         <result property="password" column="password"></result>
         <result property="account" column="account"></result>
     </association>
 </resultMap>
<select id="getRecordByAll" resultMap="recordMap">
    select r.*,u.id as uid,u.password,u.account
    from record as r,users as u
    where u.id=r.user_id
</select>
RecordMapper mapper = sqlSession.getMapper(RecordMapper.class);
List<Record> recordByAll = mapper.getRecordByAll();
recordByAll.forEach(e->{
    System.out.println(e);
});

[圖片上傳失敗...(image-1b449e-1590670310779)]

首先我需要在record類里定義一個user對象才可以進(jìn)行映射,association標(biāo)簽用于自定user對象,porperty屬性書寫對象在封裝類中的屬性名,javaType中則寫其對應(yīng)類型(這里寫的上面定義的別名)

3.一對多結(jié)果集映射

? 一對多結(jié)果映射,例如我要查找每個用戶的所有訂單,我希望它的數(shù)據(jù)結(jié)構(gòu)時這個樣子的:

[圖片上傳失敗...(image-2be592-1590670310779)]

? 這種情況下,一對多映射就發(fā)揮效果了,我們來看看它的代碼要如何書寫

<resultMap id="userMap" type="User">
    <id property="id" column="uid"></id>
    <result column="account" property="account"></result>
    <result column="password" property="password"></result>
    <collection property="record" ofType="Record">
        <id column="id" property="id"></id>
        <result property="type" column="type"></result>
        <result property="datetime" column="time"></result>
        <result property="amount" column="amount"></result>
    </collection>
</resultMap>
<select id="selectByAll" resultMap="userMap">
    select r.*,u.id as uid,u.password,u.account
   from record as r,users as u
   where u.id=r.user_id
</select>
List<User> users = mapper.selectByAll(); // 查詢所有的
users.forEach(e->{
    System.out.println(e);
});

[圖片上傳失敗...(image-79d7de-1590670310779)]

首先我需要在user類里定義一個list<record>集合才可以進(jìn)行映射,collection標(biāo)簽用于自定records對象,porperty屬性書寫對象在封裝類中的屬性名,ofType中則寫其集合對應(yīng)的泛型

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容