天天看點

MyBatis持久層架構與MybatisPlus

1.概念

MyBatis 是支援普通 SQL 查詢,存儲過程和進階映射的優秀持久層架構。MyBatis 消除了幾乎所有的 JDBC 代碼和參數的手工設定以及結果集的檢索。

  1. 簡化JDBC的開發
  2. 能夠更好的完成ORM(Object Relational Mapping對象關系映射)

 2.内部元件結構圖

MyBatis持久層架構與MybatisPlus

 3.導入依賴

        <!--mybatis依賴包-->

        <dependency>

            <groupId>org.mybatis.spring.boot</groupId>

            <artifactId>mybatis-spring-boot-starter</artifactId>

            <version>2.1.4</version>

        </dependency>

        <!--jdbc依賴包-->

        <dependency>

            <groupId>mysql</groupId>

            <artifactId>mysql-connector-java</artifactId>

            <version>5.1.48</version>

        </dependency>

 4.核心配置檔案

<!-- mybatis的核心配置檔案 -->

 <configuration>

     <environments default="test">

         <environment id="test">

             <transactionManager type="JDBC"></transactionManager>

             <dataSource type="POOLED">

             <property name="driver" value="com.mysql.jdbc.Driver"/> 

            <property name="url" value="jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf8&amp;serverTimezone=Asia/Shanghai" /> 

                <property name="username" value="root"/> 

                <property name="password" value="root"/> 

             </dataSource>

         </environment>

     </environments>

     <mappers><mapper resource="mappers/UserMapper.xml"/></mappers>

</configuration>

5.實作代碼

        //1,建立SqlSessionFactory對象,線程非安全,用來産生SqlSession

        //2,建立SqlSession,用來執行sql

        //3, 定位SQL: namespace的值+id的值,可以隻傳入id,但是,如果在mybatis的環境中有多個                                   相同id的映射名稱,就會報錯。

        //4,解析結果并列印

       InputStream in = Resources.getResourceAsStream("mybatis-config.xml");

       SqlSessionFactory session = new SqlSessionFactoryBuilder().build(in);

        SqlSession sqlSession = session.openSession();

        List<User> list = sqlSession.selectList("hello.get");

        for (User u : list) {

                System.out.println(u);

        }

<mapper namespace="hello">

        <select id="get" resultType="cn.tedu.pojo.User">

              select * from user

        </select>

</mapper>

6.ResultMap簡單使用

當資料庫的字段名和對象的屬性名一緻時,可以用簡單屬性resultType。

但是當資料庫中的字段名稱和對象中的屬性名稱不一緻時,就需要resultMap屬性。

例子: 

 <mapper namespace="cn.tedu.mybatis.pojo.PersonMapper">

    <!-- 最強大對象resultMap,結果封裝到哪個pojo對象,type就是誰 -->

    <resultMap type="Person" id="personRM">

        <!-- 映射主鍵 -->

        <id column="id" property="id"/>

        <!-- 映射其他列 -->

        <!—單獨處理屬性名和字段名不一緻的 -->

        <result property="userName" column="user_name"/>

    </resultMap>

    <select id="find"  resultMap="personRM">

        SELECT id,user_name FROM person WHERE id=#{id}

    </select>

 <mapper>

7.#{}與${}差別

#{}是預編譯處理,${}是字元串替換。

Mybatis 在處理#{}時,會将 sql 中的#{}替換為?号,調用 PreparedStatement 的 set 方法來指派;

Mybatis 在處理${}時,就是把${}替換成變量的值。

總結:

          使用#{}進行預編譯處理,可以有效的防止 SQL 注入攻擊,提高系統安全性。

注意:order by後面的關鍵字必須用${}

           傳遞表名時必須用${}

            其他情況能使用#{}就不要使用${}

8.MyBatis與SpringBoot進行整合

(1)xml檔案

#spring整合資料源 最快的資料源

spring:

  datasource:

    #使用高版本驅動時使用cj

    #serverTimezone=GMT%2B8   東8區   %2B +号

    #&useUnicode=true&characterEncoding=utf8 是否開啟unicode編碼/utf-8

    #&autoReconnect=true  斷線是否重連

    #&allowMultiQueries=true 是否允許批量操作

    driver-class-name: com.mysql.cj.jdbc.Driver

    url: jdbc:mysql://127.0.0.1:3306/jt?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true

    username: root

    #如果密碼以數字0開頭,則使用""号包裹  "0123456"

    password: root

#SpringBoot整合Mybatis配置

