嵌套查询(了解)
Mybatis嵌套查询 介绍:将多表查询的复杂sql语句拆分多个单表查询,再由mybatis框架进行嵌套组合
- 优点:减少sql复杂性
- 缺点:需要编写多次sql和多个配置,代码麻烦。故一般不推荐使用
简单示例:
* 需求:查询一个订单,与此同时查询出该订单所属的用户 1v1 # 关联查询 select * from orders o left join user u on o.uid=u.id where o.id=1; # 嵌套查询 将一条sql语句能完成的事情拆分成多条单表查询去完成 #1 查询订单号为1的订单 #2 在根据查询的订单的uid去查用户表 #3 mybatis进行组装(映射文件中组装)因最终要返回的是订单 所以去订单映射文件中组装
一对一(多对一)
需求:查询一个订单,与此同时查询出该订单所属的用户
xml映射文件
<?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="cn.test.mapper.OrdersMapper"> <resultMap id="ordersResultMap" type="orders"> <id column="id" property="id"></id> <result column="ordertime" property="ordertime"></result> <result column="address" property="address"></result> <!--mybatis框架嵌套查询(一对一映射) association标签 property:要封装的对象属性名 select:数据来源的方法名 column:方法需要的参数(uid) --> <association property="user" select="cn.test.mapper.UserMapper.findUserById" column="uid"> </association> </resultMap> <!-- 查询当前订单 --> <select id="findOrderById" parameterType="int" resultMap="ordersResultMap"> select * from orders where id=#{id} </select> </mapper>
<?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="cn.test.mapper.UserMapper"> <!-- 查询当前用户 --> <select id="findUserByUId" parameterType="int" resultType="user"> select * from user where id=#{id} </select> </mapper>
一对多
需求:查询一个用户,与此同时查询出该用户具有的订单
xml映射文件:
<?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="cn.test.mapper.UserMapper"> <resultMap id="userResultMap" type="user"> <id column="id" property="id"></id> <result column="username" property="username"></result> <result column="birthday" property="birthday"></result> <result column="sex" property="sex"></result> <result column="address" property="address"></result> <!-- 嵌套查询(一对多的映射) collection标签 property;要封装的对象集合属性 ofType:泛型类型 select:数据的来源方法 column:方法需要的参数数据(用户的id) --> <collection property="orders" ofType="Orders" select="cn.test.mapper.OrdersMapper.findBylist" column="id"> </collection> <!-- 查询当前用户 --> </resultMap> <select id="findUserById" parameterType="int" resultMap="userResultMap"> select * from user where id=#{id} </select> </mapper>
<?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="cn.test.mapper.OrdersMapper"> <!--查询多个订单--> <select id="findBylist" parameterType="int" resultType="Orders"> select * from orders where uid=#{id} </select> </mapper>
多对多(由二个一对多组成)
需求:查询用户同时查询出该用户的所有角色
xml映射文件:
<?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="cn.test.mapper.UserMapper"> <resultMap id="userResultMap" type="user"> <id column="id" property="id"></id> <result column="username" property="username"></result> <result column="birthday" property="birthday"></result> <result column="sex" property="sex"></result> <result column="address" property="address"></result> <!-- 嵌套查询(一对多的映射) collection标签 property;要封装的对象集合属性 ofType:泛型类型 select:数据的来源方法 column:方法需要的参数数据(用户的id) --> <collection property="roleList" ofType="role" select="cn.test.mapper.RoleMapper.findRoles" column="id"> </collection> </resultMap> <!--查询当前用户--> <select id="findUserById" parameterType="int" resultMap="userResultMap"> select * from user where id=#{id} </select> </mapper>
<?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="cn.test.mapper.RoleMapper"> <resultMap id="RoleResultMap" type="role"> <id column="id" property="id"></id> <result column="role_name" property="roleName"></result> <result column="role_desc" property="roleDesc"></result> </resultMap> <!--查询当前用户的角色--> <select id="findRoles" resultMap="RoleResultMap" parameterType="int"> SELECT * FROM user_role ur INNER JOIN role r ON ur.rid=r.id WHERE ur.uid=#{id} </select> </mapper>
调用存储过程
在mapper文件中可以使用statementType标记使用什么的对象操作SQL语句。
statementType:标记操作SQL的对象
取值说明:
STATEMENT:直接操作sql,不进行预编译,获取数据。$—Statement
PREPARED:预处理,参数,进行预编译,获取数据。#—PreparedStatement。默认
CALLABLE:执行存储过程。CallableStatement
示例:
- 创建insert_user存储过程(MySql)
CREATE PROCEDURE insert_user(OUT u_id INTEGER,IN u_name VARCHAR(20),IN u_sex VARCHAR(20),IN u_age INTEGER) BEGIN INSERT INTO t_user (name,sex,age) VALUES (u_name,u_sex,u_age); SET u_id=LAST_INSERT_ID(); END
在UserMapper.xml中调用insert_user存储过程
<!-- 添加用户 --> <insert id="addUser" parameterType="com.po.User" statementType="CALLABLE"> {call insert_user(#{id,mode=OUT,jdbcType=INTEGER},#{name,mode=IN},#{sex,mode=IN},#{age,mode=IN})} </insert>
2.创建deleteUser存储过程(MySql)
CREATE PROCEDURE deleteUser(IN u_id INTEGER) BEGIN DELETE FROM t_user WHERE id=u_id; END
在UserMapper.xml中调用deleteUser存储过程
<!-- 删除用户 --> <delete id="deleteUser" parameterType="Integer" statementType="CALLABLE"> {call deleteUser(#{id,mode=IN})} </delete>
3.创建updateUser存储过程(MySql)
CREATE PROCEDURE updateUser(IN u_id INTEGER,IN u_name VARCHAR(20),IN u_sex VARCHAR(20),IN u_age INTEGER) BEGIN UPDATE t_user SET name=u_name,sex=u_sex,age=u_age WHERE id=u_id; END
在UserMapper.xml中调用updateUser存储过程
<!-- 更新用户 --> <update id="updateUser" parameterType="user" statementType="CALLABLE"> {call updateUser(#{id,mode=IN},#{name,mode=IN},#{sex,mode=IN},#{age,mode=IN})} </update>
4.创建getUserById存储过程(MySql)
CREATE PROCEDURE getUserById(IN u_id INTEGER) BEGIN SELECT id,name,sex,age FROM t_user WHERE id=u_id; END
在UserMapper.xml中调用getUserById存储过程
<!-- 根据id查询用户 --> <select id="getUserById" parameterType="Integer" resultType="user" statementType="CALLABLE"> {call getUserById(#{id,mode=IN})} </select>
拓展了解
Mybatis 核心文件概述
1)environments标签
数据库环境配置
1. 其中,事务管理器(transactionManager)类型有两种:
JDBC:
这个配置就是直接使用了JDBC 的提交和回滚设置,由mybatis自己手动控制事务
MANAGED:
这个配置几乎没做什么。它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期。
例如:mybatis与spring整合后,事务交给spring容器管理。
2. 其中,数据源(dataSource)常用类型有二种:
UNPOOLED:
这个数据源的实现只是每次被请求时打开和关闭连接。
POOLED:
这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来。使用的是mybatis自带的
2)properties标签
第三方属性配置
实际开发中,习惯将数据源的配置信息单独抽取成一个properties文件
<properties resource="jdbc.properties"></properties> <environments default="mysql"> <!--mysql数据库环境--> <environment id="mysql"> <!-- 使用JDBC类型事务管理器 --> <transactionManager type="JDBC"></transactionManager> <!-- 数据源(连接池)配置 POOLED--> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"></property> <property name="url" value="${jdbc.url}"></property> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> </dataSource> </environment> </environments>
3)typeAliases标签
实体类型别名配置
<typeAliases> <!--指定一个实体类和对应的别名--> <!--<typeAlias type="com.test.domain.User" alias="user"></typeAlias>--> <!--指定当前包下所有的实体设置别名 默认: 别名(类名) --> <package name="com.test.domain"></package> </typeAliases>
4)mappers标签
映射关系配置(加载映射配置文件)
<!-- 加载指定的src目录下的映射文件 --> <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
核心配置文件标签顺序
Mybatis 的 API 概述
1)Resources
专门用于加载mybatis核心配置文件,返回的 io流
InputStream in = Resources.getResourceAsStream("SqlMapConfig.xml");
2)SqlSessionFactoryBuilder
专门用于生产SqlSessionFactory工厂对象
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(in);
3)SqlSessionFactory
一个项目只有一个工厂对象,生产sqlSession对象
// 需要手动提交事务,DML语句才会持久化到数据库中 SqlSession openSession(); // 【推荐】 // 设置是否开启自动提交,如果设置为true,开启自动提交事务 SqlSession openSession(boolean autoCommit); 参数说明 true:每一条DML类型语句都会自动提交事务 false(默认值):需要手动提交事务
4)SqlSession
是mybatis核心对象,可以对数据库进行CRUD基本操作
// 方法 <T> T selectOne(String statement, Object parameter); <E> List<E> selectList(String statement, Object parameter); int insert(String statement, Object parameter); int update(String statement, Object parameter); int delete(String statement, Object parameter); // 事务 void commit(); void rollback();
MyBatis 延迟加载(懒加载)
Mybatis的延迟加载是针对嵌套查询而言的,是指在进行查询的时候先只查询最外层的SQL,对于内层SQL将在需要使用的时候才查询出来。
使用延迟加载的场景:一对多、多对多
不推荐使用延迟加载的场景:一对一(使用 立即加载)
特别注意:延迟加载是基于 嵌套查询 组合使用
1)局部延迟加载
* 需要手动在每一个select相关标签中单独配置 <association>、<collection> fetchType="lazy" 懒加载 fetchType="eager" 立即加载
2)全局延迟加载
lazyLoadingEnabled
:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载,特定关联关系中可通过设置 fetchType
属性来覆盖该项的开关状态。默认值为false。
<!-- 在核心配置文件SqlMapConfig.xml,设置全局参数 --> <settings> <!--开启懒加载--> <setting name="lazyLoadingEnabled" value="true"/> </settings>
若是pringboot集成,在yaml配置文件中配置开启延迟加载。
mybatis: configuration: lazy-loading-enabled: true #开启延时加载开关
注意:如果全局配置了延迟加载,那么一对一也会起作用
可以为一对一设置局部的立即加载,因为局部优先级高于全局延迟加载
<association property="user" javaType="User" column="uid" select="com.itheima.mapper.UserMapper.findById" fetchType="eager"> </association>
3)指定触发延迟加载的方法配置
lazyLoadTriggerMethods
默认情况下仅仅支持自动将 equals,clone,hashCode,toString
这几个方法定义为延迟加载的加载触发方法。
* 在SqlMapConfig.xml核心配置文件,设置全局参数 <settings> <!-- 如果将 Person 的 doLazyLoadingNow()方法加入这个列表中, 则调用 doLazyLoadingNow()方法将会导致 Person 上的所有延迟加载属性的关联对象被执行加载。 --> <setting name="lazyLoadTriggerMethods" value="doLazyLoadingNow,equals,clone,hashCode,toString"/> </settings>
若是springboot集成,在yaml配置文件中配置
mybatis: configuration: lazy-loading-enabled: true # 开启延时加载开关 aggressive-lazy-loading: false # 将积极加载改为消极加载(即按需加载),默认值就是false # 指定触发延迟加载的方法 lazy-load-trigger-methods: "doLazyLoadingNow,equals,clone,hashCode,toString"