前言:
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-2mybatis-generatorlibmysql-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