Mybatis SQL映射文件详解 1 2 3 4 5 6 7 8 9 10 **映射文件中,可以编写以下的顶级元素标签** :cache – 该命名空间的缓存配置。 cache-ref – 引用其它命名空间的缓存配置。 resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。 parameterMap – 老式风格的参数映射。此元素已被废弃,并可能在将来被移除!请使用行内参数映射。文档中不会介绍此元素。 sql – 可被其它语句引用的可重用语句块。 insert – 映射插入语句。 update – 映射更新语句。 delete – 映射删除语句。 select – 映射查询语句。
增删改 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <insert id ="save" useGeneratedKeys ="true" keyProperty ="id" > insert into emp(empno,ename) values(#{empno},#{ename}) </insert > <update id ="update" > update emp set sal=#{sal} where empno = #{empno} </update > <delete id ="delete" > delete from emp where empno = #{empno} </delete >
1 2 3 public Integer save (Emp emp) ;public Integer update (Emp emp) ;public Integer delete (Integer empno) ;
select 基本语法
参数传递
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <select id ="selectEmpByEmpnoAndSal" resultType ="com.kuro.bean.Emp" > select * from emp where empno = #{emono} </select > <select id ="selectEmpByEmpnoAndSal2" resultType ="com.kuro.bean.Emp" > select * from emp where empno = #{empno} and sal >#{sal} </select > <select id ="selectEmpByEmpnoAndSal3" resultType ="com.kuro.bean.Emp" > select * from emp where empno = #{empno} and sal >#{sal} </select >
1 2 3 public List<Emp> selectEmpByEmpnoAndSal (Emp emp) ;public List<Emp> selectEmpByEmpnoAndSal2 (@Param("empno" ) Integer empno, @Param ("sal" ) Double sal) ;public List<Emp> selectEmpByEmpnoAndSal3 (Map<String,Object> map) ;
返回集合 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <select id ="selectAll" resultType ="Emp" > select * from emp </select > <select id ="selectAll2" resultType ="Emp" > select * from emp </select > <select id ="selectEmpByEmpnoReturnMap" resultType ="map" > select * from emp where empno = #{empno} </select >
1 2 3 4 public List<Emp> selectAll () ;@MapKey ("ename" )public Map<String,Emp> selectAll2 () ;public Map<Object,Object> selectEmpByEmpnoReturnMap (Integer empno) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <mapper namespace ="com.kuro.dao.DogDao" > <resultMap id ="myDog" type ="com.kuro.bean.Dog" > <id property ="id" column ="id" > </id > <result property ="name" column ="dname" > </result > <result property ="age" column ="dage" > </result > <result property ="gender" column ="dgender" > </result > </resultMap > <select id ="selectDogById" resultMap ="myDog" > select * from dog where id = #{id} </select > </mapper >
联合查询 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 <resultMap id ="myEmp" type ="com.kuro.bean.Emp" > <id column ="empno" property ="empno" > </id > <association property ="dept" javaType ="com.kuro.bean.Dept" > <id property ="deptno" column ="deptno" > </id > <result property ="dname" column ="dname" > </result > <result property ="loc" column ="loc" > </result > </association > </resultMap > <select id ="selectEmpByEmpno" resultMap ="myEmp" > select * from emp left join dept on emp.deptno = dept.deptno where empno = #{empno} </select >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <resultMap id ="myDept" type ="com.kuro.bean.Dept" > <id column ="deptno" property ="deptno" > </id > <result property ="dname" column ="dname" > </result > <result property ="loc" column ="loc" > </result > <collection property ="emps" ofType ="com.kuro.bean.Emp" > <id column ="empno" property ="empno" > </id > <result column ="ename" property ="ename" > </result > <result column ="job" property ="job" > </result > <result column ="mgr" property ="mgr" > </result > <result column ="hiredate" property ="hiredate" > </result > <result column ="sal" property ="sal" > </result > <result column ="comm" property ="comm" > </result > </collection > </resultMap > <select id ="selectDeptByDeptno" resultMap ="myDept" > select * from dept left join emp on dept.deptno = emp.deptno where dept.deptno = #{deptno} </select >
1 2 3 4 5 6 7 8 9 10 11 public class Emp { private Integer empno; private Dept dept; } public class Dept { private Integer deptno; private String dname; private String loc; private List<Emp> emps; }
分步查询 1 2 3 4 5 6 7 8 9 10 11 <select id ="selectDeptByStemp2" resultMap ="deptEmp" > select * from dept where deptno = #{deptno} </select > <resultMap id ="deptEmp" type ="com.kuro.bean.Dept" > <id column ="deptno" property ="deptno" > </id > <result property ="dname" column ="dname" > </result > <result property ="loc" column ="loc" > </result > <collection property ="emps" ofType ="com.kuro.bean.Emp" select ="com.kuro.dao.EmpDao.selectEmpByStep2" column ="deptno" fetchType ="lazy" > </collection > </resultMap >
1 2 3 public Dept selectDeptByStemp2 (Integer deptno) ;public Emp selectEmpByStep2 (Integer deptno) ;
延迟查询 当我们在进行表关联的时候,有可能在查询结果的时候不需要关联对象的属性值,那么此时可以通过延迟加载来实现功能。在全局配置文件mybatis-config.xml
中添加如下属性
1 2 3 4 <settings > <setting name ="lazyLoadingEnabled" value ="true" /> </settings >
如果设置了全局加载,但是希望在某一个sql语句查询的时候不适用延时策略,可以添加如下属性:
1 <association property ="dept" select ="com.kuro.dao.DeptDao.getDeptAndEmpsBySimple" column ="deptno" fetchType ="eager" />
动态SQL if 使用动态 SQL 最常见情景是根据条件包含 where 子句的一部分。
where 元素只会在子元素返回任何内容的情况下才插入 “WHERE” 子句。而且,若子句的开头为 “AND” 或 “OR”,where 元素也会将它们去除。
1 2 3 4 5 6 7 8 9 10 11 12 <select id ="selectEmpByCondition" resultType ="com.kuro.bean.Emp" > select * from emp <where > <if test ="empno!=null" > empno > #{empno} </if > <if test ="ename!=null" > and ename=#{ename} </if > </where > </select >
1 public Emp selectEmpByCondition (Emp emp) ;
choose 有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,MyBatis 提供了 choose 元素,它有点像 Java 中的 switch 语句。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <select id ="selectEmpByCondition" resultType ="com.kuro.bean.Emp" > select * from emp <where > <choose > <when test ="empno!=null" > empno = #{empno} </when > <when test ="ename!=null" > ename=#{ename} </when > <otherwise > 1=1 </otherwise > </choose > </where > </select >
trim 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <select id ="selectEmpByCondition" resultType ="com.kuro.bean.Emp" > select * from emp <trim prefix ="where" prefixOverrides ="and" suffixOverrides ="and|or" > <if test ="empno!=null" > empno = #{empno} and </if > <if test ="ename!=null" > ename=#{ename} or </if > </trim > </select >
foreach 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <select id ="selectEmpByDeptnos" resultType ="com.kuro.bean.Emp" > select * from emp where deptno in <foreach collection ="deptnos" separator ="," open ="(" item ="deptno" index ="idx" close =")" > #{deptno} </foreach > </select >
1 public List<Emp> selectEmpByDeptnos (@Param("deptnos" ) List<Integer> deptnos) ;
缓存机制 1 2 3 4 5 6 7 8 如果没有缓存,那么每次查询的时候都需要从数据库中加载数据,这回造成io的性能问题,所以,在很多情况下 如果连续执行两条相同的sql语句,可以直接从缓存中获取,如果获取不到,那么再去查询数据库,这意味着查询完成的结果 需要放到缓存中。 缓存分类: 1、一级缓存:表示sqlSession级别的缓存,每次查询的时候会开启一个会话,此会话相当于一次连接,关闭之后自动失效 2、二级缓存:全局范围内的缓存,sqlsession关闭之后才会生效 3、第三方缓存:继承第三方的组件,来充当缓存的作用
一级缓存 1 2 3 4 5 6 7 一级缓存:表示将数据存储在sqlsession中,关闭之后自动失效,默认情况下是开启的 在同一个会话之内,如果执行了多个相同的sql语句,那么除了第一个之外,所有的数据都是从缓存中进行查询的 在某些情况下,一级缓存可能会失效? 1、在同一个方法中,可能会开启多个会话,此时需要注意,会话跟方法没有关系,不是一个方法就只能由一个会话,所以严格记住,缓存的数据是保存在sqlsession中的 2、当传递对象的时候,如果对象中的属性值不同,也不会走缓存 3、在同一个连接中,如果修改了数据,那么缓存会失效,不同连接中,相互不受影响 4、如果在一个会话过程中,手动清空了缓存,那么缓存也会失效
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @Test public void test () throws IOException { SqlSession sqlSession = sqlSessionFactory.openSession(); EmpDao mapper = sqlSession.getMapper(EmpDao.class ) ; Emp emp = mapper.selectEmpByEmpno(7369 ); System.out.println(emp); EmpDao mapper2 = sqlSession.getMapper(EmpDao.class ) ; Emp emp2 = mapper2.selectEmpByEmpno(7369 ); System.out.println(emp2); sqlSession.close(); }
二级缓存 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 二级缓存:表示的是全局缓存,必须要等到sqlsession关闭之后才会生效 默认是不开启的,如果需要开启的话,需要进行如下设置 1、修改全局配置文件,在settings中添加配置 <setting name="cacheEnabled" value="true"/> 2、指定在哪个映射文件中使用缓存的配置 <cache></cache> 3、对应的java实体类必须要实现序列化的接口 在使用二级缓存的时候,可以包含多个属性值: eviction:缓存淘汰机制: LRU: 最近最少使用 FIFO:先进先出,按照添加缓存的顺序执行 SOFT – 软引用:基于垃圾回收器状态和软引用规则移除对象。 WEAK – 弱引用:更积极地基于垃圾收集器状态和弱引用规则移除对象。 flushInterval:设置间隔多长时间进行缓存刷新 size:引用的条数,是一个正整数,缓存中可以存储多少个对象,一般不设置,如果设置的话不要太大,会导致内存溢出 readonly:只读属性: true:只读缓存,会给所有的调用的方法返回该对象的实例,不安全 false:读写缓存,只是返回缓存对象的拷贝,比较安全 一级缓存跟二级缓存有没有可能同时存在数据? 不会同时存在,因为二级缓存生效的时候,是在sqlsession关闭的时候 当查询数据的时候,我们是先查询一级缓存还是先查询二级缓存? **先查询二级缓存,然后再查询一级缓存**
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Test public void test () throws IOException { SqlSession sqlSession = sqlSessionFactory.openSession(); EmpDao mapper = sqlSession.getMapper(EmpDao.class ) ; Emp emp = mapper.selectEmpByEmpno(7369 ); System.out.println(emp); sqlSession.close(); SqlSession sqlSession2 = sqlSessionFactory.openSession(); EmpDao mapper2 = sqlSession2.getMapper(EmpDao.class ) ; Emp emp2 = mapper2.selectEmpByEmpno(7369 ); System.out.println(emp2); sqlSession2.close(); }
第三方缓存 1 2 3 4 5 可以使用第三方缓存,如`Ehcache` 1.新增`maven` 依赖 1.在映射文件中使用缓存的配置 <cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache> 2.新建配置文件`ehcache.xml` 并配置相关属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 <dependency > <groupId > org.ehcache</groupId > <artifactId > ehcache</artifactId > <version > 3.8.1</version > </dependency > <dependency > <groupId > org.mybatis.caches</groupId > <artifactId > mybatis-ehcache</artifactId > <version > 1.2.0</version > </dependency > <dependency > <groupId > org.slf4j</groupId > <artifactId > slf4j-api</artifactId > <version > 2.0.0-alpha1</version > </dependency > <dependency > <groupId > org.slf4j</groupId > <artifactId > slf4j-log4j12</artifactId > <version > 2.0.0-alpha1</version > <scope > test</scope > </dependency >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <?xml version="1.0" encoding="UTF-8"?> <ehcache xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation ="http://ehcache.org/ehcache.xsd" > <diskStore path ="D:\ehcache" /> <defaultCache maxElementsInMemory ="1" maxElementsOnDisk ="10000000" eternal ="false" overflowToDisk ="true" timeToIdleSeconds ="120" timeToLiveSeconds ="120" diskExpiryThreadIntervalSeconds ="120" memoryStoreEvictionPolicy ="LRU" > </defaultCache > </ehcache >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 属性说明: diskStore:指定数据在磁盘中的存储位置。 defaultCache:当借助CacheManager.add("demoCache")创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略 以下属性是必须的: maxElementsInMemory - 在内存中缓存的element的最大数目 maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大 eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断 overflowToDisk - 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上 以下属性是可选的: timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大 timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大 diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区. diskPersistent - 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。 diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作 memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出)
代码生成器 1 2 3 4 可以通过generator反向从数据库表动态生成实体类和一些基础的查询API: 1.引入依赖 2.编写`mybatis-generator.xml` 配置 3.运行代码生成映射
1 2 3 4 5 <dependency > <groupId > org.mybatis.generator</groupId > <artifactId > mybatis-generator-core</artifactId > <version > 1.4.0</version > </dependency >
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 <!DOCTYPE generatorConfiguration PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN" "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd" > <generatorConfiguration > <context id ="simple" targetRuntime ="MyBatis3Simple" > <jdbcConnection driverClass ="com.mysql.cj.jdbc.Driver" connectionURL ="jdbc:mysql://192.168.85.111:3306/demo?serverTimezone=UTC" userId ="root" password ="123456" /> <javaModelGenerator targetPackage ="com.kuro.bean" targetProject ="src/main/java" /> <sqlMapGenerator targetPackage ="com.kuro.dao" targetProject ="src/main/resources" /> <javaClientGenerator type ="XMLMAPPER" targetPackage ="com.kuro.dao" targetProject ="src/main/java" /> <table tableName ="emp" /> <table tableName ="dept" /> </context > </generatorConfiguration >
1 2 3 4 5 6 7 8 9 10 11 12 public class MyTest { public static void main (String[] args) throws Exception { List<String> warnings = new ArrayList<String>(); boolean overwrite = true ; File configFile = new File("mybatis-generator.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 ); } }