基本概念 Mybatis是由Apache的一个开源项目iBatis,2010年迁移到了谷歌,iBatis3.x正式更名为Mybatis。是一个基于Java持久层框架包括SQL Maps和Data Access Object(DAO),Mybatis支持定制化SQL、存储过程以及高级映射的特性,几乎避免了所有的JDBC代码和手动参数以及获取结果集,通过简单的xml或注解用于配置和原始映射,是一个非常优秀的半自动ORM框架。相较于jpa,能处理较复杂的SQL语句,性能也高于jpa
基础案例 1.首先先加入相关的依赖,需要注意mysql版本带来的问题 2.创建mybatis配置文件,对mybatis进行配置,配置文件中通过environment的id属性来区分不同的环境 3.创建业务接口,业务xml,xml中的id要和接口的相同 4.在测试中,获取配置文件,获取SqlSessionFactoryBuilder,获取SqlSessionFactory(这里需要开启事务) 5.最后获取SqlSession,通过SqlSession来获取接口,调用相应的dao方法
相关代码 相关依赖
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.5.10</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.30</version > </dependency >
创建用户表
1 2 3 4 5 6 7 8 9 10 DROP TABLE IF EXISTS `t_user`;CREATE TABLE `t_user` ( `id` int (0 ) NOT NULL AUTO_INCREMENT, `username` varchar (20 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL , `password` varchar (20 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL , `age` int (0 ) NULL DEFAULT NULL , `sex` char (8 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL , `email` varchar (20 ) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NULL DEFAULT NULL , PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8mb4;
mybatis-config.xml
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 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/mybatis?serverTimezone=GMT& useUnicode=true& characterEncoding=utf8& autoReconnect=true& useSSL=false& allowPublicKeyRetrieval=true" /> <property name ="username" value ="root" /> <property name ="password" value ="123456" /> </dataSource > </environment > </environments > <mappers > <mapper resource ="mappers/UserMapper.xml" /> </mappers > </configuration >
UserMapper.xml
1 2 3 4 5 6 7 8 9 10 11 12 <?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.eldpepar.mapper.UserMapper" > <insert id ="insertUser" > INSERT INTO t_user values (null, 'admin', '123456', 23, '男', 'admin@eldpepar.com') </insert > </mapper >
UserMapper.java
1 2 3 4 5 package com.eldpepar.mapper; public interface UserMapper { int insertUser(); }
测试插入数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Test public void testInsert () throws IOException { InputStream is = Resources.getResourceAsStream("mybatis-config.xml" ); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is); SqlSession sqlSession = sqlSessionFactory.openSession(true ); UserMapper mapper = sqlSession.getMapper(UserMapper.class); int result = mapper.insertUser(); System.out.println(result); }
查询案例 查询案例中,需要设置resultType(默认映射)和resultMap(自定义映射),如果字段名和属性名不一致的时候使用自定义映射。
代码示例 UserMapper.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?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.eldpepar.mapper.UserMapper" > <select id ="selectUserById" resultType ="com.eldpepar.pojo.User" > SELECT * FROM t_user WHERE id=#{id} </select > <select id ="selectAll" resultType ="com.eldpepar.pojo.User" > SELECT * FROM t_user </select > </mapper >
UserMapper.java
1 2 3 4 5 6 7 8 9 10 11 package com.eldpepar.mapper; import com.eldpepar.pojo.User; import java.util.List; public interface UserMapper { User selectUserById(int id); List<User > selectAll(@Param("id") int); }
User.java
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 package com.eldpepar.pojo; public class User { private Integer id; private String username; private String password; private Integer age; private String sex; private String email; public User(Integer id, String username, String password, Integer age, String sex, String email) { this.id = id; this.username = username; this.password = password; this.age = age; this.sex = sex; this.email = email; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } @Override public String toString() { return "User{" + "id=" + id + ", username='" + username + '\'' + ", password='" + password + '\'' + ", age=" + age + ", sex='" + sex + '\'' + ", email='" + email + '\'' + '}'; } }
测试查询数据
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 @Test public void selectId () throws IOException { InputStream is = Resources.getResourceAsStream("mybatis-config.xml" ); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is); SqlSession sqlSession = sqlSessionFactory.openSession(true ); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.selectUserById(1 ); System.out.println(user); }@Test public void selectAll () throws IOException { InputStream is = Resources.getResourceAsStream("mybatis-config.xml" ); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is); SqlSession sqlSession = sqlSessionFactory.openSession(true ); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.selectAll(); users.forEach(System.out::println); }
核心配置 1.引入properties配置文件,使用的时候,用${}来引用
1 2 3 4 5 6 7 8 9 10 11 12 <properties resource ="jdbc.properties" > </properties > <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 >
2.表中下划线转驼峰,延时加载
1 2 3 4 5 6 <settings > <setting name ="mapUnderscoreToCamelCase" value ="true" /> <setting name ="lazyLoadingEnabled" value ="true" /> </settings >
3.typeAlias设置某个具体的类型的别名,通过type设置别名的类型的全类名,通过alias设置此类型的别名,且别名不区分大小写。若不设置此属性,该类型拥有默认的别名,即类名。如果需要一个包下所有类,则使用package
1 2 3 4 5 6 <typeAliases > <typeAlias type ="com.eldpepar.bean.User" > </typeAlias > <typeAlias type ="com.eldpepar.bean.User" alias ="user" > <package name ="com.eldpepar.bean" /> </typeAliases >
4.transactionManager设置事物管理的方式,有JDBC和MANAGED两种。JDBC事物需要手动处理
5.dataSource设置数据源,POOLED使用数据库连接池,UNPOOLED不使用连接池,JNDI调用上下文数据源
配置模板 在实际开发过程中,数据库的配置,orm的映射等等过程,往往只需要修改一些简单的文件就可以实现相应的功能。为了简化操作,可以添加模板在idea中,方便下次直接使用
mybatis配置
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 <?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" > </properties > <typeAliases > <package name ="" > </package > </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 ="" /> </mappers > </configuration >
mapper配置
1 2 3 4 5 6 7 <?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 ="" > </mapper >
添加步骤 1.进入设置页面,进入Edit选择File and Code Temp 2.输入文件名称和后缀进行添加
获取参数 1.MyBatis获取参数值的两种方式:${}和#{}
2.${}的本质就是字符串拼接,#{}的本质就是占位符赋值
3.${}使用字符串拼接的方式拼接sql,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引号;但是#{}使用占位符赋值的方式拼接sql,此时为字符串类型或日期类型的字段进行赋值时,可以自动添加单引号
SqlSession工具类
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 package com.eldpepar.util;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 java.io.IOException;import java.io.InputStream;public class SqlSessionUtils { public static SqlSession getSqlSession () { SqlSession sqlSession = null ; InputStream is = null ; try { is = Resources.getResourceAsStream("mybatis-config.xml" ); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is); sqlSession = sqlSessionFactory.openSession(true ); } catch (IOException e) { e.printStackTrace(); } return sqlSession; } }
多个字面量类型 若mapper接口中的方法参数为多个时,此时MyBatis会自动将这些参数放在一个map集合中
以arg0,arg1…为键,以参数为值 以param1,param2…为键,以参数为值 基本使用方法 1.只需要通过${} 和#{} 访问map集合的键就可以获取相对应的值,其中${} 需要手动加单引号
2.使用arg或者param都行,要注意的是,arg是从arg0开始的,param是从param1开始的
检测登录案例
1 2 3 4 <select id ="checkLogin" resultType ="User" > select * from t_user where username = #{arg0} and password = #{arg1} </select >
集合类型 若mapper接口中的方法需要的参数为多个时,此时可以手动创建map集合,将这些数据放在map中只需要通过${}和#{}访问map集合的键就可以获取相对应的值
登录示例
1 2 3 4 5 6 7 8 <select id ="checkLoginByMap" resultType ="User" > select * from t_user where username = #{username} and password = #{password}</select >
测试代码
1 2 3 4 5 6 7 8 9 10 @Test public void checkLoginMap () throws IOException { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); Map<String,Object> map = new HashMap<>(); map.put("username" ,"admin" ); map.put("password" ,"123456" ); User user = mapper.checkLoginByMap(map); System.out.println(user); }
实体类类型 若mapper接口中的方法参数为实体类对象时 此时可以使用${}和#{},通过访问实体类对象中的属性名获取属性值
添加示例
1 2 3 4 <insert id ="insertUser" > insert into t_user values(null, #{username}, #{password}, #{age}, #{sex}, #{email})</insert >
测试代码
1 2 3 4 5 6 7 8 9 @Test public void insertUser () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = new User(null ,"Zhangsan" ,"123456" ,23 ,"男" ,"eldp@eldpepar.com" ); int res = mapper.insertUser(user); System.out.println(res); }
@Param标识参数 通过@Param注解标识mapper接口中的方法参数,此时,会将这些参数放在map集合中 1.以@Param注解的value属性值为键,以参数为值 2.以param1,param2…为键,以参数为值 3.只需要通过${}和#{}访问map集合的键就可以获取相对应的值
登录验证
1 2 3 4 5 6 7 8 9 <select id ="CheckLoginByParam" resultType ="User" > select * from t_user where username = #{username} and password = #{password}</select > <select id ="CheckLogin" resultType ="User" > select * from t_user where username = #{param1} and password = #{param2}</select >
测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Test public void CheckLoginByParam () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.CheckLoginByParam("Zhangsan" , "123456" ); System.out.println(user); }@Test public void CheckLogin () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = mapper.CheckLogin("Zhangsan" , "123456" ); System.out.println(user); }
特殊SQL 在实际开发中,往往会有模糊查询、批量删除等等特殊需求的SQL
模糊查询 模糊查询有三种方式,第三种方式也是用到的最多的方式模糊查询
1 2 3 4 5 6 <select id ="getUserByLike" resultType ="User" > select * from t_user where username like "%"#{username}"%"</select >
测试代码
1 2 3 4 5 6 7 @Test public void getUserByLike () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.getUserByLike("Zhang" ); users.forEach(System.out::println); }
批量删除 批量删除中只能使用${},如果使用#{}会把数据看成整体,就无法实现批量的操作
批量删除
1 2 3 4 <delete id ="deleteMore" > delete from t_user where id in (${ids})</delete >
测试代码
1 2 3 4 5 6 7 @Test public void deleteMore () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); int result = mapper.deleteMore("4,5,6,7,8,9" ); System.out.println(result); }
动态设置表名 动态设置表名不能使用#来操作,因为表明是不可加单引号的
动态设置表明
1 2 3 4 <select id ="getUserByTableName" resultType ="User" > select * from ${tableName}</selecet >
添加功能获取自增主键 使用场景 1.添加班级信息 2.获取新添加的班级id 3.为班级分配学生,即将某学生的班级id修改为新添加的班级id
配置方法 需要在mapper.xml中设置两个属性
userGeneratedKeyS: 设置使用自增的主键 keyProperty: 一五年增删改有统一的返回值是受影响的行数,因此只能将获取的自增主键放在传输的参数user对象的某个属性中 添加功能获取自增主键
1 2 3 4 <insert id ="insertUser" userGeneratedKey ="true" keyProperty ="id" > insert into t_user values (null, #{username}, #{password}, #{age}, #{sex}, #{email})</insert >
测试代码
1 2 3 4 5 6 7 8 @Test public void deleteMore () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); UserMapper mapper = sqlSession.getMapper(UserMapper.class); User user = new User(null , "ton" , "123" , 23 , "男" , "123@eldpepar.com" ); mapper.insertUser(user); System.out.println(user); }
字段与属性冲突 有些情况下,表中的字段名和类中的属性名无法对应,为了解决这种情况,可以设置字段别名来解决
(一)字段别名方法 可以和在sql语句中,设置字段的别名解决字段冲突的问题
1 2 3 4 <select id ="getAllEmp" resultType ="Emp" > select eid,emp_name empName,age,sex,email from t_emp</select >
(二)修改全局配置 可以通过修改mybatis全局配置的方法,可以将下划线自动映射为驼峰
1 2 3 4 <settings > <setting name ="mapUnderscoreToCamelCase" value ="true" /> </settings >
(三)自定义映射resultMap 1.resultMap中的id属性表示映射的名称是唯一的,type表示需要映射的实体类的类型 2.子标签中的id表示映射的是主键,result表示的是映射的是普通字段 3.子标签中的property设置的是实体类中的属性名,column设置映射关系中表中的字段名
强制规则 如果字段名和实体类中的属性名相同,也需要进行映射,也就是需要列出全部属性
1 2 3 4 5 6 7 8 9 10 11 <resultMap id ="empResultMap" type ="Emp" > <id property ="eid" column ="eid" > </id > <result property ="empName" column "emp_name "> </result > <result property ="age" column "age "> </result > <result property ="sex" column "sex "> </result > <result property ="email" column "email "> </result > </resultMap > <select id ="getAllEmp" resultMap ="empResultMap" > select * from t_emp</select >
(四)多对一映射 处理多对一映射的映射通常有多种方式,也是实际开发中非常常见的一种需求,比如查询员工信息以及员工信息所对应的部门信息
一:级联属性赋值方式 这种方式并不常用员工类
1 2 3 4 5 6 7 8 public class Emp { private Integer eid; private String empName; private Integer age; private String sex; private String email; private Dept dept; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <resultMap id ="empAndDeptResultMapOne" type ="Emp" > <id property ="eid" column ="eid" > </id > <result property ="empName" column ="emp_name" > </result > <result property ="age" column ="age" > </result > <result property ="sex" column ="sex" > </result > <result property ="email" column ="email" > </result > <result property ="dept.did" column ="did" > </result > <result property ="dept.deptName" column ="dept_name" > </result > </resultMap > <select id ="getEmpAndDept" resultMap ="empAndDeptResultMapOne" > select * from t_emp left join t_dept on t_emp.did = t_dept.did where t_emp.eid = #{eid}</select >
二:association方法解决 其中的property属性表示的是需要处理多对的映射关系的属性名,javaType表示该属性的类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <resultMap id ="empAndDeptResultMapTwo" type ="Emp" > <id property ="eid" column ="eid" > </id > <result property ="empName" column ="emp_name" > </result > <result property ="age" column ="age" > </result > <result property ="sex" column ="sex" > </result > <result property ="email" column ="email" > </result > <association property ="dept" javaType ="Dept" > <id property ="did" column ="did" > </id > <result property ="deptName" column ="dept_name" > </result > </association > </resultMap > <select id ="getEmpAndDept" resultMap ="empAndDeptResultMapTwo" > select * from t_emp left join t_dept on t_emp.did = t_dept.did where t_emp.eid = #{eid}</select >
三:分步查询方式 分步查询是执行多步来实现相关的查询业务,分步查询的优点是可以使用懒加载的方式,会更加的灵活。association中的select用于设置分布查询的sql的唯一标识(namespace.SQLId或mapper接口的全类名.方法名),column用于设置分步查询的条件
1.查询员工信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <resultMap id ="empAndDeptByStepResultMap" type ="Emp" > <id property ="eid" column ="eid" > </id > <result property ="empName" column ="emp_name" > </result > <result property ="age" column ="age" > </result > <result property ="sex" column ="sex" > </result > <result property ="email" column ="email" > </result > <association property ="dept" select ="com.atguigu.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo" column ="did" > </association > </resultMap > <select id ="getEmpAndDeptByStepOne" resultMap ="empAndDeptByStepResultMap" > select * from t_emp where eid = #{eid}</select >
2.查询部门信息
1 2 3 4 5 6 7 8 9 <resultMap id ="EmpAndDeptByStepTwoResultMap" type ="Dept" > <id property ="did" column ="did" > </id > <result property ="deptName" column ="dept_name" > </result > </resultMap > <select id ="getEmpAndDeptByStepTwo" resultMap ="EmpAndDeptByStepTwoResultMap" > select * from t_dept where did = #{did}</select >
延迟加载 延迟加载需要在核心配置文件中设置全局配置信息。 1.lazyLoadingEnable是延迟加载的全局开关,当开启时,所有关联的对象都会进行延迟加载。 2.aggressiveLazyLoading当开启时,任何方法调用都会加载对象所有属性,否则每个属性都会按需加载。 3.如果分步查询中的某一步不需要延迟加载,可以设置association和collection中的fetchType属性来控制,其中lazy是延迟加载,eager是立即加载。
(五)一对多映射 对一是对应的对象,对多是对应的集合。在一对多的业务中,经常需要获取部门以及部门中所有员工信息的这类需求,这里就需要用到一对多的映射关系来进行相应的处理。
一:使用collection collection用来处理一对多的映射关系,其中ofType表示该属性对饮的集合中存储的数据的类型。
部门类
1 2 3 4 5 public class Dept { private Integer did; private String deptName; private List<Emp> emps; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <resultMap id ="DeptAndEmpResultMap" type ="Dept" > <id property ="did" column ="did" > </id > <result property ="deptName" column ="dept_name" > </result > <collection property ="emps" ofType ="Emp" > <id property ="eid" column ="eid" > </id > <result property ="empName" column ="emp_name" > </result > <result property ="age" column ="age" > </result > <result property ="sex" column ="sex" > </result > <result property ="email" column ="email" > </result > </collection > </resultMap > <select id ="getDeptAndEmp" resultMap ="DeptAndEmpResultMap" > select * from t_dept left join t_emp on t_dept.did = t_emp.did where t_dept.did = #{did}</select >
二:分步查询 分步查询同样可以使用前面的延迟加载,配置方式相同 1.查询部门信息
1 2 3 4 5 6 7 8 9 10 11 12 <resultMap id ="DeptAndEmpByStepOneResultMap" type ="Dept" > <id property ="did" column ="did" > </id > <result property ="deptName" column ="dept_name" > </result > <collection property ="emps" select ="com.atguigu.mybatis.mapper.EmpMapper.getDeptAndEmpByStepTwo" column ="did" > </collection > </resultMap > <select id ="getDeptAndEmpByStepOne" resultMap ="DeptAndEmpByStepOneResultMap" > select * from t_dept where did = #{did}</select >
2.根据部门id查询部门中的所有员工
1 2 3 4 <select id ="getDeptAndEmpByStepTwo" resultType ="Emp" > select * from t_emp where did = #{did}</select >
动态SQL MyBatis框架的动态SQL技术是一种根据特定条件动态拼装SQL语句的功能,它存在的意义是为了解决拼接SQL语句字符串时的痛点问题
if标签 1.if标签可通过test属性(即传递过来的数据)的表达式进行判断,若表达式的结果为true,则标签中的内容会执行;反之标签中的内容不会执行 2.在where后面添加一个恒成立条件1=1
这个恒成立条件并不会影响查询的结果 这个1=1可以用来拼接and语句,例如:当empName为null时 ①如果不加上恒成立条件,则SQL语句为select * from t_emp where and age = ? and sex = ? and email = ?,此时where会与and连用,SQL语句会报错 ②如果加上一个恒成立条件,则SQL语句为select * from t_emp where 1= 1 and age = ? and sex = ? and email = ?,此时不报错 使用示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <select id ="getEmpByCondition" resultType ="Emp" > select * from t_emp where 1=1 <if test ="empName != null and empName !=''" > and 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标签 where和if一般结合使用
若where标签中的if条件都不满足,则where标签没有任何功能,即不会添加where关键字 若where标签中的if条件满足,则where标签会自动添加where关键字,并将条件最前方多余的and/or去掉 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <select id ="getEmpByCondition" 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 !=''" > and sex = #{sex} </if > <if test ="email != null and email !=''" > and email = #{email} </if > </where > </select >
where标签不能去掉条件后多余的and/or
1 2 3 4 5 6 <if test ="empName != null and empName !=''" > emp_name = #{empName} and</if > <if test ="age != null and age !=''" > age = #{age}</if >
trim标签 1.trim用于去掉或添加标签中的内容 2.常用属性
prefix:在trim标签中的内容的前面添加某些内容 suffix:在trim标签中的内容的后面添加某些内容 prefixOverrides:在trim标签中的内容的前面去掉某些内容 suffixOverrides:在trim标签中的内容的后面去掉某些内容 3.若trim中的标签都不满足条件,则trim标签没有任何效果,也就是只剩下select * from t_emp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 <select id ="getEmpByCondition" resultType ="Emp" > select * 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} and </if > <if test ="sex != null and sex !=''" > sex = #{sex} or </if > <if test ="email != null and email !=''" > email = #{email} </if > </trim > </select >
1 2 3 4 5 6 7 8 @Test public void getEmpByCondition () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class); List<Emp> emps= mapper.getEmpByCondition(new Emp(null , "张三" , null , null , null , null )); System.out.println(emps); }
choose、when、otherwise标签 1.choose、when、otherwise相当于if…else if..else 2.when至少要有一个,otherwise至多只有一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <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 >
1 2 3 4 5 6 7 @Test public void getEmpByChoose () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class); List<Emp> emps = mapper.getEmpByChoose(new Emp(null , "张三" , 23 , "男" , "123@qq.com" , null )); System.out.println(emps); }
foreach标签 1.相关属性:
collection:设置要循环的数组或集合 item:表示集合或数组中的每一个数据 separator:设置循环体之间的分隔符,分隔符前后默认有一个空格,如, open:设置foreach标签中的内容的开始符 close:设置foreach标签中的内容的结束符 2.批量删除:
1 2 3 4 5 6 7 <delete id ="deleteMoreByArray" > delete from t_emp where eid in <foreach collection ="eids" item ="eid" separator ="," open ="(" close =")" > #{eid} </foreach > </delete >
1 2 3 4 5 6 7 @Test public void deleteMoreByArray () { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class); int result = mapper.deleteMoreByArray(new Integer[]{6 , 7 , 8 , 9 }); System.out.println(result); }
3.批量添加
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <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 > ```z ```java @Test public void insertMoreByList() { SqlSession sqlSession = SqlSessionUtils.getSqlSession(); DynamicSQLMapper mapper = sqlSession.getMapper(DynamicSQLMapper.class); Emp emp1 = new Emp(null,"a",1,"男","123@321.com",null); Emp emp2 = new Emp(null,"b",1,"男","123@321.com",null); Emp emp3 = new Emp(null,"c",1,"男","123@321.com",null); List<Emp > emps = Arrays.asList(emp1, emp2, emp3); int result = mapper.insertMoreByList(emps); System.out.println(result); }
SQL片段 sql片段,可以记录一段公共sql片段,在使用的地方通过include标签进行引入 声明sql片段:标签1 <sql id ="empColumns" > eid,emp_name,age,sex,email</sql >
引用sql片段:标签1 2 3 4 <select id ="getEmpByCondition" resultType ="Emp" > select <include refid ="empColumns" > </include > from t_emp</select >
缓存 缓存是将查询到的数据进行记录,下次访问的时候会从缓存中读取,不会进行新的查询。其中一级缓存是默认开启的。
缓存查询顺序 1.先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用 2.如果二级缓存没有命中,再查询一级缓存 3.如果一级缓存也没有命中,则查询数据库 4.SqlSession关闭之后,一级缓存中的数据会写入二级缓存
一级缓存 1.一级缓存是SqlSession级别的,通过同一个SqlSession查询的数据会被缓存,下次查询相同的数据,就会从缓存中直接获取,不会从数据库重新访问 2.使一级缓存失效的四种情况:
不同的SqlSession对应不同的一级缓存 同一个SqlSession但是查询条件不同 同一个SqlSession两次查询期间执行了任何一次增删改操作 同一个SqlSession两次查询期间手动清空了缓存 二级缓存 1.二级缓存是SqlSessionFactory级别,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取 2.二级缓存开启的条件
在核心配置文件中,设置全局配置属性cacheEnabled=“true”,默认为true,不需要设置 在映射文件中设置标签 3.二级缓存必须在SqlSession关闭或提交之后有效 4.查询的数据所转换的实体类类型必须实现序列化的接口
使二级缓存失效的情况:两次查询之间执行了任意的增删改,会使一级和二级缓存同时失效 1.测试类 需要特别注意的是,之前实现的SqlSessionUtils中每次调用会new一个新的SqlSessionFactory,所有需要重新实现实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @Test public void testCache () { try { InputStream is = Resources.getReResourcesAsStream("mybaits-config.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().buid(is); SqlSession sqlSession1 = sqlSessionFactory.openSession(true ); CacheMapper mapper1 = sqlSession1.getMapper(CacheMapper.class); System.out.println(mapper1.getEmpByEid(1 )); sqlSession1.close(); SqlSession sqlSession2 = sqlSessionFactory.openSession(true ); CacheMapper mapper2 = sqlSession2.getMapper(CacheMapper.class); System.out.println(mapper2.getEmpByEid(1 )); sqlSession2.close(); } catch (IOException e) { e.printStackTrace(); } }
2.相关配置
在mapper配置文件中添加的cache标签可以设置一些属性 eviction属性:缓存回收策略 1.LRU(Least Recently Used) – 最近最少使用的:移除最长时间不被使用的对象。 2.FIFO(First in First out) – 先进先出:按对象进入缓存的顺序来移除它们。 3.SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。 4.WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。 5.默认的是 LRU
flushInterval属性:刷新间隔,单位毫秒 1.默认情况是不设置,也就是没有刷新间隔,缓存仅仅调用语句(增删改)时刷新
1.代表缓存最多可以存储多少个对象,太大容易导致内存溢出
1.true:只读缓存;会给所有调用者返回缓存对象的相同实例。因此这些对象不能被修改。这提供了很重要的性能优势。 2.false:读写缓存;会返回缓存对象的拷贝(通过序列化)。这会慢一些,但是安全,因此默认是false
第三方缓存EHCache 1.导入相关依赖
1 2 3 4 5 6 7 8 9 10 11 12 <dependency > <groupId > org.mybatis.caches</groupId > <artifactId > mybatis-ehcache</artifactId > <version > 1.2.1</version > </dependency > <dependency > <groupId > ch.qos.logback</groupId > <artifactId > logback-classic</artifactId > <version > 1.2.3</version > </dependency >
2.创建配置文件(名字必须叫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:\atguigu\ehcache" /> <defaultCache maxElementsInMemory="1000" maxElementsOnDisk="10000000" eternal="false" overflowToDisk="true" timeToIdleSeconds="120" timeToLiveSeconds="120" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"> </defaultCache > </ehcache >
3.设置二级缓存的类型 在mapper配置文件中设置缓存的类型
1 <cache type ="org.mybatis.caches.ehcache.EhcacheCache" />
4.加入logback日志 存在SLF4J时,作为简易日志的log4j将失效,此时我们需要借助SLF4J的具体实现logback来打印日志。创建logback的配置文件logback.xml,名字固定,不可改变
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"?> <configuration debug ="true" > <appender name ="STDOUT" class ="ch.qos.logback.core.ConsoleAppender" > <encoder > <pattern > [%d{HH:mm:ss.SSS}] [%-5level] [%thread] [%logger] [%msg]%n</pattern > </encoder > </appender > <root level ="DEBUG" > <appender-ref ref ="STDOUT" /> </root > <logger name ="com.atguigu.crowd.mapper" level ="DEBUG" /> </configuration >
逆向工程 1.正向工程:先创建Java实体类,由框架负责根据实体类生成数据库表。Hibernate是支持正向工程的 2.逆向工程:先创建数据库表,由框架负责根据数据库表,反向生成如下资源:
Java实体类 Mapper接口 Mapper映射文件 实现步骤 1.导入相关的依赖
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 <dependencies > <dependency > <groupId > org.mybatis</groupId > <artifactId > mybatis</artifactId > <version > 3.5.9</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.27</version > </dependency > <dependency > <groupId > log4j</groupId > <artifactId > log4j</artifactId > <version > 1.2.17</version > </dependency > </dependencies > <build > <plugins > <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.mchange</groupId > <artifactId > c3p0</artifactId > <version > 0.9.2</version > </dependency > <dependency > <groupId > mysql</groupId > <artifactId > mysql-connector-java</artifactId > <version > 8.0.27</version > </dependency > </dependencies > </plugin > </plugins > </build >
2.创建核心配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?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 ="" /> </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 ="" /> </mappers > </configuration >
3.创建逆向工程的配置文件(文件名必须是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 38 39 <?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 ="MyBatis3Simple" > <jdbcConnection driverClass ="com.mysql.cj.jdbc.Driver" connectionURL ="jdbc:mysql://localhost:3306/mybatis" userId ="root" password ="123456" > </jdbcConnection > <javaModelGenerator targetPackage ="com.atguigu.mybatis.pojo" targetProject =".\src\main\java" > <property name ="enableSubPackages" value ="true" /> <property name ="trimStrings" value ="true" /> </javaModelGenerator > <sqlMapGenerator targetPackage ="com.atguigu.mybatis.mapper" targetProject =".\src\main\resources" > <property name ="enableSubPackages" value ="true" /> </sqlMapGenerator > <javaClientGenerator type ="XMLMAPPER" targetPackage ="com.atguigu.mybatis.mapper" targetProject =".\src\main\java" > <property name ="enableSubPackages" value ="true" /> </javaClientGenerator > <table tableName ="t_emp" domainObjectName ="Emp" /> <table tableName ="t_dept" domainObjectName ="Dept" /> </context > </generatorConfiguration >
4.执行maven中的MBG插件中的generate目标
QBC Query By Criteria根据条件来查询,其中必须要使用MyBatis3配置才支持生成example
1.selectByExample:按条件查询,需要传入一个example对象或者null;如果传入一个null,则表示没有条件,也就是查询所有数据 2.example.createCriteria().xxx:创建条件对象,通过andXXX方法为SQL添加查询添加,每个条件之间是and关系 3.example.or().xxx:将之前添加的条件通过or拼接其他条件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @Test public void testMBG () { try { InputStream is = Resources.getReResourcesAsStream("mybaits-config.xml" ); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().buid(is); SqlSession sqlSession = sqlSessionFactory.openSession(true ); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); List<Emp> list = mapper.selectByExample(null ); EmpExample example = new EmpExample(); example.createCriteria().andEmpNameEqualTo("张三" ).andAgeCreaterThanOrEqualsTo(20 ); list.forEach(emp -> Ststem.out.println(emp)); } catch (IOException e) { e.printStackTrace(); } }
修改的情况 1.updateByPrimaryKey:通过主键进行数据修改,如果某一个值为null,也会将对应的字段改为null 2.updateByPrimaryKeySelective():通过主键进行选择性数据修改,如果某个值为null,则不修改这个字段
分页插件 在SQL语句中,分页的相关参数如下 1.index:当前分页的索引 2.pageSize:每页显示的条数 3.pageNum:当前页的页码 4.index=(pageNum-1)*pageSize
使用方法 1.添加相关依赖
1 2 3 4 5 6 <dependency > <groupId > com.github.pagehelper</groupId > <artifactId > pagehelper</artifactId > <version > 5.2.0</version > </dependency >
2.在MyBatis的核心配置文件进行相关配置
1 2 3 4 <plugins > <plugin interceptor ="com.github.pagehelper.PageInterceptor" > </plugin > <plugins >
3.使用方法 在查询功能之前使用PageHelper.startPage(int pageNum, int pageSize)开启分页功能
pageNum:当前页的页码 pageSize:每页显示的条数 1 2 3 4 5 6 7 8 9 10 11 12 @Test public void testPageHelper () throws IOException { InputStream is = Resources.getResourceAsStream("mybatis-config.xml" ); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is); SqlSession sqlSession = sqlSessionFactory.openSession(true ); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); PageHelper.startPage(1 ,4 ); List<Emp> emps = mapper.selectByExample(null ); emps.forEach(System.out::println); }
相关数据 1.直接输出的方式
1 2 3 4 5 6 7 8 9 10 11 12 13 @Test public void testPageHelper () throws IOException { InputStream is = Resources.getResourceAsStream("mybatis-config.xml" ); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is); SqlSession sqlSession = sqlSessionFactory.openSession(true ); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); Page<Object> page = PageHelper.startPage(1 , 4 ); List<Emp> emps = mapper.selectByExample(null ); System.out.println(page); }
2.使用PageInfo 在查询获取list集合之后,使用PageInfopageInfo = new PageInfo<>(Listlist, intnavigatePages)获取分页相关数据
list:分页之后的数据 navigatePages:导航分页的页码数 1 2 3 4 5 6 7 8 9 10 11 12 @Test public void testPageHelper () throws IOException { InputStream is = Resources.getResourceAsStream("mybatis-config.xml" ); SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder(); SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBuilder.build(is); SqlSession sqlSession = sqlSessionFactory.openSession(true ); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); PageHelper.startPage(1 , 4 ); List<Emp> emps = mapper.selectByExample(null ); PageInfo<Emp> page = new PageInfo<>(emps,5 ); System.out.println(page); }
常用数据 1.pageNum:当前页的页码 2.pageSize:每页显示的条数 3.size:当前页显示的真实条数 4.total:总记录数 5.pages:总页数 6.prePage:上一页的页码 7.nextPage:下一页的页码 8.isFirstPage/isLastPage:是否为第一页/最后一页 9.hasPreviousPage/hasNextPage:是否存在上一页/下一页 10.navigatePages:导航分页的页码数 11.navigatepageNums:导航分页的页码,[1,2,3,4,5]