Mybatis简介 1. Mybatis的特性
Mybatis支持定制化SQL,存储过程以及高级映射的优秀的持久层框架。
MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集。
MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和ava的POJO(Plain Old JavaObjects,普通的Java对象)映射成数据库中的记录。
MyBatis是一个半自动的ORM (Object Relation Mapping)框架。
2. Mybatis的下载 Mybatis的下载地址:https://github.com/mybatis/mybatis-3
Mybatis的快速入门
新建maven工程
打包方式修改成jar
包
1 <packaging > jar</packaging >
引入依赖
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 <dependencies > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.5.7</version > </dependency > <dependency > <groupId > junit</groupId > <artifactId > junit</artifactId > <version > 4.12</version > <scope > test</scope > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.16</version > </dependency > <dependency > <groupId > org.projectlombok</groupId > <artifactId > lombok</artifactId > <version > 1.18.12</version > <scope > provided</scope > </dependency > </dependencies >
MySQL不同版本的注意事项
1、驱动类driver-class-name
MySQL 5版本使用jdbc5驱动,驱动类使用:com.mysql.jdbc.Driver
MySQL 8版本使用jdbc8驱动,驱动类使用:com.mysql.cj.jdbc.Driver
2、连接地址url MySQL 5版本的url: jdbc:mysql://localhost:3306/ssm
MySQL 8版本的url: jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
否则运行测试用例报告如下错误:
java.sql.SQLException: The server time zone value ‘Öйú±ê׼ʱ¼ä’ is unrecognized or represents more
数据库创建t_user
表
1 2 3 4 5 6 7 8 CREATE TABLE `t_user` ( `id` int PRIMARY KEY AUTO_INCREMENT, `username` varchar(20), `password` varchar(20), `age` int, `gender` char(1), `email` varchar(50) );
创建对应的实体类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package com.atguigu.mybatis.pojo;@AllArgsConstructor @NoArgsConstructor @Data public class User { private Integer id; private String username; private String password; private Integer age; private String gender; private String email; }
创建Mapper接口
1 2 3 4 5 6 7 8 package com.atguigu.mybatis.mapper;public interface UserMapper { int insertUser () ; }
创建Mybatis的核心配置文件在src/main/resources
目录下。
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 <?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/ssm?serverTimezone=UTC" /> <property name ="username" value ="root" /> <property name ="password" value ="root" /> </dataSource > </environment > </environments > <mappers > <mapper resource ="mappers/UserMapper.xml" /> </mappers > </configuration >
创建MyBatis的映射文件
相关概念:ORM(Object Relationship Mapping)对象关系映射。
对象:Java的实体类对象
关系:关系型数据库
映射:二者之间的对应关系
映射文件的命名规则: 表所对应的实体类的类名+Mapper.xml
例如:表t_user,映射的实体类为User,所对应的映射文件为UserMapper.xml
因此一个映射文件对应一个实体类,对应一张表的操作 MyBatis映射文件用于编写SQL,访问以及操作表中的数据MyBatis映射文件存放的位置是src/main/resources/mappers
目录下
MyBatis中可以面向接口操作数据,要保证两个一致 :
1 2 3 4 5 6 7 8 9 10 <?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.atguigu.mybatis.mapper.UserMapper" > <insert id ="insertUser" > insert into t_user values(null,'admin','123456',23,'男','12345@qq.com') </insert > </mapper >
小提示:可以在IDEA中安装上MybatisX插件。安装后接口和Mapper就会对应起来可以方便跳转。
添加日志记录
添加依赖
1 2 3 4 5 <dependency > <groupId > log4j</groupId > <artifactId > log4j</artifactId > <version > 1.2.17</version > </dependency >
加入log4j的配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?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(调试)
从左到右打印的内容越来越详细
Mybatis核心文件详解
核心配置文件中的标签必须按照固定的顺序: properties?,settings?,typeAliases?,typeHandlers?,objectFactory?,objectWrapperFactory?,refl ectorFactory?,plugins?,environments?,databaseIdProvider?,mappers?
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 <?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 > <properties resource ="jdbc.properties" /> <typeAliases > <package name ="com.atguigu.mybatis.pojo" /> </typeAliases > <environments default ="development" > <environment id ="development" > <transactionManager type ="JDBC" /> <dataSource type ="POOLED" > <property name ="driver" value ="${jdbc.driver}" /> <property name ="url" value ="${jdbc.url}" /> <property name ="username" value ="${jdbc.username}" /> <property name ="password" value ="${jdbc.password}" /> </dataSource > </environment > </environments > <mappers > <package name ="com.atguigu.mybatis.mapper" /> </mappers > </configuration >
Mybatis获取参数的两种方式 MyBatis获取参数值的两种方式:${}
和#{}
${}的本质就是字符串拼接,#{}的本质就是占位符赋值
${}使用字符串拼接的方式拼接sql,若为字符串类型或日期类型的字段进行赋值时,需要手动加引号。
但是#{}使用占位符赋值的方式拼接sql,此时为字符串类型或日期类型的字段进行赋值时, 可以自动加引号。
${}
有可能会存在SQL注入风险。推荐使用#{}
1. 单个参数 若mapper接口中的方法参数为单个的字面量类型
此时可以使用${}和#{}以任意的名称获取参数的值,注意${}需要手动加引号。
1 User getUserByUserName (@Param("username") String username) ;
1 2 3 4 5 6 7 <select id ="getUserByUserName" resultType ="com.atguigu.mybatis.pojo.User" > select * from t_user where userName = #{username} </select > <select id ="getUserByUserName" resultType ="com.atguigu.mybatis.pojo.User" > select * from t_user where userName = "${username}" </select >
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public static SqlSession getSqlSession () { SqlSession sqlSession = null ; try { InputStream is = Resources.getResourceAsStream("mybatis-config.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder ().build(is); sqlSession = sqlSessionFactory.openSession(true ); } catch (IOException e) { throw new RuntimeException (e); } return sqlSession; } @Test public void testgetUserByUserName () { SqlSession sqlSession = getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User user = userMapper.getUserByUserName("root" ); System.out.println(user); sqlSession.close(); }
2. 多个参数 若mapper接口中的方法参数为多个时,此时MyBatis会自动将这些参数放在一个map集合中
以arg0,arg1…为键,以参数为值;以param1,param2…为键,以参数为值;
因此只需要通过${}和#{}访问map集合的键就可以获取相对应的值。${}需要手动加引号。
1 User checkLogin (String username,String password) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 <select id ="checkLogin" resultType ="com.atguigu.mybatis.pojo.User" > select * from t_user where userName = #{arg0} AND password = #{arg1} </select > <select id ="checkLogin" resultType ="com.atguigu.mybatis.pojo.User" > select * from t_user where userName = #{param1} AND password = #{param2} </select > <select id ="checkLogin" resultType ="com.atguigu.mybatis.pojo.User" > select * from t_user where userName = #{username} AND password = #{password} </select >
测试代码
1 2 3 4 5 6 7 8 @Test public void testCheckLogin () { SqlSession sqlSession = getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); User user = userMapper.checkLogin("admin" ,"123456" ); System.out.println(user); sqlSession.close(); }
@Param注解是用于传递参数,从而可以与SQL中的的字段名相对应,如果只有单个参数可以省略。
1 2 3 <select id ="checkLogin" resultType ="com.atguigu.mybatis.pojo.User" > select * from t_user where userName = "${username}" AND password = "${password}" </select >
1 2 3 4 5 6 7 8 @Test public void testCheckLogin () { SqlSession sqlSession = getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); List<User> user = userMapper.checkLogin("admin" ,"abc\" or \"1\"=\"1" ); System.out.println(user); sqlSession.close(); }
实际上我并没有输入正确的账号和密码,却可以获取所有的用户数据。
1 2 3 4 Preparing: select * from t_user where userName = "admin" AND password = "abc" or "1"="1" (BaseJdbcLogger.java:137) DEBUG 10-21 22:56:37,987 ==> Parameters: (BaseJdbcLogger.java:137) DEBUG 10-21 22:56:38,020 <== Total: 7 (BaseJdbcLogger.java:137) [User(id=1, username=admin, password=123456, age=23, gender=男, email=12345@qq.com), User(id=2, username=root, password=123456, age=23, gender=男, email=12345@qq.com), User(id=3, username=heima, password=123456, age=23, gender=男, email=12345@qq.com), User(id=4, username=guigu, password=123456, age=23, gender=男, email=12345@qq.com), User(id=5, username=zhangsan, password=123456, age=23, gender=男, email=12345@qq.com), User(id=6, username=lisi, password=123456, age=23, gender=男, email=12345@qq.com), User(id=7, username=wangwu, password=123456, age=23, gender=男, email=12345@qq.com)]
3. map做为参数 若mapper接口中的方法需要的参数为多个时,此时可以手动创建map集合,将这些数据放在 map中。可以使用@Param()
注解
只需要通过${}和#{}访问map集合的键就可以获取相对应的值。${}需要手动加引号。
1 User checkLogin2 (Map map) ;
1 2 3 <select id ="checkLogin2" resultType ="com.atguigu.mybatis.pojo.User" > select * from t_user where userName = #{username} AND password = #{password} </select >
1 2 3 4 5 6 7 8 9 10 11 @Test public void testCheckLogin () { SqlSession sqlSession = getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); Map map = new HashMap <>(); map.put("username" ,"admin" ); map.put("password" ,"123456" ); User user = userMapper.checkLogin2(map); System.out.println(user); sqlSession.close(); }
如果方法中有多个参数并且其中含有map时,可以使用arg0.键
或param.键
调用。
1 2 User checkLogin2 (Map map,Map map1) ;
1 2 3 4 5 6 7 8 9 10 11 12 @Test public void testCheckLogin () { SqlSession sqlSession = getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); Map map = new HashMap <>(); map.put("username" ,"admin" ); Map map1 = new HashMap <>(); map1.put("password" ,"123456" ); User user = userMapper.checkLogin2(map,map1); System.out.println(user); sqlSession.close(); }
1 2 3 4 <select id ="checkLogin2" resultType ="com.atguigu.mybatis.pojo.User" > select * from t_user where userName = #{arg0.username} AND password = #{arg1.password} </select >
了解:如果参数类型是list时,可以通过下标获取。
4. 实体类型做为参数 若mapper接口中的方法参数为实体类对象时
此时可以使用${}和#{},通过访问实体类对象中的属性名获取属性值,注意${}需要手动加引号
1 boolean insertUser (User user) ;
1 2 3 <insert id ="insertUser" > insert into t_user values(null,#{username},#{password},#{age},#{gender},#{email}) </insert >
1 2 3 4 5 6 7 8 @Test public void testInsertUser () { SqlSession sqlSession = getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); boolean flag = userMapper.insertUser(new User (null , "test" , "123456" , 23 , "男" , "123@qq.com" )); System.out.println("结果:" + flag); sqlSession.close(); }
5. @Param注解 @Param注解是用于传递参数,从而可以与SQL中的的字段名相对应,如果只有单个参数可以省略。
建议:所有参数类型都加上@Param(),养成良好习惯。
Mybatis的查询功能 1. 查询一个实体类对象 1 User getUserById (@Param("id") int id) ;
1 2 3 <select id ="getUserById" resultType ="User" > select * from t_user where id = #{id} </select >
如果查询结果出现的记录数大于1条会报TooManyResultsException
异常。
2. 查询一个list集合 1 List<User> getUserList () ;
1 2 3 <select id ="getUserList" resultType ="User" > select * from t_user </select >
3. 查询单个数据
1 2 3 <select id ="getCount" resultType ="_integer" > select count(id) from t_user </select >
4. 查询一条数据为map 1 Map<String, Object> getUserToMap (@Param("id") int id) ;
1 2 3 <select id ="getUserToMap" resultType ="map" > select * from t_user where id = #{id} </select >
如果是Map接收返回值,字段为null值不会返回(Map中没有字段为null值的键和值)
5. 查询多条数据为map集合 5.1 方式一:使用List包着Map 1 List<Map<String, Object>> getAllUserToMap () ;
1 2 3 <select id ="getAllUserToMap" resultType ="map" > select * from t_user </select >
5.2 方式二:使用注解 将表中的数据以map集合的方式查询,一条数据对应一个map;若有多条数据,就会产生多个map集合,并且最终要以一个map的方式返回数据,此时需要通过@MapKey
注解设置map集合的键,值是每条数据所对应的map集合。
1 2 @MapKey("id") Map<String, Object> getAllUserToMap () ;
1 2 3 4 5 6 7 8 9 10 11 <select id ="getAllUserToMap" resultType ="map" > select * from t_user </select >
特殊的SQL执行 1. 模糊查询 1 List<User> testMohu (@Param("mohu") String mohu) ;
1 2 3 4 5 6 <select id ="testMohu" resultType ="User" > select * from t_user where username like "%"#{mohu}"%" </select >
2. 批量删除 1 int deleteMore (@Param("ids") String ids) ;
1 2 3 <delete id ="deleteMore" > delete from t_user where id in (${ids}) </delete >
3. 动态设置表名 1 List<User> getAllUser (@Param("tableName") String tableName) ;
1 2 3 <select id ="getAllUser" resultType ="User" > select * from ${tableName} </select >
4. 添加功能获取自增的主键 1 2 3 4 5 6 7 8 9 int insertUser (User user) ;
1 2 3 4 <insert id ="insertUser" useGeneratedKeys ="true" keyProperty ="id" > insert into t_user values(null,#{username},#{password},#{age},#{sex}) </insert >
自定义ResultMap 1. 下划线转驼峰 若字段名和实体类中的属性名不一致,但是字段名符合数据库的规则(使用_),实体类中的属性名符合Java的规则(使用驼峰)。
此时也可通过以下两种方式处理字段名和实体类中的属性的映射关系
可以通过为字段起别名 的方式,保证和实体类中的属性名保持一致
1 2 3 <select id ="getByEmpId" resultType ="com.atguigu.mybatis.pojo.Emp" > select emp_id empId,emp_name empName,age,gender from t_emps where emp_Id = #{empId} </select >
可以在MyBatis的核心配置 文件中设置一个全局配置信息mapUnderscoreToCamelCase ,可以在查询表中数据时,自动将_类型的字段名转换为驼峰
例如:字段名user_name,设置了mapUnderscoreToCamelCase,此时字段名就会转换为 userName
1 2 3 <settings > <setting name ="mapUnderscoreToCamelCase" value ="true" /> </settings >
2. ResultMap处理字段和属性的映射关系 resultMap:设置自定义映射
属性:
id:表示自定义映射的唯一标识。
type:查询的数据要映射的实体类的类型。
子标签:
id:设置主键的映射关系
result:设置普通字段的映射关系
association:设置多对一、一对一的映射关系(设置实体类型属性)
collection:设置一对多的映射关系(设置集合类型属性)
子标签属性:
property:设置映射关系中实体类中的属性名
column:设置映射关系中表中的字段名
1 2 3 4 5 6 7 8 9 10 11 <resultMap id ="userMap" type ="User" > <id property ="id" column ="id" > </id > <result property ="userName" column ="user_name" > </result > <result property ="password" column ="password" > </result > <result property ="age" column ="age" > </result > <result property ="sex" column ="sex" > </result > </resultMap > <select id ="testMohu" resultMap ="userMap" > select id,user_name,password,age,sex from t_user where user_name like concat('%',#{mohu},'%') </select >
3. 多对一映射处理
查询员工所对应的部门信息
3.1 方式一:级联方式处理映射关系 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 <resultMap id ="empDeptMap" type ="Emp" > <id column ="eid" property ="eid" > </id > <result column ="ename" property ="ename" > </result > <result column ="age" property ="age" > </result > <result column ="sex" property ="sex" > </result > <result column ="did" property ="dept.did" > </result > <result column ="dname" property ="dept.dname" > </result > </resultMap > <select id ="getEmpAndDeptByEid" resultMap ="empDeptMap" > select emp.*,dept.* from t_emp emp left join t_dept dept on emp.did = dept.did where emp.eid = #{eid} </select >
3.2 方式二:使用association处理映射关系 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <resultMap id ="empDeptMap" type ="Emp" > <id column ="eid" property ="eid" > </id > <result column ="ename" property ="ename" > </result > <result column ="age" property ="age" > </result > <result column ="sex" property ="sex" > </result > <association property ="dept" javaType ="Dept" > <id column ="did" property ="did" > </id > <result column ="dname" property ="dname" > </result > </association > </resultMap > <select id ="getEmpAndDeptByEid" resultMap ="empDeptMap" > select emp.*,dept.* from t_emp emp left join t_dept dept on emp.did = dept.did where emp.eid = #{eid} </select >
3.3 方式三:分步查询 1 2 3 4 5 6 Emp getEmpByStep (@Param("eid") int eid) ;
association的属性详情
select:设置分步查询,查询某个属性的值的sql的标识(namespace.sqlId)
column:将sql以及查询结果中的某个字段设置为分步查询的条件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <resultMap id ="empDeptStepMap" type ="Emp" > <id column ="eid" property ="eid" > </id > <result column ="ename" property ="ename" > </result > <result column ="age" property ="age" > </result > <result column ="sex" property ="sex" > </result > <association property ="dept" select ="com.atguigu.MyBatis.mapper.DeptMapper.getEmpDeptByStep" column ="did" > </association > </resultMap > <select id ="getEmpByStep" resultMap ="empDeptStepMap" > select * from t_emp where eid = #{eid} </select >
1 2 3 4 5 6 Dept getEmpDeptByStep (@Param("did") int did) ;
1 2 3 4 <select id ="getEmpDeptByStep" resultType ="Dept" > select * from t_dept where did = #{did} </select >
分别查询的优势,延迟加载(懒加载)
但是必须在核心配置文件中设置全局配置信息:
lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载
aggressiveLazyLoading:当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载
此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql。此时可通过association和 collection中的fetchType属性设置当前的分步查询是否使用延迟加载, fetchType=”lazy(延迟加载)|eager(立即加载)”
一对多映射处理 方式一:collection 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <resultMap id ="deptEmpMap" type ="Dept" > <id property ="did" column ="did" > </id > <result property ="dname" column ="dname" > </result > <collection property ="emps" ofType ="Emp" > <id property ="eid" column ="eid" > </id > <result property ="ename" column ="ename" > </result > <result property ="age" column ="age" > </result > <result property ="sex" column ="sex" > </result > </collection > </resultMap > <select id ="getDeptEmpByDid" resultMap ="deptEmpMap" > select dept.*,emp.* from t_dept dept left join t_emp emp on dept.did = emp.did whe </select >
方式二:分步查询 1 2 3 4 5 6 Dept getDeptByStep (@Param("did") int did) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 <resultMap id ="deptEmpStep" type ="Dept" > <id property ="did" column ="did" > </id > <result property ="dname" column ="dname" > </result > <collection property ="emps" fetchType ="eager" select ="com.atguigu.MyBatis.mapper.EmpMapper.getEmpListByDid" column ="did" > </collection > </resultMap > <select id ="getDeptByStep" resultMap ="deptEmpStep" > select * from t_dept where did = #{did} </select >
1 2 3 4 5 6 List<Emp> getEmpListByDid (@Param("did") int did) ;
1 2 3 4 <select id ="getEmpListByDid" resultType ="Emp" > select * from t_emp where did = #{did} </select >
Mybatis动态Sql Mybatis框架的动态SQL技术是一种根据特定条件动态拼装SQL语句的功能,它存在的意义是为了 解决拼接SQL语句字符串时的痛点问题。
1. if if标签可通过test属性的表达式进行判断,若表达式的结果为true,则标签中的内容会执行;反之标签中的内容不会执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 <select id ="getEmpListByMoreTJ" resultType ="Emp" > select * from t_emp where 1=1 <if test ="ename != '' and ename != null" > and ename = #{ename} </if > <if test ="age != '' and age != null" > and age = #{age} </if > <if test ="sex != '' and sex != null" > and sex = #{sex} </if > </select >
2. where where和if一般结合使用:
若where标签中的if条件都不满足,则where标签没有任何功能,即不会添加where关键字。
若where标签中的if条件满足,则where标签会自动添加where关键字,并将条件最前方多余的and去掉。
注意:where标签不能去掉条件最后多余的and
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <select id ="getEmpListByMoreTJ2" resultType ="Emp" > select * from t_emp <where > <if test ="ename != '' and ename != null" > ename = #{ename} </if > <if test ="age != '' and age != null" > and age = #{age} </if > <if test ="sex != '' and sex != null" > and sex = #{sex} </if > </where > </select >
3. trim trim用于去掉或添加标签中的内容
常用属性:
prefix:在trim标签中的内容的前面添加某些内容。
prefixOverrides:在trim标签中的内容的前面去掉某些内容。
suffix:在trim标签中的内容的后面添加某些内容。
suffixOverrides:在trim标签中的内容的后面去掉某些内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <select id ="getEmpListByMoreTJ" resultType ="Emp" > select * from t_emp <trim prefix ="where" suffixOverrides ="and" > <if test ="ename != '' and ename != null" > ename = #{ename} and </if > <if test ="age != '' and age != null" > age = #{age} and </if > <if test ="sex != '' and sex != null" > sex = #{sex} </if > </trim > </select >
4. choose、when、otherwise choose、when、 otherwise相当于if…else if..else
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <select id ="getEmpListByChoose" resultType ="Emp" > select <include refid ="empColumns" > </include > from t_emp <where > <choose > <when test ="ename != '' and ename != null" > ename = #{ename} </when > <when test ="age != '' and age != null" > age = #{age} </when > <when test ="sex != '' and sex != null" > sex = #{sex} </when > <when test ="email != '' and email != null" > email = #{email} </when > </choose > </where > </select >
5. foreach 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <insert id ="insertMoreEmp" > insert into t_emp values <foreach collection ="emps" item ="emp" separator ="," > (null,#{emp.ename},#{emp.age},#{emp.sex},#{emp.email},null) </foreach > </insert > <delete id ="deleteMoreByArray" > delete from t_emp where <foreach collection ="eids" item ="eid" separator ="or" > eid = #{eid} </foreach > </delete > <delete id ="deleteMoreByArray" > delete from t_emp where eid in <foreach collection ="eids" item ="eid" separator ="," open ="(" close =")" > #{eid} </foreach > </delete >
6. Sql片段 1 2 3 4 <sql id ="empColumns" > eid,ename,age,sex,did </sql > select <include refid ="empColumns" > </include > from t_emp
Mybatis缓存 1. Mybatis一级缓存(默认开启) 一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就 会从缓存中直接获取,不会从数据库重新访问。
使一级缓存失效的四种情况:
一级缓存示例:
1 2 3 4 5 6 7 8 9 10 11 @Test public void TestGetByEmpId () { SqlSession sqlSession = getSqlSession(); EmpMapper empMapper = sqlSession.getMapper(EmpMapper.class); List<Emp> emp1 = empMapper.getByEmpId(1 ); List<Emp> emp2 = empMapper.getByEmpId(1 ); System.out.println(emp1); System.out.println(emp2); sqlSession.close(); }
在看记录可以知道,sql只执行了一次,第二次是从缓存中获取的。
1 2 3 4 5 DEBUG 10-25 20:08:44,458 ==> Preparing: select emp_id empid,emp_name empName,age,gender from t_emps where emp_Id = ? (BaseJdbcLogger.java:137) DEBUG 10-25 20:08:44,489 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) DEBUG 10-25 20:08:44,522 <== Total: 1 (BaseJdbcLogger.java:137) [Emp(empId=1, empName=张三, age=12, gender=男, deptId=null, dept=null)] [Emp(empId=1, empName=张三, age=12, gender=男, deptId=null, dept=null)]
2. MyBatis的二级缓存 二级缓存是SqlSessionFactory级别 ,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取。
二级缓存开启的条件:
在核心配置文件中,设置全局配置属性cacheEnabled="true"
,默认为true,不需要设置。
在映射文件中设置<cache/>
标签。
二级缓存必须在SqlSession关闭或提交之后有效。
查询的数据所转换的实体类类型必须实现序列化的接口(implements Serializable
)。
使二级缓存失效的情况:
两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Test public void testEacheTwo () throws IOException { InputStream is = Resources.getResourceAsStream("mybatis-config.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder ().build(is); SqlSession sqlSession1 = sqlSessionFactory.openSession(true ); EmpMapper mapper1 = sqlSession1.getMapper(EmpMapper.class); List<Emp> emp1 = mapper1.getByEmpId(1 ); System.out.println(emp1); sqlSession1.close(); SqlSession sqlSession2 = sqlSessionFactory.openSession(true ); EmpMapper mapper2 = sqlSession2.getMapper(EmpMapper.class); List<Emp> emp2 = mapper2.getByEmpId(1 ); System.out.println(emp2); sqlSession2.close(); }
2.1 二级缓存的相关配置 在mapper配置文件中添加的cache标签可以设置一些属性:
eviction属性:缓存回收策略,默认的是 LRU。
LRU(Least Recently Used) – 最近最少使用的:移除最长时间不被使用的对象。
FIFO(First in First out) – 先进先出:按对象进入缓存的顺序来移除它们。
SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
flushInterval属性:刷新间隔,单位毫秒。
默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句时刷新。
size属性:引用数目,正整数。
代表缓存最多可以存储多少个对象,太大容易导致内存溢出。
readOnly属性:只读, true/false
true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。
false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是 false。
3. 缓存查询的顺序 先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。
如果二级缓存没有命中,再查询一级缓存。
如果一级缓存也没有命中,则查询数据库。
SqlSession关闭之后,一级缓存中的数据会写入二级缓存。
分页插件
添加依赖
1 2 3 4 5 <dependency > <groupId > com.github.pagehelper</groupId > <artifactId > pagehelper</artifactId > <version > 5.2.0</version > </dependency >
配置分页插件
在Mybatis核心文件中配置插件
1 2 3 4 <plugins > <plugin interceptor ="com.github.pagehelper.PageInterceptor" > </plugin > </plugins >
1. 分页插件的使用
在查询功能之前使用PageHelper.startPage(int pageNum, int pageSize)
开启分页功能
在查询获取list集合之后,使用PageInfo pageInfo = new PageInfo<>(List list, int navigatePages)
获取分页相关数据。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Test public void testGetUserAll () { SqlSession sqlSession = getSqlSession(); UserMapper userMapper = sqlSession.getMapper(UserMapper.class); PageHelper.startPage(1 , 3 ); List<User> users = userMapper.getUserAll(); PageInfo pageInfo = new PageInfo <>(users); System.out.println(pageInfo); sqlSession.close(); }
1 2 3 4 5 6 7 8 9 10 11 pageNum:当前页的页码 pageSize:每页显示的条数 size:当前页显示的真实条数 total:总记录数 pages:总页数 prePage:上一页的页码 nextPage:下一页的页码 isFirstPage/isLastPage:是否为第一页/最后一页 hasPreviousPage/hasNextPage:是否存在上一页/下一页 navigatePages:导航分页的页码数 navigatepageNums:导航分页的页码,[1 ,2 ,3 ,4 ,5 ]