多表查询
一对一查询
案例:用户和订单,一个订单对应一个用户,这里采用的是mapper代理的方式进行测试
第一步
根据需求写出对应的sql语句
-- 采用内连接查询的方式,通过order的外键id查询出对应的user的id
select * from orders o,user u WHERE o.uid = u.id
第二步
编写对应实体类和dao层的方法
第三步
一对一映射关系,resultMap表示该查询结果的返回类型,一个result对应一个属性和一个表字段。当实体类中包含某个对象属性时,我们采用的是association标签,之后的属性为javaType。
注:sqlsessionfactory:SqlSessionFactory是MyBatis的关键对象,它是个单个数据库映射关系经过编译后的内存镜像.SqlSessionFactory对象的实例可以通过SqlSessionFactoryBuilder对象类获得,而SqlSessionFactoryBuilder则可以从XML配置文件或一个预先定制的Configuration的实例构建出SqlSessionFactory的实例.每一个MyBatis的应用程序都以一个SqlSessionFactory对象的实例为核心。同时SqlSessionFactory也是线程安全的,SqlSessionFactory一旦被创建,应该在应用执行期间都存在。在应用运行期间不要重复创建多次,建议使用单例模式.SqlSessionFactory是创建SqlSession的工厂。
sqlsession:SqlSession是MyBatis的关键对象,是执行持久化操作的对象,类似于JDBC中的Connection.它是应用程序与持久层之间执行交互操作的一个单线程对象,也是MyBatis执行持久化操作的关键对象。SqlSession对象完全包含以数据库为背景的所有执行SQL操作的方法,它的底层封装了JDBC连接,可以用SqlSession实例来直接执行被映射的SQL语句.每个线程都应该有它自己的SqlSession实例。SqlSession的实例不能被共享,同时SqlSession也是线程不安全的,绝对不能讲SqlSeesion实例的引用放在一个类的静态字段甚至是实例字段中.也绝不能将SqlSession实例的引用放在任何类型的管理范围中,比如Servlet当中的HttpSession对象中。使用完SqlSeesion之后关闭Session很重要,应该确保使用finally块来关闭它。
mapper:通用mapper 可以极大的方便开发人员进行ORM,提供极其方便的单表增删改查。 什么是通用mapper,一句话简单说,它就是个辅助mybatis简单的开发组件。它不是为了替代mybatis,而是让mybatis的开发更方便。
一对多查询
案例:用户和订单,一个用户对应多个订单,这里采用的是mapper代理的方式进行测试
第一步
根据需求写出对应的sql语句
-- 因为不管用户有没有订单,我们都要查询出用户信息,所以不能使用内连接查询,要使用左/右外连接进行查询
-- 因为查询出来的结果有两个id属性值,无法区分,所以给o.id取个别名
select u.*,o.id oid,o.orderTime,o.total from user u left join orders o on o.uid = u.id
第二步
编写对应实体类和dao层的方法
第三步
一对一映射关系,resultMap表示该查询结果的返回类型,一个result对应一个属性和一个表字段。当实体类中包含某个对象属性时,我们采用的是collection标签,之后的属性为ofType。
一对多查询
案例:用户和订单,一个用户对应多个订单,这里采用的是mapper代理的方式进行测试
第一步
根据需求写出对应的sql语句
-- 因为有中间表的关系,这里不用取别名,先左外连接查询出用户关联的角色的id,再通过角色的id查询出相应角色信息。两次左外连接查询
select * from user u left join sys_user_role ur on u.id = ur.userid
left join sys_role r on r.id = ur.roleid
第二步
编写对应实体类和dao层的方法
第三步
一对一映射关系,resultMap表示该查询结果的返回类型,一个result对应一个属性和一个表字段。当实体类中包含某个对象属性时,我们采用的是collection标签,之后的属性为ofType。
基于注解开发
@Insert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result ⼀起使⽤,封装多个结果集
@One:实现⼀对⼀结果集封装
@Many:实现⼀对多结果集封装
一对一
一对多
多对多
注意事项:
在sqlmapconfig.xml中,我们要注意注解扫描的包不再是配置文件,要以package,或者使用mapper的class属性
缓存
一级缓存
一级缓存是在sqlsession级别上的,通过sqlsession我们可以看到唯一一个和缓存有关系的方法为cache clear->defaultsqlsession.cacheclear->executor.clearLocalCache->baseexecutor.clearLocalCache->PerpetualCache.clear->map,到这我们发现一级缓存的数据结构就是HashMap。
然后我们一一查看,发现executor里面有一个createCacheKey方法
通过query调用createCacheKey,然后在执行query
//根据 sqlSessionFactory 产⽣ session SqlSession sqlSession = sessionFactory.openSession(); UserMapper userMapper = sqlSession.getMapper(IUserMapper.class); //第⼀次查询,发出sql语句,并将查询的结果放⼊缓存中 User u1 = userMapper.selectUserById( 1 ); System.out.println(u1); //第⼆步进⾏了⼀次更新操作,sqlSession.commit() u1.setSex("⼥"); userMapper.updateUser(u1); sqlSession.commit(); //第⼆次查询,由于是同⼀个sqlSession.commit(),会清空缓存信息 //则此次查询也会发出sql语句 User u2 = userMapper.selectUserById(1); System.out.println(u2); sqlSession.close();
注意事项: 当我们增删除并且提交事务后悔清空一级缓存中的内容。
二级缓存
mybatis默认不开始二级缓存,需要在sqlmapconfig.xml和映射mapper进行配置。基于PerpetualCache类实现的,增删改并提交事务也会清空缓存。 `
映射文件 `
注解形式@CacheNamespace
`//根据 sqlSessionFactory 产⽣ session SqlSession sqlSession1 = sessionFactory.openSession(); SqlSession sqlSession2 = sessionFactory.openSession(); SqlSession sqlSession3 = sessionFactory.openSession();
IUserDao userMapper1 = sqlSession1.getMapper(IUserDao.class);
IUserDao userMapper2 = sqlSession2.getMapper(IUserDao.class);
IUserDao userMapper3 = sqlSession3.getMapper(IUserDao.class);
//第⼀次查询,发出sql语句,并将查询的结果放⼊缓存中
User u1 = userMapper1.findById(1);
sqlSession1.close();//清空一级缓存
User u2 = userMapper2.findById(1);
System.out.println(u1==u2);`
注意:
*开启了⼆级缓存后,还需要将要缓存的pojo实现Serializable接⼝,为了将缓存数据取出执⾏反序列化操 作,因为⼆级缓存数据存储介质多种多样,不⼀定只存在内存中,有可能存在硬盘中,如果我们要再取 这个缓存的话,就需要反序列化了。所以mybatis中的pojo都去实现Serializable接⼝。
mybatis中还可以配置userCache和flushCache等配置项,userCache是⽤来设置是否禁⽤⼆级缓 存的,在statement中设置useCache=false可以禁⽤当前select语句的⼆级缓存,即每次查询都会发出 sql去查询,默认情况是true,即该sql使⽤⼆级缓存*
<select id="selectUserByUserId" useCache="false" resultType="com.lagou.pojo.User" parameterType="int"> select * from user where id=#{id} </select>
设置statement配置中的flushCache="true”属性,默认情况下为true,即刷新缓存,如果改成false则 不 会刷新。使⽤缓存时如果⼿动修改数据库表中的查询数据会出现脏读。一般默认
<select id="selectUserByUserId" flushCache="true" useCache="false" resultType="com.lagou.pojo.User" parameterType="int"> select * from user where id=#{id} </select>
分页查询插件
1.导入坐标
<dependency> <groupId>com.github.pagehelper</groupId> <artifactId>pagehelper</artifactId> <version>3.7.5</version> </dependency> <dependency> <groupId>com.github.jsqlparser</groupId> <artifactId>jsqlparser</artifactId> <version>0.9.1</version> </dependency>
2.配置插件
<!--注意:分⻚助⼿的插件 配置在通⽤馆mapper之前*-->* <plugin interceptor="com.github.pagehelper.PageHelper"> <!—指定⽅⾔ —> <property name="dialect" value="mysql"/> </plugin>
3.pageinfo类提供了很多属性供我们使用。
核心配置文件标签引入顺序
核心配置文件中的 引入标签的顺序: 是由约束决定的 通过约束 【ctrl+鼠标左键】 可以进入 约束查看 按照约束规定的顺序书写就可以 顺序应该是:
1.<properties自闭合标签 resource属性/>
a. 标签作用:引入数据库的配置文件 文件名一般是:JDBC.properties
b. 属性值:配置文件路径(String) 由于配置文件一般放在src文件夹下 所以一般写成配置文件的名称
2.<settings标签>
<setting标签 name属性1 value属性1/>
<setting标签 name属性2 value属性2/>
</settings>
a. settings标签:引入插件的配置文件(相对根标签 内部可以引入多个插件)
b. setting 标签:引入单个插件的配置文件 在settings 标签内部 可以有多个
b1. name 属性:自行百度 很多属性 根据需求查找
b2. value 属性:自行百度 很多属性 根据需求查找
3.<typeAliases标签>
<typeAlias 自闭合标签 type属性 alias属性/>
<package 自闭合标签 name属性/>
</typeAliases>
a.typeAliases 标签 : 这里是对映射配置文件中的 resultType和 parameterType 起别名,为了使用便捷【相对根标签、起别名是对于自定义的javaBean类】
b.typeAlias 标签: 对于单个javaBean类 起别名
b1.type属性:从src包开始的javaBean类的路径
b2.alias属性:起的别名【一般是以类名为别名 首字母小写】
c.package 标签: 因为一般情况 我们会把所有的javaBean类 都放到bean这个包下 ,所以这里提供的这个标签是对一个包下所有的javaBean类起别名
c.name属性:从src包开始的bean包的路径
-----------------------中间间隔-----[暂时还没有配置的标签]-----------------------------
4.typeHandlers
5.objectFactory
6.objectWrapperFactory
7.reflectorFactory
----------------------中间间隔------[暂时还没有配置的标签]-----------------------------
8.<plugins标签>
<plugin标签 interceptor属性></plugin>
</plugins>
a.plugins标签:配置插件的相对根标签 由于可能会配置多个插件 所以需要plugins根标签包裹
b.plugin标签:配置单个插件的标签
b1. interceptor属性:值为需要配置的插件的路径
配置分页功能的属性值为:com.github.pagehelper.PageInterceptor 固定路径【lib直接在src下】
9.<environments标签 default属性>
<environment标签 id属性>
<transactionManager标签 type属性>
<dataSource标签 type属性>
<property标签 name属性 value属性/>
<property标签 name属性 value属性/>
<property标签 name属性 value属性/>
........
</dataSource标签>
</environment标签>
<environment标签 id属性>
<transactionManager标签 type属性>
<dataSource标签 type属性>
<property标签 name属性 value属性/>
<property标签 name属性 value属性/>
<property标签 name属性 value属性/>
........
</dataSource>
</environment>
<environments>
a. environments标签:用来引入数据库环境的配置单元根标签【可包含多个】
a1. default 属性:与 environment中的id属性相对应 使用哪个environment中的id属性 就代表使用哪个配置
b. environment 标签:单个数据库环境引入的根标签
b1. id属性:每一个属性的唯一标识 通过id属性的值 来找到使用的是哪个配置
c. transactionManager标签:确定事务管理模式的标签
c1. type属性:确定使用哪个事务管理模式 一共两个值:
JDBC:正常的commit和rollback 方式
MANAGE:百度查具体是什么意思
d. dataSource 标签:规定使用何种数据源的标签(连接池,第三方容器等等)【相对根标签】
d1. type属性:具体确定数据源使用何种模式配置 一共三个值:
UNPOOLED:百度查含义
POOLED:使用连接池
JNDI:其他框架提供的容器
e. property 标签:配置数据源的单个配置标签 【其实就是JDBC所需要的的配置 包含:驱动配置,数据库地址,登录账号,密码等等】
e1.name:与JDBC的配置文件中的key一一对应 每个key对应一条property 标签语句
e2.value:底层获取 JDBC配置文件 对应key的值 格式是: ${变量名} 变量名为了代码的可读性一般和key是一样的
-----------------------中间间隔-----[暂时还没有配置的标签]-----------------------------
10.databaseIdProvider
-----------------------中间间隔-----[暂时还没有配置的标签]-----------------------------
11.<mappers 根标签>【基于配置文件sql语句来实现 使用这个Mappers标签中的格式】
<mapper标签 resource属性>
</mappers>
a. mappers标签 :引入映射文件(sql语句的配置文件)的根标签
b. mapper标签:mapper标签 一次有且只能有一个 使用哪一个Mapper接口 就配置哪个相对应的Mapper
resource:引入单个映射文件的标识:与每一个映射文件自定义文件名相同【需要加后缀】
11.<mappers 根标签> 【基于注解配置sql语句 使用这个Mappers标签中的格式】
<package标签 name属性/>
</mappers>
a.mappers标签:解释同上
package属性:值是 你的数据访问层的哪个接口所在的是哪个包 需要注意的是:这里是包路径而不是文件路径 而且包路径可以不用那么精确
比如 准确包路径是: com.xxx.mapper 写成 com.xxx也可以 写成 com也可以【底层通过遍历找到】