为何须要动态SQL?有时候须要根据实际传入的参数来动态的拼接SQL语句。
最经常使用的就是:where和if标签
java
if:字符判断
choose (when, otherwise):分支选择
trim (where, set):字符串截取;其中where标签封装查询条件,set标签封装修改条件
foreach:遍历,实现批处理
mysql
//携带了哪一个字段,查询条件就带上哪一个字段的值 public List<Employee> getEmployeeByConditionIf(Employee employee);
<select id="getEmployeeByConditionIf" resultType="com.neuedu.entity.Employee"> select *from tbl_employee where id = #{id} and user_name = #{userName} and email = #{email} and gender = #{gender} </select>
<select id="getEmployeeByConditionIf" resultType="com.neuedu.entity.Employee"> select *from tbl_employee where <!-- test:判断表达式(OGNL) OGNL参照PPT或者官方文档。 c:if test 从参数中取值进行判断 碰见特殊符号,应该去写转义字符:参考W3CSchool>>HTML>>ISO8859 --> <if test="id != null"> id = #{id} </if> <if test="userName != null && userName !=''"> and user_name = #{userName} </if> <if test="email != null and email.trim() != """> and email = #{email} </if> <!-- ognl会进行字符串和数字的转换判断;"0"==0,"1"==1 --> <if test="gender == 0 or gender == 1"> and gender = #{gender} </if> </select>
@Test public void testGetEmployee(){ EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); Employee employee = new Employee(); employee.setId(1); employee.setUserName("张三丰"); employee.setEmail("sunwukong@163.com"); employee.setGender(1); List<Employee> list = mapper.getEmployeeByConditionIf(employee); System.out.println(list); }
测试结果没问题 。
可是仔细来讲,上面的sql语句是有问题的,当咱们不给动态sql语句传递id值的时候,sql语句的拼装就会有问题! sql
1.给where后面加上1=1,之后的条件均可以使用and xxx了
2.mybatis可使用where标签来将全部的查询条件包括在内。mybatis就会将where标签中拼装的sql,多出来的and或者or去掉!数据库
//须要注意:where标签只会去掉第一个多出来的and或者orapi
3.也就是说使用where标签有时候仍是不能解决问题的,那怎么办呢?咱们这里可使用trim标签!缓存
<select id="getEmployeeByConditionIf" resultType="com.neuedu.entity.Employee"> select *from tbl_employee <!-- 后面多出的and或者or where标签不可以解决 prefix="":前缀:trim标签体重是整个字符串拼串后的结果。 prefix给拼串后的整个字符串加一个前缀 prefixOverrides="": 前缀覆盖:去掉整个字符串前面多余的字符 suffix="":后缀 suffix给拼串后的整个字符串加一个后缀 suffixOverrides="": 后缀覆盖:去掉整个字符串后面多余的字符 --> <trim prefix="where" suffixOverrides="and"> <if test="id != null"> id = #{id} and </if> <if test="userName != null && userName !=''"> user_name = #{userName} and </if> <if test="email != null and email.trim() != """> email = #{email} and </if> <!-- ognl会进行字符串和数字的转换判断;"0"==0,"1"==1 --> <if test="gender==0 or gender==1"> gender = #{gender} </if> </trim> </select>
choose (when, otherwise):若是带了id,就用id查,若是带了userName就用userName查,只会进入其中一个!
案例演示:安全
public List<Employee> getEmployeeByConditionChoose(Employee employee);
<!-- public List<Employee> getEmployeeByConditionChoose(Employee employee); --> <select id="getEmployeeByConditionChoose" resultType="com.neuedu.entity.Employee"> select *from tbl_employee <where> <!-- 若是带了id,就用id查,若是带了userName就用userName查,只会进入其中一个! --> <choose> <when test="id != null"> id = #{id} </when> <when test="userName != null"> user_name like #{userName} </when> <when test="email != null"> email = #{email} </when> <otherwise> 1=1 </otherwise> </choose> </where> </select>
set元素会动态前置set关键字,同时也会消除无关的逗号。
session
public void updateEmp(Employee employee);
<update id="updateEmp"> update tbl_employee <set> <if test="userName != null"> user_name = #{userName}, </if> <if test="email != null"> email = #{email}, </if> <if test="gender != null"> gender = #{gender}, </if> </set> where id = #{id} </update>
@Test public void testGetEmployee(){ EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); Employee employee = new Employee(); employee.setId(1); employee.setUserName("哈哈"); employee.setEmail("sunwukong@163.com"); employee.setGender(1); mapper.updateEmp(employee); }
<update id="updateEmp"> update tbl_employee <trim prefix="set" suffixOverrides=","> <if test="userName != null"> user_name = #{userName}, </if> <if test="email != null"> email = #{email}, </if> <if test="gender != null"> gender = #{gender}, </if> </trim> where id = #{id} </update>
动态SQL的另外一个经常使用的操做是须要对一个集合进行遍历,一般在构建in条件语句的时候!
foreach元素容许指定一个集合,声明集合项和索引变量,并能够指定开闭匹配的字符串以及在迭代之间放置分隔符。
案例演示:mybatis
public List<Employee> getEmpsByConditionForeach(@Param("ids") List<Integer> ids);
<!-- public List<Employee> getEmpsByConditionForeach(List<Integer> ids); --> <select id="getEmpsByConditionForeach" resultType="com.neuedu.entity.Employee"> select * from tbl_employee where id in <!-- collection:指定要遍历的集合 item:将当前遍历出的元素赋值给指定的变量 separator:每一个元素之间的分隔符 open:遍历出全部记过拼接一个开始的字符 close:遍历出全部结果拼接一个结束的字符 --> <foreach collection="ids" open="(" close=")" separator="," item="id"> #{id} </foreach> </select>
@Test public void testGetEmployee(){ EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); List<Integer> asList = Arrays.asList(1,2,3,4); List<Employee> emps = mapper.getEmpsByConditionForeach(asList); for (Employee employee : emps) { System.out.println(employee); } }
foreach标签还能够用于批量保存数据,以下所示:
app
public void addEmps(@Param("emps") List<Employee> emps);
<!-- public void addEmps(@Param("emps") List<Employee> emps); --> <!-- MySQL下批量保存:能够foreach遍历,mysql支持values(),(),()语法 --> <insert id="addEmps"> INSERT INTO tbl_employee(user_name,gender,email,d_id) VALUES <foreach collection="emps" item="emp" separator=","> (#{emp.userName},#{emp.gender},#{emp.email},#{emp.depart.id}) </foreach> </insert>
@Test public void testGetEmployee(){ EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); List<Employee> emps = new ArrayList<Employee>(); emps.add(new Employee(0, 1, "allen", "allen@163.com", new Department(1))); emps.add(new Employee(0, 0, "tom", "tom@163.com", new Department(2))); emps.add(new Employee(0, 1, "mux", "mux@163.com", new Department(1))); mapper.addEmps(emps); }
MyBatis 包含一个很是强大的查询缓存特性,它能够很是方便地配置和定制。缓存能够极大的提高查询效率。
MyBatis系统中默认定义了两级缓存:一级缓存和二级缓存。
SqlSession级别的缓存,一级缓存是一致开启的,无法关闭。方法之间不共用!
与数据库同一次会话期间查询到的数据放在本地缓存中。
之后若是须要获取相同的数据,直接从缓存中拿,不必再去查询数据库;
–一、默认状况下,只有一级缓存(SqlSession级别的缓存,也称为本地缓存)开启。
–二、二级缓存须要手动开启和配置,他是基于namespace级别的缓存。
–三、为了提升扩展性。MyBatis定义了缓存接口Cache。咱们能够经过实现Cache接口来自定义二级缓存。
@Test public void testGetEmployee(){ EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); Employee emp = mapper.getEmployeeById(2); System.out.println(emp); Employee emp2 = mapper.getEmployeeById(2); System.out.println(emp2); System.out.println(emp == emp2); }
@Test public void testGetEmployee() throws IOException{ SqlSessionFactory sessionFactory = testBefore(); SqlSession openSession= sessionFactory.openSession(); EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); Employee emp = mapper.getEmployeeById(2); System.out.println(emp); SqlSession openSession2= sessionFactory.openSession(); EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class); Employee emp2 = mapper2.getEmployeeById(2); System.out.println(emp2); System.out.println(emp == emp2); openSession.close(); openSession2.close(); }
@Test public void testGetEmployee() throws IOException{ SqlSessionFactory sessionFactory = testBefore(); SqlSession openSession= sessionFactory.openSession(); EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); Employee emp = mapper.getEmployeeById(2); System.out.println(emp); Employee emp2 = mapper.getEmployeeById(3); System.out.println(emp2); System.out.println(emp == emp2); openSession.close(); }
@Test public void testGetEmployee() throws IOException{ SqlSessionFactory sessionFactory = testBefore(); SqlSession openSession= sessionFactory.openSession(); EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); Employee emp = mapper.getEmployeeById(2); System.out.println(emp); mapper.updateEmp(new Employee(1, 1, "张三丰","zhangsanfeng@163.com", new Department(1))); Employee emp2 = mapper.getEmployeeById(2); System.out.println(emp2); System.out.println(emp == emp2); openSession.close(); }
@Test public void testGetEmployee() throws IOException{ SqlSessionFactory sessionFactory = testBefore(); SqlSession openSession= sessionFactory.openSession(); EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); Employee emp = mapper.getEmployeeById(2); System.out.println(emp); //手动清空缓存 openSession.clearCache(); Employee emp2 = mapper.getEmployeeById(2); System.out.println(emp2); System.out.println(emp == emp2); openSession.close(); }
【全局缓存】:基于namespace级别的缓存:一个namespace对应一个二级缓存。
【一级缓存的范围仍是过小了,每次SqlSession一关闭,一级缓存中的数据就消失】
因此从这个角度讲:能跨sqlSession的缓存为二级缓存!
一、一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中。
2.若是会话关闭,一级缓存中的数据会被保存到二级缓存中;新的会话查询信息,就能够参照二级缓存中。
3.SqlSession === EmployeeMapper ===> Employee
DepartmentMapper ===> Department
不一样namespace查出的数据会放在本身对应的缓存中(map)
效果:数据会从二级缓存中获取
查出的数据都会被默认先放在一级缓存中。
只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中。
须要注意的是:在哪一个Mapper.xml文件中开启了<cache>缓存标签,哪一个Mapper中就开启了二级缓存。
1).在MyBatis全局配置文件中开启全局二级缓存配置:
<setting name="cacheEnabled" value="true"/>
2).去mapper.xml中配置使用二级缓存:
<cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024" type=""></cache> <!-- eviction=“FIFO”:缓存回收策略: LRU –最近最少使用的:移除最长时间不被使用的对象。 FIFO –先进先出:按对象进入缓存的顺序来移除它们。 SOFT –软引用:移除基于垃圾回收器状态和软引用规则的对象。 WEAK –弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。 默认的是LRU。 flushInterval:缓存刷新间隔 缓存多长时间清空一次,默认不清空,设置一个毫秒值。 size:引用数目,正整数 表明缓存最多能够存储多少个对象,太大容易致使内存溢出 readOnly:是否只读,true/false true:只读缓存;mybatis认为全部从缓存中获取数据的操做都是只读操做,不会修改数据。 mybatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户。不安全,速度快。 false:非只读:mybatis以为获取的数据可能会被修改。 mybatis会利用序列化&反序列化的技术克隆一份。安全,速度慢。 type:指定自定义缓存的全类名 实现cache接口便可! -->
3).咱们的POJO须要实现序列化接口[implements Serializable]
测试二级缓存【测试代码】:
@Test public void testGetEmployee() throws IOException{ SqlSessionFactory sessionFactory = testBefore(); //开启两个会话 SqlSession openSession= sessionFactory.openSession(); SqlSession openSession2 = sessionFactory.openSession(); //利用两个openSession对象获取两个mapper对象 EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class); EmployeeMapper mapper2 = openSession2.getMapper(EmployeeMapper.class); //用第一个openSession获取的mapper对象查询2号员工信息 Employee emp = mapper.getEmployeeById(2); System.out.println(emp); //关闭第一个openSession对象 openSession.close(); //用第二个openSession获取的mapper对象查询2号员工信息 Employee emp2 = mapper2.getEmployeeById(2); System.out.println(emp2); openSession2.close(); } //能够看到只发送了一次SQL语句,第二次查询时从二级缓存中拿到的数据,并无发送新的sql语句。 //须要注意的是:只有一级缓存中关闭的状况下,二级缓存才会被使用。
须要注意的是:在哪一个Mapper.xml文件中开启了<cache>缓存标签,哪一个Mapper中就开启了二级缓存。可用DepartmentMapper.xml验证
和缓存有关的设置/属性:
1)cacheEnabled="true": false:关闭缓存(二级缓存关闭)【一级缓存一直可用】
2)每一个select标签都有useCache="true";
false:不使用缓存(一级缓存依然使用,二级缓存不使用)
3)每一个增删改标签都有一个flushCache="true":增删改执行完成后就会清楚缓存【一级二级缓存都会被清空】
查询标签:flushCache = "false"
若是flushCache = true;每次查询以前都会清空缓存,缓存是没有被使用!
MyBatis官方提供的逆向工程,能够将单表生成经常使用的Mapper、Entity等配置
从数据库表反向生成mapper.java/mapper.xml/entity/辅助查询类
mybatis-3.2.3.jar
mybatis-generator-core-1.3.2.jar
mysql-connector-java-5.1.28-bin.jar
<?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"> <generatorConfiguration> <!-- 无Example等内容(本身选择有无Example) --> <context id="Mysql" targetRuntime="MyBatis3Simple" defaultModelType="flat"> <!-- 有Example查询条件内容 --> <!-- <context id="testTables" targetRuntime="MyBatis3"> --> <commentGenerator> <!-- 是否去除自动生成的注释 true:是 : false:否 --> <property name="suppressAllComments" value="true" /> </commentGenerator> <!--数据库链接的信息:驱动类、链接地址、用户名、密码 (本身修改)--> <jdbcConnection driverClass="com.mysql.jdbc.Driver" connectionURL="jdbc:mysql://localhost:3306/user" userId="root" password="123456"> </jdbcConnection> <!-- 默认false,把JDBC DECIMAL 和 NUMERIC 类型解析为 Integer,为 true时把JDBC DECIMAL 和 NUMERIC 类型解析为java.math.BigDecimal --> <javaTypeResolver> <property name="forceBigDecimals" value="false" /> </javaTypeResolver> <!-- targetProject:生成Entity类的路径 --> <javaModelGenerator targetProject=".\src" targetPackage="com.neuedu.mybatis.entities"> <!-- enableSubPackages:是否让schema做为包的后缀 --> <property name="enableSubPackages" value="false" /> <!-- 从数据库返回的值被清理先后的空格 --> <property name="trimStrings" value="true" /> </javaModelGenerator> <!-- targetProject:XXXMapper.xml映射文件生成的路径 --> <sqlMapGenerator targetProject=".\src" targetPackage="com.neuedu.mybatis.mapper"> <!-- enableSubPackages:是否让schema做为包的后缀 --> <property name="enableSubPackages" value="false" /> </sqlMapGenerator> <!-- targetPackage:Mapper接口生成的位置 --> <javaClientGenerator type="XMLMAPPER" targetProject=".\src" targetPackage="com.neuedu.mybatis.mapper"> <!-- enableSubPackages:是否让schema做为包的后缀 --> <property name="enableSubPackages" value="false" /> </javaClientGenerator> <!-- 数据库表名字和咱们的entity类对应的映射指定(需本身修改) --> <table tableName="person" domainObjectName="Person" /> <table tableName="stu" domainObjectName="Stu" /> <!-- 有些表的字段须要指定java类型 <table schema="" tableName=""> <columnOverride column="" javaType="" /> </table> --> </context> </generatorConfiguration>
package com.neuedu.mybatis.mbg; import java.io.File; import java.util.ArrayList; import java.util.List; import org.mybatis.generator.api.MyBatisGenerator; import org.mybatis.generator.config.Configuration; import org.mybatis.generator.config.xml.ConfigurationParser; import org.mybatis.generator.internal.DefaultShellCallback; public class Mbg_GeneratorUtil { public void generator() throws Exception { List<String> warnings = new ArrayList<String>(); boolean overwrite = true; //指定 逆向工程配置文件 File configFile = new File("generatorConfig.xml"); ConfigurationParser cp = new ConfigurationParser(warnings); Configuration config = cp.parseConfiguration(configFile); DefaultShellCallback callback = new DefaultShellCallback(overwrite); MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,callback, warnings); myBatisGenerator.generate(null); } public static void main(String[] args) throws Exception { try { new Mbg_GeneratorUtil().generator(); } catch (Exception e) { e.printStackTrace(); } } }