天天看點

MyBatis架構常用優化方法

一、優化之Session工具類

1、單獨使用 MyBatis時,可以封裝一個用來擷取MyBatis中Session的工具類

回顧之前使用Mybatis來操作資料庫的步驟:

(1)啟動mybatis架構( SqlSession---->SqlSessionFactory---->SqlSessionFactoryBuilder)

(2)通過 SqlSessionFactoryBuilder來得到SqlSessionFactory ;

(3)通過SqlSessionFactory 來建立SqlSession;

(4)讀source檔案下的mybatis.xml,将mybatis.xml檔案轉化成流

(5)建立SqlSession對象

public static SqlSession session;

   //啟動mybatis架構,讀配置檔案,擷取Session對象
   public void getSession() throws IOException{
        // (1)啟動mybatis架構
        // SqlSession---->SqlSessionFactory---->SqlSessionFactoryBuilder
        SqlSessionFactoryBuilder sfb = new SqlSessionFactoryBuilder();
        // 讀source檔案下的mybatis.xml,将mybatis.xml檔案轉化成流
        InputStream ins = Resources.getResourceAsStream("mybatis.xml");
        // (2)建立SQLSessionFactory工廠對象
        SqlSessionFactory ssf = sfb.build(ins);
        // (3)建立SqlSession對象
         session = ssf.openSession();
    }
           

每次操作資料庫都要重複上述步驟,是以,可以像封裝jdbcUtil一樣,将它封裝成一個工具類!

2、MybatisUtil.java

package cn.java.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.IOException;
import java.io.InputStream;

public class MybatisUtil {
   //1.SqlSessionFactoryBuilder:
    // 作用:負責建構SqlSessionFactory對象,使用build()方法可建立多個SqlSessionFactory對象
    // 生命周期:隻存在于方法體内,用過就不需要了
    // 一旦建立了SqlSessionFactory,就不再需要該類了,一般用于局部變量
   //2.SqlSessionFactory:MyBatis應用的核心
      //作用:建立SqlSession對象
      //生命周期:與應用的生命周期相同,作用于Application
    //  單例模式:factory的執行個體化的過程是一個比較耗費性能的過程
     // 一旦被建立就應該在應用的運作期間一直存在,保證有且隻有一個factory,不要重新建立另一個,常使用單例模式
   //3.SqlSession:用于請求或方法的作用域,用完之後需要關閉,不要占用資源
     //作用:包含了執行SQL語句的所有方法
    //對應一次資料庫會話,會話結束必須關閉

    private static SqlSessionFactory factory;

    //靜态代碼塊,随着類的加載而執行,而且隻執行一次
    static{
        try {
            InputStream is = Resources.getResourceAsStream("mybatis.xml");
            factory = new SqlSessionFactoryBuilder().build(is);
           // threadLocal = new ThreadLocal<SqlSession>();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public static SqlSession getSession(){
        SqlSession session=null;
        //通過session工廠擷取到一個session
        session = factory.openSession();
        //調用session的查詢集合方法
        return session;
    }


   //  關閉SqlSession
   public static void closeSession(SqlSession sqlSession){
       sqlSession.close();
   }

   //測試
   public static void main(String[] args) {
       //System.out.println(MybatisUtil.factory==MybatisUtil.factory);
       for (int i = 0; i < 5; i++) {
           System.out.println(MybatisUtil.factory.hashCode());
       }
   }
}

           

以後每次使用隻需要調用即可,不用重複編寫加載配置檔案和建立SqlSession代碼,資料庫操作完成後,SqlSession關閉。

二、優化之将連接配接字元串寫入配置檔案

引入資料庫配置檔案

<!-- 引入資料庫配置檔案 -->
	<properties resource="database.properties"/>

	<settings>
		<setting name="logImpl" value="LOG4j"/>
	</settings>

