一、優化之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局部配置檔案名一定要與它所支援的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語句,實作動态查詢
<:小于
>:大于
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);
}
}