Mybatis应用手册-独家焦点
重学mybatis框架,汇成应用手册,整理mybatis中用法且附相关实例,方便日常回顾目录结构:
-----------------
Github手册地址:https://github.com/xbhog/MyBatisAppManual
(资料图片)
----------------
MyaBtis基本知识点mybatis初始对应关系需要注意:
pojo类对应数据库中表中的字段。mapper接口对应的xml中的命名空间。接口中的抽象方法对应xml中的id值。最后需要引入映射文件文件:
对于增删改来说,返回值都是整形,可以默认,对于查询来说就比较复杂一些。
查询的标签select必须设置属性resultType或resultMap,用于设置实体类和数据库表的映射
关系
当查询的数据为多条时,不能使用实体类作为返回值,只能使用集合,否则会抛出异常
resultType:自动映射,用于属性名和表中字段名一致的情况resultMap:自定义映射,用于一对多或多对一或字段名和属性名不一致的情况以下使用需要注意顺序
可以使用properties标签引入properties文件。
配置的方式可以有三种:
完全在mybatis-config.xml配置中写生成xxx.properties,通过外部xml配置引入一部分在xml中配置,一部分通过外部引入实现更多的请看MyBatis官网-属性
MyBatis的使用:数据库表--> pojo -->mapperService -->mapper.xml
其中在mapper.xml中通过返回的值是对象,就需要通过
resultType/resultMap
返回结果集,如果不使用别名,那么我们需要使用类的全限定名称。
别名的使用,用的最多的为
(以包为单位,设置该包下所有的类型都拥有默认的别名(类名即别名),且类名不区分大小写)。方便,不用使用全类名了。
结束
映射器(Mappers):
既然 MyBatis 的行为已经由上述元素配置完了,我们现在就要来定义 SQL 映射语句了。 但首先,我们需要告诉 MyBatis 到哪里去找到这些语句。 在自动查找资源方面,Java 并没有提供一个很好的解决方案,所以最好的办法是直接告诉 MyBatis 到哪里去找映射文件。 你可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等
有四种实现方式:
详情查看MyBatis官网--映射器(Mappers)
我们说最后一个:
resource中的mapper,也可以以包为单位,将包下所有的映射文件引入核心配置文件
注意:此方式必须保证mapper接口和mapper映射文件必须在相同的包下
创建的时候需要在resource不支持像Java一样的使用 ,我们可以使用 xxx/xxx/xxx;
MyBatis传参的方式获取参数值的两种方式(重要) xxx表示传入的参数
${} 本质为字符串拼接:形式:**xxx**
,有sql注入的风险,单引号需要手动字符串拼接
#{}本质为占位符赋值:形式:**?**
-->**"xxx"**
若mapper接口中的方法参数为单个的字面量类型(例:String name) ,此时可以使用${}
和#{}
以任意的名称(xxx)获取参数的值,注意**${}**
-->字符串(传啥就是啥),所以需要手动加单引号.
// javaprivate void getParams(UserMapper mapper) { User aaa = mapper.getUserByParams("aaa"); System.out.println(aaa);}//mapperUser getUserByParams(@Param("username") String username);//xml
第二种情况:多个字面量传入若mapper接口中的方法参数为多个时,此时MyBatis会自动将这些参数放在一个map集合中,以arg0,arg1...为键,以参数为值;以 param1,param2...为键,以参数为值;因此只需要通过\({}和#{}访问map集合的键就可以获取相对应的值**,注意**`**\){}`需要手动加单引号 ;**
//java private void moreArgs(UserMapper mapper) { User userByName = mapper.getUserByName("xbhog"); User userByNameAndPwd = mapper.getUserByNameAndPwd("xbhog", "14646"); System.out.println(userByName + "----" + userByNameAndPwd);}//mapperUser getUserByName(String name);User getUserByNameAndPwd(String name,String pwd);//xml
相关映射:
第三种情况:map传入若mapper接口中的方法需要的参数为多个时,出来MyBatis自动创建Map,我们也可以手动创建map集合,将这些数据放在map中 ;
只需要通过${}和#{}
访问map集合的键就可以获取相对应的值,注意${}
需要手动加单引号
//javaprivate void mapArgs(UserMapper mapper) { Map map = Map.of("username","xbhog"); User user = mapper.checkLogin(map); System.out.println(user);}//mapperUser checkLogin(Map map);//xml
第四种情况:对象传入若mapper接口中的方法参数为实体类对象时
此时可以使用${}和#{}
,通过访问实体类对象中的属性名获取属性值,注意${}
需要手动加单引号
//javaprivate void insertObject(UserMapper mapper) { int i = mapper.insertUserByUser(new User(null, "aaa", "aaa")); System.out.println(i);}//mapperint insertUserByUser(User user);//xml insert into user values (null,#{name},#{pwd});
第五种情况:@param()传入需求:之前传参后接受参数的名字都是不固定的,最好我们自己给它限定名字。
对于单个或多个自变量使用@param()来确定传入的参数名****(这才是重重点)
可以通过@Param注解来标识mapper接口中方法参数名的确定名
此时,会将这些参数放在map集合中,以@Param注解的value属性值为键,以参数为值;以
param1,param2...为键,以参数为值;只需要通过${}和#{}
访问map集合的键就可以获取相对应的值,
注意${}
需要手动加单引号
//javaUser aaa = mapper.getUserByParams("aaa"); System.out.println(aaa);/*mapper:mybatis会把param中的username当作key,String后面的作为value*/User getUserByParams(@Param("username") String username);//xml
MyaBtis各种查询功能查询一个实体类或者查询一个List集合/*** 根据用户id查询用户信息 * @param id * @return */ //mapper 查询一个实体类User getUserById(@Param("id") int id); /*xml*///-------------------------------------------------------------------//mapper 查询一个List集合List getUserAll();/*xml(如果在核心配置中使用了别名,可以直接使用user,不用使用全类名)*//*上简化---核心配置mybatis-config.xml*/ /*简化 别名不区分大小写*/
查询一条/多条数据为map集合//javaprivate void getUserToMap(UserMapper mapper) { List
List版本:
//将表中的数据以map集合的方式查询,一条数据对应一个map;//若有多条数据,就会产生多个map集合,此时可以将这些map放在一个list集合中获取List> getUserToMap();//结果 [{name=世杰, id=1, pwd=123456}, {name=Update, id=3, pwd=success}, {name=xbhog, id=4, pwd=14646}, {name=ylj, id=5, pwd=111111}, {name=ylj, id=6, pwd=111111}, {name=ylj, id=7, pwd=111111}, {name=aaa, id=8, pwd=aaa}]
特殊Sql的执行(都需要使用${}
)模糊匹配://javaprivate void mohuTest(UserMapper mapper) { List xbhog = mapper.testMohu("x"); System.out.println(xbhog);}//mapperList testMohu(@Param("mohu") String mohu);//三种方式都能实现//使用#{}//select * from user where name like "%#{mohu}%"报错select * from user where name like "%?%"
批量删除://java private void batchDelete(UserMapper mapper) { int i = mapper.deleteAll("1,2,10"); System.out.println(i);}//mapperint deleteAll(@Param("ids") String ids);//xml delete from user where id in (#{ids}); //如果使用#{}--->?delete from user where id in (?); //报错,不符合mysql语法格式,所以需要使用${}//正确方式打开delete from user where id in (${ids});
动态设置表名:/** * 动态设置表名*/List getAllByTableName(@Param("tableName") String tableName);//xml,使用#{}会造成 "user" 报错,// ${} --> user//testList user = mapper.getAllByTableName("user");user.forEach(System.out::println);//resultUser(id=3, name=Update, pwd=success)...
获取自增的主键useGeneratedKeys:设置使用自增的主键
keyProperty:因为增删改有统一的返回值是受影响的行数,因此只能将获取的自增的主键放在传输的参
数user对象的某个属性中
//javaTeacherMapper mapper = sqlSession.getMapper(TeacherMapper.class);Teacher t1 = new Teacher(null, "aaa");mapper.insertDemo(t1);System.out.println(t1);//mapperint insertDemo(Teacher teacher);//xml insert into teacher values (null,#{name});
无法获取自增后的id:
数据库中的id需要设置主键且自增。
数据库中的主键一直没法自增,在设计表的选项中无法修改自增。这应该是表设计的问题。
自定义映射ResultMap解决的问题:
因为属性中命名规范和数据库中的命名规范不同(empName---emp_name);reslutType只能获取属性和数据库中相同的属性的值,所以这时候需要使用ResultMap.
若字段名和实体类中的属性名不一致,则可以通过resultMap设置自定义映射
多对一:多个员工对应一个部门
一对多:一个部门对应多个员工
所以部门中的员工实体类是List结合,员工实体类中是对象属性;
实体类:
@Data@AllArgsConstructor@NoArgsConstructorpublic class Dept { private Integer did; private String deptName; private List emps;}@Data@AllArgsConstructor@NoArgsConstructorpublic class Emp { private Integer eid; private String empName; private Integer age; private Dept dept;}
多对一:对象联表查询://javaprivate void selectTableQuery(EmpMapper mapper) { //连表查询 Emp empAllDate = mapper.getEmpAllDate(3); System.out.println(empAllDate);}/** * mapper 联表查询多对一 */Emp getEmpAllDate(@Param("eid") Integer eid);//映射文件 xml //结果Emp(eid=3, empName=王五, age=24, dept=Dept(did=3, deptName=C, emps=null))
注意的几点:dept.xxx会爆红,可忽略
resutMap id对应的是select中reslutMap;resultMap返回的类型对应的是select返回的数据;对于emp实体类中的dept需要单独将属性拿出来对应association//javaprivate void associationQuery(EmpMapper mapper) { Emp empAndDept = mapper.getEmpAndDept(3); System.out.println(empAndDept);}//mapperEmp getEmpAndDept(@Param("eid") Integer eid);//xml //结果DEBUG 06-03 15:49:36,486 ==> Preparing: select * from t_emp emp left join t_dept dept on emp.eid = dept.did where eid=?; (BaseJdbcLogger.java:137) DEBUG 06-03 15:49:36,503 ==> Parameters: 3(Integer) (BaseJdbcLogger.java:137) DEBUG 06-03 15:49:36,533 <== Total: 1 (BaseJdbcLogger.java:137) Emp(eid=3, empName=王五, age=24, dept=Dept(did=3, deptName=C, emps=null))
基于association的分步查询:先查emp,在查dept,最后综合。
//javaprivate void splitSept(EmpMapper mapper) { //分步查询 Emp empAndDeptStepOne = mapper.getEmpAndDeptStepOne(3); System.out.println(empAndDeptStepOne.getEmpName());}/**mapper * 分步查询 stepOne */Emp getEmpAndDeptStepOne(@Param("eid") Integer eid);//xml /** * 分步查询:stepTwo, */Dept getDeptSetupTwo(@Param("did") Integer did);
select:设置下一步查询点,使用绝对定位,命名空间+id值column:字段值是作为下一步的入参在mybatis-config.xml开启懒加载,
fetchType:控制延迟加载的效果,懒加载开启是前提,eager,(全部执行),lazy表示延迟加载(用到啥执行啥)DEBUG 06-03 15:54:30,048 ==> Preparing: select * from t_emp where t_emp.eid=? (BaseJdbcLogger.java:137) DEBUG 06-03 15:54:30,066 ==> Parameters: 3(Integer) (BaseJdbcLogger.java:137) DEBUG 06-03 15:54:30,098 ====> Preparing: select * from t_dept where t_dept.did=?; (BaseJdbcLogger.java:137) DEBUG 06-03 15:54:30,098 ====> Parameters: 3(Integer) (BaseJdbcLogger.java:137) DEBUG 06-03 15:54:30,123 <==== Total: 1 (BaseJdbcLogger.java:137) DEBUG 06-03 15:54:30,123 <== Total: 1 (BaseJdbcLogger.java:137) Emp(eid=3, empName=王五, age=24, dept=Dept(did=3, deptName=C, emps=null))单查:DEBUG 06-03 15:55:46,779 ==> Preparing: select * from t_emp where t_emp.eid=? (BaseJdbcLogger.java:137) DEBUG 06-03 15:55:46,796 ==> Parameters: 3(Integer) (BaseJdbcLogger.java:137) DEBUG 06-03 15:55:46,899 <== Total: 1 (BaseJdbcLogger.java:137) 王五
一对多:集合(collection)//javapublic void demoTest2() throws IOException { SqlSession sqlSession = getSqlSession(); EmpMapper mapper = sqlSession.getMapper(EmpMapper.class); Dept deptAndEmpByStepOne = mapper.getDeptAndEmpByStepOne(1); System.out.println(deptAndEmpByStepOne);}/**mapper * 通过id查询部门中的员工信息,一对多 * @param did * @return */Dept getDeptAndEmpByStepOne(@Param("did") Integer did);//xml DEBUG 06-03 16:43:46,330 ==> Preparing: select * from t_dept dept where dept.did=?; (BaseJdbcLogger.java:137) DEBUG 06-03 16:43:46,346 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) DEBUG 06-03 16:43:46,402 <== Total: 1 (BaseJdbcLogger.java:137) DEBUG 06-03 16:43:46,402 ==> Preparing: select * from t_emp emp where did = ?; (BaseJdbcLogger.java:137) DEBUG 06-03 16:43:46,403 ==> Parameters: 1(Integer) (BaseJdbcLogger.java:137) DEBUG 06-03 16:43:46,422 <== Total: 2 (BaseJdbcLogger.java:137) Dept(did=1, deptName=A, emps=[Emp(eid=1, empName=张三, age=22, dept=null), Emp(eid=4, empName=赵六, age=25, dept=null)])
需要注意的是集合采用的用collection标签
动态sql:标签的作用在我看来是对sql的一步一步的解耦,方便后续的拓展和使用。
常用的标签如下:
** **
****
forEachsql片段if与where标签if标签可通过test属性的表达式进行判断,若表达式的结果为true,则标签中的内容会执行;反之标签中
的内容不会执行。
若where标签中的if条件都不满足,则where标签没有任何功能,即不会添加where关键字若where标签中的if条件满足,则where标签会自动添加where关键字,并将条件最前方多余的and去掉User getDemo(User user); //测试User xbhog = mapper.getDemo(new User(null, "xbhog", null));System.out.println(xbhog);//结果DEBUG 06-03 20:58:12,645 ==> Preparing: select * from user WHERE name =? (BaseJdbcLogger.java:137) DEBUG 06-03 20:58:12,668 ==> Parameters: xbhog(String) (BaseJdbcLogger.java:137) DEBUG 06-03 20:58:12,758 <== Total: 1 (BaseJdbcLogger.java:137) User(id=2, name=xbhog, pwd=111111)
其中注意点:
使用****
标签只能忽略****
标签中前面的and,不能忽略后面的and如果不知道什么原因,sql正确,语法正确,对应关系、实体类和数据库都对应但是还无法查出数据,请查看MySQL对应关系和版本信息(大概率是它的锅)
trim标签
去掉
标签前面的内容,
去掉
标签后面的内容
用于去掉或添加标签中的内容
prefix:在trim标签中的内容的前面添加某些内容
prefixOverrides:在trim标签中的内容的前面去掉某些内容
如果lastName
= null,sql: ....where and age = ?(错误)
通过prefixOverrides 可以除去把首个“and”去掉
last_name=#{lastName} and age=#{age} and phone=#{phone}
suffix:在trim标签中的内容的后面添加某些内容
suffixOverrides:在trim标签中的内容的后面去掉某些内容
最后一个if条件不成立,去除最后的引号字段:suffixOverrides=“”
choose&when&otherwise标签相当于if...else if..else
foreach标签属性:
collection:设置要循环的数组或集合item:表示集合或数组中的每一个数据separator:设置循环体之间的分隔符open:设置foreach标签中的内容的开始符close:设置foreach标签中的内容的结束符主要用来批处理数据
Integer insertAllUser(@Param("users") List users); insert into user values (null,#{user.name},#{user.age},#{user.pwd}) UserMapper mapper = sqlSession.getMapper(UserMapper.class);User user1 = new User(null, "a1", 22, "aaa");User user2 = new User(null, "a2", 22, "aaa");User user3 = new User(null, "a3", 22, "aaa");List users = Arrays.asList(user1, user2, user3);Integer i = mapper.insertAllUser(users);System.out.println(i);DEBUG 06-03 22:04:15,713 ==> Preparing: insert into user values (null,?,?,?) , (null,?,?,?) , (null,?,?,?) (BaseJdbcLogger.java:137) DEBUG 06-03 22:04:15,736 ==> Parameters: a1(String), 22(Integer), aaa(String), a2(String), 22(Integer), aaa(String), a3(String), 22(Integer), aaa(String) (BaseJdbcLogger.java:137) DEBUG 06-03 22:04:15,779 <== Updates: 3 (BaseJdbcLogger.java:137) 3
需要注意的是,在获取user中的属性的时候,一定要使用xxx.属性名的格式,否则找不到对应。
Integer deleteAllUser(@Param("userIds") List userIds); delete from user where id=#{userId} //另一种写法 delete from user where id in #{userId} List userIds = Arrays.asList(10, 11, 12);Integer integer = mapper.deleteAllUser(userIds);
sql片段sql片段,可以记录一段公共sql片段,在使用的地方通过include标签进行引入
name,age,pwd
缓存:官网-缓存
mybatis有一级缓存和二级缓存,并且支持使用第三方缓存,
一级缓存规则:SqlSession级别的
,就是通过同一个参数使用sqlsession查询出来的数据才会放到一级缓存中。
二级缓存规则:SqlSessionFactory级别
,通过同一个SqlSessionFactory创建的SqlSession查询的结果会被缓存;此后若再次执行相同的查询语句,结果就会从缓存中获取。
二级缓存必须在SqlSession关闭或提交之后有效,一级二级缓存在中间出现增删改mybatis都会清除缓存(失效)中的数据。
查询顺序:
先查询二级缓存,因为二级缓存中可能会有其他程序已经查出来的数据,可以拿来直接使用。如果二级缓存没有命中,再查询一级缓存如果一级缓存也没有命中,则查询数据库SqlSession关闭之后,一级缓存中的数据会写入二级缓存MyBatis的逆向工程
尚硅谷-mybatis
关键词: