1、概念
MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。
优点:
1)与JDBC相比,减少了50%的代码量
2) 最简单的持久化框架,简单易学
3)SQL代码从程序代码中彻底分离出来,可以重用
4)提供XML标签,支持编写动态SQL
5)提供映射标签,支持对象与数据库的ORM字段关系映射
缺点:
1)SQL语句编写工作量大,熟练度要高
2)数据库移植性比较差,如果需要切换数据库的话,SQL语句会有很大的差异
2、创建mybatis项目步骤
1)创建普通的maven项目
2)导入相关的依赖
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.4</version> </dependency> <!-- https://mvnrepository.com/artifact/mysql/mysql-connector-java --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.16</version> </dependency>
3)创建对应的数据表
4)创建与表对应的实体类对象
5)创建对应的dao类/接口
6)编写配置文件:
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> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <!--配置数据库连接--> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/demo?serverTimezone=UTC"/> <property name="username" value="root"/> <property name="password" value="123456"/> </dataSource> </environment> </environments> <!--引入每一个接口对应点xml文件--> <mappers> <mapper resource="EmpDao.xml"/> </mappers> </configuration>
SQL映射文件:EmpDao.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"> <!--namespace:编写接口的全类名,就是告诉要实现该配置文件是哪个接口的具体实现--> <mapper namespace="com.llxazy.dao.EmpDao"> <!-- select:表示这个操作是一个查询操作 id表示的是要匹配的方法的名称 resultType:表示返回值的类型,查询操作必须要包含返回值的类型 #{属性名}:表示要传递的参数的名称 --> <select id="findEmpByEmpno" resultType="com.llxazy.bean.Emp"> select * from emp where empno = #{empno} </select> </mapper>
7)编写测试类
package com.llxazy.test;
import com.llxazy.bean.Emp;
import com.llxazy.dao.EmpDao;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
public class MyTest {
@Test
public void test01() {
// 根据全局配置文件创建出SqlSessionFactory
// SqlSessionFactory:负责创建SqlSession对象的工厂
// SqlSession:表示跟数据库建议的一次会话
String resource = "mybatis-config.xml";
InputStream inputStream = null;
try {
inputStream = Resources.getResourceAsStream(resource);
} catch (IOException e) {
e.printStackTrace();
}
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
// 获取数据库的会话
SqlSession sqlSession = sqlSessionFactory.openSession();
Emp empByEmpno = null;
try {
// 获取要调用的接口类
EmpDao mapper = sqlSession.getMapper(EmpDao.class);
// 调用方法开始执行
empByEmpno = mapper.findEmpByEmpno(7369);
} catch (Exception e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
System.out.println(empByEmpno);
}
}
<?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> <!--引入外部配置文件,类似于Spring中的property-placeholder resource:从类路径引入 url:从磁盘路径或者网络路径引入 --> <properties resource="db.properties"></properties> <!--用来控制mybatis运行时的行为,是mybatis中的重要配置--> <settings> <!--设置列名映射的时候是否是驼峰标识--> <setting name="mapUnderscoreToCamelCase" value="true"/> </settings> <!--typeAliases表示为我们引用的实体类起别名,默认情况下我们需要写类的完全限定名 如果在此处做了配置,那么可以直接写类的名称,在type中配置上类的完全限定名,在使用的时候可以忽略大小写 还可以通过alias属性来表示类的别名 --> <typeAliases> <!--<typeAlias type="com.llxazy.bean.Emp" alias="Emp"></typeAlias>--> <!--如果需要引用多个类,那么给每一个类起别名肯定会很麻烦,因此可以指定对应的包名,那么默认用的是类名--> <package name="com.llxazy.bean"/> </typeAliases> <!-- 在实际的开发过程中,我们可能分为开发环境,生产环境,测试环境等等,每个环境的配置可以是不一样的 environment就用来表示不同环境的细节配置,每一个环境中都需要一个事务管理器以及数据源的配置 我们在后续的项目开发中几乎都是使用spring中配置的数据源和事务管理器来配置,此处不需要研究 --> <!--default:用来选择需要的环境--> <environments default="development"> <!--id:表示不同环境的名称--> <environment id="development"> <transactionManager type="JDBC"/> <!--配置数据库连接--> <dataSource type="POOLED"> <!--使用${}来引入外部变量--> <property name="driver" value="${driverClassname}"/> <property name="url" value="${url}"/> <property name="username" value="${username}"/> <property name="password" value="${password}"/> </dataSource> </environment> </environments> <!-- 在不同的数据库中,可能sql语句的写法是不一样的,为了增强移植性,可以提供不同数据库的操作实现 在编写不同的sql语句的时候,可以指定databaseId属性来标识当前sql语句可以运行在哪个数据库中 --> <databaseIdProvider type="DB_VENDOR"> <property name="MySQL" value="mysql"/> <property name="SQL Server" value="sqlserver"/> <property name="Oracle" value="orcl"/> </databaseIdProvider> <!--将sql的映射文件适用mappers进行映射--> <mappers> <!-- 指定具体的不同的配置文件 class:直接引入接口的全类名,可以将xml文件放在dao的同级目录下,并且设置相同的文件名称,同时可以使用注解的方式来进行相关的配置 url:可以从磁盘或者网络路径查找sql映射文件 resource:在类路径下寻找sql映射文件 --> <!--<mapper resource="EmpDao.xml"/> <mapper resource="UserDao.xml"/> <mapper class="com.llxazy.dao.EmpDaoAnnotation"></mapper>--> <!-- 当包含多个配置文件或者配置类的时候,可以使用批量注册的功能,也就是引入对应的包,而不是具体的配置文件或者类 但是需要注意的是, 1、如果使用的配置文件的形式,必须要将配置文件跟dao类放在一起,这样才能找到对应的配置文件. 如果是maven的项目的话,还需要添加以下配置,原因是maven在编译的文件的时候只会编译java文件 <build> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.xml</include> </includes> </resource> </resources> </build> 2、将配置文件在resources资源路径下创建跟dao相同的包名 --> <package name="com.llxazy.dao"/> </mappers> </configuration>
4、使用注解进行增删改查操作
EmpDaoAnnotation.java
package com.llxazy.dao; import com.llxazy.bean.Emp; import org.apache.ibatis.annotations.Delete; import org.apache.ibatis.annotations.Insert; import org.apache.ibatis.annotations.Select; import org.apache.ibatis.annotations.Update; public interface EmpDaoAnnotation { @Select("select * from emp where empno = #{empno}") public Emp findEmpByEmpno(Integer empno); @Update("update emp set ename=#{ename} where empno = #{empno}") public int updateEmp(Emp emp); @Delete("delete from emp where empno = #{empno}") public int deleteEmp(Integer empno); @Insert("insert into emp(empno,ename) values(#{empno},#{ename})") public int insertEmp(Emp emp); }
5、
在映射文件中,可以编写以下的*元素标签:
cache – 该命名空间的缓存配置。
cache-ref – 引用其它命名空间的缓存配置。
resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
parameterMap – 老式风格的参数映射。此元素已被废弃,并可能在将来被移除!请使用行内参数映射。文档中不会介绍此元素。
sql – 可被其它语句引用的可重用语句块。
insert – 映射插入语句。
update – 映射更新语句。
delete – 映射删除语句。
select – 映射查询语句。
1)insert、update、delete元素
描述 | |
---|---|
id |
在命名空间中唯一的标识符,可以被用来引用这条语句。 |
parameterType |
将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。 |
parameterMap |
用于引用外部 parameterMap 的属性,目前已被废弃。请使用行内参数映射和 parameterType 属性。 |
flushCache |
将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:(对 insert、update 和 delete 语句)true。 |
timeout |
这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。 |
statementType |
可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。 |
useGeneratedKeys |
(仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。 |
keyProperty |
(仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset )。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
keyColumn |
(仅适用于 insert 和 update)设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。 |
databaseId |
支持自增和不支持自增的写法:
<!--如果数据库支持自增可以使用这样的方式--> <insert id="insertUser" useGeneratedKeys="true" keyProperty="id"> insert into user(user_name) values(#{userName}) </insert> <!--如果数据库不支持自增的话,那么可以使用如下的方式进行赋值查询--> <insert id="insertUser2" > <selectKey order="BEFORE" keyProperty="id" resultType="integer"> select max(id)+1 from user </selectKey> insert into user(id,user_name) values(#{id},#{userName}) </insert>
2)
<!-- 当查询语句中包含多个参数时,如果使用#{属性名称}就无法获取具体的值了,那么应该如何使用呢? 下面就是mybatis的参数传递方式 1、如果是单个参数, 基本类型:使用#{随便写} 引用类型:使用#{类的属性名称} 2、多个参数: 当查询的时候传入多个参数的时候,就无法简单的通过#{参数名}来获取值了, 只能通过arg0,arg1...或者param1,param2等方式来获取值 原因就在于,mybatis在传入多个参数的时候,会将这些参数封装到一个map中,此时map中的key就是 arg0,arg1,param1,param2这些值,但是很明显,这样的传值方式不是很友好,没有办法根据参数的名称来 获取具体的值,因此可以使用如下的方式来指定参数的key是什么 Emp selectEmpByNoAndName(@Param("empno") Integer empno, @Param("ename") String ename); 也就是通过@Param来指定存入map中的key值是什么 3、使用map来传递参数: 依然是直接使用#{key}来获取具体的属性值 --> <select id="selectEmpByNoAndName" resultType="com.llxazy.bean.Emp"> select * from emp where empno=#{empno} and ename=#{ename} </select> <select id="selectEmpByNoAndName2" resultType="com.llxazy.bean.Emp"> select * from emp where empno=#{empno} and ename=#{ename} </select>
<!-- 当使用#{}来获取值的时候会发现打印的sql语句如下: select * from emp where empno=? and ename=? 当使用${}来获取值的时候会发现打印的sql语句如下: select * from emp where empno=7369 and ename=‘SMITH‘ 通过刚刚的案例大家已经发现了存在的问题了, 使用#{}方式进行取值:采用的是参数预编译的方式,参数的位置使用?进行替代,不会出现sql注入的问题 使用${}方式进行取值:采用的是直接跟sql语句进行拼接的方式 此处大家需要注意,如果我们的sql语句中的某些值不支持参数预编译,那么就必须要使用${}的方式来取值了 --> <select id="selectEmpByNoAndName" resultType="com.llxazy.bean.Emp"> select * from #{t} where empno=${empno} and ename=${ename} </select>
(3)
<!--当返回值的结果是集合的时候,返回值的类型依然写的是集合中具体的类型--> <select id="selectAllEmp" resultType="com.llxazy.bean.Emp"> select * from emp </select> <!--在查询的时候可以设置返回值的类型为map,当mybatis查询完成之后会把列的名称作为key 列的值作为value,转换到map中 --> <select id="selectEmpByEmpReturnMap" resultType="map"> select * from emp where empno = #{empno} </select> <!--注意,当返回的结果是一个集合对象时,返回值的类型一定要写集合具体value的类型 同时在dao的方法上要添加@MapKey的注解,来设置key是什么结果 @MapKey("empno") Map<Integer,Emp> getAllEmpReturnMap();--> <select id="getAllEmpReturnMap" resultType="com.llxazy.bean.Emp"> select * from emp </select>
对应的dao类:
package com.llxazy.dao; import com.llxazy.bean.Emp; import org.apache.ibatis.annotations.MapKey; import org.apache.ibatis.annotations.Param; import java.util.List; import java.util.Map; public interface EmpDao { public Emp findEmpByEmpno(Integer empno); public int updateEmp(Emp emp); public int deleteEmp(Integer empno); public int insertEmp(Emp emp); Emp selectEmpByNoAndName(@Param("empno") Integer empno, @Param("ename") String ename,@Param("t") String tablename); Emp selectEmpByNoAndName2(Map<String,Object> map); List<Emp> selectAllEmp(); Map<String,Object> selectEmpByEmpReturnMap(Integer empno); @MapKey("empno") Map<Integer,Emp> getAllEmpReturnMap(); }
(4)自定义结果集----resultMap
<?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.llxazy.dao.DogDao"> <!-- 在使用mybatis进行查询的时候,mybatis默认会帮我们进行结果的封装,但是要求列名跟属性名称一一对应上 在实际的使用过程中,我们会发现有时候数据库中的列名跟我们类中的属性名并不是一 一对应的,此时就需要起别名 起别名有两种实现方式: 1、在编写sql语句的时候添加别名 2、自定义封装结果集 --> <!--根据查询的数据进行结果的封装要使用resultMap属性,表示使用自定义规则--> <select id="selectDogById" resultMap="myDog"> select * from dog where id = #{id} </select> <!--自定义结果集,将每一个列的数据跟javaBean的对象属性对应起来 type:表示为哪一个javaBean对象进行对应 id:唯一标识,方便其他属性标签进行引用 --> <resultMap id="myDog" type="com.llxazy.bean.Dog"> <!-- 指定主键列的对应规则: column:表示表中的主键列 property:指定javaBean的属性 --> <id column="id" property="id"></id> <!--设置其他列的对应关系--> <result column="dname" property="name"></result> <result column="dage" property="age"></result> <result column="dgender" property="gender"></result> </resultMap> <!--可以在sql语句中写别名--> <!-- <select id="selectDogById" resultType="com.llxazy.bean.Dog"> select id id,dname name,dage age,dgender gender from dog where id = #{id} </select>--> <!--这种方式是查询不到任何结果的,因为属性名跟列名并不是一一对应的--> <!-- <select id="selectDogById" resultType="com.llxazy.bean.Dog"> select * from dog where id = #{id} </select>--> </mapper>
<?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 namespace="com.llxazy.dao.EmpDao"> <!--再做查询的时候,有时候需要关联其他对象,因此需要使用关联查询 可以通过下面自定义结果集的方式实现 --> <select id="selectEmpAndDept" resultMap="empDept"> select * from emp left join dept on emp.deptno = dept.deptno where empno = #{empno}; </select> <resultMap id="empDept" type="com.llxazy.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="common"></result> <result column="deptno" property="dept.deptno"></result> <result column="dname" property="dept.dname"></result> <result column="loc" property="dept.loc"></result> </resultMap> <!--在mybatis中还提供了一种简单的形式,使用association标签可以搞定 --> <resultMap id="empDept" type="com.llxazy.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="common"></result> <association property="dept" javaType="com.llxazy.bean.Dept"> <id column="deptno" property="deptno"></id> <result column="dname" property="dname"></result> <result column="loc" property="loc"></result> </association> </resultMap> </mapper>
<?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.llxazy.dao.DeptDao"> <!--定义查询集合元素--> <select id="getDeptAndEmps" resultMap="deptEmp"> select * from dept left join emp on dept.deptno = emp.deptno where dept.deptno=#{deptno} </select> <resultMap id="deptEmp" type="com.llxazy.bean.Dept"> <id property="deptno" column="deptno"></id> <result property="dname" column="dname"></result> <result property="loc" column="loc"></result> <!--封装集合类的元素 property:指定集合的属性 ofType:指定集合中的元素类型 --> <collection property="emps" ofType="com.llxazy.bean.Emp"> <id property="empno" column="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="common"></result> </collection> </resultMap> </mapper>
<?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.llxazy.dao.DeptDao"> <select id="getDeptAndEmpsBySimple" resultType="com.llxazy.bean.Dept"> select * from dept where deptno = #{deptno} </select> </mapper>
<?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.llxazy.dao.EmpDao"> <select id="selectEmpAndDeptBySimple" resultMap="simpleEmpAndDept"> select * from emp where empno = #{empno} </select> <resultMap id="simpleEmpAndDept" type="com.llxazy.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="common"></result> <association property="dept" select="com.llxazy.dao.DeptDao.getDeptAndEmpsBySimple" column="deptno"> </association> </resultMap> </mapper>
集合的分步查询
<?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.llxazy.dao.EmpDao"> <select id="selectEmpByStep" resultType="com.llxazy.bean.Emp"> select * from emp where deptno = #{deptno} </select> </mapper>
<?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.llxazy.dao.DeptDao"> <select id="getDeptAndEmpsByStep" resultMap="deptEmpByStep"> select * from dept where deptno = #{deptno} </select> <resultMap id="deptEmpByStep" type="com.llxazy.bean.Dept"> <id property="deptno" column="deptno"></id> <result property="dname" column="dname"></result> <result property="loc" column="loc"></result> <!--封装集合类的元素 property:指定集合的属性 ofType:指定集合中的元素类型 --> <collection property="emps" ofType="com.llxazy.bean.Emp" select="com.llxazy.dao.EmpDao.selectEmpByStep" column="deptno"> </collection> </resultMap> </mapper>
<settings> <!--开启延时加载--> <setting name="lazyLoadingEnabled" value="true"/> </settings>
如果设置了全局加载,但是希望在某一个sql语句查询的时候不适用延时策略,可以添加如下属性:
<association property="dept" select="com.llxazy.dao.DeptDao.getDeptAndEmpsBySimple" column="deptno" fetchType="eager"/>
3)动态sql
-
-
choose (when, otherwise)
-
trim (where, set)
-
<select id="getEmpByCondition" resultType="com.llxazy.bean.Emp"> select * from emp <where> <if test="empno!=null"> empno > #{empno} </if> <if test="ename!=null"> and ename like #{ename} </if> <if test="sal!=null"> and sal > #{sal} </if> </where> </select>
trim截取字符串:
<!-- trim截取字符串: prefix:前缀,为sql整体添加一个前缀 prefixOverrides:去除整体字符串前面多余的字符 suffixOverrides:去除后面多余的字符串 --> <select id="getEmpByCondition" resultType="com.llxazy.bean.Emp"> select * from emp <trim prefix="where" prefixOverrides="and" suffixOverrides="and"> <if test="empno!=null"> empno > #{empno} and </if> <if test="ename!=null"> ename like #{ename} and </if> <if test="sal!=null"> sal > #{sal} and </if> </trim> </select>
(2)
<!--foreach是对集合进行遍历 collection="deptnos" 指定要遍历的集合 close="" 表示以什么结束 index="" 给定一个索引值 item="" 遍历的每一个元素的值 open="" 表示以什么开始 separator="" 表示多个元素的分隔符 --> <select id="getEmpByDeptnos" resultType="Emp"> select * from emp where deptno in <foreach collection="deptnos" close=")" index="idx" item="deptno" open="(" separator=","> #{deptno} </foreach> </select>
(3)choose
不想使用所有的条件,而只是想从多个条件中选择一个使用.
<select id="getEmpByConditionChoose" resultType="com.llxazy.bean.Emp"> select * from emp <where> <choose> <when test="empno!=null"> empno > #{empno} </when> <when test="ename!=null"> ename like #{ename} </when> <when test="sal!=null"> sal > #{sal} </when> <otherwise> 1=1 </otherwise> </choose> </where> </select>
(4)set
<update id="updateEmpByEmpno"> update emp <set> <if test="empno!=null"> empno=#{empno}, </if> <if test="ename!=null"> ename = #{ename}, </if> <if test="sal!=null"> sal = #{sal} </if> </set> <where> empno = #{empno} </where> </update>
4)缓存
MyBatis 内置了一个强大的事务性查询缓存机制,它可以非常方便地配置和定制。 为了使它更加强大而且易于配置,我们对 MyBatis 3 中的缓存实现进行了许多改进。
默认情况下,只启用了本地的会话缓存,它仅仅对一个会话中的数据进行缓存。 要启用全局的二级缓存,只需要在你的 SQL 映射文件中添加一行:
<cache/>
当添加上该标签之后,会有如下效果:
-
映射语句文件中的所有 select 语句的结果将会被缓存。
-
映射语句文件中的所有 insert、update 和 delete 语句会刷新缓存。
-
缓存会使用最近最少使用算法(LRU, Least Recently Used)算法来清除不需要的缓存。
-
缓存不会定时进行刷新(也就是说,没有刷新间隔)。
-
缓存会保存列表或对象(无论查询方法返回哪种)的 1024 个引用。
-
缓存会被视为读/写缓存,这意味着获取到的对象并不是共享的,可以安全地被调用者修改,而不干扰其他调用者或线程所做的潜在修改。
在进行配置的时候还会分为一级缓存和二级缓存:
一级缓存:线程级别的缓存,是本地缓存,sqlSession级别的缓存
二级缓存:全局范围的缓存,不止局限于当前会话
(1)一级缓存的使用
一级缓存是sqlsession级别的缓存,默认是存在的。
在下面的案例中,大家发现我发送了两个相同的请求,但是sql语句仅仅执行了一次,那么就意味着第一次查询的时候已经将结果进行了缓存。
@Test public void test01() { SqlSession sqlSession = sqlSessionFactory.openSession(); try { EmpDao mapper = sqlSession.getMapper(EmpDao.class); List<Emp> list = mapper.selectAllEmp(); for (Emp emp : list) { System.out.println(emp); } System.out.println("--------------------------------"); List<Emp> list2 = mapper.selectAllEmp(); for (Emp emp : list2) { System.out.println(emp); } } catch (Exception e) { e.printStackTrace(); } finally { sqlSession.close(); } }
在大部分的情况下一级缓存是可以的,但是有几种特殊的情况会造成一级缓存失效:
1、一级缓存是sqlSession级别的缓存,如果在应用程序中只有开启了多个sqlsession,那么会造成缓存失效
2、在编写查询的sql语句的时候,一定要注意传递的参数,如果参数不一致,那么也不会缓存结果
3、如果在发送过程中发生了数据的修改,那么结果就不会缓存
二级缓存是全局作用域缓存,默认是不开启的,需要手动进行配置。
Mybatis提供二级缓存的接口以及实现,缓存实现的时候要求实体类实现Serializable接口,二级缓存在sqlSession关闭或提交之后才会生效。
a、缓存的使用
步骤:
1、全局配置文件中添加如下配置: <setting name="cacheEnabled" value="true"/>
2、需要在使用二级缓存的映射文件出使用<cache/>标签标注
3、实体类必须要实现Serializable接口
b、缓存的属性
LRU:最近最少使用的,移除最长时间不被使用的对象
FIFO:先进先出,按照对象进入缓存的顺序来移除
SOFT:软引用,移除基于垃圾回收器状态和软引用规则的对象
WEAK:弱引用,更积极地移除基于垃圾收集器状态和弱引用规则的对象
flushInternal:刷新间隔,单位毫秒
默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新
size:引用数目,正整数
代表缓存最多可以存储多少个对象,太大容易导致内存溢出
readonly:只读,true/false
true:只读缓存,会给所有调用这返回缓存对象的相同实例,因此这些对象不能被修改。
false:读写缓存,会返回缓存对象的拷贝(序列化实现),这种方式比较安全,默认值
c、二级缓存的作用范围
如果设置了全局的二级缓存配置,那么在使用的时候需要注意,在每一个单独的select语句中,可以设置将查询缓存关闭,以完成特殊的设置
1、在setting中设置,是配置二级缓存开启,一级缓存默认一直开启
<setting name="cacheEnabled" value="true"/>
2、select标签的useCache属性:
在每一个select的查询中可以设置当前查询是否要使用二级缓存,只对二级缓存有效
3、sql标签的flushCache属性
增删改操作默认值为true,sql执行之后会清空一级缓存和二级缓存,而查询操作默认是false
4、sqlSession.clearCache()
只是用来清楚一级缓存
(3)
<!-- https://mvnrepository.com/artifact/org.ehcache/ehcache --> <dependency> <groupId>org.ehcache</groupId> <artifactId>ehcache</artifactId> <version>3.8.1</version> </dependency> <!-- https://mvnrepository.com/artifact/org.mybatis.caches/mybatis-ehcache --> <dependency> <groupId>org.mybatis.caches</groupId> <artifactId>mybatis-ehcache</artifactId> <version>1.2.0</version> </dependency> <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-api --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>2.0.0-alpha1</version> </dependency> <!-- https://mvnrepository.com/artifact/org.slf4j/slf4j-log4j12 --> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>2.0.0-alpha1</version> <scope>test</scope> </dependency>
b、导入ehcache配置文件
<?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> <!-- 属性说明: l diskStore:指定数据在磁盘中的存储位置。 l defaultCache:当借助CacheManager.add("demoCache")创建Cache时,EhCache便会采用<defalutCache/>指定的的管理策略 以下属性是必须的: l maxElementsInMemory - 在内存中缓存的element的最大数目 l maxElementsOnDisk - 在磁盘上缓存的element的最大数目,若是0表示无穷大 l eternal - 设定缓存的elements是否永远不过期。如果为true,则缓存的数据始终有效,如果为false那么还要根据timeToIdleSeconds,timeToLiveSeconds判断 l overflowToDisk - 设定当内存缓存溢出的时候是否将过期的element缓存到磁盘上 以下属性是可选的: l timeToIdleSeconds - 当缓存在EhCache中的数据前后两次访问的时间超过timeToIdleSeconds的属性取值时,这些数据便会删除,默认值是0,也就是可闲置时间无穷大 l timeToLiveSeconds - 缓存element的有效生命期,默认是0.,也就是element存活时间无穷大 diskSpoolBufferSizeMB 这个参数设置DiskStore(磁盘缓存)的缓存区大小.默认是30MB.每个Cache都应该有自己的一个缓冲区. l diskPersistent - 在VM重启的时候是否启用磁盘保存EhCache中的数据,默认是false。 l diskExpiryThreadIntervalSeconds - 磁盘缓存的清理线程运行间隔,默认是120秒。每个120s,相应的线程会进行一次EhCache中数据的清理工作 l memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出) -->
c、在mapper文件中添加自定义缓存
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>