SSM—3.Mybatis

不放手, 不錯過發表於2020-09-24

1.Mybatis介紹

MyBatis 本是apache的一個開源專案iBatis, 2010年這個專案由apache software foundation 遷移到了google code,並且改名為MyBatis 。2013年11月遷移到Github。

iBATIS一詞來源於"internet"和"abatis"的組合,是一個基於Java的持久層框架。iBATIS提供的持久層框架包括SQL Maps和Data Access Objects(DAOs)

MyBatis 是一款優秀的持久層框架,它支援定製化 SQL、儲存過程以及高階對映。MyBatis 避免了幾乎所有的JDBC 程式碼和手動設定引數以及獲取結果集。MyBatis 可以使用簡單的 XML 或註解來配置和對映原生資訊,將介面和 Java 的 POJOs(Plain Ordinary Java Object,普通的 Java物件)對映成資料庫中的記錄。

2.搭建Mybatis

2.1.新增驅動包(mysql.jar和mybatis的jar包)

        <!--mybatis依賴包-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.4.6</version>
        </dependency>

        <!--mysql驅動包-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.40</version>
        </dependency>

2.2.新增配置檔案:src/mybatis-config.xml

連線資料庫的配置檔案的作用:

  • (1).指定連線資料庫的url,username,password,driver
  • (2).由框架自動獲取連線
  • (3).指定了事務的管理物件

配置檔案中default要和id值相同,default表示預設訪問環境,但是也可以自己指定使用哪個id資料來源,程式碼如下:

SqlSession session=
new SqlSessionFactoryBuilder().build(r,"a2").openSession();
<?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>   

 <!--連線資料庫的環境,default="環境的id"-->
    <environments default="a1">
        <environment id="a1">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://localhost:3306/springmvc?useUnicode=true&amp;characterEncoding=utf-8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <!-- 指定maper檔案的路徑(maven專案從resources原始檔夾下找資源)-->
    <mappers>
        <!--<mapper resource="包名/mapper檔名"/>-->
        <mapper resource="StudentMapper.xml"/>
    </mappers>
</configuration>

 2.3.建立實體類和介面類

i儘可能 實體類屬性名=表列名

 2.4.新增mapper檔案

注:在mapper檔案中儲存sql語句

<mapper namespace="介面的完整路徑">
    <insert id="方法名" parameterType="引數型別">
    //sql
    </insert>
    <select id="方法名" resultType="查詢後的返回值型別">
        //sql語句---注:sql語句沒有分號
    </select>
</mapper>

示例: 

