mybatis中,封裝了一個sqlsession 對象(裏面封裝有connection對象),由此對象來對數據庫進行CRUD操做。php
mybatis有一個配置的xml,用於配置數據源、映射Mapping,xml的文件名能夠任取,爲了方便,咱們仍是起mybatis-config.xmlhtml
咱們讀取此配置的xml,得到一個sqlsession,以後由此對象類進行數據庫的CRUD操做java
Reader reader = Resources.getResourceAsReader("mybatis-config.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader); SqlSession sqlSession = factory.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> <!-- 引入外部資源文件--> <properties resource="jdbc.properties"/> <!-- 配置數據源環境 --> <environments default="development"> <environment id="development"> <!-- 數據庫事務管理類型 --> <transactionManager type="JDBC"/> <!-- 數據源,type=pooled 說明是使用鏈接池方式,能夠節省資源 --> <dataSource type="POOLED"> <!-- 調用資源文件裏的用戶信息--> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments> </configuration>
Dao類中每次進行CRUD操做,都要執行一次openSession方法
來得到SqlSession對象
,形成資源的浪費和代碼的重複mysql
因此,和以前的JdbcUtil
工具類同樣,咱們也定義定義一個工具類MyBatisUtil
,用來返回SQLSession對象
git
static SqlSessionFactory sqlSessionFactory = null; static { try { // 加載mybatis配置文件,並建立SqlSessionFactory實例 String resource = "mybatis-config.xml"; InputStream inputStream = Resources.getResourceAsStream(resource); //這個build方法能夠接受幾種不一樣的參數,如Reader/InputSteam等 sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); } catch (IOException e) { } } public static SqlSession getSqlSession() { return sqlSessionFactory.openSession(); } public static void closeSqlSession(SqlSession sqlSession){ if (sqlSession != null) { sqlSession.close(); } }
mapper文件放在了resources下面程序員
Mybatis中,sql語句則是寫在了xml文件中,這些xml文件也稱爲mapper映射文件github
mapper標籤若是帶有xmln屬性,IDEA會報解析xml錯誤,得把xmln屬性刪除sql
<?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"> <!-- namespace: 命名空間,用於標識每個Mapper XML文件中的語句,預防在不一樣的Mapper XML文件中存在相同的語句ID --> <mapper namespace="employeeMapper"> <!-- resultType: 也稱爲自動映射,只有在表的列名與POJO類的屬性徹底一致時使用,會比較方便,全類名 --> <select id="selectAll" resultType="com.wan.bean.Employee"> select * from employee </select> </mapper>
<?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> <!-- 省略數據源配置--> <mappers> <mapper resource="com/wan/mapping/employeeMapper.xml"/> <!--若是還有mapper,則繼續添加 --> </mappers> </configuration>
SqlSession sqlSession = MybatisUtil.getSqlSession(); // 調用語句,若是有參數,傳入參數 //參數爲命名空間namespace+id,執行xml中的sql語句 List<Employee> list = sqlSession.selectList("employeeMapper.selectAll");
PS:若是是插入、更新和刪除操做,還須要提交操做,默認是不會自動提交的數據庫
sqlSession.commit();
<select id="selectAll" resultType="com.wan.bean.Employee"> select * from employee </select>
resultType
屬性須要全類名,咱們可使用typeAliases
標籤來簡化輸入數組
typeAliases
標籤須要在mybatis-config.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="jdbc.properties"/> <!--指定一個bean包 --> <typeAliases> <package name="com.wan.bean"/> </typeAliases> <!--省略配置數據源等--> </configuration>
以後咱們的mapper文件中就能夠這樣寫
<!--resultType就能夠不用寫全包名 --> <select id="selectAll" resultType="Employee"> select * from employee </select>
我這裏只介紹用法,詳解請看下面的參考連接
1. 文件路徑註冊
<mappers> <mapper resource="com/wan/mapper/EmployeeMapper.xml" /> </mappers>
2. 包名掃描註冊
<mappers> <package name="com.wan.mapper" /> </mappers>
使用這種,必須保證xxxMapper.java和xxxMapper.xml二者名字如出一轍!並且是要在同一包裏
3. 類名註冊
<mappers> <mapper class="com.shizongger.chapter2.mapper.UserMapper" /> </mappers>
4. url註冊
<mappers> <mapper url="file:/home/shizongger/workspace/Chapter3/src/com/shizongger/chapter2/mapper/RoleMapper.xml" /> </mappers>
方法名 | 說明 |
---|---|
insert | 插入 |
delete | 刪除 |
update | 更新 |
selectOne | 查找單行結果,返回一個Object |
selectList | 查找多行結果,返回一個List |
使用和以前同樣,第一個參數傳入一個namespce+id,就能夠找到指定的mapper文件裏面的sql語句,並執行
Employee中,屬性名和表的列名對應
<select id="selectAll" resultType="Employee"> select * from employee </select>
若是屬性和表的列名不一致,可使用列名映射resultMap標籤,(也就是自動轉爲別名)
<!--type也是須要全包名,因爲以前定義的別名,因此就能夠不寫--> <resultMap id="baseResultMap" type="Employee"> <!--使用映射,把對應的列名映射爲對應的屬性 --> <id property="empno" column="EMPNO" /> <result property="ename" column="ENAME"/> <result property="job" column="JOB"/> <result property="mgr" column="MGR"/> <result property="hiredate" column="HIREDATE"/> <result property="sal" column="SAL"/> <result property="comm" column="COMM"/> <result property="deptno" column="DEPTNO"/> </resultMap> <!--引用上面定義的resultMap--> <select id="selectAll" resultMap="baseResultMap"> select * from employee </select>
帶條件查詢
<select id="selectById" parameterType="int" resultMap="baseResultMap"> <!-- 若是參數類型是簡單的基本或者包裝類型,#{} 裏面的能夠任取,都是能夠得到參數 --> select * from EMPLOYEE where EMPNO=#{id} </select> //使用 Employee e = sqlsession.selectOne("employeeMapper.selectById",7369);
上面的select語句至關於一個預編譯語句
String s = "SELECT * FROM employee WHERE empno=?"; PreparedStatement ps = conn.prepareStatement(s); ps.setInt(1,empno);
多條件查詢
可使用where標籤,固然,以前的單條件也可使用where標籤,where標籤好處是會自動刪除多餘的and
<select id="selectSelective" parameterType="Employee" resultMap="baseResultMap"> select * from EMPLOYEE <where> <!--自動刪除多餘的and --> <!--#至關於從傳入的bean對象(Employee)中經過getDeptno方法得到屬性值 --> and deptno=#{deptno} and sal>=2000 </where> </select>
大小比較條件
條件中有大小比較,<
號得經過CDATA存放條件
<select id="selectSelective" parameterType="Employee" resultMap="baseResultMap"> select * from EMPLOYEE <where> <!--loSal爲Employee的一個屬性,#{loSal}至關因而經過Employee對象的get方法來得到loSal的屬性值 --> and SAL>=#{loSal} <!--CDATA中的數據不會被解析器解析 --> <![CDATA[ and SAL<=#{hiSal} ]]> </where> </select>
#與$區別:
${}
用在咱們可以肯定值的地方,也就是咱們程序員本身賦值的地方。 而#{}
通常用在用戶輸入值的地方!!
參考: MyBatis中#{}和${}的不一樣和${}的妙用
模糊查詢:
模糊查詢中須要使用%
等通配符,咱們能夠在xml中定義好,自動拼接通配符
<select id="selectSelective" parameterType="Employee" resultMap="baseResultMap"> select * from EMPLOYEE <where> <if test="ename != null"> <!--使用bind標籤,設置格式,自動拼接通配符 --> <bind name="pattern" value="'%' + ename + '%'"/> and ENAME like #{pattern} </if> </where> </select>
動態查詢
Mybatis中提供了if
標籤用來實現動態查詢,和JSTL標籤庫使用相似
<select id="selectSelective" parameterType="Employee" resultMap="baseResultMap"> select * from EMPLOYEE <where> <!--#{ename}實際上是經過Employee類中的get方法來得到對象的ename屬性值 --> <if test="ename != null"> and ename=#{ename} </if> <if test="job != null and job.trim().length>0"> and JOB=#{job} </if> <if test="deptno != null"> and DEPTNO=#{deptno} </if> </where> </select>
主鍵爲序列
某個主鍵是由oracle中的序列生成的
<insert id="insert_1" parameterType="Employee"> <!-- keyProperty: 表示將從序列得到的值賦予實體的哪一個屬性 order: 表示主鍵值生成的方式,可取值:BEFORE | AFTER 因爲不一樣的數據庫對插入的數據時主鍵生成方式是不一樣,例如: mysql and ms server: 主鍵生成方式爲後生成方式。 oracle: 主鍵生成方式預生成. --> <!--調用數據庫中的序列,並賦值給傳入的Employee對象的empno屬性 --> <selectKey keyProperty="empno" resultType="integer" order="BEFORE"> select EMP_SEQ.nextval from dual </selectKey> <!-- 若是使用這種整表插入的方式,那當數據庫表的某些列能夠爲空值時,我將要告訴底層的JDBC驅動如何處理空值的狀況,這不是mybatis所須要的, 而是底層有些JDBC驅動所需的特性,實際上就是讓JDBC驅動去調用PrepareStatement.setNull()來設置空值 --> <!--若是是經常使用的數據類型int,date等,jdbcType能夠省略不寫 --> insert into EMPLOYEE values (#{empno},#{ename},#{job},#{mgr,jdbcType=INTEGER},#{hiredate,jdbcType=DATE},#{sal,jdbcType=DOUBLE},#{comm,jdbcType=DOUBLE},#{deptno,jdbcType=INTEGER}) </insert>
複用sql語句
把insert要插入的列名和數值寫在sql標籤裏,以後方便重用,以後重用的時候須要使用include
子標籤拼接sql語句
<!--insert into employee(ENAME,JOB..) values(xx,xx) --> <!--(ENAME,JOB..) --> <sql id="insert_set_column"> <!-- suffixOverrides屬性,會自動把多餘的「,」刪除 --> <trim prefix="(" suffix=")" suffixOverrides=","> empno, <if test="ename != null">ENAME,</if> <if test="job != null">JOB,</if> <if test="mgr != null">MGR,</if> <if test="hiredate != null">HIREDATE,</if> <if test="sal != null">SAL,</if> <if test="comm != null">COMM,</if> <if test="deptno != null">DEPTNO,</if> </trim> </sql> <!--(xx,xx,xx) --> <sql id="insert_values"> <trim prefix="values(" suffix=")" suffixOverrides=","> #{empno}, <if test="ename != null">#{ename},</if> <if test="job != null">#{job},</if> <if test="mgr != null">#{mgr},</if> <if test="hiredate != null">#{hiredate},</if> <if test="sal != null">#{sal},</if> <if test="comm != null">#{comm},</if> <if test="deptno != null">#{deptno},</if> </trim> </sql> <insert id="insert_2" parameterType="Employee"> <selectKey keyProperty="empno" resultType="integer" order="BEFORE"> select EMP_SEQ.nextval from dual </selectKey> insert into EMPLOYEE <!--拼接sql --> <include refid="insert_set_column"/> <include refid="insert_values"/> </insert>
<update id="update_1" parameterType="Employee"> update EMPLOYEE <set> <if test="ename != null and ename.trim().length>0">ENAME=#{ename},</if> <if test="job != null and job.trim().length>0">JOB=#{job},</if> <if test="mgr != null">MGR=#{mgr},</if> <if test="hiredate != null">HIREDATE=#{hiredate},</if> <if test="sal != null">SAL=#{sal},</if> <if test="comm != null">COMM=#{comm},</if> <if test="deptno != null">DEPTNO=#{deptno},</if> </set> <!-- <where>若是帶多條件的更依然可使<where>元素動態生成where子句</where> --> where EMPNO=#{empno} </update>
<update id="update_2" parameterType="Employee"> update EMPLOYEE <trim prefix="set" suffixOverrides=","> <if test="ename != null and ename.trim().length>0">ENAME=#{ename},</if> <if test="job != null and job.trim().length>0">JOB=#{job},</if> <if test="mgr != null">MGR=#{mgr},</if> <if test="hiredate != null">HIREDATE=#{hiredate},</if> <if test="sal != null">SAL=#{sal},</if> <if test="comm != null">COMM=#{comm},</if> <if test="deptno != null">DEPTNO=#{deptno},</if> </trim> <!-- <where>若是帶多條件的更依然可使<where>元素動態生成where子句</where> --> where EMPNO=#{empno} </update>
<delete id="delete" parameterType="Employee"> delete EMPLOYEE EMPNO=#{empno} <!--條件多的話也可使用<where>...</where> --> </delete>
咱們以前,上面都是在Dao類中寫上一段sqlsession.selectOne/selectList,仍是比較麻煩
因此mybatis提供了一種簡單的方法,使用動態代理(接口類)能夠簡化步驟
Mybatis中有這樣的約定:
知足上面的條件,Mybatis就會將接口類中的方法和mapper中的sql語句一一對應起來,而不須要再次新建一個Dao,在Dao類裏面編寫方法
具體步驟:
1. 實體類編寫
2. 新建接口類
若是方法的返回值爲void,則mapper中就不須要定義resultType屬性
若是方法返回值是List,mapper中的resultType爲泛型T
package com.wan.mapping; import com.wan.bean.Employee; import java.util.List; /** * @author StarsOne * @date Create in 2019/9/16 0016 20:38 * @description */ public interface EmployeeMapper { List<Employee> selectAll(); }
2. 編寫mapper.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.wan.mapping.EmployeeMapper"> <!--特例:返回值爲list,resultType=bean類--> <select id="selectAll" resultType="Employee" > select * from employee </select> </mapper>
3. 註冊mapper
這裏咱們因爲使用了package註冊mapper,必定保證xxmapper.java和xxmapper.xml兩個名字相同,大小寫都要同樣
保證Mapper.xml和接口的那個Mapper在相同的包路徑,在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> <!--省略數據源配置 -->... <!-- 註冊SQL映射文件,在這些文件中寫SQL語句 --> <mappers> <!--指定整個包中的所有Mapper --> <package name="com.wan.mapper"/> </mappers> </configuration>
4. 使用
使用仍是和以前同樣,得到SqlSession對象,此對象有個getMapper方法,把接口類傳入,就能夠回調接口的方法了
Reader reader = Resources.getResourceAsReader("mybatis-config.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader); SqlSession sqlSession = factory.openSession(); EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class); List<Employee> employees = mapper.selectAll();
接口類中的方法名與EmployeeMapper.xml中的對應
使用:
EmployeeMapper mapper = sqlsession.getMapper(EmployeeMapper.class); mapper.selectById(7369);
Mybatis中提供了foreach標籤,用於遍歷
若是方法參數傳入了一個List,可使用此標籤遍歷,例子以下:
<!--至關於select * from employee where job in (...)) --> <select id="selectByJobs" parameterType="list" resultMap="baseResultMap"> select * from EMPLOYEE <where> <foreach item="job" collection="list" open="JOB IN(" close=")" separator=","> #{job} </foreach> </where> </select>
foreach標籤的屬性主要有 item,index,collection,open,separator,close,使用和JSTL標籤裏面的foreach標籤差很少
屬性名 | 說明 |
---|---|
item | 表示集合中每個元素進行迭代時的別名 |
index | 指定一個名字,用於表示在迭代過程當中,每次迭代到的位置, |
open | 表示該語句以什麼開始, |
separator | 表示在每次進行迭代之間以什麼符號做爲分隔 符, |
close | 表示以什麼結束。 |
關鍵屬性:collection
參考:mybatis 中 foreach collection的三種用法
使用ThreadLocal對象,保證每一個線程取出的SqlSession是同一個對象
方法 | 說明 |
---|---|
void set(Object value) | 設置當前線程的線程局部變量的值。 |
public Object get() | 該方法返回當前線程所對應的線程局部變量。 |
public void remove() | 將當前線程局部變量的值刪除,目的是爲了減小內存的佔用,該方法是JDK 5.0新增的方法。 |
protected Object initialValue() | 返回該線程局部變量的初始值,該方法是一個protected的方法,顯然是爲了讓子類覆蓋而設計的。 |
static ThreadLocal<SqlSession> threadLocal = new ThreadLocal<SqlSession>(); //設置 threadLocal.set(sqlsession); //取出 SqlSession s = threadLocal.get();
<!-- 結果集映射: 列《》屬性名 --> <resultMap id="baseResultMap" type="Employee"> <!-- 專門映射主鍵列 --> <id property="empno" column="EMPNO" /> <result property="ename" column="ENAME"/> <result property="job" column="JOB"/> <result property="mgr" column="MGR"/> <result property="hiredate" column="HIREDATE"/> <result property="sal" column="SAL"/> <result property="comm" column="COMM"/> <result property="deptno" column="DEPTNO"/> </resultMap> <!-- 擴展另外一個結果映射 --> <resultMap id="extendBaseResultMap" type="Employee" extends="baseResultMap"> <association property="department" javaType="Department"> <!-- 關聯的嵌套結果 --> <id property="deptno" column="DEPTNO"/> <result property="dname" column="DNAME"/> <result property="location" column="LOC"/> </association> </resultMap> <!-- 1.嵌套結果(推薦使用) 優勢:性能好,一條語句把全部實體的數據徹底查詢出來。 缺點:對SQL編寫的要求高了,由於涉及多表鏈接查詢 --> <select id="selectById" resultMap="extendBaseResultMap" parameterType="int"> select e.EMPNO, e.ENAME, e.JOB, e.MGR, e.HIREDATE, e.SAL, e.COMM, d.DEPTNO, d.DNAME, d.LOC from EMPLOYEE E inner join DEPARTMENT D on E.DEPTNO = D.DEPTNO where E.EMPNO=#{id} </select> <!-- 2. 嵌套查詢 優勢:編寫SQL簡單,無需作多表的鏈接查詢;關聯的實體經過單獨的SQL語句查詢並單獨封裝。 缺點:執行了N+1條件語句。性能差 --> <resultMap id="extendBaseResultMap_2" type="Employee" extends="baseResultMap"> <association property="department" column="DEPTNO" select="selectDepartmentById" /> </resultMap> <select id="selectDepartmentById" parameterType="int" resultType="Department"> select deptno, dname, loc as location from DEPARTMENT where DEPTNO=#{id} </select> <select id="selectById_2" resultMap="extendBaseResultMap_2" parameterType="int"> select e.EMPNO, e.ENAME, e.JOB, e.MGR, e.HIREDATE, e.SAL, e.COMM, e.DEPTNO from EMPLOYEE E where E.EMPNO=#{id} <!-- or e.empno=7902 or e.empno=7844 --> </select>
分頁的話,像之前那樣使用三層嵌套查詢也能夠實現。
不過,有開發者爲Mybatis開了個一個插件PageHelper,能夠用來更爲簡單地使用分頁查詢
1.添加jar包
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <!--自動下載最新版本 --> <version>REALSE</version> </dependency>
2.配置攔截器插件
<!-- plugins在配置文件中的位置必須符合要求,不然會報錯,順序以下: properties?, settings?, typeAliases?, typeHandlers?, objectFactory?,objectWrapperFactory?, plugins?, environments?, databaseIdProvider?, mappers? --> <plugins> <!-- com.github.pagehelper爲PageHelper類所在包名 --> <plugin interceptor="com.github.pagehelper.PageInterceptor"> <!-- 使用下面的方式配置參數,後面會有全部的參數介紹 --> <property name="param1" value="value1"/> </plugin> </plugins>
3.代碼使用
只有在查詢以前調用過startPage
或者是offsetPage
方法,後面的查詢出來的List結果就會進行分頁查詢
下面的兩個都是查詢第一頁,每一頁有10條數據
//第二種,Mapper接口方式的調用,推薦這種使用方式。 PageHelper.startPage(1, 10); List<Employee> employees = employeeMapper.selectAll(); //第三種,Mapper接口方式的調用,推薦這種使用方式。 PageHelper.offsetPage(1, 10); List<Employee> employees = employeeMapper.selectAll();
這裏提一下,這個插件還帶有一個PageInfo
類,裏面有能夠記錄各類信息
剛開始,我覺得是和我以前本身封裝的Page同樣,詳情請看Jsp學習筆記(4)——分頁查詢
可是,其實不同的,這個PageInfo就是一個封裝而已,只是用來存放數據而已,裏面有各類信息
屬性 | 說明 |
---|---|
pageNum | 當前頁號(第幾頁) |
pageSize | 每頁的顯示的數據個數 |
size | 當前頁的顯示的數據個數 |
startRow | 當前頁面第一個元素在數據庫中的行號 |
endRow | 當前頁面最後一個元素在數據庫中的行號 |
pages | 總頁數 |
prePage | 上一頁的頁號 |
nextPage | 下一頁頁號 |
isFirstPage | 是否爲第一頁 |
isLastPage | 是否爲最後一頁 |
hasPreviousPage | 是否有前一頁 |
hasNextPage | 是否有下一頁 |
navigatePages | 導航頁碼數 |
navigatepageNums | 全部導航頁號 |
navigatePages | 導航條上的第一頁 |
navigateFirstPage | 導航條上的第一頁 |
navigateLastPage | 導航條上的最後一頁 |
有個getTotal
方法,能夠得到查詢結果的總記錄數
PageHelper.startPage(1, 10); List<Employee> employees = mapper.selectAll(); PageInfo<Employee> pageInfo = new PageInfo<>(employees);
原文出處:https://www.cnblogs.com/stars-one/p/11537439.html