前言:
MyBatis 的強大特性之一便是它的動態 SQL。如果你有使用 JDBC 或其它類似框架的經驗,你就能體會到根據
不同條件拼接 SQL 語句的痛苦。例如拼接時要確保不能忘記添加必要的空格,還要注意去掉列表最后一個列名的逗號。
利用動態 SQL 這一特性可以徹底擺脫這種痛苦。
Mybatis Generator可以幫我們根據數據庫表自動生成pojo類和SQL映射文件,SQL映射文件提供了增刪改查功能。
動態SQL
到網上找一個經典的mysql數據庫表
CREATE TABLE `emp` (
#編號
`empno` int(11) NOT NULL,
#姓名
`ename` varchar(255) NOT NULL,
#職位
`job` varchar(255) DEFAULT NULL,
`mgr` int(11) DEFAULT NULL,
#入職時間
`hiredate` date NOT NULL,
#薪水
`sal` decimal(7,2) DEFAULT NULL,
#獎金級別
`comm` decimal(7,2) DEFAULT NULL,
#部門編號
`deptno` int(11) DEFAULT NULL,
PRIMARY KEY (`empno`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
#部門表
CREATE TABLE `dept` (
#部門編號
`deptno` int(11) NOT NULL,
#部門名稱
`dname` varchar(255) NOT NULL,
#部門地址
`loc` varchar(255) DEFAULT NULL,
PRIMARY KEY (`deptno`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO emp VALUES(7369,'SMITH','CLERK',7902,'1980-12-17',800,NULL,20);
INSERT INTO emp VALUES(7499,'ALLEN','SALESMAN',7698,'1981-02-20',1600,300,30);
INSERT INTO emp VALUES(7521,'WARD','SALESMAN',7698,'1981-02-22',1250,500,30);
INSERT INTO emp VALUES(7566,'JONES','MANAGER',7839,'1981-04-02',2975,NULL,20);
INSERT INTO emp VALUES(7654,'MARTIN','SALESMAN',7698,'1981-09-28',1250,1400,30);
INSERT INTO emp VALUES(7698,'BLAKE','MANAGER',7839,'1981-05-01',2850,NULL,30);
INSERT INTO emp VALUES(7782,'CLARK','MANAGER',7839,'1981-06-09',2450,NULL,10);
INSERT INTO emp VALUES(7788,'SCOTT','ANALYST',7566,'1987-04-19',3000,NULL,20);
INSERT INTO emp VALUES(7839,'KING','PRESIDENT',NULL,'1981-11-17',5000,NULL,10);
INSERT INTO emp VALUES(7844,'TURNER','SALESMAN',7698,'1981-09-08',1500,0,30);
INSERT INTO emp VALUES(7876,'ADAMS','CLERK',7788,'1987-05-23',1100,NULL,20);
INSERT INTO emp VALUES(7900,'JAMES','CLERK',7698,'1981-12-03',950,NULL,30);
INSERT INTO emp VALUES(7902,'FORD','ANALYST',7566,'1981-12-03',3000,NULL,20);
INSERT INTO emp VALUES(7934,'MILLER','CLERK',7782,'1982-01-23',1300,NULL,10);
INSERT INTO dept VALUES(10, 'ACCOUNTING', 'NEW YORK');
INSERT INTO dept VALUES(20, 'RESEARCH', 'DALLAS');
INSERT INTO dept VALUES(30, 'SALES', 'CHICAGO');
INSERT INTO dept VALUES(40, 'OPERATIONS', 'BOSTON');
表准備完成之后,開始編寫動態SQL語句
If
動態 SQL 通常要做的事情是根據條件包含 where 子句的一部分。
<select id="selectEmployeeByCondition1" parameterType="Employee" resultMap="EmployeeBaseMap"> select * from emp where 1 = 1 <if test="empno != null"> and empno = #{empno} </if>
<if test="ename != null"> and ename like #{ename} </if>
<if test="job != null"> and job = #{job} </if>
</select>
這條語句會根據empno,ename,job是否為空插入條件, " where 1 = 1 "是為了避免3個條件都為空出現"select * from emp where "這種SQL語句。
每個if條件之間必須顯式用and或or進行連接。
choose,when,otherwise
有時我們不想應用到所有的條件語句,而只想從中擇其一項。針對這種情況,MyBatis 提供了 choose 元素
choose類似if else if else,只會選擇中一個條件執行。
<!-- choose -->
<select id="selectEmployeeByCondition2" parameterType="Employee" resultMap="EmployeeBaseMap"> select * from emp where <choose>
<when test="empno != null"> empno = #{empno} </when>
<when test="ename"> ename like #{ename} </when>
<when test="job != null"> job = #{job} </when>
<otherwise> 1 = 1 </otherwise>
</choose>
</select>
如果三個when條件都不滿足,則會選中<otherwise>拼接到where條件后面!
where set trim
上面的幾條動態SQL語句都會加上where條件,有時候我們希望如果條件都不滿足的情況下不加where字句。
<select id="selectEmployeeByCondition3" parameterType="Employee" resultMap="EmployeeBaseMap"> select * from emp <where>
<if test="empno != null"> and empno = #{empno} </if>
<if test="ename != null"> and ename like #{ename} </if>
<if test="job != null"> and job = #{job} </if>
</where>
</select>
where 元素只會在至少有一個子元素的條件返回 SQL 子句的情況下才去插入“WHERE”子句。
而且,若語句的開頭為“AND”或“OR”,where 元素也會將它們去除。
和where等價的trim
<select id="selectEmployeeByCondition4" parameterType="Employee" resultMap="EmployeeBaseMap"> select * from emp <trim prefix="where" prefixOverrides="and | or"> <if test="empno != null"> and empno = #{empno} </if> <if test="ename != null"> and ename like #{ename} </if> <if test="job != null"> and job = #{job} </if> </trim> </select>
<trim> 標簽有兩個屬性,prefix是指定插入的內容,prefixOverrides屬性會忽略通過管道分隔的文本序列。
它的作用是移除所有指定在 prefixOverrides 屬性中的內容,並且插入 prefix 屬性中指定的內容。
假設一和三條件滿足,SQL語句就會變成 " select * from emp where empno = #{empno} and job = #{job} ";
<set>標簽是專門為更新語句而准備的。
<update id="updateEmployee" parameterType="Employee"> update emp <set> <if test="ename != null"> ename = #{ename}, </if> <if test="job != null"> job = #{job}, </if> <if test="mgr != null"> mgr = #{mgr}, </if> <if test="salary != null"> sal = #{salary}, </if> <if test="comment != null"> comm = #{comment}, </if> <if test="dept != null and dept.deptNo != null"> deptno = #{dept.deptNo} </if> </set> where empno = #{empno} </update>
set 元素可以用於動態包含需要更新的列,而舍去其它的
這里,set 元素會動態前置 SET 關鍵字,同時也會刪掉無關的逗號,
因為用了條件語句之后很可能就會在生成的 SQL 語句的后面留下這些逗號。
當然trim也可以完成set標簽的功能
<update id="updateEmployee2" parameterType="Employee"> update emp <trim prefix="set" prefixOverrides=",">
<if test="ename != null"> ename = #{ename}, </if>
<if test="job != null"> job = #{job}, </if>
<if test="mgr != null"> mgr = #{mgr}, </if>
<if test="salary != null"> sal = #{salary}, </if>
<if test="comment != null"> comm = #{comment}, </if>
<if test="dept != null and dept.deptNo != null"> deptno = #{dept.deptNo} </if>
</trim> where empno = #{empno} </update>
forEach
動態 SQL 的另外一個常用的操作需求是對一個集合進行遍歷,通常是在構建 IN 條件語句的時候。
<select id="selectEmployeeByEmpnos" parameterType="list" resultMap="EmployeeBaseMap"> select * from emp where empno in <foreach collection="list" open="(" close=")" item="item" index="index" separator=","> #{item} </foreach>
</select>
foreach 元素的功能非常強大,它允許你指定一個集合,聲明可以在元素體內使用的集合項(item)和
索引(index)變量。它也允許你指定開頭與結尾的字符串以及在迭代結果之間放置分隔符。
這個元素是很智能的,因此它不會偶然地附加多余的分隔符。
注意: 你可以將任何可迭代對象(如 List、Set 等)、Map 對象或者數組對象傳遞給 foreach 作為集合參數。
當使用可迭代對象或者數組時,index 是當前迭代的次數,item 的值是本次迭代獲取的元素。
當使用 Map 對象(或者 Map.Entry 對象的集合)時,index 是鍵,item 是值。
動態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"> <mapper namespace="com.briup.mapper.DynamicMapper"> <!-- 告訴數據庫如何加載數據到類的那個屬性上 --> <resultMap type="Employee" id="EmployeeBaseMap"> <id column="empno" property="empno"/> <result column="ename" property="ename"/> <result column="job" property="job"/> <result column="mgr" property="mgr"/> <result column="hiredate" property="hiredate"/> <result column="sal" property="salary"/> <result column="comm" property="comment"/> </resultMap> <resultMap type="Deptarment" id="DeptarmentBaseMap"> <id column="deptno" property="deptNo"/> <result column="dname" property="dName"/> <result column="loc" property="loc"/> </resultMap> <!-- if --> <select id="selectEmployeeByCondition1" parameterType="Employee" resultMap="EmployeeBaseMap"> select * from emp where 1 = 1 <if test="empno != null"> and empno = #{empno} </if> <if test="ename != null"> and ename like #{ename} </if> <if test="job != null"> and job = #{job} </if> </select> <!-- choose --> <select id="selectEmployeeByCondition2" parameterType="Employee" resultMap="EmployeeBaseMap"> select * from emp where <choose> <when test="empno != null"> empno = #{empno} </when> <when test="ename"> ename like #{ename} </when> <when test="job != null"> job = #{job} </when> <otherwise> 1 = 1 </otherwise> </choose> </select> <!-- where trim set --> <select id="selectEmployeeByCondition3" parameterType="Employee" resultMap="EmployeeBaseMap"> select * from emp <where> <if test="empno != null"> and empno = #{empno} </if> <if test="ename != null"> and ename like #{ename} </if> <if test="job != null"> and job = #{job} </if> </where> </select> <select id="selectEmployeeByCondition4" parameterType="Employee" resultMap="EmployeeBaseMap"> select * from emp <trim prefix="where" prefixOverrides="and | or"> <if test="empno != null"> and empno = #{empno} </if> <if test="ename != null"> and ename like #{ename} </if> <if test="job != null"> and job = #{job} </if> </trim> </select> <update id="updateEmployee" parameterType="Employee"> update emp <set> <if test="ename != null"> ename = #{ename}, </if> <if test="job != null"> job = #{job}, </if> <if test="mgr != null"> mgr = #{mgr}, </if> <if test="salary != null"> sal = #{salary}, </if> <if test="comment != null"> comm = #{comment}, </if> <if test="dept != null and dept.deptNo != null"> deptno = #{dept.deptNo} </if> </set> where empno = #{empno} </update> <update id="updateEmployee2" parameterType="Employee"> update emp <trim prefix="set" prefixOverrides=","> <if test="ename != null"> ename = #{ename}, </if> <if test="job != null"> job = #{job}, </if> <if test="mgr != null"> mgr = #{mgr}, </if> <if test="salary != null"> sal = #{salary}, </if> <if test="comment != null"> comm = #{comment}, </if> <if test="dept != null and dept.deptNo != null"> deptno = #{dept.deptNo} </if> </trim> where empno = #{empno} </update> <select id="selectEmployeeByEmpnos" parameterType="list" resultMap="EmployeeBaseMap"> select * from emp where empno in <foreach collection="list" open="(" close=")" item="item" index="index" separator=","> #{item} </foreach> </select> </mapper>
映射接口:

package com.briup.mapper; import java.util.List; import com.briup.bean.Employee; public interface DynamicMapper { public abstract List<Employee> selectEmployeeByCondition1(Employee emp); public abstract List<Employee> selectEmployeeByCondition2(Employee emp); public abstract List<Employee> selectEmployeeByCondition3(Employee emp); public abstract List<Employee> selectEmployeeByCondition4(Employee emp); public abstract void updateEmployee(Employee emp); public abstract List<Employee> selectEmployeeByEmpnos(List<Integer> empnos); }
mybatis Generator在ecplise中的使用
mybatis生成器(mbg)是mybatis mybatis和ibatis的代碼生成器。它將為mybatis的所有版本生成代碼,
並在版本2.2.0之后生成ibatis的版本。它將內省一個數據庫表(或多個表),並生成可用於訪問表的工件。
這減少了設置對象和配置文件以與數據庫表交互的初始麻煩。MBG試圖對大部分簡單的CRUD(創建、檢索、更新、刪除)
數據庫操作產生重大影響。您仍然需要為連接查詢或存儲過程手工編寫SQL和對象代碼。
MyBatis Generator will generate:
*Java POJOs that match the table structure.
*MyBatis/iBATIS Compatible SQL Map XML Files. MBG generates SQL for simple CRUD functions on each table in a configuration.
*Java client classes that make appropriate use of the above objects.
1.安裝Mybatis Generator插件
help --> Ecplise Marketplace...
然后搜索mybats,點擊安裝Mybatis Generator 1.3.7
2.創建配置文件
右鍵點擊java項目,選擇 " New -- > Other... " 然后搜索mybatis,選中Mybatis Generator configuration File。
3.准備一些包和目錄
在項目下創建一個目錄lib,把連接mysql數據庫的jar包放進去,我這里是msql-connector-java-5.1.47.jar
創建包com.briup.bean 和 com.briup.mapper,等下自動生成的pojo類和映射文件會放到這下面。
4.修改配置文件
<?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">
<!-- mybatis-generator的核心配置文件 -->
<generatorConfiguration>
<!-- 連接數據庫用到的jar包 -->
<classPathEntry location="G:\java-code-2\mybatis-generator\lib\mysql-connector-java-5.1.47.jar" />
<context id="DB2Tables" targetRuntime="MyBatis3">
<jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://188.131.246.182:3306/cnblogs" userId="study" password="123456">
<!-- 生成primary key方法 -->
<property name="userInformationSchema" value="true" />
</jdbcConnection>
<!--指定生成的類型為java類型,避免數據庫中number等類型字段 -->
<javaTypeResolver>
<property name="forceBigDecimals" value="false" />
</javaTypeResolver>
<!--自動生成的實體的存放包路徑 -->
<javaModelGenerator targetPackage="com.briup.bean" targetProject="mybatis-generator/src">
<property name="enableSubPackages" value="true" />
<property name="trimStrings" value="true" />
</javaModelGenerator>
<!--自動生成的*Mapper.xml文件存放路徑 -->
<sqlMapGenerator targetPackage="com.briup.mapper" targetProject="mybatis-generator/src">
<property name="enableSubPackages" value="true" />
</sqlMapGenerator>
<!--自動生成的*Mapper.java存放路徑 -->
<javaClientGenerator type="XMLMAPPER" targetPackage="com.briup.mapper" targetProject="mybatis-generator/src">
<property name="enableSubPackages" value="true" />
</javaClientGenerator>
<!-- 映射配置 -->
<table tableName="emp" domainObjectName="Employee"></table>
<table tableName="dept" domainObjectName="Deptarment"></table>
</context>
</generatorConfiguration>
5.運行配置文件
右鍵點擊generatorConfig.xml文件,選擇 'Run As' , 'Run Mybatis Generator'。
6.簡單的介紹如何使用Mybatis Generator生成的映射文件進行增刪改查。
如果你去看xml文件,會發現里面使用了動態的SQL語句,不過又額外的增加了一些東西。有興趣的可以研究一下。
使用之前還需要把環境搭建好,什么mybatis-config.xml全局配置文件以及其他的都要准備好,上一篇隨筆有如何在ecplise搭建一個mybatis項目。
由於自動生成的pojo類中toString都沒有重寫,不好看結果,我利用ecplise自動生成toString方法。
0)使用案例0

@SuppressWarnings("unused") @Test public void test0() { SqlSession session = null; try { session = MySqlSessionFactory.opensession(); EmployeeMapper mapper = session.getMapper(EmployeeMapper.class); // 查詢所有員工信息 EmployeeExample example = new EmployeeExample(); List<Employee> list = mapper.selectByExample(example); System.out.println("list.size(): " + list.size()); for(Employee e: list) System.out.println("name: " + e.getEname()); } catch (IOException e) { if(session != null) session.close(); e.printStackTrace(); } }
1)使用案列1
查詢工資大於1200的員工姓名和工資

@SuppressWarnings("unused") @Test public void test1() { SqlSession session = null; try { session = MySqlSessionFactory.opensession(); EmployeeMapper mapper = session.getMapper(EmployeeMapper.class); // 查詢工資大於1200的員工姓名和工資 EmployeeExample example = new EmployeeExample(); Criteria criteria = example.createCriteria(); // 添加查詢條件 criteria.andSalGreaterThan(new BigDecimal(1200)); List<Employee> list = mapper.selectByExample(example); System.out.println("list.size(): " + list.size() + " and list: " + list ); } catch (IOException e) { if(session != null) session.close(); e.printStackTrace(); } }
2)使用案列2
選擇工資不在500到1200的員工的姓名和工資

@SuppressWarnings("unused") @Test public void test2() { SqlSession session = null; try { session = MySqlSessionFactory.opensession(); EmployeeMapper mapper = session.getMapper(EmployeeMapper.class); // 選擇工資不在500到1200的員工的姓名和工資 EmployeeExample example = new EmployeeExample(); Criteria criteria = example.createCriteria(); // 添加查詢條件 criteria.andSalNotBetween(new BigDecimal(500), new BigDecimal(1200)); List<Employee> list = mapper.selectByExample(example); System.out.println("list.size(): " + list.size()); for(Employee e: list) System.out.println("salary: " + e.getSal()); } catch (IOException e) { if(session != null) session.close(); e.printStackTrace(); } }
3)使用案例3
選擇姓名中有字母a和e的員工

@SuppressWarnings({ "unused" }) @Test public void test3() { SqlSession session = null; try { session = MySqlSessionFactory.opensession(); EmployeeMapper mapper = session.getMapper(EmployeeMapper.class); // 選擇姓名中有字母a和e的員工 EmployeeExample example = new EmployeeExample(); Criteria criteria = example.createCriteria(); // 添加查詢條件 criteria.andEnameLike("%a%"); criteria.andEnameLike("%e%"); List<Employee> list = mapper.selectByExample(example); System.out.println("list.size(): " + list.size()); for(Employee e: list) System.out.println("name: " + e.getEname()); } catch (IOException e) { if(session != null) session.close(); e.printStackTrace(); } }
注:如何需要聯合查詢還需要自己寫sql語句,mybatsi Generator生成的SQL映射文件只提供增刪改查功能。
備注:mybais generator項目已經在百度網盤分享。
地址:https://pan.baidu.com/s/1ZrFsu1CcMtrBCmvk9y5IDQ
提取碼:2tk9