MyBatis
MyBatis最初是Apache的一个开源项目iBatis, 2010年6月这个项目由Apache Software Foundation迁移到了Google Code。随着开发团队转投Google Code旗下, iBatis3.x正式更名为MyBatis。代码于2013年11月迁移到Github。 iBatis一词来源于“internet”和“abatis”的组合,是一个基于Java的持久层框架。 iBatis提供的持久层框架 包括SQL Maps和Data Access Objects(DAO)。
MyBatis 特性
MyBatis 是支持定制化 SQL, 存储过程中以及高级映射的优秀的持久层框架
MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集
MyBatis 可以使用简单的 XML 或 注解用于配置和原始映射, 将接口和 Java 的 POJO 映射成数据库中的记录
MyBatis 是一个 半自动的 ORM (Object Relation Mapping) 框架
和其他持久层技术对比
JDBC
SQL 夹杂在 JAVA 代码中, 耦合度高, 导致硬编码内伤
维护不易且实际开发需求中 SQL 有变化, 频繁修改的情况多见
代码冗长, 开发效率低
Hibernate 和 JPA
操作简便, 开发效率高
程序的长难复杂 SQL 需要绕过 框架
内部自动生产 SQL, 不易做特殊优化
基于全映射的全自动框架, 大量字段的 POJO 进行部分映射时比较困难
反射操作太多, 导致数据库性能下降
MyBatis
轻量级, 性能出色
SQL 和 java 分开, 功能边界清晰, java 专注业务, SQL 语句专注数据
开发 效率稍逊于 Hibernate, 但是完全能够接受
MyBatis 使用 搭建 MyBatis
开发环境 IDE: idea 2021.3
构建工具: maven 3.8.6
MySQL 版本 : 8.x
MyBatis 版本: 3.5.11
创建 maven 工程 打包方式 <packaging>jar</packaging>
引入依赖 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.5.11</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.13.2</version > <scope > test</scope > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.30</version > </dependency >
创建 MyBatis 核心配置文件
习惯上 命名为 mybatis-config.xml, 这个 文件名仅仅只是建议, 并非强制要求. 将来整合 Spring之后, 这个配置文件可以忽略, 所以 可以直接复制
核心配置文件主要用于配置连接数据库的环境以及 MyBatis 的全局配置信息
核心配置文件存放的位置是 resources下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "https://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/book?rewriteBatchedStatements=true" /> <property name ="username" value ="root" /> <property name ="password" value ="C020611." /> </dataSource > </environment > </environments > <mappers > <mapper resource ="mappers/UserMapper.xml" /> </mappers > </configuration >
创建 mapper 接口
MyBatis 中的 mapper 接口相当于之前的 dao. 但是区别在于, mapper 只是接口, 我们不需要提供实现类
1 2 3 4 5 6 7 8 9 public interface UserMapper { int addUser () ; }
创建 MyBatis 的映射文件 相关概念 : ORM (Object Relation Mapping 对象关系映射)
对象 : Java 的实体类对象
关系 : 关系型数据库
映射 : 二者之间的关系
Java 概念
数据库概念
类
表
属性
字段/列
对象
记录/行
映射文件的命名规则 表所对应的实体类的类名+Mapper.xml
因此一个映射文件对应一个实体类, 对应一张表的操作
MyBatis 映射文件用于编写 SQL, 访问以及操作表中的数据
MyBatis 映射文件存放的位置是 src/main/resources/mappers 目录下
例如: 表t_user, 映射的实体类是 User, 所对应的映射文件应该是 UserMapper.xml
两个一致 MyBatis 中可以面向接口操作数据, 要保证两个一致:
mapper 接口的全类名和映射文件的命名空间保持一致
mapper 接口中的方法名和 映射文件中编写 SQL 标签的 id 属性保持一致
1 2 3 4 5 6 7 8 9 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.cs7eric.mybatis.mapper.UserMapper"> <insert id="addUser"> insert into t_user values(null,'qqsqsq','csq','qqq') </insert> </mapper>
通过 Junit 测试功能 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Test public void addUser () throws IOException { String resource = "mybatis-config.xml" ; InputStream inputStream = Resources.getResourceAsStream(resource); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder (); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(inputStream); SqlSession sqlSession = sqlSessionFactory.openSession(true ); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); int result = userMapper.addUser(); System.out.println(result); }
SqlSession : 代表 Java 程序和 数据库之间的会话 (HttpSession 是 Java程序 和 浏览器之间的会话)
SqlSessionFactory : 是 生产 “SqlSession” 的 工厂
“工厂模式” : 如果创建 某一个对象, 使用的过程基本固定, 那么我们就可以把创建这个对象的相关代码封装到一个工厂类中, 以后都使用这个工厂类来 生产我们需要的对象
加入 log4j 日志功能 加入依赖 1 2 3 4 5 6 <dependency > <groupId > log4j</groupId > <artifactId > log4j</artifactId > <version > 1.2.17</version > </dependency >
加入 log4j 的配置文件 log4j 的配置文件名叫作 log4j.xm, 存放的位置是resources 下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE log4j:configuration SYSTEM "log4j.dtd"> <log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/"> <appender name="STDOUT" class="org.apache.log4j.ConsoleAppender"> <param name="Encoding" value="UTF-8" /> <layout class="org.apache.log4j.PatternLayout"> <param name="ConversionPattern" value="%-5p %d{MM-dd HH:mm:ss,SSS} %m (%F:%L) \n" /> </layout> </appender> <logger name="java.sql"> <level value="debug" /> </logger> <logger name="org.apache.ibatis"> <level value="info" /> </logger> <root> <level value="debug" /> <appender-ref ref="STDOUT" /> </root> </log4j:configuration>
日志的级别 FATAL(致命)>ERROR(错误)>WARN(警告)>INFO(信息)>DEBUG(调试)
从左到右打印的内容越来越详细
核心配置文件详解
核心配置文件中的标签必须按照固定的顺序
properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,reflectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?
mybatis-config.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 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "https://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!-- 引入 properties 文件, 此时就可以 ${属性名} 的方式访问属性值 --> <properties resource="druid.properties"></properties> <settings> <!-- 将表中的字段的下划线自动转换成为驼峰 --> <setting name="mapUnderscoreToCamelCase" value="true"/> <!-- 开启延迟加载 --> <setting name="lazyLoadingEnabled" value="true"/> </settings> <typeAliases> <!-- typeAliases :设置某个具体的类型的别名 属性: type:需要设置别名的类型的别名 alias:设置此类型的别名, 若不设置此属性, 该属性拥有默认的别名, 即类名不区分大小写 若设置此属性, 此时该类型的别名只能使用 alias 所设置的值 --> <!-- 以包为单位, 设置该包下所有的类型都拥有默认的别名,即类名不区分大小写 --> <package name="com.cs7eric.mybatis.pojo"/> </typeAliases> <!-- environments: 设置多个连接数据库的环境 属性: default: 设置默认使用的环境的 id --> <environments default="druid"> <!-- environment : 设置具体的连接数据库的环境信息 属性: id: 设置环境的唯一标识,可通过 environments 标签中的 default 设置某一个环境的 ID, 表示默认使用的环境 --> <environment id="druid"> <!-- transactionManager: 设置事务管理方式 属性: type:设置事务管理的方式,type="JDBC|MANAGED" type="JDBC" : 设置当前环境的事务管理都必须手动处理 type="MANAGED" : 设置事务被管理, 例如 Spring 中的 AOP --> <transactionManager type="JDBC"/> <!-- dataSource : 设置数据源 属性: type : 设置数据源的类型,type="POOLED|UNPOOLED|JNDI" type="POOLED" : 使用数据库连接池, 即会将创建的连接进行缓存, 下次使用可以从缓存中获取, 不需要重新创建 type="UNPOOLED" : 不使用 数据库连接池, 即每次连接都需要重新创建 type="JNDI" : 调用上下文中的数据源 --> <dataSource type="POOLED"> <property name="driver" value="${druid.driver}"/> <property name="url" value="${druid.url}"/> <property name="username" value="${druid.username}"/> <property name="password" value="${druid.password}"/> </dataSource> </environment> </environments> <!-- 引入映射文件 --> <mappers> <!-- 以包为单位, 将包下所有的映射文件引入核心配置文件 注意: 此方式必须保证 mapper 接口 和 mapper 映射文件都必须在相同的包下 --> <package name="com.cs7eric.mybatis.mapper"></package> </mappers> </configuration>
mybatis-mapper.xml 1 2 3 4 5 6 7 <?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "https://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace=""> </mapper>
MyBatis 的增删改查
添加 1 2 3 <insert id="addUser"> insert into t_user values(null,'aaaaa','csq','qqq') </insert>
删除 1 2 3 4 <!-- int deleteUserById(@Param("id") Integer id); --> <delete id="deleteUserById"> delete from t_user where id = #{id} </delete>
修改 1 2 3 4 <!-- void updateUserById(User user); --> <update id="updateUserById"> update t_user set username=#{username}, password=#{password}, email=#{email} where id=#{id} </update>
查询 查询实体类对象 1 2 3 4 <!-- User queryForUserById(@Param("id") Integer id); --> <select id="queryForUserById" resultType="com.cs7eric.mybatis.pojo.User"> select * from t_user where id = #{id} </select>
查询集合 1 2 3 4 <!-- List<User> queryForList(); --> <select id="queryForList" resultType="com.cs7eric.mybatis.pojo.User"> select * from t_user </select>
MyBatis 获取参数
MyBatis 获取参数的两种方式
${}
使用字符串拼接的方式拼接 SQL, 若为字符串类型或日期类型的字段进行赋值时, 需要手动加 单引号 ;但是使用 #{}
使用占位符的方式拼接 SQL,此时为字符串类型或日期类型的字段进行赋值时, 可以自动添加单引号
单个字面量类型的参数 若 mapper 接口中的方法参数为单个的字面量类型
此时可以使用 #{}
, ${}
以任意的名称获取参数的值, 注意 ${}
需要手动加 单引号
多个字面量类型的参数 若 mapper 接口中的方法参数为多个时
此时 MyBatis 会自动将这些参数放在一个 map 集合中, 以 arg0,arg1… 为键, 以参数为值; 以 param1,param2…为键,以参数为值; 因此,只需通过 ${}
和#{}
访问集合的键就可以获取相应 的值,注意 ${}
需要手动加 单引号
MyBatis 各种查询功能
查询 一个实体类对象 1 2 3 4 5 6 7 User queryForUserById (@Param("id") Integer id) ;
1 2 3 4 <!-- User queryForUserById(@Param("id") Integer id); --> <select id="queryForUserById" resultType="com.cs7eric.mybatis.pojo.User"> select * from t_user where id = #{id} </select>
查询一个 list 集合 1 2 3 4 5 6 List<User> queryForList () ;
1 2 3 4 <!-- List<User> queryForList(); --> <select id="queryForList" resultType="com.cs7eric.mybatis.pojo.User"> select * from t_user </select>
查询单个数据
1 2 3 4 <!-- int getCount(); --> <select id="getCount" resultType="java.lang.Integer"> select count(id) from t_user </select>
查询一条数据为 map 集合 1 2 3 4 5 6 7 8 @MapKey("id") Map<String, Object> getUserToMap (@Param("id") Integer id) ;
1 2 3 4 <!-- Map<String,Object> getUserToMap(@Param("id") Integer id); --> <select id="getUserToMap" resultType="java.util.Map"> select * from t_user where id = #{id} </select>
查询多条数据为 map 集合 方式一 将表中的数据以map集合的方式查询,一条数据对应一个map;若有多条数据,就会产生多个map集合,此时可以将这些map放在一个list集合中获取
1 2 3 4 5 6 7 @MapKey("id") List<Map<String,Object>> getUsersToMap () ;
1 2 3 <select id="getUsersToMap" resultType="java.util.Map"> select * from t_user </select>
方式二 将表中的数据以map集合的方式查询,一条数据对应一个map;若有多条数据,就会产生多个map集合,并且最终要以一个map的方式返回数据,此时需要通过**@MapKey**注解设置map集合的键,值是每条数据所对应的map集合
1 2 3 4 5 6 7 @MapKey("id") List<Map<String,Object>> getUsersToMap () ;
1 2 3 4 <!-- List<Map<String,Object>> getUsersToMap(); --> <select id="getUsersToMap" resultType="java.util.Map"> select * from t_user </select>
特殊 SQL 的执行
模糊查询 1 2 3 4 5 6 7 List<User> fuzzySearch (@Param("key") String key) ;
1 2 3 <select id="fuzzySearch" resultType="com.cs7eric.mybatis.pojo.User"> select * from t_user where username like "%"#{key}"%" </select>
批量删除 1 2 3 4 5 6 7 int deleteMore (@Param("ids") String ids) ;
1 2 3 4 <!-- int deleteMore(@Param("ids") String ids); --> <delete id="deleteMore"> delete from t_user where id in (${ids}) </delete>
动态设置表名 1 2 3 4 5 6 7 List<User> getAllUsers (@Param("tableName") String tableName) ;
1 2 3 4 <!-- List<User> getAllUsers(@Param("tableName") String tableName);--> <select id="getAllUsers" resultType="com.cs7eric.mybatis.pojo.User"> select * from ${tableName} </select>
添加功能获取自增的主键
useGeneratedKeys :设置使用自增的主键
keyProperty : 因为增删改有统一的返回值是受影响的行数, 因此只能将获取的自增的主键放在传输的参数 user 对象的某个属性中
1 2 3 4 5 6 7 int insertUser (User user) ;
1 2 3 <insert id="insertUser" useGeneratedKeys="true" keyProperty="id"> insert into t_user values(null, #{username}, #{password}, #{email}) </insert>
自定义映射 resultMap
resultMap 处理字段和属性的映射关系 若字段名和实体类中的属性名不一致, 则可以通过 resultMap 设置自定义映射
resultMap:设置自定义映射
属性:
id : 表示自定义映射的唯一标识
type : 查询的数据要映射的实体类的类型
子标签:
id : 设置主键的映射关系
result : 设置 普通字段的映射关系
association : 设置多对一的映射关系
collection : 设置 一对多的映射关系
属性:
property : 设置 映射关系中 实体类的属性名
column :设置映射关系中 表中的字段名
若字段名和实体类中的属性名不一样,但是字段名符合数据库的规则,实体类中的属性符合 java 的规则,此时也可以通过以下两种方式处理字段名和实体类中的属性的映射关系:
可以通过为字段名起别名的方式, 保证和实体类中的属性名保持一致
可以在 MyBatis 的核心配置文件中设置一个全局配置信息 **mapUnderscoreToCamelCase **, 可以在查询表中数据时,自动将字段名 转换成为驼峰
<settings><setting name="mapUnderscoreToCamelCase" value="true"/></settings>
1 2 3 4 5 6 7 User queryForUser (@Param("id") Integer id) ;
1 2 3 4 5 6 7 8 9 <resultMap id="userMap" type="com.cs7eric.mybatis.pojo.User"> <id property="id" column="id"></id> <result property="username" column="username"></result> <result property="password" column="password"></result> <result property="email" column="email"></result> </resultMap> <select id="queryForUser" resultMap="userMap"> select * from t_user where id = #{id} </select>
多对一映射处理 级联方式处理映射关系 1 2 3 4 5 6 7 8 9 <resultMap id="empDeptMap" type="com.cs7eric.mybatis.pojo.Emp"> <id column="eid" property="eid"></id> <result column="ename" property="ename"></result> <result column="did" property="dept.did"></result> <result column="dname" property="dept.dname"></result> </resultMap> <select id="getEmpAndDeptById" resultMap="empDeptMap"> select emp.*, dept.* from emp left join dept on emp.did = dept.did where eid = #{eid} </select>
1 2 3 4 5 6 7 Emp getEmpAndDeptById (@Param("eid") Integer eid) ;
使用 association 1 2 3 4 5 6 7 8 <resultMap id="empDeptMap2" type="com.cs7eric.mybatis.pojo.Emp"> <id column="eid" property="eid"></id> <result column="ename" property="ename"></result> <association property="dept" javaType="com.cs7eric.mybatis.pojo.Dept"> <id column="did" property="did"></id> <result column="dname" property="dname"></result> </association> </resultMap>
分布查询 查询员工信息 1 2 3 4 5 6 7 Emp getEmpByStep (@Param("eid") Integer eid) ;
1 2 3 4 5 6 7 8 <resultMap id ="getEmpByStep" type ="com.cs7eric.mybatis.pojo.Emp" > <id column ="eid" property ="eid" > </id > <result column ="ename" property ="ename" > </result > <association property ="dept" select ="com.cs7eric.mybatis.mapper.DeptMapper.getDeptByStep" column ="did" > </association > </resultMap > <select id ="getEmpByStep" resultMap ="getEmpByStep" > select * from emp where eid = #{eid} </select >
根据员工所对应的部门 id,查询部门信息 1 2 3 4 5 6 7 Dept getDeptByStep (@Param("did") Integer did) ;
1 2 3 <select id ="getDeptByStep" resultType ="com.cs7eric.mybatis.pojo.Dept" > select * from dept where did = #{did} </select >
一对多映射处理 collection
oyType : 设置 collection 标签所处理的集合属性中存储数据的类型
1 2 3 4 5 6 7 Dept getDeptByDid (@Param("did") Integer did) ;
1 2 3 4 5 6 7 8 9 10 11 <resultMap id ="deptEmpMap" type ="com.cs7eric.mybatis.pojo.Dept" > <id column ="did" property ="did" > </id > <result column ="dname" property ="dname" > </result > <collection property ="emps" ofType ="com.cs7eric.mybatis.pojo.Emp" > <id property ="eid" column ="eid" > </id > <result property ="ename" column ="ename" > </result > </collection > </resultMap > <select id ="getDeptByDid" resultMap ="deptEmpMap" > select dept.*, emp.* from dept left join emp on dept.did = emp.did where dept.did = #{did} </select >
分布查询 查询部门信息 1 2 3 4 5 6 7 Dept getDeptByStep2 (@Param("did") Integer did) ;
1 2 3 4 5 6 7 8 9 <resultMap id ="getDeptByStep" type ="com.cs7eric.mybatis.pojo.Dept" > <id column ="did" property ="did" > </id > <result column ="dname" property ="dname" > </result > <collection property ="emps" fetchType ="eager" select ="com.cs7eric.mybatis.mapper.EmpMapper.listAllByDid" column ="did" > </collection > </resultMap > <select id ="getDeptByStep2" resultMap ="getDeptByStep" > select * from dept where did = #{did} </select >
根据部门 id 查询部门中的所有员工 1 2 3 <select id ="listAllByDid" resultType ="com.cs7eric.mybatis.pojo.Emp" > select * from emp where did = #{did} </select >
1 2 3 4 5 6 7 List<Emp> listAllByDid (@Param("did") Integer did) ;
分布查询优点 可以实现延迟加载, 但是必须在核心配置文件中设置全局配置信息
lazyLoadingEnabled : 延迟加载的全局开关。当开启时, 所有关联对象都会延迟加载
aggressiveLazyLoading : 当开启时, 任何方法的调用都会加载该对象的所有属性。 否则每个属性会按需加载
此时就可以实现按需加载,获取的数据是什么,就只会执行相应的 SQL。此时可通过 association 和 collection 中 的fetchType 属性设置当前的分布查询是否使用延迟加载, fetchType="lazy(延迟加载)|eager(立即加载)"
动态 SQL
MyBatis 框架的动态 SQL 技术是一种根据特定条件动态拼装 SQL 语句的功能,它存在的意义是为了解决拼接 SQL 语句字符串时的痛点问题
if if 标签可通过 test 属性的表达式进行判断,若表达式的结果为 TRUE。则标签中的内容会执行;反之标签中的内容 不会执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <select id ="getEmpByConditionOne" resultType ="Emp" > select * from t_emp where 1=1 <if test ="empName != null and empName != ''" > emp_name = #{empName} </if > <if test ="age != null and age != ''" > and age = #{age} </if > <if test ="sex != null and sex != ''" > and sex = #{sex} </if > <if test ="email != null and email != ''" > and email = #{email} </if > </select >
where 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <select id ="getEmpByConditionTwo" resultType ="Emp" > select * from t_emp <where > <if test ="empName != null and empName != ''" > emp_name = #{empName} </if > <if test ="age != null and age != ''" > and age = #{age} </if > <if test ="sex != null and sex != ''" > or sex = #{sex} </if > <if test ="email != null and email != ''" > and email = #{email} </if > </where > </select >
where 和 if 一般结合使用
若 where 标签中的 if条件都不满足,则 where 标签没有任何功能,即不会添加 where 关键字
若 where 标签中的 if 条件满足,则 where 标签会自动添加 where 关键字,并将条件最前方多余的 and 去掉
注意 : where 标签不能去掉条件最后多余的 and
trim 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <select id ="getEmpByCondition" resultType ="Emp" > select <include refid ="empColumns" > </include > from t_emp <trim prefix ="where" suffixOverrides ="and|or" > <if test ="empName != null and empName != ''" > emp_name = #{empName} and </if > <if test ="age != null and age != ''" > age = #{age} or </if > <if test ="sex != null and sex != ''" > sex = #{sex} and </if > <if test ="email != null and email != ''" > email = #{email} </if > </trim > </select >
trim 用于去掉或者添加 标签中的内容
常用属性:
prefix : 在 trim 标签中的内容的前面添加某些内容
prefixOverrides : 在 trim 标签中的内容前面去掉某些内容
suffix : 在 trim 标签中的内容的后面添加某些内容
suffixOverrides : 在 trim 标签中的内容的后面去掉某些内容
choose、when、otherwise cwo 相当于 if...else if...else
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 <select id ="getEmpByChoose" resultType ="Emp" > select * from t_emp <where > <choose > <when test ="empName != null and empName != ''" > emp_name = #{empName} </when > <when test ="age != null and age != ''" > age = #{age} </when > <when test ="sex != null and sex != ''" > sex = #{sex} </when > <when test ="email != null and email != ''" > email = #{email} </when > <otherwise > did = 1 </otherwise > </choose > </where > </select >
foreach 1 2 3 4 5 6 7 <insert id ="insertMoreByList" > insert into t_emp values <foreach collection ="emps" item ="emp" separator ="," > (null,#{emp.empName},#{emp.age},#{emp.sex},#{emp.email},null) </foreach > </insert >
1 2 3 4 5 6 7 8 9 10 11 12 13 <delete id ="deleteMoreByArray" > delete from t_emp where <foreach collection ="eids" item ="eid" separator ="or" > eid = #{eid} </foreach > </delete >
属性:
collection : 设置要循环的数组或集合
item : 表示 集合或数组中的每一个数据
separator : 设置循环体之间的分隔符
open : 设置 foreach 标签中的内容的开始符
close : 设置 foreach 标签中的内容的结束符
SQL 片段 SQL 片段,可以记录一段公共 SQL 片段,在使用的地方通过 include
标签进行引入
1 <sql id ="empColumns" > eid,emp_name,age,sex,email</sql >
1 select <include refid="empColumns"></include> from t_emp
MyBatis 的 缓存
MyBatis 的一级缓存 以及缓存是 SqlSession 级别的,通过同一个 SqlSession 查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问
使一级缓存失效的情况
不同的 SqlSession 对应不同的一级缓存
同一个 SqlSession 但是查询条件不同
同一个 SqlSession 两次查询期间执行了任何一次 增删改 操作
同一个 SqlSession 两次查询期间手动清空了缓存
MyBatis 的二级缓存 二级缓存是 SqlSessionFactory 级别的,通过同一个 SqlSessionFactory 创建 的 SqlSession 查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取
二级缓存开启的条件
在核心配置文件中,设置全局配置属性 cacheEnabled="true"
, 默认为 true,不需要设置
在 映射文件中设置 <cache/>
二级缓存必须在 SqlSession 关闭或提交之后有效
查询的数据所转换的实体类必须实现序列化的接口
使二级缓存失效的情况 两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效
二级缓存的相关配置 在 mapper 配置文件添加的 cache 标签可以设置一些属性
eviction 属性:缓存回收策略
LRU (Least Recently Used)- 最近最少使用的:移除最长时间不被使用的对象
FIFO(First in First out)- 先进先出: 俺对象进入缓存的顺序来移除他们
SOFT - 软引用 : 移除基于垃圾回收器状态和 软引用规则的对象
WEAK - 弱引用 :更积极地移除基于垃圾回收器状态和 弱引用规则的对象
默认的是 : LRU
flushInterval 属性 : 刷新间隔,单位毫秒
默认情况是不设置,也就是没有时间间隔,缓存仅仅调用语句时刷新
size 属性 : 引用数目,正整数
代表缓存最多可以储存多少个对象,太大容易导致内存溢出
readOnly 属性 : 只读,true,false
true : 只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势
false : 读写缓存 ;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是 false
MyBatis 缓存查询的顺序
先查询二级缓存,因为二级缓存中可能会有其他 程序已经查询出来的数据,可以拿来直接使用
如果二级缓存没有 命中,在查询一级缓存
如果一级缓存也没有命中,则查询数据库
SqlSession 关闭之后,一级缓存中的数据会写入二级缓存
整合第三方第三方缓存 EHCache 添加依赖 1 2 3 4 5 6 7 8 9 10 11 12 <dependency > <groupId > ch.qos.logback</groupId > <artifactId > logback-classic</artifactId > <version > 1.2.3</version > </dependency > <dependency > <groupId > org.mybatis.caches</groupId > <artifactId > mybatis-ehcache</artifactId > <version > 1.2.3</version > </dependency >
各 jar 包的功能
jar包名称
作用
mybatis-ehcache
Mybatis和EHCache的整合包
ehcache
EHCache核心包
slf4j-api
SLF4J日志门面包
logback-classic
支持SLF4J门面接口的一个具体实现
创建 EHCache 的配置文件 ehcache.xml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?xml version="1.0" encoding="utf-8" ?> <ehcache xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation ="../config/ehcache.xsd" > <diskStore path ="D:\C\ehcache" /> <defaultCache maxElementsInMemory ="1000" maxElementsOnDisk ="10000000" eternal ="false" overflowToDisk ="true" timeToIdleSeconds ="120" timeToLiveSeconds ="120" diskExpiryThreadIntervalSeconds ="120" memoryStoreEvictionPolicy ="LRU" > </defaultCache > </ehcache >
设置二级缓存的类型 1 <cache type ="org.mybatis.caches.ehcache.EhcacheCache" />
加入 logback 日志 存在SLF4J时,作为简易日志的log4j将失效,此时我们需要借助SLF4J的具体实现logback来打印日志。 创建logback的配置文件logback.xml
MyBatis 的逆向工程
正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。Hibernate是支持正向工程的。
逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源:
Java实体类
Mapper接口
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 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 <build > <plugins > <plugin > <groupId > org.apache.maven.plugins</groupId > <artifactId > maven-compiler-plugin</artifactId > <version > 3.8.0</version > <configuration > <source > 1.8</source > <target > 1.8</target > </configuration > </plugin > <plugin > <groupId > org.mybatis.generator</groupId > <artifactId > mybatis-generator-maven-plugin</artifactId > <version > 1.3.0</version > <dependencies > <dependency > <groupId > org.mybatis.generator</groupId > <artifactId > mybatis-generator-core</artifactId > <version > 1.3.2</version > </dependency > <dependency > <groupId > com.alibaba</groupId > <artifactId > druid</artifactId > <version > 1.2.15</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.30</version > </dependency > </dependencies > </plugin > </plugins > </build >
创建 逆向工程的配置文件 文件名必须是 generatorConfig.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 26 27 28 29 30 31 32 33 34 35 36 37 <?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 > <context id ="DB2Tables" targetRuntime ="MyBatis3" > <jdbcConnection driverClass ="com.mysql.jdbc.Driver" connectionURL ="jdbc:mysql://localhost:3306/book" userId ="root" password ="C020611." > </jdbcConnection > <javaModelGenerator targetPackage ="com.cs7eric.mybatis.pojo" targetProject =".\src\main\java" > <property name ="enableSubPackages" value ="true" /> <property name ="trimStrings" value ="true" /> </javaModelGenerator > <sqlMapGenerator targetPackage ="com.cs7eric.mybatis.mapper" targetProject =".\src\main\resources" > <property name ="enableSubPackages" value ="true" /> </sqlMapGenerator > <javaClientGenerator type ="XMLMAPPER" targetPackage ="com.cs7eric.mybatis.mapper" targetProject =".\src\main\java" > <property name ="enableSubPackages" value ="true" /> </javaClientGenerator > <table tableName ="emp" domainObjectName ="Emp" /> <table tableName ="dept" domainObjectName ="Dept" /> </context > </generatorConfiguration >
QBC 查询 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Test public void testMBG () { try { InputStream is = Resources.getResourceAsStream("mybatis-config.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder ().build(is); SqlSession sqlSession = sqlSessionFactory.openSession(true ); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); mapper.updateByPrimaryKeySelective(new Emp (1 ,"admin" ,22 ,null ,"456@qq.com" ,3 )); } catch (IOException e) { e.printStackTrace(); } }
分页插件
分页插件使用步骤 添加依赖 1 2 3 4 5 6 <dependency > <groupId > com.github.pagehelper</groupId > <artifactId > pagehelper</artifactId > <version > 5.2.0</version > </dependency >
配置分页插件 在 MyBatis 的核心配置文件中配置插件
1 2 3 <plugins > <plugin interceptor ="com.github.pagehelper.PageInterceptor" > </plugin > </plugins >
分页插件的使用 a>在查询功能之前使用PageHelper.startPage(int pageNum, int pageSize)开启分页功能
-
pageNum:当前页的页码
pageSize:每页显示的条数
b>在查询获取list集合之后,使用 PageInfo<T> pageInfo = new PageInfo<>(List<T> list, intnavigatePages)
获取分页相关数据
list:分页之后的数据
navigatePages:导航分页的页码数
c>分页相关数据
PageInfo{ pageNum=8, pageSize=4, size=2, startRow=29, endRow=30, total=30, pages=8, list=Page{count=true, pageNum=8, pageSize=4, startRow=28, endRow=32, total=30, pages=8, reasonable=false, pageSizeZero=false}, prePage=7, nextPage=0, isFirstPage=false, isLastPage=true, hasPreviousPage=true, hasNextPage=false, navigatePages=5, navigateFirstPage4, navigateLastPage8, navigatepageNums=[4, 5, 6, 7, 8] }
常用数据:
pageNum:当前页的页码
pageSize:每页显示的条数
size:当前页显示的真实条数
total:总记录数
pages:总页数
prePage:上一页的页码
nextPage:下一页的页码
isFirstPage/isLastPage:是否为第一页/最后一页
hasPreviousPage/hasNextPage:是否存在上一页/下一页
navigatePages:导航分页的页码数
navigatepageNums:导航分页的页码,[1,2,3,4,5]