<!--namespace="介面的完整路徑"-->
<mapper namespace="com.yhp.dao.StudentDao">
     <!--id="方法名"-->
     <select id="getall" resultMap="com.yhp.bean.Student">
         select * from student
     </select>
    <!--增刪改返回的是收影響的行數,不需要配置resultType,parameterType引數型別-->
    <insert id="insertStudent" parameterType="com.yhp.bean.Student" useGeneratedKeys="true" keyProperty="studentId">
        insert into student(studentno,stuname) values(#{studentNo},#{stuName})
    </insert>
</mapper>

 2.5.修改mybatis的配置檔案,讓該配置檔案知道mapper檔案的存在

<!-- 指定maper檔案的路徑(maven專案從resources原始檔夾下找資源)-->
<mappers>
    <mapper resource="StudentMapper.xml"/>
    <mapper resource="WifeMapper.xml"></mapper>
</mappers>

 2.6.獲得SqlSession,通過該物件進行資料的操作

//1.載入配置檔案
Reader r=Resources.getResourceAsReader("mybatis-config.xml");
//2.建立SqlSessionFactoryBuilder物件
SqlSessionFactoryBuilder builder= new SqlSessionFactoryBuilder();
//3.得到session工廠
SqlSessionFactory factory=builder.build(r);
//4.得到session
SqlSession session= factory.openSession();
//5.調取sql語句,insert("方法的完整路徑"),路徑=namespace+id
int rs=session.insert("dao.EmpDao.insertEmp",e);
session.commit();//增刪改需要這樣手動提交事務
 //6.關閉資源
session.close();
reader.close();

示例:

public class Test1 {
    public static void main(String[] args) {
        try {
            //1.載入配置檔案
            Reader reader = Resources.getResourceAsReader("mybatis.xml");
            //2.得到sqlSessionFactoryBuilder
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            SqlSessionFactory build = builder.build(reader);
            //3.得到SqlSession
            SqlSession session = build.openSession();
            //4.操作sql
            //方法引數是被調取的sql的完整路徑=namespace+id
            List<Student> list = session.selectList("com.yhp.dao.StudentDao.getall");
            //5.遍歷
            for (Student student : list) {
                System.out.println(student);
            }
            //6.關閉資源
            session.close();
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2.7 配置多資料來源以及資料來源切換

修改mybaits.xml檔案,新增其餘資料來源

<!--連線資料庫的環境,default="環境的id"-->
<environments default="a1">
    <environment id="a1">
        <transactionManager type="JDBC"/>
        <dataSource type="POOLED">
            <property name="driver" value="com.mysql.jdbc.Driver"/>
            <property name="url" value="jdbc:mysql://localhost:3306/hunan"/>
            <property name="username" value="root"/>
            <property name="password" value="123456"/>
        </dataSource>
    </environment>
    <environment id="a2">
        //...
    </environment>
</environments>

當前預設是a1資料來源,如何實時切換到a2資料來源

//呼叫工程時新增資料來源引數,切換到a2資料來源
SqlSessionFactory build = builder.build(reader,"a2");

 

3.Mybatis實現CRUD

3.1 mapper檔案中引數的讀取

  • 單個基本型別引數或 String 型別:
    • mapper讀取引數:#{引數名(也可以是自定義名稱)}
       <!--id=呼叫的介面的對應方法,parameterType=引數型別,resultType=返回值型別-->
      <select id="findById" parameterType="int" resultType="com.yhp.bean.Student">
          select * from student where studentid=#{id}
      </select>
      
      //Test2,4.操作sql
      //selectOne()查詢一條資料
      //方法引數是(被調取的sql的完整路徑=namespace+id,sql引數值),id=3
      Student o = session.selectOne("com.yhp.dao.StudentDao.findById", 3);

       

  • 引數型別為物件型別時,讀取引數的語法: #{物件中的屬性名}
    • insert,delete,update,select中的resultType引數可以省略
      <!--增刪改返回的是收影響的行數,不需要配置resultType-->
      <insert id="insertStudent" parameterType="com.yhp.bean.Student">
          insert into student(studentno,stuname) values(#{studentNo},#{stuName})
      </insert>
      
      //Test3,4.操作sql
      Student student = new Student();
      student.setStudentNo("s00114");
      student.setStuName("謝大腳4");
      int insert = session.insert("com.yhp.dao.StudentDao.insertStudent", student);
      session.commit();//增刪改需要提交事務,自動開啟事務

       

  • 多個引數值的情況
    • 將引數封裝到map集合中,再將map集合傳遞給mapper檔案
    • 取值的時候,#{map的key值}
      <!--public int insertStudent2(String name,String stuno);//錯誤,只能有一個引數
          public int insertStudent3(Map map);//正確
          單引數情況下,parameterType的值只要是引用型別都可以-->
      <insert id="insertStudent3" parameterType="com.yhp.bean.Student">
          insert into student(studentno,stuname) values(#{sno},#{sname})
      </insert>
      
      //Test4,4.操作sql
      Map map=new HashMap();
      map.put("sno","sno001");
      map.put("sname","廣坤1");
      int insert = session.insert("com.yhp.dao.StudentDao.insertStudent3",map);
      session.commit();

       

  • 處理結果沒有和實體類做對應的時候,可以返回map型別
    <select id="jisuan" resultType="map">

    示例: 

    <!-- public Map find();-->
    <select id="find" resultType="map">
        select max(studentid) max,min(studentid) min,avg(studentid) avg from student
    </select>
    
    //Test5,4.操作sql
    Map map = session.selectOne("com.yhp.dao.StudentDao.find");
    Set<Map.Entry> entrySet = map.entrySet();
    for (Map.Entry o : entrySet) {
        System.out.println(o);
    }

    處理多個聚合函式:使用map作為方法的返回值,預設key是列名

  • 在做查詢時,如果需要將查詢的結果和實體類屬性自動對應的話,要求:屬性名=列名
    • 新增:session.insert("namespace+id"[,傳遞給sql的引數值]);
    • 修改:session.update("namespace+id"[,傳遞給sql的引數值]);
    • 刪除:session.delete("namespace+id"[,傳遞給sql的引數值]);
    • 單行:session.selectOne("namespace+id"[,傳遞給sql的引數值]);
    • 多行:session.selectList("namespace+id"[,傳遞給sql的引數值]);

3.2 注意:

  • 增刪改的時候需要提交事務
    • session.commit();
    • session.rollback();
  • 查詢的時候要新增resultType屬性

 3.3 插入資料庫亂碼問題解決

<property name="url" value="jdbc:mysql://localhost:3306/springmvc?useUnicode=true&amp;characterEncoding=utf-8"/>

3.4 推薦:除錯介面和mapper.xml檔案的外掛

4.省略實現類

Reader r=Resources.getResourceAsReader("mybatis.xml");
SqlSession session=
new SqlSessionFactoryBuilder().build(r).openSession();
//引數是介面的class類
StudentDao dao=session.getMapper(StudentDao.class);
public class TestDao {
    public static void main(String[] args) {
        try {
            //1.獲得sqlSession物件
            Reader reader = Resources.getResourceAsReader("mybatis.xml");
SqlSession session = new SqlSessionFactoryBuilder().build(reader).openSession();
            //2.得到要呼叫的方法,引數是介面的class類
            StudentDao studentDao = session.getMapper(StudentDao.class);
            List<Student> studentList = studentDao.getall();
            for (Student student : studentList) {
                System.out.println(student);
            }
            //3.關閉資源
            session.close();
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

 

5.ThreadLocal處理sqlSession

5.1 ThreadLocal概念

介紹:ThreadLocal是什麼呢?其實ThreadLocal並非是一個執行緒的本地實現版本,它並不是一個Thread,而是threadlocalvariable(執行緒區域性變數)。也許把它命名為ThreadLocalVar更加合適。執行緒區域性變數(ThreadLocal)其實的功用非常簡單,就是為每一個使用該變數的執行緒都提供一個變數值的副本,是Java中一種較為特殊的執行緒繫結機制,是每一個執行緒都可以獨立地改變自己的副本,而不會和其它執行緒的副本衝突。

5.2 示例:

public class TestThread {
    private ThreadLocal<String> threadLocal=new ThreadLocal<String>();
    private List<String> list=new ArrayList<String>();
    class A extends  Thread{
        @Override
        public void run() {
           //存值
            System.out.println("A執行緒開始存值");
            threadLocal.set("thread內容");
            list.add("list內容");
            System.out.println("A---threadLocal="+threadLocal.get());
        }
    }
    class B extends  Thread{
        @Override
        public void run() {
            //  取值
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("B執行緒取資料");
            System.out.println("b---threadLocal="+threadLocal.get());
            System.out.println("list="+list.get(0));
        }
    }

    public static void main(String[] args) {
        TestThread testThread = new TestThread();
        TestThread.A a=testThread.new A();
        TestThread.B b=testThread.new B();
        a.start();
        b.start();
    }
}

 輸出結果:

結論:普通存值實現執行緒共享,ThreadLocal實現執行緒獨立

 

5.3 SessionUtil類:

public class SqlSessionUtil {
    private static ThreadLocal<SqlSession>threadLocal=new ThreadLocal<SqlSession>();
    private static SqlSessionFactory sqlSessionFactory;
    /**
     * 載入配置檔案
     */
    static {
        try {
            Reader resourceAsReader = Resources.getResourceAsReader("mybatis.xml");
            sqlSessionFactory= new SqlSessionFactoryBuilder().build(resourceAsReader,"a2");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    /**
     * 獲取SqlSession
     * @return
     */
    public static SqlSession getSession(){
        //從當前執行緒獲取
        SqlSession session = threadLocal.get();
        if(session==null){
            session = sqlSessionFactory.openSession();
            //將sqlSession與當前執行緒繫結
            threadLocal.set(session);
        }
        return session;
    }
    /**
     * 關閉Session
     */
    public static void closeSession(){
        //從當前執行緒獲取
        SqlSession session = threadLocal.get();
        if(session!=null){
            session.close();
            threadLocal.remove();
        }
    }
}

6.給類起別名

//mybatis.xml
<typeAliases>
    <!--給類起別名-->
  <!--  <typeAlias type="com.yhp.bean.Student" alias="student"></typeAlias>-->
    <!--指定哪些包的類可以使用別名,預設別名:類名首字母小寫(實際使用的時候,全部小寫也可以做結果對映) -->
    <package name="com.yhp.bean"></package>
</typeAliases>

//StudentMapper.xml
<select id="getall" resultMap="student">
         select * from student
</select>

7.獲得新增資料的id

適用於可以自增的主鍵列上

<insert useGeneratedKeys="true" keyProperty="userid">

示例: 

<!--useGeneratedKeys="true" keyProperty="studentId" 將自增的studentId對映到student引數的studentId屬性上-->
<insert id="insertStudent" parameterType="com.yhp.bean.Student" useGeneratedKeys="true" keyProperty="studentId">
    insert into student(studentno,stuname) values(#{studentNo},#{stuName})
</insert>

8.log4j顯示sql語句

log4j 日誌記錄

步驟一: 新增jar包

          <!--log4j依賴包-->
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.5</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.12</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

步驟二:新增log4j.properties檔案

#日誌級別DEBUG,輸出位置Console
log4j.rootLogger=DEBUG, Console 
log4j.appender.Console=org.apache.log4j.ConsoleAppender  
log4j.appender.Console.layout=org.apache.log4j.PatternLayout 
#輸出格式
log4j.appender.Console.layout.ConversionPattern=%d [%t] %-5p [%c] - %m%n  

log4j.logger.java.sql.ResultSet=INFO  
log4j.logger.org.apache=INFO  
#狀態連線級別
log4j.logger.java.sql.Connection=DEBUG  
#狀態通道級別
log4j.logger.java.sql.Statement=DEBUG  
#預狀態通道級別
log4j.logger.java.sql.PreparedStatement=DEBUG   

9.Mybatis複雜查詢

9.1 in 查詢

foreach標籤中屬性說明:

  • item 表示集合中每一個元素進行迭代時的別名,等同於c 標籤中的var
  • index 指定一個名字,用於表示在迭代過程中,每次迭代到的位置,可以不寫
  • open 表示該語句以什麼開始,
  • separator 表示在每次進行迭代之間以什麼符號作為分隔符,
  • close 表示以什麼結束,

注意:在使用foreach 的時候最關鍵的也是最容易出錯的就是collection 屬性,

  • collection該屬性是必須指定的
  • list 時取值list,陣列時取值array,map 時取值map 的key 值

(1)引數是list

//dao
public List<Student> finda(List list);
//mapper.xml
<select id="finda" resultType="student">
   select * from student where studentid in
    <foreach collection="list" item="sid" open="(" close=")" separator=",">
        #{sid}
    </foreach>
</select>
//測試
 SqlSession session = SqlSessionUtil.getSession();
 StudentDao2 studentDao2 = session.getMapper(StudentDao2.class);
 List list=new ArrayList();
 list.add(1);
 list.add(3);
 list.add(5);
 list.add(7);
 List<Student> students = studentDao2.finda(list);
 SqlSessionUtil.closeSession();

注意:parameterType 的值可以省略

(2)引數是陣列

//dao
public List<Student> findb(int[] ids);
//mapper.xml
<select id="findb" resultType="student">
    select * from student where studentid in
    <foreach collection="array" item="sid" open="(" close=")" separator=",">
        #{sid}
    </foreach>
</select>
//測試 
 SqlSession session = SqlSessionUtil.getSession();
  StudentDao2 studentDao2 = session.getMapper(StudentDao2.class);
  int[] ids=new int[]{1,3,5,7};
  List<Student> students = studentDao2.findb(ids);
  SqlSessionUtil.closeSession();

 注意:parameterType 的值可以省略

(3)引數Map

//dao
public List<Student> findc(Map map);
//mapper
<select id="findc" resultType="student">
    select * from student where studentid in
    <foreach collection="ids" item="sid" open="(" close=")" separator=",">
        #{sid}
    </foreach>
</select>
//測試
SqlSession session = SqlSessionUtil.getSession();
StudentDao2 studentDao2 = session.getMapper(StudentDao2.class);
Map map=new HashMap();
List list=new ArrayList();
list.add(1);
list.add(3);
list.add(5);
list.add(7);
map.put("ids",list);
List<Student> students = studentDao2.findc(map);
SqlSessionUtil.closeSession();

 注意:parameterType 的值可以省略

傳的值:

  • Map map=new HashMap();
  • map.put("keya", list1);

9.2 模糊查詢

(1)模糊查+分頁

如果傳遞的引數是多個時?------使用Map 集合

  • String sql;
  • StringBuffer sql;//動態sql的儲存

(2)動態sql

模糊查詢:

<if test="屬性名!=屬性值">
    and ename like '${屬性名}'
</if>

注意:

  • test屬性中讀取屬性值時直接寫屬性名
  • 模糊查詢讀取屬性時使el 表示式,${屬性名}
  • 除以上位置外,都使用#{屬性名}
  • 多個條件時使用and,or 拼接
  • 如果傳遞過來的是map型別,則test屬性中寫的是key

#{}:相當於佔位符

#{id}:其中的id可以表示輸入引數的名稱,如果是簡單型別名稱可以任意

${}:表示拼接sql語句

value: 表 示 獲 取 輸 入 的 參 數 值 {}會引起SQL隱碼攻擊,一般情況下不推薦使用。

示例:

<if test="ename!=null and ename!=''">
    and ename like '%${屬性名}%'
</if>
或者(推薦):
    and sname like "%"#{username}"%"
或者:
    sname like concat(concat('%',#{username}),'%')

示例:

   <!--public List<Student> findd(Map map);-->
    <select id="findd" resultType="student">
         select * from student where 1=1
         <if test="stuname!=null and stuname !=''">
             and  stuname like "%"#{stuname}"%"
         </if>
        <if test="sno!=null and sno!=''">
             and studentno=#{sno}
        </if>
    </select>
    <!--public List<Student> finde(Student stu);-->
    <select id="finde" resultType="student">
        select * from student where 1=1
        <if test="stuName!=null and stuName !=''">
            and  stuname like "%"#{stuName}"%"
        </if>
        <if test="studentNo!=null and studentNo!=''">
            and studentno=#{studentNo}
        </if>
    </select>
        Map map=new HashMap();
        map.put("stuname","張");
        map.put("sno","s1101");
        List<Student> students = studentDao2.findd(map)

9.3 區間查詢

  • between 開始值 and 結束值
  • 列名 >=開始時間 and 列名<=結束時間
<if test="stu.endTime!=null and stu.endTime!=''">
    and regdate <![CDATA[ <= ]]> #{stu.endTime}
</if>

示例: 

<select id="findf" resultType="student">
    <!--select * from student where studentid between  #{begin} and #{end}-->
    /*  <![CDATA[ <= ]]>表示使"小於等於"正確編譯,xml預設小於號為開始尖括號    */
    select * from student where studentid>=#{begin} and studentid   <![CDATA[ <= ]]>  #{end}
</select>
Map map=new HashMap();
map.put("begin",1);
map.put("end",5);
List<Student> students = studentDao2.findf(map);

 

9.4 resultMap

(1)處理單表關係(屬性名!= 表名)

通過給列起別名,讓別名=屬性名,也可以實現資料對應

  • resultType="指定返回值的型別"
    • 當列名和屬性名一致時使用
  • resultMap = 所需resultMap標籤的id值
    • 1.當列名和屬性名不一致
    • 2.做多表查詢時
  • mybatis 能實現的是單標的自動操作
<mapper namespace="com.yhp.dao.GradeDao">
    <resultMap id="rs2" type="com.yhp.bean.Grade">
        <!--可以手動指定列名和屬性名的關係,先描述自身的資訊,然後描述關聯表的資訊,id表述主鍵列,非主鍵列使用result描述-->
        <id column="gid" property="gid"></id>
        <result column="gname" property="gname"></result>
        <collection property="studentList" ofType="com.yhp.bean.Student">
            <id column="studentid" property="studentId"></id>
            <result column="stuname" property="stuName"></result>
            <result column="studentno" property="studentNo"></result>
            <result column="stuage" property="age"></result>
        </collection>
    </resultMap>
   <select id="findbyGid" resultMap="rs2">
      select * from grade g,student s
      where s.gradeid=g.gid and g.gid=#{id}
   </select>
</mapper>

(2)處理多表關係

兩表聯查:一對多和多對一

注:

  • 如果是單表查詢,select 中使用resultType 設定返回的型別即可
  • 但是如果是多表聯查,那麼select 查詢的結果需要單獨使用resultMap 標籤來進行結果的對映
  • 存的是集合的話使用Collection 子標籤 和 ofType="Java型別">
  • 存的是一方的話使用association 子標籤 和 javaType="Java型別"
  • resultType 和resultMap 屬性只能出現一個

格式:

一對多:

<resultMap type="" id="自定義名稱">
    <id property="id" column="dept_id" /><!--主鍵列-->
    <result property="java 屬性名" column="列名" />
    <collection property="屬性名" ofType="java 型別">
        <id property="屬性名" column="列名" />
        <result property="屬性名" column="列名" />
    </collection>
</resultMap>

多對一:

<resultMap type="" id="">
    <id property="" column="" />
    <result property="" column="" />
    <association property="" javaType="">
        <id property="" column="" />
        <result property="" column="" />
    </association>
</resultMap>

JavaType和ofType都是用來指定物件型別的,但是JavaType是用來指定pojo中屬性的型別,而ofType指定的是對映到list集合屬性中pojo的型別。

注意:

  • 增加mapper.xml檔案別忘記在mybatis.xml中指定路徑
  • 多表聯查使用resultMap標籤所有屬性都要寫上去,無論屬性名是否與列明相同
  • 多對多就是兩個多對一,在各自實體類新增對方的集合
  • 關於中間類,如果中間表全是兩表的資訊就不用建中間類,如果有其餘的資訊就要建

 

10.pageHelper分頁

sql 語句只需要查詢資料,不實現分頁程式碼

方式1:記憶體分頁(邏輯分頁)

Mybatis使用RowBounds物件進行分頁,它是針對ResultSet結果集執行的記憶體分頁,而非物理分頁。可以在sql內直接書寫帶有物理分頁的引數來完成物理分頁功能,也可以使用分頁外掛來完成物理分頁。

優缺點

  • 物理分頁每次都要訪問資料庫,邏輯分頁只訪問一次
  • 物理分頁佔用記憶體少,邏輯分頁相對較多
  • 物理分頁資料每次都是最新的,邏輯分頁有可能滯後

rowBounds實現分頁:

SqlSession sqlSession = 
new SqlSessionFactoryBuilder().build(Resources.getResourceAsReader("mybatis-config.xml"))
    .openSession();
//null指dao方法沒有引數,rowBounds(開始位置下標,顯示條數)
List<Users> usersList = 
    sqlSession.selectList("com.yhp.dao.UsersDao.findall",null, new RowBounds(0,3));

for (Users users : usersList) {
    System.out.println(users.getUsername());
}

sql語句:

<select id="findall" resultType="com.yhp.bean.Users">
    select * from users
</select>

方式2:使用分頁外掛(物理分頁)

分頁外掛的基本原理是使用Mybatis提供的外掛介面,實現自定義外掛,在外掛的攔截方法內攔截待執行的sql,然後重寫sql,根據dialect方言,新增對應的物理分頁語句和物理分頁引數。

示例:

<select id="findall" resultType="bean.Emp">
    select * from emp
</select>

(a)匯入jar 包

  • 分頁外掛:pagehelper.jar
  • sql 解析工具:jsqlparser.jar

(b) 在MyBatis 的總體檔案中配置外掛

  • 放到之前
<plugins>
    <!-- PageHelper4.1.6 -->
    <plugin interceptor="com.github.pagehelper.PageHelper">
        <property name="dialect" value="mysql"/>
    </plugin>
</plugins>

注意:外掛5.1以後interceptor不同,並且不需要指定資料庫名字

     <!--分頁外掛-->
    <plugins>
        <plugin interceptor="com.github.pagehelper.PageInterceptor">
        </plugin>
    </plugins>

 (c) 在執行查詢之前設定

  • PageHelper.startPage(當前頁,每頁條數)

示例:

public static void main(String[] args) {
        try {
            //1.載入配置檔案
            Reader reader = Resources.getResourceAsReader("mybatis.xml");
            //2.得到sqlSessionFactoryBuilder
            SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
            SqlSessionFactory build = builder.build(reader,"a2");
            //3.得到SqlSession
            SqlSession session = build.openSession();
            //4.操作sql
            //4.1 在工具類中指定頁碼值和顯示條數
            PageHelper.startPage(5,3);
            //4.2 調取dao層方法,得到結果集
            List<Student> list = session.selectList("com.yhp.dao.StudentDao.getall");
            //4.3 建立分頁工具類物件並將list集合封裝到PageInfo物件中
            PageInfo<Student> info = new PageInfo<Student>(list);
            //5.從分頁資料中獲得資料
            for (Student student : info.getList()) {
                System.out.println(student);
            }
            System.out.println("當前頁條數:"+info.getSize());
            System.out.println("總條數:"+info.getTotal());
            System.out.println("總頁數:"+info.getPages());
            System.out.println("上一頁:"+info.getPrePage()); //如果沒有上一頁,則返回0
            System.out.println("下一頁:"+info.getNextPage());//如果沒有下一頁,則返回0
            System.out.println("當前頁:"+info.getPageNum());
            System.out.println("顯示條數:"+info.getPageSize());
            //6.關閉資源
            session.close();
            reader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

pageHelper 分頁原理

原理:

PageHelper攔截的是org.apache.ibatis.executor.Executor的query方法,其傳參的核心原理是通過ThreadLocal進行的。

當我們需要對某個查詢進行分頁查詢時,我們可以在呼叫Mapper進行查詢前呼叫一次PageHelper.startPage(..),這樣PageHelper會把分頁資訊存入一個ThreadLocal變數中。

在攔截到Executor的query方法執行時會從對應的ThreadLocal中獲取分頁資訊,獲取到了,則進行分頁處理,處理完了後又會把ThreadLocal中的分頁資訊清理掉,以便不影響下一次的查詢操作。

所以當我們使用了PageHelper.startPage(..)後,每次將對最近一次的查詢進行分頁查詢,如果下一次查詢還需要進行分頁查詢,需要重新進行一次PageHelper.startPage(..)。

這樣就做到了在引入了分頁後可以對原來的查詢程式碼沒有任何的侵入性。

此外,在進行分頁查詢時,我們的返回結果一般是一個java.util.List,PageHelper分頁查詢後的結果會變成com.github.pagehelper.Page型別,其繼承了java.util.ArrayList,所以不會對我們的方法宣告造成影響。

com.github.pagehelper.Page中包含有返回結果的分頁資訊,包括總記錄數,總的分頁數等資訊,所以一般我們需要把返回結果強轉為com.github.pagehelper.Page型別。

11.快取

11.1 一級快取

SqlSession 的快取 ------>自動開啟

11.2 二級快取

做到從不同的快取中共享資料

SqlSessionFactory 的快取 --->需要手動開啟

對映配置檔案中配置

<mapper namespace="介面路徑">
    <cache eviction="FIFO"
    flushInterval="60000"
    size="512"
    readOnly="true"/>
</mapper>

說明:

  • eviction: 二級快取中,快取的物件從快取中移除的策略,回收策略為先進先出
  • flushInterval: 重新整理快取的事件間隔,單位:毫秒
  • size: 快取物件的個數
  • readOnly: 是否是隻讀的

測試程式碼:

//不同qlSession,要同一個sqlSessionFactory
SqlSessionFactory factory= new SqlSessionFactoryBuilder()
.build(Resources.getResourceAsReader("mybatis-config.xml"));
SqlSession sqlSession1=factory.openSession();
Student student = sqlSession1.selectOne("com.yhp.dao.StudentDao.findbystuid", 1);
System.out.println(student.getSname());
sqlSession1.close();
System.out.println("===================================");
SqlSession sqlSession2= factory.openSession();
student = sqlSession2.selectOne("com.yhp.dao.StudentDao.findbystuid", 1);
System.out.println(student.getSname());
sqlSession2.close();
  • cache元素用來開啟當前mapper的namespace下的二級快取,該元素的屬性設定如下:
  • flushInterval:重新整理間隔,可以被設定為任意的正整數,而且它們代表一個合理的毫秒形式的時間段,預設情況下是不設定的,也就是沒有重新整理間隔,快取僅僅呼叫語句時重新整理。
  • size:快取數目,可以被設定為任意正整數,要記住你的快取物件數目和你執行環境可用記憶體資源數目,預設值是1024.
  • readOnly:只讀,屬性可以被設定為true或false,只讀的快取會給所有呼叫者返回快取物件的相同例項,因此這些物件不能被修改。這提供了很重要的效能優勢,可讀寫的快取會返回快取物件的拷貝(通過序列化),這會慢一些,但是安全,因此預設是false。
  • eviction:收回策略,預設為LRU,有如下幾種:
  • LRU:最近最少使用的策略,移除最長時間不被使用的物件。
  • FIFO:先進先出策略,按物件進入快取的順序來移除它們。
  • SOFT:軟引用策略,移除基於垃圾回收器狀態和軟引用規則的物件。
  • WEAK:弱引用策略,更積極地移除基於垃圾收集器狀態和弱引用規則的物件。

注意:

  • 使用二級快取時,與查詢結果對映的java物件必須實現java.io.Serializable介面的序列化和反序列化操作,
  • 如果存在父類,其成員都需要實現序列化介面,實現序列化介面是為了對快取資料進行序列化和反序列化操作,因為二級快取資料儲存介質多種多樣,不一定在記憶體,有可能是硬碟或者遠端伺服器。

12.Mybatis註解

推薦:http://www.yanhongzhi.com/post/mybatis-annotation.html

在mybatis中可以將sql語句通過註解的方式定義在java中,此時mybaits.xml配置檔案掃描該註解的位置即可,程式碼如下:

<mapper class="com.dao.StudentDao"></mapper>

 ------定義在介面方法上

12.1 @Insert

@Insert("insert into student(username,password,birthday) values(#{user_name},#{password},#
{birthday})")
@Options(useGeneratedKeys = true,keyProperty = "userid")
public int insertstu(Student student);

12.2 @Delete

@Delete("delete from student where userid=#{userid}")
public int deleteuser(int userid);

12.3 @Update

@Update("update student set username=#{user_name},sex=#{sex} where userid=#{userid}")
public int updateuser(Student stu);

12.4 @Select

示例一:普通查詢 

@Select("select * from student")
/* @Results({
@Result(id = true, property = "id", column = "test_id")
@Result(column = "username",property = "user_name")
})*/

注意:多個@Result的時候兩側加大括號{},主鍵寫上id=true

示例二:帶函式查詢

@Select("select count(*) from student")
public int totalCount();

//計算id的最大值,最小值,平均值,結果多屬性且不是實體類則用Map返回
@Select("select max(studentid) max,min(studentid) min,avg(studentid) avg from student")
public Map total2();
//測試
/*   int i = studentDao.totalCount();
   System.out.println("total="+i);*/
   
   Map map = studentDao.total2();
   Set<Map.Entry> set = map.entrySet();
   for (Map.Entry entry : set) {
       System.out.println(entry);
   }
   SqlSessionUtil.closeSession();

------sql註解定義在統一的工具類SqlUtil裡

12.5 @SelectProvider、@InsertProvider、@DeleteProvider、@UpdateProvider

  • @SelectProvider(type = 自定義sql所在的類.class, method = "sql定義的方法")
    • 例項:@SelectProvider(type = SqlTemp.class,method ="getall44")
  • @InsertProvider(type = SqlTemp.class,method = "insert")
  • @DeleteProvider(type = SqlTemp.class,method = "delete")
  • @UpdateProvider(type = SqlTemp.class,method = "update")

示例:

public class SqlUtil {
    //返回值一定要是字串
    public String insertMethod(){
        return "insert into student(studentno,stuname,stuage) values(#{studentNo},#{stuName},#{age})";
    }

    public String updateMethod(){
        return "update student set studentno=#{studentNo},stuname=#{stuName} where studentid=#{studentId}";
    }
    public String selectall(){
        return "select * from student";
    }
    //不推薦
    public String gradestudent(Map map){
        StringBuffer sql=new StringBuffer("select * from student s,grade g where
        s.gid=g.gradeid");
        if (map.get("uname")!=null){
            sql.append(" and username like '%"+map.get("uname")+"%'");
        }
        if(map.get("gname")!=null){
            sql.append(" and gradename like '%"+map.get("gname")+"%'");
        }
        return sql.toString();
    }
}

然後需要在對應介面中加入註解標識

public interface StudentDao2 {
    //增刪改查
    @InsertProvider(type = SqlUtil.class,method = "insertMethod")
    @Options(useGeneratedKeys = true,keyProperty = "studentId")//獲得新增資料的id
    public int insertStu(Student student);

    @UpdateProvider(type = SqlUtil.class,method = "updateMethod")
    public int updateStu(Student student);

    @SelectProvider(type = SqlUtil.class,method = "selectall")
    @Results({
            @Result(column = "stuage",property = "age")
    })
    public List<Student> findall();
}
  •  type=工具類.class
  • method=工具類中對應的方法名

12.6 @ResultType

當實體類和表列名一致時指定返回型別

@ResultType(Student.class)
public List<Student> findall44();

12.7@ResultMap

當實體類和表列名不一致時或註解實現兩表聯查   匹配mapper檔案定義好的ResultMap標籤

@ResultMap("mapper檔案中的id名即可")
public List<Student> findall33();

注意:

  • (1)mapper檔案中namespace的值要寫當前介面的全路徑
  • (2)配置檔案中載入介面和mapper.xml二選一

例項程式碼:

mapper檔案: 這裡namespace一定是介面的完整路徑

<mapper namespace="com.xzk.dao.StudentDao2">
    <resultMap id="rs3" type="com.xzk.bean.Student">
        <id column="studentid" property="studentId"></id>
        <result column="stuname" property="stuName"></result>
        <result column="studentno" property="studentNo"></result>
        <result column="stuage" property="age"></result>
        <association property="grade" javaType="com.xzk.bean.Grade">
            <id column="gid" property="gid"></id>
            <result column="gname" property="gname"></result>
        </association>
    </resultMap>

</mapper>

 配置檔案:只需要掃描mapper檔案,不需要掃描介面

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

介面:

@Select("select * from student s ,grade g where s.gid=g.cid")
@ResultMap("com.yhp.dao.StudentDao2.a1")
public List<Student> findstu_grade();

 

12.8 @Param

繫結引數,

@Insert("insert into student(sname1,sex) values(#{sname},#{sex})")
public int insertStu(@Param("sname") String name, @Param("sex")String usersex);

注意:在自定義sql的方法方法中只能使用#{} 

12.9 @Options

(1)@Options(useGeneratedKeys = true,keyProperty = "studentId")

//增刪改查
@Insert("insert into student(sname1,sex) values(#{sname},#{sex})")
@Options(useGeneratedKeys = true,keyProperty = "studentId")//將新增資料的id對映到引數屬性上
public int insertStu(Student student);

(2)開啟二級快取

@Options(useCache = true,
flushCache = Options.FlushCachePolicy.FALSE, //表示查詢時不重新整理緩
timeout = 10000) //表示查詢結果快取10000秒

注意:需要和@CacheNamespace一起使用,並且物件需要實現序列化介面

12.10 @CacheNamespace

@CacheNamespace(size = 512) : 定義在該名稱空間內允許使用內建快取,最大值為512個物件引用,

讀寫預設 是開啟的,

快取內省重新整理時間為預設3600000毫秒,用來修飾介面

@CacheNamespace
public interface StudentDao2 {
。。。
}

12.11 動態sql(不推薦)

@Select(" <script>select * from student where 1=1 " +
        " <if test=\"name!=null and name!=''\">" +
        " and username like '%${name}%'" +
        " </if>" +
        " <if test=\"'pass!=null'\">" 
        " and password like '%${pass}%'"+
        " </if></script>") 

13.Lombok外掛

在idea工具中新增Lombok外掛:

安裝依賴包:

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.2</version>
    <scope>provided</scope>
</dependency>

lombok的使用

  • @Data 註解在類上;提供類所有屬性的 getting 和 setting 方法,此外還提供了equals、canEqual、hashCode、toString 方法
  • @Setter :註解在屬性上;為屬性提供 setting 方法
  • @Getter :註解在屬性上;為屬性提供 getting 方法
  • @Log4j :註解在類上;為類提供一個 屬性名為log 的 log4j 日誌物件
  • @NoArgsConstructor :註解在類上;為類提供一個無參的構造方法
  • @AllArgsConstructor :註解在類上;為類提供一個全參的構造方法
  • @Cleanup : 可以關閉流
  • @Builder : 被註解的類加個構造者模式
  • @Synchronized : 加個同步鎖
  • @SneakyThrows : 等同於try/catch 捕獲異常
  • @NonNull : 如果給引數加個這個註解 引數為null會丟擲空指標異常
  • @Value : 註解和@Data類似,區別在於它會把所有成員變數預設定義為private final修飾,並且不會生成set方法。
  • @ToString 重寫toString()方法

示例:

@Data
@NoArgsConstructor
@AllArgsConstructor
//@Value
public class Student implements Serializable {
    private int studentId; //屬性名=列名
    private String studentNo;
    private String stuName;
    //private int stuAge;
    private int age;
    private Grade grade;
}

14.Mybatis自動化

作用:反向生成實體類,介面,mapper.xml

新增依賴包:

        <!--.Mybatis自動化依賴包-->
        <dependency>
            <groupId>org.mybatis.generator</groupId>
            <artifactId>mybatis-generator-core</artifactId>
            <version>1.3.5</version>
        </dependency>

載入外掛:

<build>
        <plugins>
            <plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.5</version>
                <configuration>
                    <!--配置檔案的路徑-->                   
<configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
                    <overwrite>true</overwrite>
                </configuration>
                <dependencies>
                    <dependency>
                        <groupId>org.mybatis.generator</groupId>
                        <artifactId>mybatis-generator-core</artifactId>
                        <version>1.3.5</version>
                    </dependency>
                </dependencies>
            </plugin>
        </plugins>
    </build>

修改配置檔案:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
        PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
        "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">

<!-- 配置生成器 -->
<generatorConfiguration>
    <!--資料庫驅動jar -->
    <classPathEntry
            location="D:\maven\m2\repository\mysql\mysql-connector-java\5.1.40\mysql-connector-java-5.1.40.jar" />

    <context id="MyBatis" targetRuntime="MyBatis3">

        <!--去除註釋 -->
        <commentGenerator>
            <property name="suppressAllComments" value="true" />
        </commentGenerator>

        <!--資料庫連線 -->
        <jdbcConnection driverClass="com.mysql.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/springmvc"
                        userId="root"
                        password="123456">
        </jdbcConnection>

        <!--生成實體類 指定包名 以及生成的地址 (可以自定義地址,但是路徑不存在不會自動建立
        使用Maven生成在target目錄下,會自動建立) -->
        <javaModelGenerator targetPackage="com.sh.bean"
                            targetProject="D:\IdeaProjects\KaiKeBa\Mybatis自動化\src\main\java">
            <property name="trimStrings" value="true" />
        </javaModelGenerator>


        <!--生成SQLmapper檔案 -->
        <sqlMapGenerator targetPackage="mapper"
                         targetProject="D:\IdeaProjects\KaiKeBa\Mybatis自動化\src\main\resources">
        </sqlMapGenerator>
        <!--生成Dao檔案,生成介面 -->
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="com.sh.dao"
                             targetProject="D:\IdeaProjects\KaiKeBa\Mybatis自動化\src\main\java">
        </javaClientGenerator>


        <table tableName="student" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false"
               enableSelectByExample="false" selectByExampleQueryId="false">
        </table>
        <table tableName="grade" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false"
               enableSelectByExample="false" selectByExampleQueryId="false">
        </table>
        <table tableName="role" enableCountByExample="false"
               enableUpdateByExample="false" enableDeleteByExample="false"
               enableSelectByExample="false" selectByExampleQueryId="false">
        </table>

    </context>
</generatorConfiguration>

執行:maven Project選項卡->plugins->找到mybatis-generator-core,雙擊執行就會自動生成

注意:執行一次即可,如果執行過程中,未完全成功。則將原來生成的程式碼刪除後,再次執行。

切記!切記!切記!

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


 

相關文章