mybatis:

  #設定别名包

  type-aliases-package: com.jt.pojo

  #加載映射檔案

  mapper-locations: classpath:/mappers/*.xml

  #開啟駝峰映射

  configuration:

    map-underscore-to-camel-case: true

(2)為接口建立代理對象

@Mapper

@MapperScan("com.jt.mapper")

(3)實作資料查詢時有2種Sql的寫法

    将所有的Sql語句都寫到xml 映射檔案中. (萬能操作方式)

    可以将Sql語句通過注解的方式辨別在接口方法中.(隻适用于簡單操作)

       @select  @insert  @update  @delete

9.MyBatisPlus

使用MP主要完成單表的CURD操作簡化開發

(1) 說明:

  1. POJO應該與資料庫中的表完成映射
  2. POJO中的屬性與表中的字段一一映射

     注解:

  1. @TableName(“demo_user”) //實作對象與表名映射
  2. //設定主鍵自增 @TableId(type = IdType.AUTO)
  3. @TableField(“name”) 實作屬性與字段映射(如果屬性與字段的名稱一緻,則注解可以省略)

(2)Mapper繼承公共的接口BaseMapper,添加泛型對象

(3)建立條件構造器,封裝where條件(預設的關系連結符 and)

            邏輯運算符 > gt, < lt, = eq, >= ge, <= le, != ne

            like:leftlike,rightlike

            關鍵字: order by 排序     預設規則: 升序 asc 降序 desc

     QueryWrapper queryWrapper = new QueryWrapper();

(4)利用MP實作分頁查詢查詢

PageResult
@Data
@Accessors(chain = true)
public class PageResult {
    private String query; //查詢的key
    private Integer pageNum; //頁數
    private Integer pageSize; //條數
    private Long total; //總數
    private Object rows; //分頁後的結果
}        
UserController

/**
 * 需求: 利用分頁展現使用者user清單資料
 * URL: /user/list   GET方式
 * 請求參數:  http://localhost:8091/user/list?query=查詢關鍵字&pageNum=1&pageSize=10
 * 傳回值: SysResult對象(PageResult)
 * */
@GetMapping("/list")
public SysResult getUserList(PageResult pageResult){
    pageResult = userService.getUserList(pageResult);
    return SysResult.success(pageResult);
}       
UserServiceImpl
/**
 * 利用MP的方式實作分頁查詢
 * API說明:selectPage(arg1,arg2)
 * arg1:  MP中的分頁對象  固定的
 * arg2:  MP分頁中的條件構造器
 * @param pageResult
 * @return
 */
@Override
public PageResult getUserList(PageResult pageResult) {
    //1.定義MP的分頁對象  arg1:頁數  arg2:行數
    IPage iPage = new Page(pageResult.getPageNum(),pageResult.getPageSize());
    //2.建構查詢條件構造器
    QueryWrapper queryWrapper = new QueryWrapper();
    //判斷使用者資料是否有效   有效true  無效false
    boolean flag = StringUtils.hasLength(pageResult.getQuery());
    queryWrapper.eq(flag,"username",pageResult.getQuery());
    //經過MP分頁查詢将所有的分頁資料(total/結果/頁面/條數)封裝到iPage對象
    iPage = userMapper.selectPage(iPage,queryWrapper);
    //從分頁對象中擷取分頁@Transactional後的總記錄數/結果
    return pageResult.setTotal(iPage.getTotal()).setRows(iPage.getRecords());
}      
MP配置類
//命名規則:類似于配置檔案 則把這個類稱之為"配置類" 一般Config接我
@Configuration //辨別是一個配置類(代替之前的xml檔案)
public class MybatisPlusConfig {
    //鋪墊:xml中通過标簽管理對象,将對象交給Spring容器管理 <Bean>
    //配置類:将方法的傳回值交給Spring容器管理 @Bean注解

    /**
     * 關于MP分頁規則說明
     *  規則:需要設定一個攔截器,将分頁的sql進行動态的拼接
     *  sql規則:現在的sql都支援sql92标準
     * */
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new                      PaginationInnerInterceptor(DbType.MARIADB));
        return interceptor;
    }
}      

(5)MP實作自動填充

//pojo基類,完成2個任務,2個日期,實作序列化
@Data
@Accessors(chain=true)
public class BasePojo implements Serializable{
   @TableField(fill = FieldFill.INSERT)
   private Date created;  //表示入庫時需要指派
   @TableField(fill = FieldFill.INSERT_UPDATE)
   private Date updated;  //表示入庫/更新時指派.
}
      
MP對外暴露了一個自動填充的接口MetaObjectHandler ,使用者隻需要實作該接口,并且重寫其中的方法。即可以實作自動填充的功能。
@Component //将對象交給Spring容器管理  不屬于C/S/M
public class MyMetaObjectHandler implements MetaObjectHandler {

