2022-09-19 张宁杰 第六小组 mybatis(2)
如果我们导入依赖出现错误如何处理?
- 到我们的仓库中删除下载好的jar包,然后重新下载
resultType:
- 结果集的自动映射,必须写,对象属性名和查询结果的列名必须对应上
parameterType:
- 参数类型,可以自动解析,可以写不写,如果是我们自定义的引用数据类型,建议写上全类名
@Param的含义
List<User> selectUserByIds(@Param("ids") List<Integer> ids);
使用注解来给参数命名@Param("命名")
dao层方法传入参数的两种策略
1、传入User对象,传入一个参数。
场景:User类中的属性非常多,id,username,password,realname,profile,gender.....
User user = new User(null,"admin","123456",null,null,null,null
2、传入username,password两个参数,规范要求,如果需要传入多个参数,参数个数不要超过3个
- 当我们的mapper接口传入多个参数的时候,尽量保证类型的统一。
- 参数的类型要么是java内置的数据类型,包装器类型或者是String,要么是集合
- 正例:
Integer ,String
String String
String List - 反例:
User Student
User List
Integer Student
- 如果传入的是User对象,在mapper.xml映射文件中,就必须和对象的属性名匹配
- 如果传入的是java内置的数据类型的参数,String,Integer..,如果只传一个参数,直接使用#{参数名},#{param1},#
- 如果传入的是java内置的数据类型的参数,String,Integer..,如果传入多个参数,必须使用#{paramN}.#{arg0},不能用#
- 原理(mybatis传入参数的策略,封装成了一个map集合):
Mybatis在封装参数的时候,封装成了一个Map集合,
如果传入的是User对象,value:{“username”:"admin","password":"123456","id":"1"}
【"username":"admin"】,【"password":"123456"】
如果传入的是一个内置类型的参数,字面量 【"xxxx":"admin"】
如果从传入的是多个参数,【"param1":"admin"】,【"param2":"123456"】,底层封装的map集合,arg0和arg1。起了个默认的别名叫param1,param2
如果传入的是集合,【"param1":"{1,2,3,4,5}"】
dao层中的方法:
User selectUserByUsernameAndPassword(@Param("username") String username,@Param("password") String password);
- 结论:如果需要传入多个参数,我们希望在mapper.xml中和传入对象一样使用参数的名直接赋值,#{username},使用@Param注解起名,最好还是通过传入对象来解决问题!!!!
测试类规范
- 测试类的类名,以目标类开头,以Test结尾
- 测试类中的测试方法,尽量以目标方法的方法名结尾,test目标方法名
mybatis配置文件部分标签
-
typeAliases标签
-
<!-- 别名:各类起别名,给实体类起别名 --> <!-- 注意事项:typeAliases必须放在environments上面 --> <typeAliases> <!-- <typeAlias type="com.jsoft.entity.User" alias="user"></typeAlias>--> <!-- 当前包下的所有的实体类都是以类名的首字母小写来设置别名 --> <!-- 场景:当前包下的某个类不想以类名小写来当做别名 --> <package name="com.jsoft.entity"/> </typeAliases>
-
dataSource标签,从外部文件读取相应的配置
-
<!-- 引入外部的资源文件 --> <properties resource="db.properties"></properties> <dataSource type="com.jsoft.datasource.DruidDataSourceFactory"> <!-- property标签中的name属性只需要按照druid的命名规则命名即可 --> <property name="druid.driverName" value="${druid.driverName}"/> <property name="druid.url" value="${druid.url}"/> <property name="druid.username" value="${druid.username}"/> <property name="druid.password" value="${druid.password}"/> </dataSource> <!--资源文件--> package com.jsoft.datasource; import com.alibaba.druid.pool.DruidDataSource; import org.apache.ibatis.datasource.DataSourceFactory; import javax.sql.DataSource; import java.util.Properties; public class DruidDataSourceFactory implements DataSourceFactory { private Properties properties; @Override public void setProperties(Properties properties) { // 直接把mybatis-config中的参数封装成properties this.properties = properties; } @Override public DataSource getDataSource() { DruidDataSource druidDataSource = new DruidDataSource(); druidDataSource.configFromPropety(properties); return druidDataSource; } }
-
settings标签,mybatis的其他配置文件,此标签要放在properties下
-
<settings> <!-- 数据库的字段名以下划线命名的自动转换成小驼峰 --> <setting name="mapUnderscoreToCamelCase" value="true"/> <!-- 日志实现:记录发生过的事情,主要是记录在本地,后期运营维护,配置文件log4j.properties中 --> <setting name="logImpl" value="STDOUT_LOGGING"/> </settings>
数据库表(实体类)关系
- 一对一、一对多
多表查询的问题
-
使用SQL99语法
-
sql语句应该使用左连接还是内连接?
- 无论主表中的数据是否和从表有关联关系,主表中的数据都需要显示出来(主从关系),此时不使用内连接
- 只要有关联的数据,必须用inner join
- 如果两张表中的数据都是有关联的(并列关系),推荐使用inner join,效率高于left join
-
resultMap:自定义结果集映射
-
一对一
- 第一种方式
-
<resultMap id="employeeResult" type="employee"> <id property="id" column="eid"></id> <result property="name" column="ename"></result> //dept的封装策略 <association property="dept" javaType="dept"> <id property="id" column="did"></id> <result property="name" column="dname"></result> </association> </resultMap> <select id="findEmployeeById" resultMap="employeeResult"> select e.id eid,e.name ename,d.id did,d.name dname from employee e inner join dept d on e.did = d.id where e.id = #{id} </select>
- 第二种方式(分页查询)
-
id->员工->did->部门
-
<resultMap id="employeeResult" type="employee"> <!--如果列名和类中的属性名相同,id,result可以省略的--> <!-- 分步查询,dept的封装策略,前提条件,在对应的对象的mapper接口中,恰好有要查询的数据 --> <association property="dept" javaType="dept" select="com.jsoft.dao.DeptMapper.findDeptById" column="did"></association> </resultMap> <select id="findEmployeeById" resultMap="employeeResult"> select id,name,did from employee where id = #{id} </select> <!--这种写法不规范,因为在DeptMapper中可能有一个根据部门号查询数据的方法,最好不要这样书写--> <select id="findDeptById" resultType="dept"> select id,name from dept where id = #{id} </select> <!--这块代码应该存在于DeptMapper中-->
一对多
-
did->部门->did->员工
-
<!--在EmployeeMapper.xml中--> <select id="getEmpByDid" resultType="com.jsoft.entity.Employee"> select id,name,did from employee where did = #{did} </select> <!--在DeptMapper.xml中--> <resultMap id="deptEmpResult" type="dept"> <id property="id" column="did"></id> <result property="name" column="dname"></result> <collection property="employees" ofType="employee" select="com.jsoft.dao.EmployeeMapper.getEmpByDid" column="id"> </collection> </resultMap> <select id="findAllDepts" resultMap="deptEmpResult"> select id,name from dept </select>
-
一级缓存:sqlSession级别的缓存
- 执行流程:第一次发起查询sql查询用户id为3的用户,先去缓存中查看是否有id为3的用户,如果没有,再去数据库中查询,
- 如果查到了,则把这条记录放进缓存中。
- 如果session执行了插入,更新,删除操作,以及缓存会被清空
- mybatis是默认开启一级缓存
- 一级缓存失效的情况:
* 1、sqlSession不同
* 2、当sqlSession对象相同,查询的数据不同。
* 3、当sqlSession对象相同,两次查询之间进行插入,修改,删除的操作
* 4、当sqlSession对象相同,两次查询之间手动清除了一级缓存
二级缓存:mapper级别的缓存,接口级别的。
- 多个sqlSession去操作同一个mapper的sql语句,多个sqlSession可以共用二级缓存,所得到的数据会存在二级缓存中
- 二级缓存是跨sqlSession的
- 二级缓存相比一级缓存范围更大。多个sqlSession可以共享一个二级缓存
- 当session关闭时,会把数据提交到二级缓存
- 查询时,从二级缓存中获取到的是数据的镜像,并不是真实的数据
-
<!-- 开启二级缓存 --> <!-- eviction:缓存满了的淘汰机制 1.LRU:最近最少使用的,最长时间不适用的 2.FIFO:先进先出 3.SOFT:软引用,基于垃圾回收器状态的软引用规则 4.WEAK:弱引用,基于垃圾回收机状态的弱引用规则 flushInterval:刷新时间间隔,单位毫秒 size:缓存最多可以存多少个对象,一般情况下1024个,不宜设置过大 redOnly:只读,默认是false,不允许修改缓存 --> <cache eviction="LRU" flushInterval="10000" ></cache>