	<!-- 配置資料庫連接配接環境:driver、url、username、password -->
	<environments default="mysql">
		<!-- 開始配置mysql -->
		<environment id="mysql">
			<!--配置事務  -->
			<transactionManager type="JDBC"></transactionManager>
			<!-- 配置資料源 -->
			<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>
           

三、優化之去DAO層

目的是簡化項目結構

MyBatis架構常用優化方法

Mybatis局部配置檔案名一定要與它所支援的mapper層中的接口同名,如:UserMapper.xml要與UserMapper.java同名。可以将mapper層接口與Mybatis局部配置檔案放在同一個包下,mapper層接口中定義的方法名,一定要與Mybatis局部配置檔案的SQL語句的id同名,注意mapper層中,沒有接口的實作類。

四、優化之使用@Before、@After注解

1、@Before注解

在測試類中的某個方法前加@Before注解,可以使得在執行其他方法前,先執行該方法。

//Before注解:在每次調用測試方法之前,自動調用init()方法
    @Before
    public void init(){
        session= MybatisUtil.getSession();
        //um就是Mapper的實作類
        um=session.getMapper(UserMapper.class);
    }
           

2、@After注解

在測試類中的某個方法前加@After注解,可以使得在執行其他方法後,再執行該方法。

//每次調用測試方法之後,自動調用一下destory()
    @After
    public void destory(){
        MybatisUtil.closeSession(session);
    }
           

五、優化之使用@Select注解

六、優化之使用别名

給包下所有的實體類取别名

<!-- 配置别名 -->
	<typeAliases>
		<typeAlias type="cn.java.pojo.Student" alias="student"/>
		<typeAlias type="cn.java.pojo.User" alias="user"/>
	</typeAliases>
           

七、MyBatis 架構的優化練習

1、資料庫準備

在MySchool資料庫中,建立一個student表,表結構如下:

student(
id bigint(20) 自增,
sname varchar(40),
dept varchar(40),
age int)
           

并在表中插入幾條資料,其中一條為自己的資訊

2、完整代碼

database.properties
#mysql8
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql:///myschool?serverTimezone=Hongkong
username=root
password=你的密碼
           
log4j.properties
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 
           
mybatis.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>

	<!-- 引入資料庫配置檔案 -->
	<properties resource="database.properties"/>

	<!-- 開啟log4j,輸出SQL語句 -->
	<settings>
		<setting name="logImpl" value="LOG4j"/>
	</settings>

	<!-- 配置别名 -->
	<typeAliases>
		<typeAlias type="cn.java.pojo.Student" alias="student"/>
		<typeAlias type="cn.java.pojo.User" alias="user"/>
	</typeAliases>

	<!-- 配置資料庫連接配接環境:driver、url、username、password -->
	<environments default="mysql">
		<!-- 開始配置mysql -->
		<environment id="mysql">
			<!--配置事務  -->
			<transactionManager type="JDBC"></transactionManager>
			<!-- 配置資料源 -->
			<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>
	
	<!-- 關聯局部SQL映射配置檔案 ,在每一個mapper裡,指定SQL映射檔案名及全路徑,可使用“copy qualified name””-->
	<mappers>
		<package name="cn.java.mapper"/>
	</mappers>
	
	
</configuration>
           
Student.java
package cn.java.pojo;

public class Student {
    private Long id;
    private String sname;
    private String dept;
    private int age;

    public Student() {
    }