    // 入庫操作時調用created/updated
    /**
     * setFieldValByName(arg1,arg2,arg3)
     * arg1 自動填充的字段名稱
     * arg2 自動填充的值
     * arg3 metaObject(固定寫法)
     */

    @Override
    public void insertFill(MetaObject metaObject) {
        //設定時間變量
        Date date = new Date();
        this.setFieldValByName("created", date, metaObject);
        this.setFieldValByName("updated", date, metaObject);
    }

    //更新操作時調用updated
    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updated", new Date(), metaObject);
    }
}      

10.面試題:

(1)當實體類中的屬性名和表中的字段名不一樣 ,怎麼辦?

  1. 在Mapper.xml檔案中使用resultMap來定義映射規則
  2. 寫SQL語句時起别名

(2)模糊查詢like怎麼寫?

  1. 在java代碼中添加SQL通配符%(傳參)    
  2. 在SQL語句拼接通配符%,但是可能會引起SQL注入攻擊

例子1: 

   string wildcardname = “%smi%”;

   list<name> names = mapper.selectlike(wildcardname);

   <select id=”selectlike”>

      select * from foo where bar like #{value}

   </select>

例子2

    string wildcardname = “smi”;

   list<name> names = mapper.selectlike(wildcardname);

    <select id=”selectlike”>

       select * from foo where bar like "%"#{value}"%"

    </select>

(3)Mybatis Dao層 接口的工作原理是什麼?

       Dao接口的工作原理是JDK動态代理,Mybatis運作時,使用JDK動态代理為Dao接口生成代理對象。

(4)Mybatis 的一級、二級緩存?

一級緩存:

       Mybatis支援緩存,但在沒有配置的情況下,預設情況下它隻啟用一級緩存。級别1緩存隻對相同的SqlSession啟用。是以,如果SQL參數一模一樣,我們使用相同的SqlSession對象調用映射方法,通常隻執行SQL一次,因為第一個查詢使用SelSession MyBatis将把它放在緩存中,和将來查詢,如果沒有聲明需要重新整理,如果緩存中沒有,SqlSession将擷取目前緩存的資料,并且不會再次向資料庫發送SQL。

二級緩存:

       MyBatis的二級緩存是Application級别的緩存,它可以提高對資料庫查詢的效率,以提高應用的性能。二級緩存與一級緩存其機制相同,預設也是采用PerpetualCache,HashMap存儲,不同在于其存儲作用域為Mapper(Namespace),并且可自定義存儲源,如Ehcache。預設不打開二級緩存,要開啟二級緩存,使用二級緩存屬性類需要實作Serializable序列化接口(可用來儲存對象的狀态),可在它的映射檔案中配置<cache/>。

(5)什麼是 MyBatis 的接口綁定?有哪些實作方式?

       接口綁定,就是在MyBatis中任意定義接口,然後把接口裡面的方法和SQL語句綁定,我們直接調用接口方法就可以,這樣比起原來了SqlSession提供的方法我們可以有更加靈活的選擇和設定。

       接口綁定有兩種實作方式,一種是通過注解綁定,就是在接口的方法上面加上@Select、@Update等注解,裡面包含Sql語句來綁定;另外一種就是通過xml裡面寫SQL來綁定,在這種情況下,要指定xml映射檔案裡面的namespace必須為接口的全路徑名。

總結:當Sql語句比較簡單時候,用注解綁定,當SQL語句比較複雜時候,用xml綁定,一般用xml綁定的比較多。

(6)使用 MyBatis 的 mapper 接口調用時有哪些要求?

  1. Mapper接口方法名和mapper.xml中定義的每個sql的id相同; 
  2. Mapper接口方法的輸入參數類型和mapper.xml中定義的每個sql的parameterType的類型相同;
  3. Mapper接口方法的輸出參數類型和mapper.xml中定義的每個sql的resultType的類型相同;
  4. Mapper.xml檔案中的namespace即是mapper接口的類路徑。

 (7)JDBC和MyBatis的差別?

       JDBC是java提供了一套專門用于和資料庫對接的api,java.sql.*,其規範了如何和資料庫進行對接,實作由各資料庫廠商進行各自的實作和擴充。學習JDBC重點在學習如何使用其api。

       MyBatis架構是輕量級封裝了JDBC,我們已經看不到這些api,連接配接connection、語句preparedstatement、結果集ResultSet,而關注的是mybatis架構體系如何去使用,一旦寫好,我們關注的是java對象。

繼續閱讀