    public Student(Long id, String sname, String dept, int age) {
        this.id = id;
        this.sname = sname;
        this.dept = dept;
        this.age = age;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getSname() {
        return sname;
    }

    public void setSname(String sname) {
        this.sname = sname;
    }

    public String getDept() {
        return dept;
    }

    public void setDept(String dept) {
        this.dept = dept;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student[" + "id=" + id + ", " +
                "sname='" + sname + '\'' +
                ", dept='" + dept + '\'' +
                ", age=" + age +
                ']';
    }
}
           
StudentMapper.java
package cn.java.mapper;

import cn.java.pojo.Student;
import org.apache.ibatis.annotations.Select;

import java.util.List;
import java.util.Map;

public interface StudentMapper {
    // 該方法使用Student實體類
    public List<Student> getAllStudent();

    //直接在以下方法上加@Select注解,簡化代碼
    @Select("select * from student")
    public List<Student> findAllStudent();

    // 該方法傳回多條記錄,不使用實體類,用Map資料類型去接受
    public List<Map<String, Object>>  getAllStudentMap();

    // 該方法使用了帶一個參數的查詢語句,傳回一條記錄
    public Map<String, Object> getStudentById(long id);

    // 該方法使用了有多個參數的 select語句
    public Map<String, Object> getStudentByMulCondition(Map<String, Object> map);

    // 該方法插入一條記錄,帶參數,更新操作一定要送出事務
    public int  addStudent(Map<String, Object> map);

    // 該方法插入多條記錄,帶參數,更新操作一定要送出事務
    public int addStudentBatch(List<Student> list) ;

    // 該方法使用了動态查詢,查詢條件不确定
    public List<Map<String, Object>>  getStudentByDynam(Map<String, Object> map) ;

    // 該方法使用了動态修改,查詢條件不确定
    public int updateStudentByDynam(Map<String, Object> map)  ;

    //    根據id删除記錄
    public long deleteStudentById(long id);
    //    根據多個id删除多條記錄
    public int deleteStudentByIds(long[] ids);

}
           
StudentMapper.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檔案中的接口名一緻-->
<!--  sql語句儲存在Mybatis的局部配置檔案中
     解釋:
	(1)namespace:命名空間,其值為某一個dao層接口的具體路徑,
	    表示這個類要使用相應的SQL語句,這個具體路徑不要自己寫,可以選中該類,右鍵,選擇“copy reference”,然後粘貼即可
	(2)select标簽存放查詢語句;
	 (3)id:在整個配置檔案中id值必須唯一,一般情況下,其值與dao層類中的使用該SQL語句的方法名保持一緻;
	 (4)resultType:指定目前sql查詢語句傳回的資料類型。類型不是sql語句的最終類型,
	     而是某一條資料的類型,一般用實體類表示,也要用該實體類的“全路徑”來表示,運作時系統會自動将實體類的對象建立出來
       (5)可以編寫多條sql語句
 -->
<mapper namespace="cn.java.mapper.StudentMapper">
     <!-- 
		1.select語句傳回多條User實體對象
		resultType="cn.java.entity.User"表示傳回User實體類
	 -->
    <select id="getAllStudent" resultType="student">
		SELECT * FROM student
	</select>
	<!-- 
		2.select語句傳回List<Map<String,Object>,可以不使用實體類,直接用Map資料類型,更加簡單,簡化程式
	 -->
	<select id="getAllStudentMap" resultType="Map">
		SELECT * FROM student
	</select>
	
	<!-- 
		3.SQL語句帶一個參數
		parameterType:指定SQL語句接收的參數類型
	 -->
	<select id="getStudentById" resultType="map" parameterType="Long">
		SELECT * FROM student WHERE id=#{0}
	</select>
	
	<!--
		4.SQL語句帶多個參數,用Map封裝,parameterType=Map,傳回一條記錄,按key取參數值
		#在擷取參數時可防止SQL注入攻擊,應盡量使用#;模糊查詢時,使用$
	  -->
	<select id="getStudentByMulCondition" resultType="map" parameterType="Map">
		SELECT * FROM student WHERE sname='${sname}' AND dept='${dept}' AND age='${age}'
	</select>
	
	<!-- 添加資料
		5. delete、insert、update操作沒有resultType屬性,預設傳回int型
		  parameterType=Map,表示參數類型為Map,用Map封裝參數
		  #表示在擷取參數時可防止SQL注入攻擊,應盡量使用#;模糊查詢時,使用$
	      INSERT INTO users SET username=,這種SQL語句是MmySql特有的擴充功能
	 -->
	<insert id="addStudent" parameterType="Map">
		INSERT INTO student SET sname=#{sname},dept=#{dept},age=#{age}
	</insert>

	<insert id="addStudentBatch" parameterType="student">
		insert into
		student(sname,dept,age)
		values
		<foreach collection="list" item="student" separator=",">
			(#{student.sname},#{student.dept},#{student.age})
		</foreach>
	</insert>
	
	<!-- 
		6.動态SQL語句,實作動态查詢
		  &lt:小于
		  &gt:大于
		  where标簽:當有查詢條件時,就使用where指令,當沒有查詢條件時,就不用where指令
		  if test:判斷是否對某個字段進行查詢
	 -->
	<select id="getStudentByDynam" resultType="Map" parameterType="Map">
		SELECT * FROM student
		<where>
			<if test="sname!=null">
				sname=#{sname}
			</if>
			<if test="dept!=null">
				and dept=#{dept}
			</if>
			<if test="age!=null">
				and age=#{age}
			</if>
		</where>
	</select>
	
	<!-- 
		7.動态修改update語句
		 set标簽:當有更新字段時,就使用set指令,當沒有更新字段時,
		     語句為UPDATE course SET id=NULL WHERE id=NULL
	 -->
	<update id="updateStudentByDynam" parameterType="Map">
	   update student
	   <set>
		   <if test="sname!=null">
			   sname=#{sname},
		   </if>
		   <if test="dept!=null">
			   dept=#{dept},
		   </if>
		   <if test="age!=null">
			   age=#{age},
		   </if>
		   id=#{id}
	   </set>
	   where id=#{id}
	</update>

	<!-- 
	    8.根據id删除記錄
	    根據id删除學生記錄
	-->
	<delete id="deleteStudentById" parameterType="long">
		DELETE FROM student WHERE id=#{id}
	</delete>

	<!-- 
	    9.根據多個id删除記錄
	    根據多個id删除多個學生記錄
	-->
	<delete id="deleteStudentByIds" parameterType="long[]" >
		DELETE FROM student WHERE id IN
		<foreach collection="array" item="id" open="(" close=")" separator=",">
			#{id}
		</foreach>
	</delete>
	
</mapper>


           
MybatisUtil.java
package cn.java.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.IOException;
import java.io.InputStream;

public class MybatisUtil {
   // 擷取SqlSession
    public static SqlSession getSession(){
        SqlSession session=null;
        InputStream inputStream=null;
        try {
            //配置檔案的路徑
            String resource = "mybatis.xml";
            //加載配置檔案,得到一個輸入流
            inputStream = Resources.getResourceAsStream(resource);
            //擷取MyBatis的Session工廠
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            //通過session工廠擷取到一個session
            session = sqlSessionFactory.openSession(true);  //true表示自動送出事務
            //調用session的查詢集合方法
            return session;
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

   //  關閉SqlSession
    public static void closeSession(SqlSession session){
        if(session!=null){
            session.close();
        }
    }
}

           
Test_Student.java
import cn.java.mapper.StudentMapper;
import cn.java.pojo.Student;
import cn.java.utils.MybatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class Test_Student {
    // SqlSession session=null;
    SqlSession session=null;
    StudentMapper um=null;

    //Before注解:在每次調用測試方法之前,自動調用init()方法
    @Before
    public void init(){
        session= MybatisUtil.getSession();
        //um就是Mapper的實作類
        um=session.getMapper(StudentMapper.class);
    }
    //每次調用測試方法之後,自動調用一下destory()
    @After
    public void destory(){
        MybatisUtil.closeSession(session);
    }

    @Test
    public void testGetAllStudent(){
        um= session.getMapper(StudentMapper.class);
        List<Student> studentList = um.getAllStudent();
        for (Student student : studentList) {
            System.out.println(student);
        }
    }

    @Test
    public void testFindAllStudent(){

        List<Student> studentList = um.findAllStudent();
        for (Student student : studentList) {
            System.out.println(student);
        }
    }

    @Test
    // 2.該方法傳回多條記錄,不使用實體類,用Map資料類型去接受
    public void testGetAllStudentMap() {
        List<Map<String, Object>> studentList =um.getAllStudentMap();
        for (Map<String, Object> map : studentList) {
            System.out.println(map);
        }
    }

    @Test
    // 3.該方法使用了帶一個參數的查詢語句,傳回一條記錄
    public void testGetStudentById() {
        // 傳遞參數,直接傳
        Long id =20201002222L;
        Map<String, Object> studentMap = um.getStudentById(id);
        System.out.println(studentMap);
    }

    @Test
    // 4.該方法使用了有多個參數的 select語句
    public void testGetStudentByMulCondition()  {
        // 聲明一個Map對象,可以使用Map或實體類同時傳遞多個參數,用map更簡單
        Map<String, Object> paramMap = new HashMap<String, Object>();
        // 封裝參數
        paramMap.put("sname", "李四");
        paramMap.put("dept", "軟體工程");
        paramMap.put("age", "21");
        // 傳遞參數
        Map<String, Object> studentMap = um.getStudentByMulCondition(paramMap);
        System.out.println(studentMap);
    }

    // 5.該方法插入一條記錄,帶參數,更新操作一定要送出事務
    @Test
    public void testAddStudent() {
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("sname", "徐三");
        paramMap.put("dept", "計算機");
        paramMap.put("age", "19");
        int resultInt = um.addStudent(paramMap);
        System.out.println(resultInt);
    }

    // 6.該方法插入多條記錄,帶參數,更新操作一定要送出事務
    @Test
    public  void testAddStudentBatch() {
        List<Student> list = new ArrayList<>();
        Student student;
        for (int i = 0; i < 5; i++) {
            student = new Student();
            student.setSname("test" + i);
            student.setDept("網絡安全");
            student.setAge(19);
            list.add(student);
        }
        int resultInt = um.addStudentBatch(list);
        System.out.println(resultInt);
    };

    // 7.該方法使用了動态查詢,查詢條件不确定
    @Test
    public void getStudentByDynam()  {
        // 可以使用Map或實體類同時傳遞多個參數
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("age", "20");
        List<Map<String, Object>> studentList =um.getStudentByDynam(paramMap);
        for (Map<String, Object> map : studentList) {
            System.out.println(map);
        }
    }

    // 8.該方法使用了動态修改,查詢條件不确定
    @Test
    public void updateStudentByDynam() {
        // 可以使用Map或實體類同時傳遞多個參數
        Map<String, Object> paramMap = new HashMap<String, Object>();
        paramMap.put("id","20201001111");
        paramMap.put("dept", "計算機");
        int resultInt = um.updateStudentByDynam(paramMap);
        System.out.println(resultInt);
    }

    @Test
    public void deleteStudentById(){
        long id = 20201005582L;
        long resInt = um.deleteStudentById(id);
        System.out.println(resInt);
    }

    @Test
    public void deleteStudentByIds(){
        long[] ids = new long[]{20201005574L,20201005575L,20201005576L,
                20201005577L,20201005578L,20201005579L,20201005581L};
        long resInt = um.deleteStudentByIds(ids);
        System.out.println(resInt);
    }



}